iOS 使用coreML — yolo model
最近有人問怎麼在iOS上使用訓練好的模型,雖然網路上已經有sampleCode了,不過可能coreMlTools更新過的關係,有點小小的不同,所以記錄一下踩坑的過程QQ。
大致上分為三步:
step1: 訓練模型(yolov3, keras),取得h5
這個我就不記錄怎麼train了,google滿多詳細的教學,可以照著做
這裡使用的是keras,如果是用pytorch或其它工具的話,本篇不適用。
step2: convert h5 file -> coreML
這邊的重點是用apple的coremltools去轉,網路上的sampleCode轉出來,跟實際結果有小小的不同,所以這邊是小小的重點。
還有一個就是trained好的模型,有些小地方還要改QQ,不能直接貼Code QQ,否則會遇到yolo_head is not defined
那為什麼要把h5轉成coreMl呢?因為iOS吃coreML,不會吃h5,而蘋果也寫了一個工具,讓我們可以把h5轉成coreML。
step3: XCode — load coreML
這裡就不多加介紹,直接放別人的網誌了XD
step1: 訓練模型(yolov3, keras),取得h5
基本上模型不會由手機端訓練,一般應該是用電腦先跑個一段時間,訓練出來的模型是一個h5的檔案。
如果只是想要練習怎麼把h5轉coreML的話,可以看下面這篇,參照quickStart取得h5檔案
基本上就是整包clone下來後,依序複製貼上下面兩程程式碼就可以取得h5了,轉出來的h5就在model_data這個資料夾裡頭。
wget https://pjreddie.com/media/files/yolov3.weights
python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5
這樣就有已經trained好的h5了
如果要自己訓練資料的話,再google看看,滿多詳細的教學可以依序完成。
step2: convert h5 file -> coreML
本文重點記錄,大概再分成2小部份:
1: how to convert h5->coreML
用的是蘋果官方的小工具,可以看到github下方有寫如何使用,基本上也是直接照著做,用virtualenv去建置環境,我一開始偷懶,不管什麼虛擬環境,直接inistall,結果各種錯誤,(像是 coreml object has no attribute ‘convert’ ),踩一堆奇怪的坑,所以建議直接依照蘋果的作法,打開終端機,依序貼指令上去。
# 安裝virtualenv
pip install virtualenv# 會建一個"mlvirtualenv"名稱的資料夾
mkdir mlvirtualenv# 路徑進入至資料夾"mlvirtualenv"
cd mlvirtualenv# 建一個虛擬環境,名稱叫作"pythonenv"
virtualenv pythonenv# 啟動叫作"pythonenv"的虛擬環境
source pythonenv/bin/activate
# 這時候你的終端機前方應該就變成(pythonenv)了
# 再貼上下指令去安裝coreMlTools
pip install -U coremltools
好了,這樣coreMlTools就裝好了
之後因為是虛擬環境,所以要再裝一次tensorflow, keras
pip install keras==2.2.4
pip install tensorflow==1.14.0
2: how to implement h5 -> coreML by coremltools
這裡分兩段記錄,一個是已經上述qqwwee已經訓練好的model;另一個則是自己訓練的要怎麼做。
字面上看起來可能有點奇怪,明明都是h5,為什麼要分成「qqwwee的」跟「自己訓練的」,不都一樣嗎?
呃,其實我也不知道…..囧>||||||
如果是自己train的,轉coreML的時候會遇到一些怪事,要額外再多寫一點code
這是qqwwee的h5,用以下python程式碼,看是要貼到記錄本,存成py檔,還是直接在終端機輸入python,再依序貼上程式碼。
# import 蘋果的小工具
import coremltools# 利用coremltools,把同層資料中,名稱為為"yolo.h5"的檔案轉型為mlModel
# 可以看到這裡用的是keras,如果你是用別的架構訓練的話,這裡不會work
model = coremltools.converters.keras.convert("./yolo.h5",
input_names='image',
image_input_names='image',
input_name_shape_dict={'image':
[None, 416, 416, 3]
},
image_scale=1/255.)# 把剛剛的model存到同層資料夾,名稱命名為"yolo.mlmodel"
model.save("./yolo.mlmodel")
如果是自己trained的模型,依照上述的作法,可能會噴出’yolo_head’ is not defined這個錯誤,所以要稍作修改,我猜應該是因為qqwwee裡頭train完是寫save_weight,而不是save_model吧(?)
# 這裡的yolo3,是qqwwee內的yolo,所以要特別注意這時候你的路徑在哪裡
from yolo3.model import yolo_eval, yolo_body, tiny_yolo_body
from keras.layers import Input
import coremltools# 最後一個參數YOUR_CLASS_NUM,是你辨識幾類,如果有80類,就是打80
model = yolo_body(Input(shape=(None, None, 3)), 3, YOUR_CLASS_NUM)# 去讀你同層資料夾中,名為"customData.h5"的檔案
model.load_weights("./customData.h5")# 這裡基本上就同剛剛的寫法了, 只是變成讀model,而不是讀路徑
mlModel = coremltools.converters.keras.convert(model,
input_names='image',
image_input_names='image',
input_name_shape_dict={'image': [None, 416, 416, 3]},
image_scale=1/255.)# 把剛剛的mlModel存到同層資料夾,名稱命名為"customData.mlmodel"
mlModel.save("./customData.mlmodel")
好了,走到這裡就完成h5 -> mlModel了
step3: XCode — load coreML
這時候把模型直接拉到Xcode裡頭,應該就會發現「蛤蛤蛤???我的output的size根本不一樣啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊(大崩潰)」
左圖輸出有3維(255x13x13),是網上sampleCode的model;
而右圖輸出有1維(255),則是用上述方法轉出來的model。
所以用網上sampleCode直接執行就會GG
還好,原sampleCode的作者,也有針對這個issue回答,只要在Yolo.swift中
原本的
let channelStride = features.strides[0].intValue
let yStride = features.strides[1].intValue
let xStride = features.strides[2].intValue改為
let channelStride = features.strides[2].intValue
let yStride = features.strides[3].intValue
let xStride = features.strides[4].intValue
也就是改index而已,因為原本的模型是拿到3維,但我們現在會拿到5維,前兩維都是1,所以往後取資料而已。
走到這裡,如果一開始用的是qqwwee的模型,現在就已經結束了,只要替換model就可以順利執行了。
那如果是自己trained的模型,還要改什麼呢?
1- 模型的分類數量
在yolo.swift中
let numClasses = 80 // 這裡改成你自己分幾類
2-對應的label
在Helpers.swift中
let labels = ["1", "2", "3", ......]
labels的元素改為你要顯示的名稱
至於他整篇sampleCode是怎麼做的呢
可以參考原文
或是這位網友有介紹原本的sampleCode大概是怎樣的作法