この記事は鈴鹿高専Advent Calendar 2022 3日目の記事です
先日のパソコン甲子園で制作したアプリでtensorflowを用いた画像認識を実装したので、機械学習の環境や工夫した点などを書きます
機械学習の環境
google cloud platformのgoogle compute engineを使い学習環境を立てました
詳しい環境↓
- OS: ubuntu 20.04
- CPU: vCPU×2
- GPU: NVIDIA T4
- RAM: 8GB
- ストレージ: 100GB
なんでローカルではなくクラウドを使ったか、それはチーム内でcudaが使えるマシンを持ってる人がいなかったからです!(私のPCはRadeonしかない)
NVIDIAが配布しているdockerイメージを使ったので構築自体はすぐ終わりました、なので何をしたかはそんなに覚えてないため説明は割愛します
インスタンスにはSSHで接続してデータセットとかはSCPコマンドで転送しました
tensorflow初めて触ってみた
さて、ここからが本題です
初めてtensorflowを触ってみました!
そもそも機械学習がほぼ初めてだったので機械学習とはなんぞやをざっくりと調べてお勉強しました
ちょうど工学実験で簡単に機械学習をやったのでそれを深掘りする感じで学習しました、間違ってることもあると思いますが豆腐メンタルなので優しく教えていただけるとありがたいです
機械学習とは
tensorflowを使うにしても雰囲気は理解しないといけないよな、ということでyoutubeやtensorflow公式ドキュメントや解説記事を読んだりしました
私なりにざっくりまとめると
「入力データを分割して重みがある枝を生やして、複数の枝を接続したものを新しいデータとする。これを一つの層として、更に繰り返し層を重ねて重みを調整しながら最終的に良い感じのデータが出力されるようにする。」
こういう感じになりました
そして実験では転移学習も触れたので少し深く調べてみると色々とやり方があるようで、私は今回はFine Tuningを試しました
転移学習とは
既存の学習済みモデルのデータはそのままに、新たに追加したモデルのデータのみ学習する手法
Fine Tuningは学習済みモデルのデータの一部も再学習させる手法のことで、学習させるデータに最適化されるらしい(?)
書いてみる
今回はVGG19を使いました
このような感じで接続しました、最後の活性化関数がsoftmaxではなくsigmoidなのはマルチラベル分類を採用したためです、損失関数もマルチラベル分類ができるようにするためにbinary_crossentropy
を使っています
コードはこちら
from keras.models import Model
from keras.layers import Dense, Dropout, Input, GlobalAveragePooling2D
import tensorflow_addons as tfa
from keras.applications.vgg19 import VGG19
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.regularizers import l2
import tensorflow as tf
dataPath = "image"
dataPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), dataPath)
train = ImageDataGenerator(rescale=1./255,
zoom_range=0.2,
horizontal_flip=True,
rotation_range=40,
validation_split=0.2)
trainGenerator = train.flow_from_directory(dataPath, target_size=(
384, 216), batch_size=16, class_mode="categorical", shuffle=True, subset="training")
valGenerator = train.flow_from_directory(dataPath, target_size=(
384, 216), batch_size=16, class_mode="categorical", shuffle=True, subset="validation")
HEIGHT = 384
WIDTH = 216
baseModel = VGG19(weights="imagenet",
include_top=False,
input_tensor=Input(shape=(HEIGHT, WIDTH, 3)),)
for layer in baseModel.layers[:-2]:
layer.trainable = False
x = baseModel.output
x = GlobalAveragePooling2D(name="output1")(x)
x = Dropout(0.5, name="output3")(x)
x = Dense(512, activation=tfa.activations.rrelu,
kernel_regularizer=l2(0.008), name="output2")(x)
pridection = Dense(20, activation="sigmoid", name="output4")(x)
model = Model(inputs=baseModel.input, outputs=pridection)
model.compile(optimizer='rmsprop', loss="binary_crossentropy",
metrics=["accuracy"])
model.summary()
early_stopping = EarlyStopping(monitor="val_loss", patience=10, min_delta=0)
check_point = ModelCheckpoint(
"./tmp/model/model.h5", save_best_only=True, mode="max", monitor='val_accuracy')
history = model.fit(trainGenerator, validation_data=valGenerator, epochs=100,
callbacks=[early_stopping, check_point])
l2ノルムで正則化したりModelCheckPointで検証データでの正答率が一番高いやつを保存したり色々工夫してみました
学習開始
あとはこれを学習させるだけです、今回は画像枚数5800枚くらいで20種類の分類をしました
データセットとプログラムをローカルマシンからGCPに立てたインスタンスにscpで送ってdockerで実行して...
データ数全然足りませんでした
正答率は検証用データで74.8%、実際にその場で写真を撮って推論させてもこのくらいか少し低いくらいでした
でもこれ以上データを集めるのは時間的に難しかったので妥協しました
挑戦してみた感想などなど
9月半ばから1ヶ月半ずっと勉強して実験してみるを何十回も繰り返しましたがかなり難しかったです
l2ノルム正則化でどれくらい考慮させるかなど、パラメータ調整にものすごく時間がかかりました
初めは検証しながら学習をしてなかったので過学習にも悩まされ、検証データを用いることの大切さがものすごく分かりました
あとは過学習を抑制するための方法が複数あり、それぞれについて勉強するのは楽しかったです
余談ですがGCPとかtensorflowはGoogleですしパソコン甲子園に向けて制作したアプリではフレームワークはflutterでDBやauthにはfirebase使ってます、これらもGoogleですね、Googleすごい