訓練されたAlexnetモデルで移行学習をテストする
最近TensorflowでAlexnetのモデルを実現し、Imagenetのデータセットでトレーニングをしたところ、テスト結果はTop 5の71%近くの精度に達した.この訓練されたモデルが他の画像分類のタスクに使用できるかどうかをテストしたいので、Tensorflowの移行学習チュートリアルで述べたFlowers分類タスクのデータを選択してテストをしました.
まずFlowersのデータをダウンロードします.具体的にはTensorflowの紹介を参照してください.ダウンロードしたデータを解凍すると5つのフォルダがあり、それぞれ5種類の花の画像があります.画像をTFRECord形式のデータに変換するプログラムを作成し、後続の処理を便利にしました.コードは次のとおりです.
プログラムの実行が完了すると、5つのTFRECordファイルが生成され、各ファイルは1つの花の画像データに対応します.
その後、以前に訓練したAlexnetを利用して移行学習を行うことができます.前回のブログで私のAlexnetのモデルを紹介しましたが、既存のモデルの最後の層の全接続層に代わって新しい全接続層を追加し、前の数層の訓練されたパラメータを保持し、新しい画像データだけで新しい全接続層を訓練します.そのため、新しい画像データを元のAlexnetモデルで計算した後、最後から2番目のレイヤの計算結果を出力し、新しく追加した全接続レイヤで計算する必要があります.既存のAlexnetモデルのこれらの層のパラメータは訓練不可に設定され、新しく追加された層だけを訓練すればよい.以下に、従来のAlexnetモデルのコードを示します.最後から2番目のレイヤおよび上のレイヤのパラメータは、Trainable=Falseに変更する必要があります.モデルコードは次のとおりです.
次に、コードを作成して、前に訓練したパラメータを読み出して新しい訓練を行うことができます.コードは以下の通りです.
最初のEPOCHを訓練すると,試験セットの精度は60%に達することができ,遷移試験は確かに既存のモデルを利用して画像の特徴を効果的に抽出し,訓練速度を速めた.50個のEPOCHを訓練し,テストセットで96%程度の精度に達し,Tensorflow公式サイトでInception V 3モデルで学習テストを移行した結果に近い.
まずFlowersのデータをダウンロードします.具体的にはTensorflowの紹介を参照してください.ダウンロードしたデータを解凍すると5つのフォルダがあり、それぞれ5種類の花の画像があります.画像をTFRECord形式のデータに変換するプログラムを作成し、後続の処理を便利にしました.コードは次のとおりです.
import os
import cv2
import tensorflow as tf
import numpy as np
def make_example(image, label):
return tf.train.Example(features=tf.train.Features(feature={
'image' : tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
'label' : tf.train.Feature(bytes_list=tf.train.BytesList(value=[label]))
}))
flower_classes = {"daisy":0, "dandelion":1, "roses":2, "sunflowers":3, "tulips":4}
for flower_class in flower_classes.keys():
writer = tf.python_io.TFRecordWriter(flower_class+".tfrecord")
folder_path = flower_class + "/"
files = os.listdir(folder_path)
label = np.array([flower_classes[flower_class]])
for jpgfile in files:
img = cv2.imread(folder_path+"/"+jpgfile, cv2.IMREAD_COLOR)
img_jpg = cv2.imencode('.jpg', img)[1].tobytes()
ex = make_example(img_jpg, label.tobytes())
writer.write(ex.SerializeToString())
writer.close()
プログラムの実行が完了すると、5つのTFRECordファイルが生成され、各ファイルは1つの花の画像データに対応します.
その後、以前に訓練したAlexnetを利用して移行学習を行うことができます.前回のブログで私のAlexnetのモデルを紹介しましたが、既存のモデルの最後の層の全接続層に代わって新しい全接続層を追加し、前の数層の訓練されたパラメータを保持し、新しい画像データだけで新しい全接続層を訓練します.そのため、新しい画像データを元のAlexnetモデルで計算した後、最後から2番目のレイヤの計算結果を出力し、新しく追加した全接続レイヤで計算する必要があります.既存のAlexnetモデルのこれらの層のパラメータは訓練不可に設定され、新しく追加された層だけを訓練すればよい.以下に、従来のAlexnetモデルのコードを示します.最後から2番目のレイヤおよび上のレイヤのパラメータは、Trainable=Falseに変更する必要があります.モデルコードは次のとおりです.
def inference(images, dropout_rate=1.0, wd=None):
with tf.variable_scope('conv1', reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(initializer=tf.truncated_normal([11,11,3,96], dtype=tf.float32, stddev=1e-1), trainable=False, name='weights')
conv = tf.nn.conv2d(images, kernel, [1,4,4,1], padding='SAME')
biases = tf.get_variable(initializer=tf.constant(0.1, shape=[96], dtype=tf.float32), trainable=False, name='biases')
bias = tf.nn.bias_add(conv, biases)
conv1 = tf.nn.relu(bias, name='conv1')
#lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1')
pool1 = tf.nn.max_pool(conv1, ksize=[1,3,3,1], strides=[1,2,2,1], padding='VALID', name='pool1')
with tf.variable_scope('conv2', reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(initializer=tf.truncated_normal([5,5,96,256], dtype=tf.float32, stddev=1e-1), trainable=False, name='weights')
conv = tf.nn.conv2d(pool1, kernel, [1,1,1,1], padding='SAME')
biases = tf.get_variable(initializer=tf.constant(0.1, shape=[256], dtype=tf.float32), trainable=False, name='biases')
bias = tf.nn.bias_add(conv, biases)
conv2 = tf.nn.relu(bias, name='conv2')
#lrn2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn2')
pool2 = tf.nn.max_pool(conv2, ksize=[1,3,3,1], strides=[1,2,2,1], padding='VALID', name='pool2')
with tf.variable_scope('conv3', reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(initializer=tf.truncated_normal([3,3,256,384], dtype=tf.float32, stddev=1e-1), trainable=False, name='weights')
conv = tf.nn.conv2d(pool2, kernel, [1,1,1,1], padding='SAME')
biases = tf.get_variable(initializer=tf.constant(0.1, shape=[384], dtype=tf.float32), trainable=False, name='biases')
bias = tf.nn.bias_add(conv, biases)
conv3 = tf.nn.relu(bias, name='conv3')
with tf.variable_scope('conv4', reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(initializer=tf.truncated_normal([3,3,384,384], dtype=tf.float32, stddev=1e-1), trainable=False, name='weights')
conv = tf.nn.conv2d(conv3, kernel, [1,1,1,1], padding='SAME')
biases = tf.get_variable(initializer=tf.constant(0.1, shape=[384], dtype=tf.float32), trainable=False, name='biases')
bias = tf.nn.bias_add(conv, biases)
conv4 = tf.nn.relu(bias, name='conv4')
with tf.variable_scope('conv5', reuse=tf.AUTO_REUSE):
kernel = tf.get_variable(initializer=tf.truncated_normal([3,3,384,256], dtype=tf.float32, stddev=1e-1), trainable=False, name='weights')
conv = tf.nn.conv2d(conv4, kernel, [1,1,1,1], padding='SAME')
biases = tf.get_variable(initializer=tf.constant(0.1, shape=[256], dtype=tf.float32), trainable=False, name='biases')
bias = tf.nn.bias_add(conv, biases)
conv5 = tf.nn.relu(bias, name='conv5')
pool5 = tf.nn.max_pool(conv5, ksize=[1,3,3,1], strides=[1,2,2,1], padding='VALID', name='pool5')
flatten = tf.layers.flatten(inputs=pool5, name='flatten')
with tf.variable_scope('local1', reuse=tf.AUTO_REUSE):
weights = tf.get_variable(initializer=tf.truncated_normal([6*6*256,4096], dtype=tf.float32, stddev=1/4096.0), trainable=False, name='weights')
if wd is not None:
weights_loss = tf.multiply(tf.nn.l2_loss(weights), wd, name='weight_loss')
tf.add_to_collection('losses', weights_loss)
biases = tf.get_variable(initializer=tf.constant(1.0, shape=[4096], dtype=tf.float32), trainable=False, name='biases')
local1 = tf.nn.relu(tf.nn.xw_plus_b(flatten, weights, biases), name='local1')
local1 = tf.nn.dropout(local1, dropout_rate)
with tf.variable_scope('local2', reuse=tf.AUTO_REUSE):
weights = tf.get_variable(initializer=tf.truncated_normal([4096,4096], dtype=tf.float32, stddev=1/4096.0), trainable=False, name='weights')
if wd is not None:
weights_loss = tf.multiply(tf.nn.l2_loss(weights), wd, name='weight_loss')
tf.add_to_collection('losses', weights_loss)
biases = tf.get_variable(initializer=tf.constant(1.0, shape=[4096], dtype=tf.float32), trainable=False, name='biases')
local2 = tf.nn.relu(tf.nn.xw_plus_b(local1, weights, biases), name='local2')
local2 = tf.nn.dropout(local2, dropout_rate)
with tf.variable_scope('local3', reuse=tf.AUTO_REUSE):
weights = tf.get_variable(initializer=tf.truncated_normal([4096,1000], dtype=tf.float32, stddev=1e-3), trainable=True, name='weights')
biases = tf.get_variable(initializer=tf.constant(1.0, shape=[1000], dtype=tf.float32), trainable=True, name='biases')
local3 = tf.nn.xw_plus_b(local2, weights, biases, name='local3')
return local3, local2
次に、コードを作成して、前に訓練したパラメータを読み出して新しい訓練を行うことができます.コードは以下の通りです.
import tensorflow as tf
import alexnet_model
imageWidth = 224
imageHeight = 224
imageDepth = 3
batch_size = 10
resize_min = 256
# TFRecord
def _parse_function(example_proto):
features = {"image": tf.FixedLenFeature((), tf.string, default_value=""),
"label": tf.FixedLenFeature((), tf.string, default_value="")}
parsed_features = tf.parse_single_example(example_proto, features)
image_decoded = tf.image.decode_jpeg(parsed_features["image"], channels=3)
shape = tf.shape(image_decoded)
height, width = shape[0], shape[1]
resized_height, resized_width = tf.cond(height
最初のEPOCHを訓練すると,試験セットの精度は60%に達することができ,遷移試験は確かに既存のモデルを利用して画像の特徴を効果的に抽出し,訓練速度を速めた.50個のEPOCHを訓練し,テストセットで96%程度の精度に達し,Tensorflow公式サイトでInception V 3モデルで学習テストを移行した結果に近い.