茨城エンジニアのPython開発日記

茨城のITベンチャー企業ではたらく2年目エンジニア。Pythonで色々なものを作成中。

論文読んでAIつくるぞ会(第4回) ~学習実行に挑戦~


ブログから記事を見つけたい場合はこちら

ブログ地図 - 茨城エンジニアのPython開発日記


こんにちは。松原です。
勉強会開始の7時20分という時間。始まる前は二度とやりたくないと思いますが、10時ごろになると目も覚めてきて、終わってみると最高の休日を過ごせているなという気持ちになります。
午前中が終わっても午後を遊べるのは大きい……

最近はApexにはまっていて、勉強会のあとに3人で潜ってみたりもしました。Y君がプラチナで、K君が完全初心者, 私がほぼ初心者。
うまいことキャリーしてもらって残り3部隊くらいまでは残れました。すごい。


0.今日の目標

今日の目標は下の画像みたいな感じです。
引き続きセマンティックセグメンテーションをやっていきます。
学習用画像, 評価用画像を読み込むload.pyは作ったので、実際に学習を実行する段階ですね。

f:id:tottorisnow33:20201219201509p:plain

1.ソースコード

train.pyはこんな感じになりました。

import tensorflow as tf
from load_2 import Get_Training_and_Test_Image

#MNISTのデータセット用意
# mnist = tf.keras.datasets.mnist

x_train, y_train, x_test, y_test = Get_Training_and_Test_Image(10, 4)

#epoch数とbatchサイズ設定
set_epoch_num = 4
set_batch_size = 2

######################NN構築(CNN)##############################
input_shape = (128, 128, 3)

# モデル構築
inputs = tf.keras.Input(shape=input_shape, name='mnist_input')
x = tf.keras.layers.Conv2D(32,  kernel_size=(3, 3), activation='relu', padding='same')(inputs)
x = tf.keras.layers.Conv2D(64,  kernel_size=(3, 3), activation='relu', padding='same')(x)
x = tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation='relu', padding='same')(x)
x = tf.keras.layers.Conv2D(64,  kernel_size=(3, 3), activation='relu', padding='same')(x)
outputs = tf.keras.layers.Conv2D(22,  kernel_size=(3, 3), activation='softmax', padding='same')(x)

#ソフトマックス関数
# outputs = tf.keras.layers.Softmax(axis=3)(x)
print('outputs', outputs.shape)

#層をセット(作ったパーツの組み合わせ終わったものをここでセッティング)
model = tf.keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")

model.summary()
######################NN構築##############################

######################NN構築(Dense)##############################
"""
#128*128*3
inputs = tf.keras.Input(shape=(128, 128, 3,))
print('inputs', inputs.shape)

#1次元配列に変更(取り扱いやすい)
# flatten = tf.keras.layers.Flatten()(inputs)
flatten = tf.keras.layers.Reshape((128*128*3,))(inputs)
print('flatten', flatten.shape)

#1次元で進行
dense_64   = tf.keras.layers.Dense(64,   activation="relu")
dense_128  = tf.keras.layers.Dense(128,  activation="relu")
dense_256  = tf.keras.layers.Dense(256,  activation="relu")
dense_1024 = tf.keras.layers.Dense(1024, activation="relu")



x = dense_64(flatten)
print('dense_64', x.shape)
x = dense_128(x)
print('dense_128', x.shape)
x = dense_256(x)
print('dense_256', x.shape)
x = dense_1024(x)
print('dense_1024', x.shape)

#128*128*22となる層
x = tf.keras.layers.Dense(360448, activation="relu")(x)
print('dense_360448', x.shape)

#(128, 128, 22)の3次元に戻す
x = tf.keras.layers.Reshape((128, 128, 22), input_shape=(360448,))(x)
print('reshape', x.shape)

#ソフトマックス関数
outputs = tf.keras.layers.Softmax(axis=3)(x)
print('outputs', outputs.shape)

#層をセット(作ったパーツの組み合わせ終わったものをここでセッティング)
model = tf.keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")

model.summary()
"""
######################NN構築##############################

#モデルのコンパイル
model.compile(
# loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False),
optimizer=tf.keras.optimizers.RMSprop(),
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_mnist_func_full_comb.h5")


最初の"from load_2 import Get_Training_and_Test_Image"の部分で、前回作ったload.pyからデータ読み込み関数をインポートしています。
引数として学習用画像枚数, 評価用画像枚数を渡して、学習, 評価それぞれのためのxデータ, yデータを返してもらうような関数ですね。


返してもらう型はモデルによって異なるので、そこの調整をload.pyでやります。
今回の場合は4次元配列で、

x: (画像枚数, 画像高さ(128), 画像幅(128), RGB(3))
y: (画像枚数, 画像高さ(128), 画像幅(128), 正解クラス数(22))

ピクセルごとのRGBが与えられた結果として、ピクセルごとに正解がついてることになっています。
正解クラス22の中で、鳥や空など対応する正解のクラスにのみ1が入ってる形ですね。

2.評価結果

評価結果はこんな感じ

f:id:tottorisnow33:20201219204737p:plain
全結合NNでの結果
f:id:tottorisnow33:20201219204809p:plain
CNNでの結果


こんな適当な学習でも64%の正答率ってすごい……
空などの比較的簡単に識別できそうで支配的な部分がうまいこといったのかな?
それにしてもうまくいきすぎてちょっと不安。

3.詰まったこと

こう書くと簡単にできそうですが、道中で結構つまりました。
原因はこのエラー文です。

(エラー文)
ValueError: Shape mismatch: The shape of labels (received (720896,)) should equal the shape of logits except for the last dimension (received (32768, 22)).


結論から言うと、損失関数の指定を間違えていたことが原因でした。
モデルコンパイル時のここですね。

# loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False),


両方とも確率の和が1になるように正規化している場合に使うものですが、おそらく上はyデータが1次元のときしか使えません。


今回の場合は画像幅, 画像高さのピクセルを指定したうえで、最後の次元であるクラスの中で正規化していてほしい。
上の損失関数はこのように、多次元の配列の最後を正規化するという指定まではしてくれないもののようです。

このあたりの仕様に確証はないのでご参考までに。



というわけで、今回は学習まで無事終了しました。
来週はdemo.pyを作って、セマンティックセグメンテーションが行われた様子を可視化したい。


それではまた次回。