ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

VGG-YOLOを作りたい その1 データの転移

Tiny-YOLOがどうやっても普通の固定小数点化では精度がでない。もうひと工夫いるんだと思います。精度を上げるのに固定小数点化をいろいろ試すのではなく、CNNの部分をVGG16にしてみようと思いました。VGG16にすることで、バッチノーマライゼーションと、Leaky ReLUがなくなるので固定小数点にしたときの精度はコントロールしやすくなります。(多分)

最初の一歩として、Kerasのexampleに含まれているVGG16学習データを、自分のNNに移してみました。最終段の形が違うためそのままでは上手く行かず、結局レイヤー単位で、get_weightsとset_weightsを繰り返しました。多分、もっと良い方法があるはずです。

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, InputLayer, Dropout

# 入力サイズ等はここを変更
width = 416
height = 416
r_w = 13
r_h = 13
r_n = 5
classes = 20


def vgg_yolo_model():
    model = Sequential()
    model.add(InputLayer(input_shape=(width, height, 3)))

    model.add(Conv2D(64, activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(64, activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(MaxPooling2D(pool_size=(2, 2), padding='same', strides=(2, 2)))

    model.add(Conv2D(128, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(128, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(MaxPooling2D(pool_size=(2, 2), padding='same', strides=(2, 2)))

    model.add(Conv2D(256, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(256, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(256, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(MaxPooling2D(pool_size=(2, 2), padding='same', strides=(2, 2)))

    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(MaxPooling2D(pool_size=(2, 2), padding='same', strides=(2, 2)))

    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(Conv2D(512, data_format="channels_last",  activation='relu',
                     padding='same', kernel_size=(3, 3), strides=(1, 1)))
    model.add(MaxPooling2D(pool_size=(2, 2), padding='same', strides=(2, 2)))

    model.add(Dropout(0.25))
    model.add(Conv2D(125, use_bias=True, data_format="channels_last", activation='relu',
                     padding='same', kernel_size=(1, 1), strides=(1, 1), kernel_initializer='random_uniform'))

    return model


def transfer_weights(my_model):
    from keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions
    def_model = VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None)

    # レイヤー単位で、17層までコピー。18層は全結合層なので不要。
    for i, l in enumerate(def_model.layers):
        if len(l.weights) == 0:
            continue

        # layer[17]までコピー
        if i >= 18:
            break

        w = l.get_weights()
        my_model.layers[i].set_weights(w)

        print('set weights layer[%d] %s' % (i, l.name))


# モデルの構築と表示
vgg_yolo_model = vgg_yolo_model()
vgg_yolo_model.summary()

# データを移す
transfer_weights(vgg_yolo_model)
# 名前をつけて保存する。
vgg_yolo_model.save_weights('weight/vgg_yolo_def.h5')

これで、weightフォルダーに、vgg_yolo_def.h5が生成されます。

重みデータは、最終層を除きサンプルの値、最終層はランダムに初期化された値になっています。