①はじめに
AIを勉強するにあたりFineTuningも勉強しておきたいと思います。今回は**ImageNet学習済みのDenseNet121モデル
を利用して、MNIST
を実施してみたいと思います。ライブラリはkeras
**を使用します。
②ファインチューニングとは
FineTuningとは和訳すると微調整という意味です。AIでのファインチューニングとは、既存のモデルの一部を再利用して、新しいモデルを構築する手法
のことです。
■ファインチューニングと転移学習の違い
ファインチューニングに似たようなものとして、転移学習があります。ファインチューニングと転移学習が、いつもごっちゃになってしまうので、ここで整理します。
-
ファインチューニング
ファインチューニングは、学習済みモデルの重みを初期値として、再度学習します。メリットは、既存モデルの重みを初期値として追加学習することで、より効率的に高精度なモデル構築が行えます。 -
転移学習
転移学習は、学習済みモデルの重みは固定し、追加した層のみを使用して学習します。メリットは、学習済のモデルをベースに追加学習を行うため、少ないデータで、かつ短い時間でモデルの構築を行うことができます。
■で、今回はどっち?
すでに出来上がった画像分類モデルを再利用して、数字の分類を実施したいと思います。今回は、「ImageNetで学習した重みをもつ画像分類のモデル」を利用して、「数字画像の分類」をさせたいと思います。ですので、今回は**ファインチューニング
**になります。
③ファインチューニングのやり方
ファインチューニング元とするモデルはDenseNet121
を利用したいと思います。こちらのモデルは軽量だけどそこそこ認識率も良いというモデル
になっております。
MNISTの学習データは、28 x 28 x 1ch
、DenseNet121の入力レイヤーは、**32以上 x 32以上 x 3ch
**となっているので、これらを合わせる必要があります。合わせる方法として、以下2通りがあります。
・MNISTの画像を合わせる方法
→モデルの入力レイヤーに合わせて、学習画像を32以上x32以上x3chに変更する方法・モデルの入力レイヤーを変更する方法
→学習画像に合わせて、モデルの入力レイヤーを1chに変更する方法
■MNISTの画像を合わせる方法
DenseNet121
の入力形式は、(height=32以上, width=32以上, channels=3ch)となっています。
MNIST
の画像形式は、(height=28, width=28, channels=1ch)となっています。学習画像サイズはDenseNet121
のネットワーク定義から32以上x32以上x3chは必須となっているので、以下の処理を施します。
- 画像を64x64にリサイズ
- グレースケール1chからRGB3chに変換
■モデルの入力レイヤーを変更する方法
DenseNet121
の入力形式は、(height=32以上, width=32以上, channels=3ch)となっています。
MNIST
の画像形式は、(height=28, width=28, channels=1ch)となっています。学習画像サイズはDenseNet121
のネットワーク定義から32以上x32以上x3chは必須となっているので、以下の処理を施します。
- 画像を64x64にリサイズ
- 1chの入力レイヤーを追加
④MNISTの画像を合わせる方法
基本的には、以下の➊~➎の手順でファインチューニングの実施が可能です。
➊学習済みの重みをダウンロード
「keras.applications.densenet.DenseNet121
」のAPIにより、簡単にDensenet121でimagenetを学習させた重みをダウンロードすることができます。毎回ダウンロードするのは大変(33MB)なので、Google Driveに保存しておきます。
from keras.applications.densenet import *
model = DenseNet121(weights='imagenet')
model.save(fullpath)
➋MNISTのデータ変換
DenseNet121の入力形式(height, width, channels)に合わせて、MNISTの画像を(28, 28, 1)から(64, 64, 3)に変換します。
import cv2
import numpy as np
from keras.datasets import mnist
img_w, img_h, img_ch = 64, 64, 3
num_classes = 10
def pretreatment(x):
# 画像サイズをリサイズ
X = []
for i in range(len(x)):
dst = cv2.resize(x[i], (img_h, img_w), interpolation=cv2.INTER_CUBIC)
dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2RGB)
X.append(dst)
# リシェイプ
X = np.array(X).reshape(len(x), img_h, img_w, img_ch)
# 0〜255 までの範囲のデータを 0〜1 までの範囲に変更
ret = X.astype('float32') / 255
return ret
# ■MNISTデータ読み込み
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train, test_size=0.2)
# ■学習(Train)データ
x_train = pretreatment(x_train)
y_train = np_utils.to_categorical(y_train, num_classes)
# ■学習(Valid)データ
x_valid = pretreatment(x_valid)
y_valid = np_utils.to_categorical(y_valid, num_classes)
# ■テストデータ
x_test = pretreatment(x_test)
y_test = np_utils.to_categorical(y_test, num_classes)
➌DneseNet121のネットワーク構築を行う
(1)DneseNet121ネットワーク構築
kerasでは「keras.applications.densenet.DenseNet121
」のAPIにより、簡単にDensenet121のネットワーク構築することができます。include_topをFalse
にしておくことで、ネットワークの出力層側にある全結合層を含めないようにすることができます。また、この時点ではweightsは読込みしないようにします。
input_tensor = x_train.shape[1:]
model = DenseNet121(include_top=False, weights=None, input_shape=input_tensor)
(2)出力層追加
ネットワークの出力層側の全結合層を構築
DenseNet121(ImageNet)は画像分類として1000クラスを判別していましたが、今回実施するのはMNISTなので0~9の10分類にクラス分けができるようにします。
from keras.layers import *
from keras.models import Model
x = Flatten()(model.layers[-1].output)
x = Dense(10, activation="softmax")(x)
Model(model.inputs, x)
➍学習済みの重みを読み込む
ネットワーク構成後、➊でダウンロードした重みを読み込みます。weightsはこちらの「load_weights
」メソッドで読み込みます。
model.load_weights(fullpath, by_name=True)
➎学習する
あとは通常通り学習させればFineTuningができます。
➏実行結果
●TEST:検証結果
Test loss : 0.01561666838824749
Test accuracy: 0.9944000244140625
⑤モデルの入力レイヤーを変更する方法
基本的には、以下の➊~➍の手順でファインチューニングの実施が可能です。
➊学習済みの重みをダウンロード
「keras.applications.densenet.DenseNet121
」のAPIにより、簡単にDensenet121でimagenetを学習させた重みをダウンロードすることができます。毎回ダウンロードするのは大変(33MB)なので、Google Driveに保存しておきます。
from keras.applications.densenet import *
model = DenseNet121(weights='imagenet')
model.save(fullpath)
➋MNISTのデータ変換
DenseNet121の入力形式(height, width, channels)に合わせて、MNISTの画像を(28, 28, 1)から(64, 64, 1)に変換します。
import cv2
import numpy as np
from keras.datasets import mnist
img_w, img_h, img_ch = 64, 64, 1
num_classes = 10
def pretreatment(x):
# 画像サイズをリサイズ
X = []
for i in range(len(x)):
dst = cv2.resize(x[i], (img_h, img_w), interpolation=cv2.INTER_CUBIC)
# dst = cv2.cvtColor(dst, cv2.COLOR_GRAY2RGB) ← 1chなので実行しない
X.append(dst)
# リシェイプ
X = np.array(X).reshape(len(x), img_h, img_w, img_ch)
# 0〜255 までの範囲のデータを 0〜1 までの範囲に変更
ret = X.astype('float32') / 255
return ret
# ■MNISTデータ読み込み
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_valid, y_train, y_valid = train_test_split(x_train, y_train, test_size=0.2)
# ■学習(Train)データ
x_train = pretreatment(x_train)
y_train = np_utils.to_categorical(y_train, num_classes)
# ■学習(Valid)データ
x_valid = pretreatment(x_valid)
y_valid = np_utils.to_categorical(y_valid, num_classes)
# ■テストデータ
x_test = pretreatment(x_test)
y_test = np_utils.to_categorical(y_test, num_classes)
➌DneseNet121を利用して新たなネットワーク構築を行う
(1)DneseNet121の入力層の前に独自の入力層を追加
from keras.layers import *
from keras.models import Model, Sequential
input_tensor = x_train.shape[1:]
input_model = Sequential()
input_model.add(InputLayer(input_shape=input_tensor))
input_model.add(Conv2D(3, (3, 3), padding='same'))
input_model.add(BatchNormalization())
input_model.add(Activation('relu'))
(2)DneseNet121ネットワーク構築
kerasでは「keras.applications.densenet.DenseNet121
」のAPIにより、簡単にDensenet121のネットワーク構築することができます。include_topをFalse
にしておくことで、ネットワークの出力層側にある全結合層を含めないようにすることができます。また、この時点ではweightsは読込みしないようにします。
model = DenseNet121(include_top=False, weights=None, input_tensor=input_model.output)
(3)出力層追加
DenseNet121(ImageNet)は画像分類として1000クラスを判別していましたが、今回実施するのはMNISTなので0~9の10分類にクラス分けができるようにします。
x = Flatten()(model.layers[-1].output)
x = Dense(num_classes, activation="softmax")(x)
return Model(model.inputs, x)
➍学習済みの重みを読み込む
ネットワーク構成後、➊でダウンロードした重みを読み込みます。weightsはこちらの「load_weights
」メソッドで読み込みます。
model.load_weights(fullpath, by_name=True)
➎学習する
あとは通常通り学習させれば、FineTuningができます。
➏実行結果
●TEST:検証結果
Test loss : 0.019007695838809013
Test accuracy: 0.9934999942779541
⑥ソースコード
今回学習させたときは、ImageDataGeneratorを使用し、以下の様な回転・縮小画像を含めて学習させ、汎化性能も考慮させています。
全体のソースコードはこちら ▶ github
⑦以上
お疲れさまでした。
ImageNet学習済みのDenseNet121モデルをベースにしているので1epocの計算量は多いものの、FineTuningなので30epocくらいでほぼ収束します。正解率、損失値もそこそこ良いです。今回は、FineTuningのために重みを読み込みましたが、学習を途中まで実施し再度学習を始める場合等へ応用がきくので、本手順を理解しておくと良いと思います。