20
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【物体検知】YOLOv4をiOS上で動かす

Posted at

はじめに

つい先日、物体検知のモデルであるYOLOの最新バージョンv4が発表されましたね!
速度を保ったまま大幅に精度を上げることができたようです!

そんなYOLOv4を今回はiOS上で動かしてみました。

環境

python=3.7.2
tensorflow=1.13.1
keras=2.3.1
coremltools=3.3

YOLOv4 の学習済みモデルを取得

こちらからダウンロードできます。

学習済みモデルの変換 Darknet →(Keras →)CoreML

Darknetで学習した or 学習済みのモデルをiOS上で動かせるように変換します。
内部では一旦 Keras のモデルに変換されてから CoreML 用のモデルに変換されています。

コンバーターはYOLOv3のものをベースにしています。(keras-yolo3/convert.py
そこで、YOLOv4 から採用された Mish Activation レイヤーに新しく対応させる必要があります。

まずはレイヤーの定義から

convert.py
class Mish(Layer):
    def __init__(self, **kwargs):
        super(Mish, self).__init__(**kwargs)
        self.supports_masking = True

    def call(self, inputs):
        return inputs * K.tanh(K.softplus(inputs))

    def get_config(self):
        config = super(Mish, self).get_config()
        return config

    def compute_output_shape(self, input_shape):
        return input_shape

このレイヤーを activation == 'Mish' の場合に適応してあげます。

convert.py
if activation == 'linear':
    all_layers.append(prev_layer)
elif activation == 'mish':
    act_layer = Mish()(prev_layer)
    prev_layer = act_layer
    all_layers.append(act_layer)
elif activation == 'leaky':
    act_layer = LeakyReLU(alpha=0.1)(prev_layer)
    prev_layer = act_layer
    all_layers.append(act_layer)

また、CoreML 側にも Mish レイヤーの情報を与える必要があります。
ちゃんと className を与えないと、Xcode側から正しく読めないみたいです。

convert.py
def convert_mish(layer):
    params = NeuralNetwork_pb2.CustomLayerParams()
    params.className = "Mish"
    params.description = "Mish Activation Layer"
    return params
convert.py
coreml_model = coremltools.converters.keras.convert(
    model, input_names='input1', image_input_names='input1', 
    output_names=['output3', 'output2', 'output1'], image_scale=1/255.,
    add_custom_layers=True,custom_conversion_functions={ "Mish": convert_mish })

convert.pyのソースコード

zsh
python3 convert.py yolov4.cfg yolov4.weights yolov4.mlmodel

実行すると、次のようなエラーが発生しますが問題ありません。yolov4.mlmodel が作成されていると思います。

You will not be able to run predict() on this Core ML model. Underlying exception message was: Error compiling model: "compiler error:  Error creating Core ML custom layer implementation from factory for layer "Mish".".
  RuntimeWarning)

iOS上で実行

今回使用したデバイスは iPhone XS, iOS 13.3 です。
作成した .mlmodel を使用するにあたって、今回こちらのリポジトリを使用させていただきました。

.mlmodel をプロジェクトフォルダに入れます

モデルを今回作成したものに切り替えます。

YOLO.swift
// let model = YOLOv3()
let model = Yolov4()

Mish レイヤーの作成

Swift 側にも Mish レイヤーを定義していきます。

Mish.swift
@objc(Mish) class Mish: NSObject, MLCustomLayer {
    // (略)
    func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws {
        for i in 0..<inputs.count {
            let input = inputs[i]
            let output = outputs[i]

            let count = input.count
            let iptr = UnsafeMutablePointer<Float>(OpaquePointer(input.dataPointer))
            let optr = UnsafeMutablePointer<Float>(OpaquePointer(output.dataPointer))

            var countAsInt32 = Int32(count)
            var one: Float = 1
            let vdspLength = vDSP_Length(count)

            vvexpf(optr, iptr, &countAsInt32)
            vDSP_vsadd(optr, 1, &one, optr, 1,vdspLength)
            vvlogf(optr, optr, &countAsInt32)
            vvtanhf(optr, optr, &countAsInt32)
            vDSP_vmul(optr, 1, iptr, 1, optr, 1, vdspLength)
        }
    }
}

後半に vvexpf とか、vDSP_vsadd とかのキモい関数が並んでいますが、数値計算などを高速に処理してくれる関数みたいで、
Accelerateと言うみたいです。Apple Document | Accelerate

ここの部分、見やすく書くと次みたいな感じになるのですが、YOLOv4の中で72回呼ばれてるということもあり、for文で1つ1つ処理してるとめっちゃ重くなっちゃうわけですね、なので配列として一気に計算させようという感じです。

for j in 0..<input.count {
    let x = input[j].floatValue
    let y = x / (1 + exp(-x))
    output[j] = NSNumber(value: y)
}

他にも GPU を使うようにしたりしています。
Mish.metalMish.swift と同じ場所に置きます。

Mish.swift
Mish.metal

実行の様子

無事に実行することができましたが、全体的にモッサリしてます。あと、精度も YOLOv3 と比べてむしろ悪くなっているような印象を受けます。

YOLOv4

v4

YOLOv3

v3

調べてみると、Mish レイヤーを無効にしている場合のCPU使用率は160%程度(おそらく最大400%)、メモリ使用量は60MBほどなのに対して、有効にしている場合のCPU使用率は30%程度、使用メモリは250MBにまで増加します。

メモリをめっちゃ使用する上に、CPUをあまり使えてないという形ですね。この辺は Swift 側のコードを最適化するのが正しそうです。

20
22
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?