論文読んでAIつくるぞ会(第8回) ~FCNの学習~
こんにちは。
もうバレンタインデーが過ぎてしまいましたね。
世の中の人はどのくらいチョコレートを食べていたのでしょうか。
チョコレート大好き、開発メンバーのKです。
さて、前回(https://tottorisnow33.hatenablog.com/entry/2021/02/13/082523)
はAccuracyが0.645から数値が一切変化しない……
という状態でした。
今回はAccuracyが0.645の壁を超えることができました!
以下、目次です。
1. 書いたソースコード
今回書いたソースコードは以下の通り。
import tensorflow as tf from load_3 import Get_Training_and_Test_Image gpus = tf.config.experimental.list_physical_devices('GPU') if gpus: try: for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True) except RuntimeError as e: print(e) x_train, y_train, x_test, y_test = Get_Training_and_Test_Image(2913, 4) #epoch数とbatchサイズ設定 set_epoch_num = 800 set_batch_size = 8 class_num = 22 ######################NN構築(CNN)############################## input_shape = (128, 128, 3) ######################################### # 1個目のブランチとの分岐点までのびる ######################################### inputs = tf.keras.Input(shape=input_shape, name='mnist_input') # conv1 conv_1_1 = tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')(inputs) conv_1_2 = tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation='relu', padding='same')(conv_1_1) # pool1 pool_1 = tf.keras.layers.MaxPooling2D((2, 2))(conv_1_2) # conv2 conv_2_1 = tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu', padding='same')(pool_1) conv_2_2 = tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu', padding='same')(conv_2_1) # pool2 pool_2 = tf.keras.layers.MaxPooling2D((2, 2))(conv_2_2) # conv3 conv_3_1 = tf.keras.layers.Conv2D(256, kernel_size=(3, 3), activation='relu', padding='same')(pool_2) conv_3_2 = tf.keras.layers.Conv2D(256, kernel_size=(3, 3), activation='relu', padding='same')(conv_3_1) conv_3_3 = tf.keras.layers.Conv2D(256, kernel_size=(3, 3), activation='relu', padding='same')(conv_3_2) # pool3 pool_3 = tf.keras.layers.MaxPooling2D((2, 2))(conv_3_3) ######################################### # 2個目のブランチとの分岐点までのびる ######################################### # conv4 conv_4_1 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(pool_3) conv_4_2 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(conv_4_1) conv_4_3 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(conv_4_2) # pool4 pool_4 = tf.keras.layers.MaxPooling2D((2, 2))(conv_4_3) ######################################### # conv7_4xを作成 ######################################### # conv5 conv_5_1 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(pool_4) conv_5_2 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(conv_5_1) conv_5_3 = tf.keras.layers.Conv2D(512, kernel_size=(3, 3), activation='relu', padding='same')(conv_5_2) # pool5 pool_5 = tf.keras.layers.MaxPooling2D((2, 2))(conv_5_3) # conv6_7 conv_6 = tf.keras.layers.Conv2D(4096, kernel_size=(int(input_shape[0]/32), int(input_shape[0]/32)), activation='relu', padding='valid')(pool_5) conv_7 = tf.keras.layers.Conv2D(4096, kernel_size=(1, 1), activation='relu', padding='valid')(conv_6) # ch数をクラス数に合わせるconv conv7_4x = tf.keras.layers.Conv2D(class_num, kernel_size=(1, 1), activation='relu', padding='valid')(conv_7) #conv7_4xを作成 conv7_4x = tf.image.resize( conv7_4x, [int(input_shape[0]/8), int(input_shape[1]/8)], method=tf.image.ResizeMethod.BILINEAR, preserve_aspect_ratio=False,antialias=False, name=None ) ######################################### # pool4_2xを作成 ######################################### # ch数をクラス数に合わせるconv pool4_2x = tf.keras.layers.Conv2D(class_num, kernel_size=(1, 1), activation='relu', padding='valid')(pool_4) #pool4_2xを作成 pool4_2x = tf.image.resize( pool4_2x, [int(input_shape[0]/8), int(input_shape[1]/8)], method=tf.image.ResizeMethod.BILINEAR, preserve_aspect_ratio=False,antialias=False, name=None ) ######################################### # pool3_1xを作成 ######################################### # ch数をクラス数に合わせるconv pool3_1x = tf.keras.layers.Conv2D(class_num, kernel_size=(1, 1), activation='relu', padding='valid')(pool_3) ######################################### #全てを足して入力解像度にリサイズ ######################################### # outputs = pool3_1x + pool4_2x + conv7_4x outputs = tf.keras.layers.add([pool3_1x, pool4_2x, conv7_4x]) outputs = tf.image.resize( outputs, [input_shape[0], input_shape[1]], method=tf.image.ResizeMethod.BILINEAR, preserve_aspect_ratio=False,antialias=False, name=None ) ######################################### #ソフトマックス ######################################### outputs = tf.keras.layers.Softmax(axis=3)(outputs) #層をセット(作ったパーツの組み合わせ終わったものをここでセッティング) model = tf.keras.Model(inputs=inputs, outputs=outputs, name="mnist_model") model.summary() ######################NN構築############################## sgd = tf.keras.optimizers.SGD(lr=0.01, decay=0.0016, momentum=0.9, nesterov=True) model.compile( loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False), optimizer=sgd, metrics=["accuracy"], ) #学習 model.fit(x_train, y_train, epochs=set_epoch_num, batch_size=set_batch_size) #評価 score = model.evaluate(x_test, y_test, verbose=2) print('損失:', score[0]) print('正答率:', score[1]) #モデルの保存 model.save("model_cnn_demo.h5")
learning rateは、論文(https://openaccess.thecvf.com/content_cvpr_2015/papers/Long_Fully_Convolutional_Networks_2015_CVPR_paper.pdf)
ではlr=0.0001ですが、せっかちな自分たちはlr=0.01にしました。
2. 学習中のAccuracyが上がらなかったのは単純なエポック数不足
前回、「Accuracyが増えない...」という状態に陥り、原因を考えいろいろ試しました。
そもそも入力画像やアノテーション画像をうまくロードできていない...?とか、
モデルがおかしい...?とか、
考えられる原因を調査していきましたが、結論としては単純に学習不足でした。。。。。
我々は学習中のAccuracyが変わらないことから学習がうまくいっていないと思い込んでいましたが、lossの変化をよく見ると、徐々にlossが小さくなっていたので実は学習は進んでいたのです。
ではなぜAccuracyが変わらなかったのかというと、おそらく、学習初期段階ではpixelごとの推定結果があいまいで、Accuracyに変化がみられるほどの確率の変化がなかったのだと思います。
例えば、あるpixelの真値が人間であるのに対して、学習初期のpredictionが背景としていたとき、学習が進むにつれてそのpixelの人間である確率が10%、20%、30%と上がっていく一方で背景である確率が80%、70%、60%下がっていくとしましょう。
学習が進むにつれてそのpixel人間である確率が上がってはいるものの、この3段階目では背景である確率が60%なので、推定結果は背景と出してしまいます。
Accuracyは、そのpixelが人間でなければ上がらないので、学習初期段階ではAccuracyに変化が見られなかったのでしょう。
というわけで今回は、エポックを800にしてがっつり学習させました。
3. 推論結果
推論には以前ブログに書いたソースを使用しました。
(https://tottorisnow33.hatenablog.com/entry/2021/01/30/081045)
まずは、学習画像に含まれているデータを推論してみました。
データはPASCAL VOC 2012のデータです。
オレンジ:人間
黄色:自転車
黒:背景
白:その他
で色付けしています。
この画像は学習データに含まれているので、当然結果はよくなるはずです。
(逆に言えば、これでうまくいってなかったら学習そのものがうまくいっていないことになります)
とりあえず、結果はよい感じなのでよかったです。
あとは、学習データに含まれない評価用データでうまくいってるか見たいので、
評価用データを探しておこうと思います。
4. 次回
次回、車や人間クラスに絞って、識別結果を数値でも見られるようにしたいと思います。
それが出来上がったら、モデルをいじくったり、新しいモデルを実装したりして、性能を向上させたいです