Help us understand the problem. What is going on with this article?

CoreML変換時の謎のInvalid dst shapeを倒す

More than 1 year has passed since last update.

謎のWarning

coremltoolsでKerasでは完璧に動くモデルを変換するとこのようなWarningが出ることがあります。

RuntimeWarning: You will not be able to run predict() on this Core ML model.Underlying exception message was: Error compiling model: "compiler error:  Invalid dst shape1 x 62 x 18 x 1->1->1 x 1 x 12 x 0 x 62 x ".

※ 最後のshapeは人によって違うと思います。

書いてあるとおりに読むと、「なんやかんや問題があって predict() が実行できません」と書いてありますが、これWarningなので実際mlmodelは生成されます。

ただ、CoreMLは学習用ではなく推論用と今の所認識しているので predict() が使えないmlmodelは何のために存在するのか理解できません。WarningではなくErrorにするべきでは。。。

何のエラーなのか

以前私の記事にも書いたことがありますが、coremltoolsで変換したmlmodelには同一のレイヤーを使い回すshared layerが存在すると結構不安定な挙動をします。(というか多分coremltoolsが色々対応できていない。)

それの一環(?)で、以下のようなモデルを変換するとKerasやTensorflowでは完璧に動作するのにmlmodelへ変換するとshapeがバグります。

input1 = Input((3,3,3))
input2 = Input((3,3,3))
input3 = Input((3,3,3))
input4 = Input((3,3,3))

conv1 = Conv2D(3, kernel_size=(2, 2), dilation_rate=(1, 1), strides=1, padding='same')
conv2 = Conv2D(3, kernel_size=(2, 2), dilation_rate=(1, 1), strides=1, padding='same')

concat = [Concatenate()([conv1(input1), conv2(input2)]), Concatenate()([conv1(input3), conv2(input4)])]

model = Model(inputs = [input1, input2, input3, input4], outputs = concat)

例示のためあまり意味はないモデルですが、conv1,conv2のレイヤーを2箇所に共有で持たせた上で2ずつ入力に適用しConcatenateでつなぎ合わせたものを出力しています。

Concatenateはaxis=-1、すなわちch次元(第3次元)で接続されるので出力は 3,3,6 のサイズが2つの出力になるはずです。Kerasではもちろんその通りです。

ではcoremltoolsでconvertしてみます。
その結果がこちら。(coreMLではch次元が1次元目に来るのでshapeの順序が変わっているのは問題ではありません。)

0 : input_1, <keras.engine.topology.InputLayer object at 0x122c67a90>
1 : input_2, <keras.engine.topology.InputLayer object at 0x122c67b00>
2 : input_3, <keras.engine.topology.InputLayer object at 0x122c67dd8>
3 : input_4, <keras.engine.topology.InputLayer object at 0x122c67f28>
4 : conv2d_1_0, <keras.layers.convolutional.Conv2D object at 0x122c67b38>
5 : conv2d_1_1, <keras.layers.convolutional.Conv2D object at 0x122c67b38>
6 : conv2d_2_0, <keras.layers.convolutional.Conv2D object at 0x122c67b70>
7 : conv2d_2_1, <keras.layers.convolutional.Conv2D object at 0x122c67b70>
8 : concatenate_1, <keras.layers.merge.Concatenate object at 0x122c732b0>
9 : concatenate_2, <keras.layers.merge.Concatenate object at 0x122c732e8>
Input name(s) and shape(s): 
4 : (C,H,W) = (3, 3, 3) 
test1 : (C,H,W) = (3, 3, 3) 
test2 : (C,H,W) = (3, 3, 3) 
test3 : (C,H,W) = (3, 3, 3) 
Neural Network compiler 0: 100 , name = conv2d_1_0, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 1: 100 , name = conv2d_1_1, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 2: 100 , name = conv2d_2_0, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 3: 100 , name = conv2d_2_1, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 4: 320 , name = concatenate_1, output shape : (C,H,W) = (12, 3, 3) <- !!!!?!?!?!??!???!?!?!??!??!??????
Neural Network compiler 5: 320 , name = concatenate_2, output shape : (C,H,W) = (12, 3, 3) <- !!!!?!?!?!??!???!?!?!??!??!??????

最後の2行を見てください。
突然C(ch)のサイズが6ではなく12になっています。今回のモデルはここで出力にして終わっているのでshapeに関するエラーも起きていませんが、もちろんこの後ろにReshapeなどのshapeの総数の変更を許さない系のレイヤーが来れば冒頭のWarningが起こるわけです。

正直この問題はKerasやTensorflowの書き方が悪いのではなく、ほぼ間違いなくcoremltoolsのバグだと思います。

ただ、これに関しては以前から私も問題視していてPRやIssueで色々訴えかけていますが、あんまりApple自身にやる気が無いのか、coremltoolsのGithubの盛り上がりが著しく低く数ヶ月単位でPRやIssueが動かないような事もザラですので、あんまり改善を期待して待っているのもよろしくなさそうです。

じゃあどうすれば良いのか

一応coremltoolsのソースを読むと、怪しいのがshared layerに関する箇所だという事は把握できました。その上でどうにかsharedじゃない状況を作りshapeについての操作を迂回する事で解決に至った感じですが、この経緯を知らない人からすると謎すぎる解法になります。

input1 = Input((3,3,3))
input2 = Input((3,3,3))
input3 = Input((3,3,3))
input4 = Input((3,3,3))

conv1 = Conv2D(3, kernel_size=(2, 2), dilation_rate=(1, 1), strides=1, padding='same')
conv2 = Conv2D(3, kernel_size=(2, 2), dilation_rate=(1, 1), strides=1, padding='same')

concat = [Concatenate()([Reshape((3,3,3))(Flatten()(conv1(input1))), Reshape((3,3,3))(Flatten()(conv2(input2)))]), Concatenate()([Reshape((3,3,3))(Flatten()(conv1(input3))), Reshape((3,3,3))(Flatten()(conv2(input4)))])]

model = Model(inputs = [input1, input2, input3, input4], outputs = concat)

なにが変わったか分かりますか?

変わったのは1行だけ、concat =の行です。
Flattenで平らにした後Reshapeで元の形状に戻すという本来何の意味もないことをしています。しかしながらこのモデルをcoremltoolsで変換すると…

0 : input_1, <keras.engine.topology.InputLayer object at 0x11d31fba8>
1 : input_2, <keras.engine.topology.InputLayer object at 0x11d31fc18>
2 : input_3, <keras.engine.topology.InputLayer object at 0x11d31fef0>
3 : input_4, <keras.engine.topology.InputLayer object at 0x11d31fc50>
4 : conv2d_1_0, <keras.layers.convolutional.Conv2D object at 0x11d31fd68>
5 : conv2d_1_1, <keras.layers.convolutional.Conv2D object at 0x11d31fd68>
6 : conv2d_2_0, <keras.layers.convolutional.Conv2D object at 0x11d31feb8>
7 : conv2d_2_1, <keras.layers.convolutional.Conv2D object at 0x11d31feb8>
8 : flatten_1, <keras.layers.core.Flatten object at 0x11d3293c8>
9 : flatten_2, <keras.layers.core.Flatten object at 0x11d329438>
10 : flatten_3, <keras.layers.core.Flatten object at 0x11d3294a8>
11 : flatten_4, <keras.layers.core.Flatten object at 0x11d329518>
12 : reshape_1, <keras.layers.core.Reshape object at 0x11d329588>
13 : reshape_2, <keras.layers.core.Reshape object at 0x11d3295c0>
14 : reshape_3, <keras.layers.core.Reshape object at 0x11d3295f8>
15 : reshape_4, <keras.layers.core.Reshape object at 0x11d329630>
16 : concatenate_1, <keras.layers.merge.Concatenate object at 0x11d329668>
17 : concatenate_2, <keras.layers.merge.Concatenate object at 0x11d3296a0>
Input name(s) and shape(s): 
4 : (C,H,W) = (3, 3, 3) 
test1 : (C,H,W) = (3, 3, 3) 
test2 : (C,H,W) = (3, 3, 3) 
test3 : (C,H,W) = (3, 3, 3) 
Neural Network compiler 0: 100 , name = conv2d_1_0, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 1: 100 , name = conv2d_1_1, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 2: 100 , name = conv2d_2_0, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 3: 100 , name = conv2d_2_1, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 4: 301 , name = flatten_1, output shape : (C,H,W) = (27, 1, 1) 
Neural Network compiler 5: 301 , name = flatten_2, output shape : (C,H,W) = (27, 1, 1) 
Neural Network compiler 6: 301 , name = flatten_3, output shape : (C,H,W) = (27, 1, 1) 
Neural Network compiler 7: 301 , name = flatten_4, output shape : (C,H,W) = (27, 1, 1) 
Neural Network compiler 8: 300 , name = reshape_1, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 9: 300 , name = reshape_2, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 10: 300 , name = reshape_3, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 11: 300 , name = reshape_4, output shape : (C,H,W) = (3, 3, 3) 
Neural Network compiler 12: 320 , name = concatenate_1, output shape : (C,H,W) = (6, 3, 3) <- YES!!!!!!!!!
Neural Network compiler 13: 320 , name = concatenate_2, output shape : (C,H,W) = (6, 3, 3) <- YES!!!!!!!!!

めでたし、めでたし。

な訳あるか:frowning2:

coreml界隈もう少し盛り上がってくれませんかね。。。

Mco7777
似非人工知能ではなく、ちゃんと人工知能だからこそできる事をしたい
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away