10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【MNIST】ImangeNetのモデルを利用してFineTuningやってみた【効率的な高精度モデルの構築】

Last updated at Posted at 2021-01-19

①はじめに

AIを勉強するにあたりFineTuningも勉強しておきたいと思います。今回は**ImageNet学習済みのDenseNet121モデルを利用して、MNISTを実施してみたいと思います。ライブラリはkeras**を使用します。

ai_study_kikaigakusyu.png

②ファインチューニングとは

FineTuningとは和訳すると微調整という意味です。AIでのファインチューニングとは、既存のモデルの一部を再利用して、新しいモデルを構築する手法のことです。

■ファインチューニングと転移学習の違い

ファインチューニングに似たようなものとして、転移学習があります。ファインチューニング転移学習が、いつもごっちゃになってしまうので、ここで整理します。

  • ファインチューニング
    ファインチューニングは、学習済みモデルの重みを初期値として、再度学習します。メリットは、既存モデルの重みを初期値として追加学習することで、より効率的に高精度なモデル構築が行えます。

  • 転移学習
    転移学習は、学習済みモデルの重みは固定し、追加した層のみを使用して学習します。メリットは、学習済のモデルをベースに追加学習を行うため、少ないデータで、かつ短い時間でモデルの構築を行うことができます。

■で、今回はどっち?

すでに出来上がった画像分類モデルを再利用して、数字の分類を実施したいと思います。今回は、「ImageNetで学習した重みをもつ画像分類のモデル」を利用して、「数字画像の分類」をさせたいと思います。ですので、今回は**ファインチューニング**になります。

③ファインチューニングのやり方

ファインチューニング元とするモデルはDenseNet121を利用したいと思います。こちらのモデルは軽量だけどそこそこ認識率も良いというモデルになっております。

MNISTの学習データは、28 x 28 x 1chDenseNet121の入力レイヤーは、**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に変換

001.png

④にてスニペットを用いて説明します

■モデルの入力レイヤーを変更する方法

DenseNet121の入力形式は、(height=32以上, width=32以上, channels=3ch)となっています。
MNISTの画像形式は、(height=28, width=28, channels=1ch)となっています。学習画像サイズはDenseNet121のネットワーク定義から32以上x32以上x3chは必須となっているので、以下の処理を施します。

  • 画像を64x64にリサイズ
  • 1chの入力レイヤーを追加

002.png

⑤にてスニペットを用いて説明します

④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

result

⑤モデルの入力レイヤーを変更する方法

基本的には、以下の➊~➍の手順でファインチューニングの実施が可能です。

➊学習済みの重みをダウンロード

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

result

⑥ソースコード

今回学習させたときは、ImageDataGeneratorを使用し、以下の様な回転・縮小画像を含めて学習させ、汎化性能も考慮させています。

全体のソースコードはこちらgithub

⑦以上

お疲れさまでした。
ImageNet学習済みのDenseNet121モデルをベースにしているので1epocの計算量は多いものの、FineTuningなので30epocくらいでほぼ収束します。正解率、損失値もそこそこ良いです。今回は、FineTuningのために重みを読み込みましたが、学習を途中まで実施し再度学習を始める場合等へ応用がきくので、本手順を理解しておくと良いと思います。

10
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?