Hatena::ブログ(Diary)

ぱたへね

2017-03-20

[]kerasでカスタムレイヤーのシリアライズを行う

Keras SSDをjsonに変換し、読みこもうとするとエラーがでる。

https://github.com/rykov8/ssd_keras

Traceback (most recent call last):
  File "/home/natu/proj/myproj/cocytus/cocytus/cocytus.py", line 70, in <module>
    main(sys.argv)
  File "/home/natu/proj/myproj/cocytus/cocytus/cocytus.py", line 54, in main
    compiler = CocytusCompiler(config)
  File "/media/natu/data/proj/myproj/cocytus/cocytus/compiler/compiler.py", line 33, in __init__
    self.model = model_from_json(json_string, custom_objects={"Normalize": Normalize, "PriorBox": PriorBox})
  File "/usr/local/lib/python3.5/dist-packages/keras/models.py", line 325, in model_from_json
    return layer_module.deserialize(config, custom_objects=custom_objects)
  File "/usr/local/lib/python3.5/dist-packages/keras/layers/__init__.py", line 46, in deserialize
    printable_module_name='layer')
  File "/usr/local/lib/python3.5/dist-packages/keras/utils/generic_utils.py", line 140, in deserialize_keras_object
    list(custom_objects.items())))
  File "/usr/local/lib/python3.5/dist-packages/keras/engine/topology.py", line 2370, in from_config
    process_layer(layer_data)
  File "/usr/local/lib/python3.5/dist-packages/keras/engine/topology.py", line 2339, in process_layer
    custom_objects=custom_objects)
  File "/usr/local/lib/python3.5/dist-packages/keras/layers/__init__.py", line 46, in deserialize
    printable_module_name='layer')
  File "/usr/local/lib/python3.5/dist-packages/keras/utils/generic_utils.py", line 141, in deserialize_keras_object
    return cls.from_config(config['config'])
  File "/usr/local/lib/python3.5/dist-packages/keras/engine/topology.py", line 1202, in from_config
    return cls(**config)
TypeError: __init__() missing 1 required positional argument: 'scale'

原因

model.to_json()を使った時に、カスタムレイヤーの情報がjsonに含まれていないため、復元時にエラーになる。model.to_json()を使って出力されたjsonファイルを確認してみる。

{"class_name": "Normalize", "inbound_nodes": [[["conv4_3", 0, 0, {}]]], "name": "conv4_3_norm", "config": {"name": "conv4_3_norm", "trainable": true}}

ここの"config"の所にscaleの情報が必要。

ドキュメントが見つけられなかったので、

https://github.com/fchollet/keras/blob/master/keras/layers/normalization.py

を参考にした。

対策

カスタムレイヤーにget_configを追加し、シリアライズに必要な情報を教える。

Normalize

保存すべき情報は、__init__の引数を見れば分かる。

def __init__(self, scale, **kwargs):

Normalizeの場合はscaleがあれば、層を復元できる。

   def get_config(self):
        config = {
            'scale': self.scale,
        }
        base_config = super(Normalize, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))
PriorBox

PriorBoxは少し複雑。

    def __init__(self, img_size, min_size, max_size=None, aspect_ratios=None,
                 flip=True, variances=[0.1], clip=True, **kwargs):

ここの__init__の引数に出てくる情報が必要になる。ソースを見ると、self.waxis等も保存したくなるが、それらは引数から計算できるので不要である。

    def get_config(self):
        config = {
            'img_size': self.img_size,
            'min_size': self.min_size,
            'max_size': self.max_size,
            'aspect_ratios': self.aspect_ratios,
            'variances': list(self.variances),
            'clip': self.clip
        }
        base_config = super(PriorBox, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

これを追加すれば良い。self.variancesがarrayだったので、listに変換している。

修正済みのソースがこちら。

https://gist.github.com/natsutan/ba0abee68b23bb042602ddc5a35217f4

実行結果

上の修正を加えて、model.to_json()再度実行する。情報がjsonに含まれているのが分かる。

{"name": "conv4_3_norm", "class_name": "Normalize", "config": {"name": "conv4_3_norm", "trainable": true, "scale": 20}, "inbound_nodes": [[["conv4_3", 0, 0, {}]]]}

{"name": "conv4_3_norm_mbox_priorbox", "class_name": "PriorBox", "config": {"name": "conv4_3_norm_mbox_priorbox", "max_size": null, "img_size": [300, 300], "trainable": true, "clip": true, "min_size": 30.0, "variances": [0.1, 0.1, 0.2, 0.2], "aspect_ratios": [1.0, 2, 0.5]}, "inbound_nodes": [[["conv4_3_norm", 0, 0, {}]]]}

jsonからの読み込み

まずは、カスタムレイヤーのインポートから。

from ssd_layers import Normalize
from ssd_layers import PriorBox

読み込む時には、custom_objectsの指定を忘れずに。

model = model_from_json(json_string, custom_objects={"Normalize": Normalize, "PriorBox": PriorBox})

実行結果

____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
input_1 (InputLayer)             (None, 300, 300, 3)   0                                            
____________________________________________________________________________________________________
conv1_1 (Conv2D)                 (None, 300, 300, 64)  1792                                         
____________________________________________________________________________________________________
conv1_2 (Conv2D)                 (None, 300, 300, 64)  36928                                        
____________________________________________________________________________________________________
pool1 (MaxPooling2D)             (None, 150, 150, 64)  0                                            
____________________________________________________________________________________________________
conv2_1 (Conv2D)                 (None, 150, 150, 128) 73856                                        
____________________________________________________________________________________________________
conv2_2 (Conv2D)                 (None, 150, 150, 128) 147584                                       
____________________________________________________________________________________________________
pool2 (MaxPooling2D)             (None, 75, 75, 128)   0                                            
____________________________________________________________________________________________________
conv3_1 (Conv2D)                 (None, 75, 75, 256)   295168                                       
____________________________________________________________________________________________________
conv3_2 (Conv2D)                 (None, 75, 75, 256)   590080                                       
____________________________________________________________________________________________________
conv3_3 (Conv2D)                 (None, 75, 75, 256)   590080                                       
____________________________________________________________________________________________________
pool3 (MaxPooling2D)             (None, 38, 38, 256)   0                                            
____________________________________________________________________________________________________
conv4_1 (Conv2D)                 (None, 38, 38, 512)   1180160                                      
____________________________________________________________________________________________________
conv4_2 (Conv2D)                 (None, 38, 38, 512)   2359808                                      
____________________________________________________________________________________________________
conv4_3 (Conv2D)                 (None, 38, 38, 512)   2359808                                      
____________________________________________________________________________________________________
pool4 (MaxPooling2D)             (None, 19, 19, 512)   0                                            
____________________________________________________________________________________________________
conv5_1 (Conv2D)                 (None, 19, 19, 512)   2359808                                      
____________________________________________________________________________________________________
conv5_2 (Conv2D)                 (None, 19, 19, 512)   2359808                                      
____________________________________________________________________________________________________
conv5_3 (Conv2D)                 (None, 19, 19, 512)   2359808                                      
____________________________________________________________________________________________________
pool5 (MaxPooling2D)             (None, 19, 19, 512)   0                                            
____________________________________________________________________________________________________
fc6 (Conv2D)                     (None, 19, 19, 1024)  4719616                                      
____________________________________________________________________________________________________
fc7 (Conv2D)                     (None, 19, 19, 1024)  1049600                                      
____________________________________________________________________________________________________
conv6_1 (Conv2D)                 (None, 19, 19, 256)   262400                                       
____________________________________________________________________________________________________
conv6_2 (Conv2D)                 (None, 10, 10, 512)   1180160                                      
____________________________________________________________________________________________________
conv7_1 (Conv2D)                 (None, 10, 10, 128)   65664                                        
____________________________________________________________________________________________________
zero_padding2d_1 (ZeroPadding2D) (None, 12, 12, 128)   0                                            
____________________________________________________________________________________________________
conv7_2 (Conv2D)                 (None, 5, 5, 256)     295168                                       
____________________________________________________________________________________________________
conv8_1 (Conv2D)                 (None, 5, 5, 128)     32896                                        
____________________________________________________________________________________________________
conv4_3_norm (Normalize)         (None, 38, 38, 512)   512                                          
____________________________________________________________________________________________________
conv8_2 (Conv2D)                 (None, 3, 3, 256)     295168                                       
____________________________________________________________________________________________________
pool6 (GlobalAveragePooling2D)   (None, 256)           0                                            
____________________________________________________________________________________________________
conv4_3_norm_mbox_conf (Conv2D)  (None, 38, 38, 63)    290367                                       
____________________________________________________________________________________________________
fc7_mbox_conf (Conv2D)           (None, 19, 19, 126)   1161342                                      
____________________________________________________________________________________________________
conv6_2_mbox_conf (Conv2D)       (None, 10, 10, 126)   580734                                       
____________________________________________________________________________________________________
conv7_2_mbox_conf (Conv2D)       (None, 5, 5, 126)     290430                                       
____________________________________________________________________________________________________
conv8_2_mbox_conf (Conv2D)       (None, 3, 3, 126)     290430                                       
____________________________________________________________________________________________________
conv4_3_norm_mbox_loc (Conv2D)   (None, 38, 38, 12)    55308                                        
____________________________________________________________________________________________________
fc7_mbox_loc (Conv2D)            (None, 19, 19, 24)    221208                                       
____________________________________________________________________________________________________
conv6_2_mbox_loc (Conv2D)        (None, 10, 10, 24)    110616                                       
____________________________________________________________________________________________________
conv7_2_mbox_loc (Conv2D)        (None, 5, 5, 24)      55320                                        
____________________________________________________________________________________________________
conv8_2_mbox_loc (Conv2D)        (None, 3, 3, 24)      55320                                        
____________________________________________________________________________________________________
conv4_3_norm_mbox_conf_flat (Fla (None, 90972)         0                                            
____________________________________________________________________________________________________
fc7_mbox_conf_flat (Flatten)     (None, 45486)         0                                            
____________________________________________________________________________________________________
conv6_2_mbox_conf_flat (Flatten) (None, 12600)         0                                            
____________________________________________________________________________________________________
conv7_2_mbox_conf_flat (Flatten) (None, 3150)          0                                            
____________________________________________________________________________________________________
conv8_2_mbox_conf_flat (Flatten) (None, 1134)          0                                            
____________________________________________________________________________________________________
pool6_mbox_conf_flat (Dense)     (None, 126)           32382                                        
____________________________________________________________________________________________________
conv4_3_norm_mbox_loc_flat (Flat (None, 17328)         0                                            
____________________________________________________________________________________________________
fc7_mbox_loc_flat (Flatten)      (None, 8664)          0                                            
____________________________________________________________________________________________________
conv6_2_mbox_loc_flat (Flatten)  (None, 2400)          0                                            
____________________________________________________________________________________________________
conv7_2_mbox_loc_flat (Flatten)  (None, 600)           0                                            
____________________________________________________________________________________________________
conv8_2_mbox_loc_flat (Flatten)  (None, 216)           0                                            
____________________________________________________________________________________________________
pool6_mbox_loc_flat (Dense)      (None, 24)            6168                                         
____________________________________________________________________________________________________
mbox_conf (Concatenate)          (None, 153468)        0                                            
____________________________________________________________________________________________________
pool6_reshaped (Reshape)         (None, 1, 1, 256)     0                                            
____________________________________________________________________________________________________
mbox_loc (Concatenate)           (None, 29232)         0                                            
____________________________________________________________________________________________________
mbox_conf_logits (Reshape)       (None, 7308, 21)      0                                            
____________________________________________________________________________________________________
conv4_3_norm_mbox_priorbox (Prio (None, 4332, 8)       0                                            
____________________________________________________________________________________________________
fc7_mbox_priorbox (PriorBox)     (None, 2166, 8)       0                                            
____________________________________________________________________________________________________
conv6_2_mbox_priorbox (PriorBox) (None, 600, 8)        0                                            
____________________________________________________________________________________________________
conv7_2_mbox_priorbox (PriorBox) (None, 150, 8)        0                                            
____________________________________________________________________________________________________
conv8_2_mbox_priorbox (PriorBox) (None, 54, 8)         0                                            
____________________________________________________________________________________________________
pool6_mbox_priorbox (PriorBox)   (None, 6, 8)          0                                            
____________________________________________________________________________________________________
mbox_loc_final (Reshape)         (None, 7308, 4)       0                                            
____________________________________________________________________________________________________
mbox_conf_final (Activation)     (None, 7308, 21)      0                                            
____________________________________________________________________________________________________
mbox_priorbox (Concatenate)      (None, 7308, 8)       0                                            
____________________________________________________________________________________________________
predictions (Concatenate)        (None, 7308, 33)      0                                            
====================================================================================================
Total params: 25,765,497.0
Trainable params: 25,765,497.0
Non-trainable params: 0.0
____________________________________________________________________________________________________

2017-03-18

[] SSDを最新のKerasで動かす。

思わずKerasを最新にしたら、APIがごっそり変わっていて涙目です。

https://github.com/rykov8/ssd_keras

これを動かそうとしたのですが、APIが変わってしまったために上手く動きません。

ワーニングを消したssd.pyがこちら。

https://gist.github.com/anonymous/4c3105119a233cb33926651c3ea1966c

後は、ここの議論を参考にssd_layers.pyのget_output_shape_forをcompute_output_shapeに名前を変更したら最新のKerasでも動作しました。

https://github.com/rykov8/ssd_keras/issues/60

2017-03-07

[][]自作フレームワークのデバッグ機能 その2

kerasから持ってきた3x3フィルターで、係数の並びをxとyを間違っていた。そこを修正したらばっちり一致した。

f:id:natsutan:20170307100853p:image

2017-03-06

[][]自作フレームワークのデバッグ機能

自作フレームワークでnumpy出力ができるようになった。これでmatplotlibを使って、同じデータを入れた時の1層目のコンボリューション出力を比較できる。青がKerasの出力で、赤が自作フレームワークの出力。ニューロンが反応する場所は合っているけど、計算結果が違う。

f:id:natsutan:20170307000823p:image

ようやくデバッグが進む。convolution2dが動けば、残りはさっと動くと思う。

2017-02-19

[] kerasで処理の途中の値を取り出す

俺俺DLフレームワークを作るにはKeras一択のような気がしてきました。

KerasでNNに特定のデータを入れた時に、狙った層の出力をnumpy形式で出力する方法です。やりかたはKeras FAQをみました。簡単です。

https://keras.io/ja/getting-started/faq/#_1

やってること

http://d.hatena.ne.jp/natsutan/20170212/1486862458

ここで作ったNNに一枚絵を入れて、その途中の計算結果を取り出しました。入力データはひらがなデータを入れていますが、やっていることはNNも含めてMNISTと完全に同じです。

モデルと学習結果を読み込む

学習後に保存したjsonファイルとhd5ファイルを読み込みます。ファイルの作り方はここを参照。

http://d.hatena.ne.jp/natsutan/20170212/1486862458

json_string = open('output/cnn.json', 'r').read()
model = model_from_json(json_string)
model.load_weights('output/cnn.h5')

画像の読み込み

「い」が入ったデータを読み込みます。行列の形を合わせる必要があります。255から引いている所はネガポジ反転なので、データがすでに直接入力できる形式になっていれば不要です。

# load image
images = np.empty([0, 28, 28], np.float32)
img_ori = Image.open('data/I.png')
img_gray = ImageOps.grayscale(img_ori)

img_ary = np.asarray(img_gray)
img_ary = 255 - img_ary
images = np.append(images, [img_ary], axis=0)

images = images.reshape(1, 28, 28, 1)
一枚の識別

model.predictで一枚だけ識別します。

ret = model.predict(images, 1, 1)
print(ret)

実行結果

1/1 [==============================] - 0s
[[  9.66260362e-18   1.00000000e+00   1.46393750e-16   3.83880911e-21
    1.73906418e-14   2.77851445e-13   2.60705761e-21   2.42816259e-22
    9.78005010e-10   9.94229803e-16   2.92022889e-13   2.50225903e-12
    1.87606130e-20   9.82728684e-17   4.47857970e-22   6.36410264e-16
    2.89718408e-16   9.22273868e-15   1.46329236e-20   1.10161611e-16
    3.02622735e-19   4.96133791e-18   4.64741527e-13   2.02620950e-12
    5.23028010e-11   2.72302085e-14   3.31416961e-09   8.62797342e-16
    3.77434917e-15   3.23395878e-13   8.63702926e-16   1.50429401e-18
    1.54558563e-13   6.33530894e-10   1.18054563e-24   1.71215462e-14
    1.62872005e-12   8.14889678e-20   4.88564665e-19   8.62093701e-15
    4.40518906e-17   6.88677597e-16   4.34340205e-16   1.31322590e-17
    4.82810310e-18   1.39269949e-18]]

入力した文字が「い」に対して、softamx出力の2番目が最大値になっているので識別できてますね。

中間層の出力を取り出す。

https://keras.io/ja/getting-started/faq/#_1 を参考に。

K.functionを使って、特定の層(例えばConvolution2D)の入出力を取り出す関数を作ります。その関数に対して、入力データを指定すると、それを処理した出力を受け取ることができます。numpy形式なので、そのままファイルに落とせます。

# output
get_1st_layer_output = K.function([model.layers[0].input],
                                  [model.layers[0].output])
layer_output = get_1st_layer_output([images,])
print(layer_output[0].shape)
np.save('output/convolution2d_out.npy', layer_output[0], allow_pickle=False)

全ソースコード

import numpy as np
from PIL import Image
from PIL import ImageOps
from keras.models import model_from_json
from keras import backend as K

json_string = open('output/cnn.json', 'r').read()
model = model_from_json(json_string)
model.load_weights('output/cnn.h5')

# load image
images = np.empty([0, 28, 28], np.float32)
img_ori = Image.open('data/I.png')
img_gray = ImageOps.grayscale(img_ori)

img_ary = np.asarray(img_gray)
img_ary = 255 - img_ary
images = np.append(images, [img_ary], axis=0)

images = images.reshape(1, 28, 28, 1)

# predict
ret = model.predict(images, 1, 1)
print(ret)

# output
get_1st_layer_output = K.function([model.layers[0].input],
                                  [model.layers[0].output])
layer_output = get_1st_layer_output([images,])
print(layer_output[0].shape)
np.save('output/convolution2d_out.npy', layer_output[0], allow_pickle=False)