昨年友人より, 某コワーキングスペースの混雑度を見える化したいので, 機械学習部分を手伝ってほしいとの依頼があった.
そこで, 混雑度を測るために, 各スペースの人物検出を行い, 定員に対してどの程度の人がいるか検知することにした.
関連記事:
・コワーキングスペースの「混雑度」を検出する(1)
・コワーキングスペースの「混雑度」を検出する(2)
・コワーキングスペースの「混雑度」を検出する(3)
・コワーキングスペースの「混雑度」を検出する(4)
Jetson Nano 2GB版で人物検出を動かそうと, モデルのコンパクト化などGPUメモリ使用の削減などを検討し始めた.
その際に, SSD学習済モデルの読み込みで少し問題があり対応したので, そのメモをまとめておく.
1. 不具合内容
SSD学習済モデルの読み込みについては, ssd_kearas[1]に含まれているssd300_inference.ipynb内では2通りが記載されている.
しかし, 試してみると, 学習済モデルの読み込み方によって, 人物検出の結果が異なるという症状に出くわした.
そもそも使い方を誤っているだけかもしれないが...
(1) Build the model and load trained weights into it
# 1: Build the Keras model K.clear_session() # Clear previous models from memory. model = ssd_300(image_size=(img_height, img_width, 3), n_classes=20, mode='inference', l2_regularization=0.0005, scales=[0.1, 0.2, 0.37, 0.54, 0.71, 0.88, 1.05], # The scales for MS COCO are [0.07, 0.15, 0.33, 0.51, 0.69, 0.87, 1.05] aspect_ratios_per_layer=[[1.0, 2.0, 0.5], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5, 3.0, 1.0/3.0], [1.0, 2.0, 0.5], [1.0, 2.0, 0.5]], two_boxes_for_ar1=True, steps=[8, 16, 32, 64, 100, 300], offsets=[0.5, 0.5, 0.5, 0.5, 0.5, 0.5], clip_boxes=False, variances=[0.1, 0.1, 0.2, 0.2], normalize_coords=True, subtract_mean=[123, 117, 104], swap_channels=[2, 1, 0], confidence_thresh=0.5, iou_threshold=0.45, top_k=200, nms_max_output_size=400) # 2: Load the trained weights into the model. # TODO: Set the path of the trained weights. ## weights_path = 'path/to/trained/weights/VGG_VOC0712_SSD_300x300_iter_120000.h5' weights_path = './trained_models/ssd300_person_epoch-10_loss-2.7150_val_loss-2.9814.h5' model.load_weights(weights_path, by_name=True) # 3: Compile the model so that Keras won't complain the next time you load it. adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0) ssd_loss = SSDLoss(neg_pos_ratio=3, alpha=1.0) model.compile(optimizer=adam, loss=ssd_loss.compute_loss)
[結果]
Predicted boxes: class conf xmin ymin xmax ymax [[ 1. 0.99 93.73 170.42 153.57 261.07] [ 1. 0.99 228.08 85.82 261.07 138.3 ]]
(2) Load a trained model
# TODO: Set the path to the `.h5` file of the model to be loaded. # model_path = 'path/to/trained/model.h5' model_path = './trained_models/ssd300_person_epoch-10_loss-2.7150_val_loss-2.9814.h5' # We need to create an SSDLoss object in order to pass that to the model loader. ssd_loss = SSDLoss(neg_pos_ratio=3, n_neg_min=0, alpha=1.0) K.clear_session() # Clear previous models from memory. model = load_model(model_path, custom_objects={'AnchorBoxes': AnchorBoxes, 'L2Normalization': L2Normalization, 'DecodeDetections': DecodeDetections, 'compute_loss': ssd_loss.compute_loss})
[結果]
Predicted boxes: class conf xmin ymin xmax ymax [[ 0.02 0.98 0.3 1.11 -1.21 1.4 0.8 0.36 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.12 0.88 -1.64 1.08 -1.53 1.23 0.83 0.36 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.3 0.7 1.79 -0.88 -1.34 1.28 0.78 0.38 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.01 0.99 0.14 -0.93 -1.26 1.06 0.8 0.38 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.12 0.88 -1.71 -1. -1.45 0.97 0.83 0.38 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.16 0.84 -0.17 -2.41 -1.12 1.21 0.8 0.41 0.14 0.14 0.1 0.1 0.2 0.2 ] [ 0.37 0.63 -0.47 1.46 -0.92 -1.38 0.82 0.34 0.14 0.28 0.1 0.1 0.2 0.2 ] [ 0.28 0.72 -0.53 -0.58 -0.98 -1.56 0.82 0.39 0.14 0.28 0.1 0.1 0.2 0.2 ] [ 0.18 0.82 0.6 2.61 0.02 2.03 0.39 0.66 0.2 0.2 0.1 0.1 0.2 0.2 ] [ 0.14 0.86 0.51 1.86 -1.35 0.56 0.39 0.66 0.27 0.27 0.1 0.1 0.2 0.2 ] [ 0.37 0.63 0.97 1.78 1.54 0.4 0.39 0.66 0.14 0.28 0.1 0.1 0.2 0.2 ] [ 0.03 0.97 0.54 -0.13 -0.09 1.97 0.39 0.71 0.2 0.2 0.1 0.1 0.2 0.2 ] [ 0.01 0.99 0.45 -0.03 -1.55 0.52 0.39 0.71 0.27 0.27 0.1 0.1 0.2 0.2 ] [ 0.02 0.98 1.24 -0.31 1.62 0.34 0.39 0.71 0.14 0.28 0.1 0.1 0.2 0.2 ] [ 0.19 0.81 1.58 -0.16 2.64 -0.67 0.39 0.71 0.12 0.35 0.1 0.1 0.2 0.2 ] [ 0.03 0.97 -1.53 -0.01 -1.86 0.6 0.45 0.71 0.27 0.27 0.1 0.1 0.2 0.2 ] [ 0.19 0.81 -2.54 -0.28 1.9 0.46 0.45 0.71 0.14 0.28 0.1 0.1 0.2 0.2 ] [ 0.36 0.64 -2.95 -0.19 2.64 -0.59 0.45 0.71 0.12 0.35 0.1 0.1 0.2 0.2 ] [ 0.23 0.77 0.45 -2.62 -0.03 2.12 0.39 0.76 0.2 0.2 0.1 0.1 0.2 0.2 ] [ 0.24 0.76 0.28 -1.79 -1.3 0.86 0.39 0.76 0.27 0.27 0.1 0.1 0.2 0.2 ]]
classが小数だったり, 座標値がマイナスだったりと, こちらの結果がおかしい.
2. 不具合原因
少し原因調査をしてみたところ, どうも学習済モデルの読み込み方によってモデル構造の最後が違うことがわかった.
(1) Build the model and load trained weights into it
__________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) (None, 300, 300, 3) 0 __________________________________________________________________________________________________ identity_layer (Lambda) (None, 300, 300, 3) 0 input_1[0][0] __________________________________________________________________________________________________ (省略) __________________________________________________________________________________________________ mbox_priorbox (Concatenate) (None, 8732, 8) 0 conv4_3_norm_mbox_priorbox_reshap fc7_mbox_priorbox_reshape[0][0] conv6_2_mbox_priorbox_reshape[0][ conv7_2_mbox_priorbox_reshape[0][ conv8_2_mbox_priorbox_reshape[0][ conv9_2_mbox_priorbox_reshape[0][ __________________________________________________________________________________________________ predictions (Concatenate) (None, 8732, 14) 0 mbox_conf_softmax[0][0] mbox_loc[0][0] mbox_priorbox[0][0] __________________________________________________________________________________________________ decoded_predictions (DecodeDete (None, <tf.Tensor 't 0 predictions[0][0] ================================================================================================== Total params: 23,745,908 Trainable params: 23,745,908 Non-trainable params: 0
(2) Load a trained model
__________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) (None, 300, 300, 3) 0 __________________________________________________________________________________________________ identity_layer (Lambda) (None, 300, 300, 3) 0 input_1[0][0] __________________________________________________________________________________________________ (省略) __________________________________________________________________________________________________ mbox_priorbox (Concatenate) (None, 8732, 8) 0 conv4_3_norm_mbox_priorbox_reshap fc7_mbox_priorbox_reshape[0][0] conv6_2_mbox_priorbox_reshape[0][ conv7_2_mbox_priorbox_reshape[0][ conv8_2_mbox_priorbox_reshape[0][ conv9_2_mbox_priorbox_reshape[0][ __________________________________________________________________________________________________ predictions (Concatenate) (None, 8732, 14) 0 mbox_conf_softmax[0][0] mbox_loc[0][0] mbox_priorbox[0][0] ================================================================================================== Total params: 23,745,908 Trainable params: 23,745,908 Non-trainable params: 0
(2)では, 最終の"decoded_predictions"層が欠落しているようだ.
ssd_keras/models内のkeras_ssd300.pyのコードを確認してみると, 以下のような部分があり, "training"と"inference"でモデル構造が異なることがわかった.
そのため, 学習済みモデルを読み込んでも, モデル内部が"training"状態のままになっていると思われる.
[コード]
if mode == 'training': model = Model(inputs=x, outputs=predictions) elif mode == 'inference': decoded_predictions = DecodeDetections(confidence_thresh=confidence_thresh, iou_threshold=iou_threshold, top_k=top_k, nms_max_output_size=nms_max_output_size, coords=coords, normalize_coords=normalize_coords, img_height=img_height, img_width=img_width, name='decoded_predictions')(predictions) model = Model(inputs=x, outputs=decoded_predictions)
3. 不具合対策
少し調べてみたが, keras.models.load_model()ではmode指定みたいなことができない.
そこで, モデルを一旦"inference"状態にしてモデルを保存すればよいのではと思い試してみた.
これはうまくいき, 人物検出が正しく動作するようになった.
"inference"状態にして保存する方法:
(1)のコードで"model.compile()"した後で, "model.save()"を行えばよい.
本来やろうとしたGPUメモリの削減とかではないが, kerasのモデル読み込みについて色々と調べたので, このあとのモデル変換などでも少しは役にたつだろう!!
----
参照URL:
[1] pierluigiferrari/ssd_keras: A Keras port of Single Shot MultiBox ...