2018年10月8日 星期一

把 Darknet 模型轉換成 CoreML 模型

要把 Darknet 模型轉換成 CoreML 模型,先用 Darkflow 把權重儲存成 TensorFlow PB 檔:
$ ./flow --model yolo-c3.cfg --load yolo-c3.weights --savepb
然後以 tfcoreml 做轉換。由於 tfcoreml 使用到不同的軟件版本組合,所以最好是用 Conda 之類的虛擬環境把軟件獨立出來:
$ git clone https://github.com/tf-coreml/tf-coreml.git
$ cd tf-coreml/
$ conda create --name tf-coreml python=3.6
$ source activate tf-coreml
$ pip install -e .
安裝好所需軟件版本後,下一步是正式轉換。把 darkflow/built_graph/yolo-c3.pb 拷到 tf-coreml 目錄,並進入 Python:
$ python
輸入以下 Python 程序。留意把下面「kerasModelPath」的值改為自己的 PB 檔路徑:
import tfcoreml as tf_converter
import tensorflow as tf

##----------------------------------------------------------------------------------------
##  We load the protobuf file from the disk and parse it to retrieve the unserialized graph_def
def load_graph(frozen_graph_filename):
    with tf.gfile.GFile(frozen_graph_filename, "rb") as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        
    # Then, we import the graph_def into a new Graph and return it 
    with tf.Graph().as_default() as graph:
        tf.import_graph_def(graph_def, name="")
    return graph

##----------------------------------------------------------------------------------------
##  Load Keras model
kerasModelPath = 'yolo-c3.pb'
graph = load_graph(kerasModelPath)
for op in graph.get_operations(): 
    print (op.name)

##----------------------------------------------------------------------------------------
##  Convert Keras model to Core ML model
##  output_feature_names: the output node name we get from the previouse step
##  image_input_names: CoreML allows image as the input, the only thing we need to do is to set which node is the image input node 
##  input_name_shape_dict: the input node name we get from the previous step, and check the cfg file to know the exact input shape size
##  is_bgr: the channel order is by BGR instead of RGB
##  image_scale: the weights is already normalized in the range from 0 to 1
coreml_model = tf_converter.convert(tf_model_path=kerasModelPath, mlmodel_path='yolo.mlmodel', output_feature_names=['grid'], image_input_names= ['image'], input_name_shape_dict={'image': [1, 416, 416, 3]}, is_bgr=True, image_scale=1/255.0)
完成後便會得到 tf-coreml/yolo-c3.mlmodel 模型檔。

2018年10月7日 星期日

把 Darknet 模型轉換成 Keras 模型


要把 Darknet 模型轉換成 Keras 模型,可以用 YAD2K: Yet Another Darknet 2 Keras。我發現它用的 Python, Keras, TensorFlow 用的又是不同的版本組合,所以最好是用 Conda 之類的虛擬環境把軟件獨立出來:
git clone https://github.com/allanzelener/yad2k.git
cd yad2k
conda env create -f environment.yml
source activate yad2k
訓練好的 YOLO v1 模型,亦即是 yolo-c3.cfg 及 yolo-c3.weights 檔案拷到 YAD2K 目錄,然後執行:
python yad2k.py yolo-c3.cfg yolo-c3.weights yolo-c3.h5
完成後便能看見 Keras 的 yolo-c3.h5 模型檔。

2018年10月6日 星期六

訓練 YOLO v1 模型

準備好訓練 YOLO v1 模型的工作後,下一步是製作模型結構及設定。我們將會訓練 YOLO v1 模型,把在 darknet/cfg/yolo.cfg 複製一份,改名為 yolo-c3.cfg。當中「c3」的意思是「Classes 3」,即模型能偵測三款物件。


打開 yolo-c3.cfg,把 # Testing# Training 中的行數,在最頭加入井號變成註釋;把 # Training 下的 batch 設定為 64、subdivisions 設定為 8。如果在訓練期間出現記憶體不足,可以將 batch 值調大,或把 subdivisions 值調低再試試。


在檔案最尾的 [convolutional] 中,把 filters 設定為 40。數值來自公式:
(5 + 偵測物件類別數量) x 5 = (5 + 3) x 5 = 40
並且,把 [region] 下的 classes 設定為 3。之後,生成一個 darknet/cfg/yolo-c3.names 文字檔,裡面記載物件類別的名字。我的情況是:
EDO Pack
烏龍茶
可口可樂
接著,生成一個 darknet/cfg/yolo-c3.data,內容為:
classes = 3
train = train.txt  
valid = test.txt  
names = yolo-c3.names  
backup = backup/
主要是告知 YOLO 有三個偵測類別、訓練的素材在哪、驗證的素材在哪、類別名稱在哪、訓練時的比重放在哪。還有,我們需要 YOLO 預設的權重檔「darknet19_448.conv.23」,可以在這裡下載

最後,確定好所需的檔案都齊備:
darknet/darknet19_448.conv.23
darknet/test.txt
darknet/train.txt
darknet/cfg/yolo-c3.cfg
darknet/cfg/yolo-c3.data
darknet/cfg/yolo-c3.names
在 Terminal 執行:
./darknet detector train cfg/yolo-c3.data cfg/yolo-c3.cfg darknet19_448.conv.23
根據我的經驗,在安裝 darknet, darkflow, YOLO-Annotation-Tool-master,...等工具時都會出現不同的錯誤。有的要求 Python 2.7;有的要求 Python 3.6。有時要 Keras 1.2.2;有時又要低一點的版本;這都可以用 Virtual Environment 來解決。但遇著 CUDA 及 CUDNN 版本問題則比較頭痛。在今次訓練中,需要分別用到 CUDNN 8 及 CUDNN 9,兩者只能選一個,只能用到哪個版本就安裝哪個版本。訓練原本做 100 次迭代便完成,但我的情況卻沒完沒了。只好手動停止。在 darknet/backup/ 中能找到權重檔,加上本身 yolo-c3.cfg 結構檔便能拿來做檢測。

2018年10月5日 星期五

訓練 YOLO v1 模型的準備工作


既然昨天成功用 YOLO v2 來檢測物件,午飯時再接再厲,再用準備好的素材訓練,看看能不能有把自定義物件檢測出來。


按照 How to train multiple objects in YOLOv2 using your own Dataset 的指示,一步一步訓練自己的模型。首先,當然是準備好素材:可樂 10 張、EDO 糖 19 張、烏龍茶 16 張,總共 45 張相片。


然後把它們按類別以數字目錄分成三組,放到 YOLO-Annotation-Tool-master/Images/ 目錄下。由於原本已經有 001 及 002 目錄,所以我用 010 放 EDO 糖, 011 放烏龍茶及 012 來放可口可樂。然後在 Terminal 跳轉到 YOLO-Annotation-Tool-master 目錄,以 Python 2.7 執行:
python main.py

一個工具視窗會彈出來。在 Image Dir 欄位輸入其中一個剛才生成的目錄數字,點擊「Load」載入相片。利用滑鼠點擊相片中物件的左上角,拖拉到右下角再點一下。這樣便完成一年物件的標韱工作。如果相片有多於一件同類物件,則把其他物件也一併標韱。


完成所有數字目錄的標韱後,在 YOLO-Annotation-Tool-master/Labels/ 目錄下會發現記錄了的標韱座標。


不過,這些數字還未 Normalize 到 0 至 1 的小數,所以還未能夠使用。這時,需要打開 convert.py,在 classes 中加入 "010", "011", "012";設定 mypath = "./Labels/010/";再把 cls = "005" 改為 cls = "010",執行一次:
python convert.py
然後 "011" 執行一次;再做 "012"。逐個數字目錄做。同時,在 YOLO-Annotation-Tool-master/ 會自動生成 010_list.txt, 011_list.txt 及 012_list.txt,裡面記載了標韱的相片路徑。


完成後在 YOLO-Annotation-Tool-master/Labels/output 會找到 Normalize 了的座標。接著執行:
python process.py
它會把素材以 80:20 比例分成訓練組及測試組,並生成 train.txt 及 test.txt。

2018年10月4日 星期四

YOLO v2 街頭物件檢測


經過反覆嘗試,很困難才能生成出 YOLO v2 的 mlmodel。可是,當放進 iPad 後卻沒有反應,無法檢測出物件。可能我太過心急,急於一步到位,要在 iPad 上判定物件,連模型本身是否正常運作也未知道;所以,決定先在電腦上成功讓模型運作,之後才放到 iPad 上。


於是,我走到街上拍攝幾段影片,用來實作 YOLO 檢測功能。


然後利用 Darknet 準備好的 YOLO v2 模型進行測試。效果不錯。


這四段影片以 4K 解像度拍攝,然後利用 CentOS 7 + Nvidia GTX680 + Tensorflow + Darkflow 來進行檢測,並把結果儲存成 AVI 格式。所以,以上片段並不是實時執行。檢測的指令如下:
python flow --model yolov2-tiny.cfg --load yolov2-tiny.weights --demo Video.MOV --gpu 1.0 --saveVideo

2018年10月2日 星期二

標韱 YOLO 物件


嘗試過預設的檢測模型後,接著是以自定義的物件來訓練模型。在 https://github.com/ManivannanMurugavel/YOLO-Annotation-Tool 找到了一個簡單好用的標韱工具。

我準備了三款物件共 45 張相片來小試牛刀。利用上面的工具,逐一定義相片中的物件,然後利用下面的指令進行訓練:
./darknet detector train cfg/yolo-obj.data cfg/yolo-obj.cfg darknet19_448.conv.23
可是,在沒有 GPU 的場景下,訓練了 80 小時也未能完成,最終竟然發生錯誤...。

2018年9月28日 星期五

初試 YOLO


放低了「機器學習」一段時間,又是時候進修一下。每次學習之前,我都會訂立一個成果,然後找方法達成。今次,我想用 iPad 鏡頭去辨認物件;在網上搜尋相關的做法,找到了一個較新的 YOLO 算法像乎不錯,於是嘗試一下。這篇文章對 YOLO 算法進行解說,清楚易明。

YOLO 的官方網頁是 https://pjreddie.com/darknet/yolo/。按照指示安裝好 Darknet,下載已訓練好的模型便能使用。不過,要執行 Darknet 需要花一點功夫去設定好運行環境;而我的過程不太順利。經過一輪版本問題後,終於成功輸入圖片並檢測當中的物件。我用的是 YOLO v2,預設能檢測 80 樣不同的物品。需要下載或自行製作 .cfg 及 .weights 檔。前者是定義智能網絡的結構,後者是該結構每一個節點的比重值。有了這兩組數據,便能進行偵測。

2018年9月25日 星期二

micro:bit 示範程式


早前想買一塊 BBC 的 micro:bit 來玩玩;到大阪日本橋時找到它,但最後沒有購買。昨天,女兒從中學帶回來一塊 micro:bit 及 robot:bit,於是我研究了一下,並編寫了第一個程式:

//----------------------------------------------------------------------------------------
//  micro:bit DEMO Program 01
//----------------------------------------------------------------------------------------
//  Platform: micro:bit Javascript or Blocks
//  Written by Pacess HO
//  Copyright Pacess Studio, 2018.  All rights reserved.
//----------------------------------------------------------------------------------------

let x = 0
let y = 0
let led1 = 0
let led2 = 0
let led3 = 0
let led4 = 0
let step = 0
let offset = 0
let direction = 0

led1 = 0
led2 = 50
led3 = 100
led4 = 150
step = 20
offset = 0
direction = 1

music.beginMelody(music.builtInMelody(Melodies.Birthday), MelodyOptions.OnceInBackground)
basic.forever(() => {

   offset = offset + direction
   if (offset <= 0) {
      direction = 1
   }
   if (offset >= 5 * 5 - 1) {
      direction = -1
   }

   x = offset % 5
   y = offset / 5

   led.plot(x, y)
   basic.pause(80)
   led.unplot(x, y)

   robotbit.rgb().setPixelColor(0, neopixel.hsl(led1, 50, 30))
   robotbit.rgb().setPixelColor(1, neopixel.hsl(led2, 50, 50))
   robotbit.rgb().setPixelColor(2, neopixel.hsl(led3, 50, 50))
   robotbit.rgb().setPixelColor(3, neopixel.hsl(led4, 50, 30))
   robotbit.rgb().show()

   led1 = led1 + step
   led2 = led2 + step
   led3 = led3 + step
   led4 = led4 + step
})

2018年9月24日 星期一

外幣兌換率數據


這個星期收集了不少數據,有些還沒有想到用途;然而,有些數據則會拿來作為投資趨勢分析,再加上 LINE Bot 作為訊息媒體,希望能及時通知自己關於投資的機會。其中一樣,就是外幣兌換率數據。利用定時執行的程式,去找出哪隻外幣值得買入,又或是留意有哪隻外幣應該拋售。

2018年9月23日 星期日

香港恆生指數數據


朋友介紹我玩期指。做了咁多年人,知道自己沒有橫財命,所有成就都要靠一雙手,腳踏實地去掙回來;炒股賭博不是我杯茶。不過,我絕對不介意從數學角度出發,去探求提升勝算之方法。研究是需要數據,於是花了一個小時,收集起恆生指數數據。