More Related Content Similar to LIFULL HOME'S「かざして検索」リリースの裏側 (20) More from Takuro Hanawa (11) LIFULL HOME'S「かざして検索」リリースの裏側2. ⾃⼰紹介
! 塙 拓朗
🏢 株式会社LIFULL
iOSエンジニア
(AWS, GCP, Firebase, )
🎧 ⾳楽が好き
takuro.hanawa
9. 建物の特徴と範囲を捉える
物体認識(Object Detection)
Ren, Shaoqing, et al. "Faster R-CNN: Towards real-time object detection with region proposal networks." Advances in neural information processing systems. 2015.
Redmon, Joseph, et al. "You only look once: Unified, real-time object detection." arXiv preprint arXiv:1506.02640 (2015)
Liu, Wei, et al. "SSD: Single Shot MultiBox Detector." arXiv preprint arXiv:1512.02325 (2015)
物体の分類(Classification)とその範囲を予測する
12. YOLOのモデルを作成する時に
必要なもの
GPUインスタンス
• Ubuntu 16.04 LTS, NVIDIA Tesla K80
フレームワーク
• darknet: https://github.com/AlexeyAB/darknet
(Required: CUDA 9.1, OpenCV 3.4.0)
データセット
• 画像データ, ラベルデータ(Class&BoundingBox)
コンパイラの相性的にUbuntu
(Linux GCC>=4.9)がとっても楽だった🙆
14. フレームワークのインストール
$ sudo dpkg -i cuda-repo-ubuntu1604_9.1.*_amd64.deb
$ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/
cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
$ sudo apt update
$ sudo apt install cuda-9-1
$ echo 'export PATH=/usr/local/cuda-9.1/bin:${PATH}' >> ~/.bashrc
$ echo 'export LD_LIBRARY_PATH=/usr/local/cuda-9.1/lib64:${LD_LIBRARY_PATH}' >>
~/.bashrc
CUDAのインストール
パスを通す
NIVIDIAから取ってきてインストール
15. darknetのインストール
フレームワークのインストール
$ git clone git@github.com:AlexeyAB/darknet.git
$ cd darknet && vim Makefile
GPU=1
CUDNN=0
OPENCV=1
GitHubからクローン(AlexeyABがフォークしたものが/)
MakefileをGPUの設定に変更する(OpenCVは任意)
$ sudo make
コンパイルする
virtualenvなどでpythonのバージョンごとに
環境を切り替えられるようにしておくと🙆
18. データセットを作る
ラベルデータ
bicycle
truck
dog
custom.names (クラスの情報)
0 0.162500 0.381250 0.203571 0.654167
1 0.839286 0.422917 0.235714 0.570833
2 0.428571 0.414583 0.214286 0.545833
img001.txt (範囲の情報)
img001.jpg [class index] [x] [y] [width] [height]
path/to/data/img108.jpg
path/to/data/img132.jpg
...
test.txt (テスト⽤のデータのパス)
path/to/data/img001.jpg
path/to/data/img002.jpg
...
train.txt (トレーニング⽤のデータのパス)
20. データセットを作る
YOLO⽤のラベルフォーマットが出⼒できる 👏
• OpenLabeling: https://github.com/Cartucho/OpenLabeling
• LabelImg: https://github.com/tzutalin/labelImg
PASCAL VOC形式なので変換が必要(だけど使いやすい)
• React Label: https://rectlabel.com/
トレーニング⽤とテスト⽤のデータセットに分けるスクリプト
• https://timebutt.github.io/static/how-to-train-yolov2-to-detect-custom-objects/
おすすめラベルデータ作成ツール
21. データセットを作る
設定ファイルを作る
[net]
max_batches = 10000 #
batch = 64 # 1
subdivisions = 8 # 1 (GPU ) (batch/subdivisions)
angle = 0 #
saturation = 1.5 #
exposure = 1.5 #
hue=.1 #
learning_rate = 0.001 #
policy = steps #
steps = 400000,450000 #
scales = .1,.1 #
[region]
classes = 3 #
[convolutional]
filters = 30 # filters = (classes + 5) * 5
tiny-yolo-custom.cfg (学習の進め⽅の設定の必要そうなところを抜粋)
学習効率を上げるなら
学習精度を上げるなら
22. 学習を開始する
darknet
├── backup
│ ├── custom_1000.weights
│ ├── custom_final.weights
│ ├── custom.backup
│ ...
├── cfg
│ ├── tiny-yolo-custom.cfg
│ ...
├── darknet
├── data
│ ├── custom.data
│ ├── custom.names
│ ├── custom
│ ... ├── train.txt
│ ├── test.txt
│ ├── img001.jpg
│ ├── img001.txt
│ ...
...
darknetのディレクトリ構成
先ほど作ったデータセットと設定ファイル
_{batch}.weight:
_final.weight:
.backup:
定期的に出⼒されるモデル
最後に出⼒されるモデル
現在学習中のモデル
classes = 3
train = data/custom/train.txt
valid = data/custom/test.txt
names = data/custom.names
backup = backup/
custom.data (データの格納先)
23. 開始 🎉
$ ./darknet detector train cfg/custom.data cfg/tiny-yolo-custom.cfg darknet19_448.conv.23
※ darknet19_448.conv.23 は重みの初期値。転移学習させる場合やバックアップから開始する場合はここを変える
https://pjreddie.com/media/files/darknet19_448.conv.23
24. 学習ログの⾒⽅
489: 0.371429, 0.913391 avg, 0.001000 rate, 2.270937 seconds
batchesごとのログ出⼒
• 489 : 現在の反復回数
cfgで設定したmax_batchesの値まで⾏う
• 0.371429 : 総損失
• 0.913391 avg : 平均損失
主にこっちを⾒て0.00Xまで下がれば学習完了でいい
ある段階から上がってしまう(過学習になる)ことがあるので
データセットを増やしたり設定ファイルの修正を⾏う
• 0.0010000 rate : cfgで設定したleaning_rate
損失がこの値を下回った場合にパラメータの更新をする
• 2.270937 seconds : この処理にかかった時間
25. 学習ログの⾒⽅
Region Avg IOU: 0.253506, Class: 1.000000, Obj: 0.200767, No Obj: 0.300631,
Avg Recall: 0.633333, count: 3
subdivisionごとのログ出⼒
• Region Avg IOU : subdivision内の全ての画像のIOUの平均
IOU(Union over Intersection)とは範囲を含めた予測の正答率
• Avg Recall : このcount内の平均再現率(recall/count)
再現率(recall)とは正解データの中で予測が正解だった⽐率
• count : subdivision内の検出されるべきオブジェクトの数
データセットの⾃分で囲ったラベルの数の合計
• Class, Obj, No Obj : 不明 🙇
https://en.wikipedia.org/wiki/Jaccard_index
26. 学習モデルを試す
$ ./darknet detector test cfg/custom.data cfg/tiny-yolo-custom.cfg backup/tiny-
yolo-custom.backup test.jpg
layer filters size
...
Loading weights from backup/tiny-yolo-custom.backup...
seen 32
Done!
test.jpg: Predicted in 0.006078 seconds.
dog: 78%
bicycle: 44%
...
適当な画像でテストしてみる 🐶
学習途中でもbackupやナンバリングされたweightファイル
でテストできるのでログを⾒ながら定期的にテストする
😇
30. アプリの実装は簡単 🤗
1. ARSessionからキャプチャー画像を取得 🤳
(AR使わないならAVCaptureSessionで良い)
2. VNImageRequestHandlerに画像を渡す 🧠
3. 予測値をMatthijs Hollemans様のお知恵を借りて
画⾯表⽰に使える値(String, CGRect)に変換する 👼
4. あとはいつものViewやLayerで描画 👏
Matthijs Hollemans: http://matthijshollemans.com/
iOS+Machine Learning, Deep LearningのコンサルしてたりRay WenderlichのGitHubのオーガナイザーだったりすごい⼈
32. Visionのリクエストで処理する
// Using VNCoreMLModel.
let vision = try VNCoreMLModel(for: mlmodel)
// Generates Vision's request handler.
DispatchQueue.global().async({ in
let handler = VNImageRequestHandler(cvPixelBuffer: buffer, orientation: .right)
let request = VNCoreMLRequest(model: vision, completionHandler: { request, _ in
guard let observations = request.results as? [VNCoreMLFeatureValueObservation],
let features = observations.first?.featureValue.multiArrayValue else { return }
// Computing bounding boxes(class, rect, score) for YOLO.
let predictions = YOLO.computeBoundingBoxes(from: features)
DispatchQueue.main.async({ in
// Show bounding boxes.
})
})
request.imageCropAndScaleOption = .scaleFill
// Requests to the vision.
try handler.perform([request])
})
• VNCoreMLModelを使うことでMLModelで処理するI/Oの画像サイズ
(モデルを作成した時の416x416)や回転などの変換処理が不要になる
33. Visionのリクエストで処理する
• まだObject Detectionに適したVNObservationクラスはないので
VNCoreMLFeatureValueObservation(回帰分析⽤の特徴データ)から
⾃前でクラス/範囲/スコアに変換する(computeBoundingBoxesのところ)
参考: http://machinethink.net/blog/yolo-coreml-versus-mps-graph/
// Using VNCoreMLModel.
let vision = try VNCoreMLModel(for: mlmodel)
// Generates Vision's request handler.
DispatchQueue.global().async({ in
let handler = VNImageRequestHandler(cvPixelBuffer: buffer, orientation: .right)
let request = VNCoreMLRequest(model: vision, completionHandler: { request, _ in
guard let observations = request.results as? [VNCoreMLFeatureValueObservation],
let features = observations.first?.featureValue.multiArrayValue else { return }
// Computing bounding boxes(class, rect, score) for YOLO.
let predictions = YOLO.computeBoundingBoxes(from: features)
DispatchQueue.main.async({ in
// Show bounding boxes.
})
})
request.imageCropAndScaleOption = .scaleFill
// Requests to the vision.
try handler.perform([request])
})
38. 学習モデルはダウンロードする
• MLModelにcompileModel(at:)というMLModelをコンパイル
(.mlmodelc)してくれるメソッドがある
let compiledUrl = try MLModel.compileModel(at: modelUrl)
let model = try MLModel(contentsOf: compiledUrl)
• 通常MLModelをXCodeからバンドルするとそれをコンパイルして
⼊出⼒をラップしたクラスを⽣成してくれるが、ダウンロードする
場合は⾃前で⽤意する
// Class for model loading and prediction
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
class CustomModel {
var model: MLModel
init(contentsOf url: URL) throws {
self.model = try MLModel(contentsOf: url)
}
...
}
39. • ⼀度ダウンロード/コンパイルしたモデルはドキュメントディレクトリ
などに保存して以後それを使う
// Find the app support directory
let fileManager = FileManager.default
let appSupportDirectory = try fileManager.url(
for: .applicationSupportDirectory,
in: .userDomainMask, appropriateFor:
compiledUrl, create: true)
// Create a permanent URL in the app support directory
let permanentUrl = appSupportDirectory
.appendingPathComponent(compiledUrl.lastPathComponent)
do {
// If the file exists, replace it. Otherwise, copy the file to the destination.
if fileManager.fileExists(atPath: permanentUrl.absoluteString) {
_ = try fileManager.replaceItemAt(permanentUrl, withItemAt: compiledUrl)
} else {
try fileManager.copyItem(at: compiledUrl, to: permanentUrl)
}
} catch {
print("Error during copy: (error.localizedDescription)")
}
https://developer.apple.com/documentation/coreml/core_ml_api/downloading_and_compiling_a_model_on_the_user_s_device
学習モデルはダウンロードする
41. 学習モデルのダイエット
Half-Precision Model
• CoreML Tools (v0.7.0以上)をインストールしてUtilクラスの
convert_neural_network_spec_weights_to_fp16メソッドに
mlmodelファイルを渡すだけ
• ただし精度は少なからず落ちるので動作検証が必要
import coremltools
# Load a model, lower its precision, and then save the smaller model.
model_spec = coremltools.utils.load_spec(‘./custom.mlmodel')
model_fp16_spec =
coremltools.utils.convert_neural_network_spec_weights_to_fp16(model_spec)
coremltools.utils.save_spec(model_fp16_spec, ‘custom_16.mlmodel')
convert.py
CoreML Tools: https://github.com/apple/coremltools
42. 学習モデルのダイエット
Lower Precision Model
• CoreML Tools (2.0b1)をインストールして
models.neural_network.quantization_utilsを使う
(pip install coremltools==2.0b1)
• quantize_weightsの引数に渡せるのはモデルと変換するbit数と量⼦化
の⽅法(線形: linear, k平均: kmeans)
※ YOLOのモデルだと8bit以下のkmeansの変換ができない 😭
import coremltools
from coremltools.models.neural_network.quantization_utils import *
model = coremltools.models.MLModel('./custom.mlmodel')
# Quantize model weight to 8bit using linear method.
linear_quantized_model = quantize_weights(model, 8, "linear")
linear_quantized_model.save('./custom_8_linear.mlmodel')
# Compare models.
compare_models(model, linear_quantized_model, './data')
convert.py
43. 学習モデルのダイエット
Lower Precision Model
• 検証⽤のメソッドcompare_modelsに両モデルとテスト画像が⼊って
いるディレクトリを渡すと性能⽐較ができる
※ YOLOのモデルだと性能の情報が全然出ない 😭
$ python convert.py
Input name(s) and shape(s):
image : (C,H,W) = (3, 416, 416)
Neural Network compiler 0: 100 , name = convolution2d_1, output shape :
(C,H,W) = (16, 416, 416)
...
Analyzing 6 images
Running Analysis this may take a while …
Output grid:
--------------
SNR: 29.3645888188 +/- 2.67386378668
PSNR: 45.9721819368 +/- 11.3184398991