畳み込みニューラルネットワーク, CNN(Convolutional Neural Network)は、画像分類において、画像の位置ずれに強くしたNNです。そのため、画像認証のマルチクラス分類に活用されます。特徴は、2次元フィルタによる畳み込み層と(最大)プーリング層の2種類の中間層で構築することです。
ここで、TensorFlowのチュートリアルのひとつで、Kerasを用いたNNの分類問題の例が下記リンクです。https://www.tensorflow.org/tutorials/keras/classification
本記事では、上記リンクの普通のNNに加えてCNNで実行する雛形コードとその比較結果を載せます。使用するデータセットは、Fashion MNIST(ファッションエムニスト)と呼び、下表のように衣料品10カテゴリの画像が7000枚あります。その内の6000枚を訓練データ、1000枚をテストデータとして使用します。
ラベル | クラス |
0 | T-shirt/top |
1 | Trouser |
2 | Pullover |
3 | Dress |
4 | Coat |
5 | Sandal |
6 | Shirt |
7 | Sneaker |
8 | Bag |
9 |
Ankle boot |
処理の流れは大まかには、下図左のようにカラー画像(0~255の数値データ:256階調8bitRGB)を下図右のように白黒画像(0~1の数値データ:10階調白黒)へ正規化して情報量を減らし、輪郭と濃淡から画像を識別して分類します。
即ち、下図のように各ピクセル(画素)の数値データを操作します。
下図は、本コードを実行した結果である。下図左はNN、右はCNNの各々15枚の結果を可視化した画像である。棒の高さは予測時の自身満々度を表す。そして、青色は的中、赤色はハズレを表す。よって、下図左の赤色は自身満々にハズした結果を意味する。
そのNNがハズした内容は、下図のように87%の確率でサンダルとして誤認識した。
一方、CNNの結果は、下図のように60%の確率でスニーカーとして的中した。
目的関数(損失関数:sparse_categorical_crossentropy)の世代進行に伴う変化の様子を下図に示す。下図左はNNで下図右はCNNである。CNNの方がlossが小さく、良いことがわかる。但し、解析時間はNNは44秒、CNNは375秒(6分25秒)で、CNNは8.5倍の時間を要した。つまり、精度と解析時間はトレードオフの関係にある。
■本プログラム
CNNは、フラグ「CNN_flag = True」で実行、NNは「CNN_flag = False」で実行する仕様です。適合度には、ハイパーパラメータの影響も少なからずあります。それらは、世代数、バッチ数、隠れ層の数、ニューロン数、学習率、ドロップ率、最適化アルゴリズムのオプションです。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # https://www.tensorflow.org/tutorials/keras/classification import tensorflow as tf from tensorflow import keras import numpy as np import pandas as pd import matplotlib.pyplot as plt import time # データの観察と正規化をする関数 def check_data_and_normalization(train_images, train_labels, test_images, test_labels): # 画像データの構成 print("train_images", train_images[0]) # 28×28の2次元配列「28×28のピクセル(画素)8ビットのnumpyアレイ形式」 print("train_images.shape", train_images.shape) # (60000, 28, 28)←訓練用の28×28のデータ数が6万枚 print("train_labels", len(set(train_labels))) # 10←ラベル数をチェック。重複があるため一旦setに変換後に要素数をカウント print("test_images.shape", test_images.shape) # (10000, 28, 28)←テスト用のデータ数は1万枚 # 代表画像を表示 plt.figure() plt.imshow(train_images[0]) # 1画素0~255階調の数値データであることがわかる plt.colorbar() plt.grid(False) #plt.show() plt.title(class_names[train_labels[0]]) plt.savefig('No0.png') plt.close() # 代表画像を数値データで表示 img_cols = test_images[0].shape[1] # 列数 pd.options.display.max_columns = img_cols # pandasのカラム表示の設定を変更 df_img_matrix = pd.DataFrame(train_images[0]) # numpyのArray形式からpandasのDataFrame形式へ変換 df_img_matrix_sorted = df_img_matrix.sort_index(ascending=False) # インデックスで降順にソートする #print(df_img_matrix_sorted) df_img_matrix_sorted.to_csv('No0.csv') # データの前処理 # 0~255階調の数値データを0~1の範囲に変換(正規化) normalized_train_images = train_images / 255.0 normalized_test_images = test_images / 255.0 # 前処理後の画像を表示 plt.figure() plt.imshow(normalized_train_images[0], cmap=plt.cm.binary) plt.colorbar() plt.grid(True) #plt.show() plt.title(class_names[train_labels[0]]) plt.savefig('normalized_No0.png') plt.close() # 代表画像を数値データで表示 df_img_matrix = pd.DataFrame(normalized_train_images[0]) df_img_matrix_sorted = df_img_matrix.sort_index(ascending=False) # インデックスで降順にソートする #print(df_img_matrix_sorted) df_img_matrix_sorted.to_csv('normalized_No0.csv') return normalized_train_images, normalized_test_images # 予測結果のパーセンテージを画像に記載する関数 def plot_image(i, predictions_array, true_label, img): if len(predictions_array) != 1: j = i else: j = 0 # 予測値のリスト内が1枚のみの場合(1枚ずつ処理)、ゼロを代入する predictions_array, true_label, img = predictions_array[j], true_label[i], img[i] plt.grid(False) plt.xticks([]) plt.yticks([]) plt.imshow(img, cmap=plt.cm.binary) predicted_label = np.argmax(predictions_array) if predicted_label == true_label: color = 'blue' else: color = 'red' plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label], 100*np.max(predictions_array), class_names[true_label]), color=color) # 予測結果を棒グラフ化する関数 def plot_value_array(i, predictions_array, true_label): if len(predictions_array) != 1: j = i else: j = 0 # 予測値のリスト内が1枚のみの場合(1枚ずつ処理)、ゼロを代入する predictions_array, true_label = predictions_array[j], true_label[i] plt.grid(False) plt.xticks([]) plt.yticks([]) thisplot = plt.bar(range(10), predictions_array, color="#777777") plt.ylim([0, 1]) predicted_label = np.argmax(predictions_array) thisplot[predicted_label].set_color('red') thisplot[true_label].set_color('blue') # 予測結果を可視化するための親関数 def visualization(predictions, test_labels, test_images, test_images_ori): # X個のテスト画像、予測されたラベル、正解ラベルを表示します。 # 正しい予測は青で、間違った予測は赤で表示しています。 num_rows = 5 num_cols = 3 num_images = num_rows * num_cols plt.figure(figsize=(2 * 2 * num_cols, 2 * num_rows)) for i in range(num_images): plt.subplot(num_rows, 2 * num_cols, 2 * i + 1) plot_image(i, predictions, test_labels, test_images_ori) plt.subplot(num_rows, 2 * num_cols, 2 * i + 2) plot_value_array(i, predictions, test_labels) #plt.show() plt.savefig(file_name + '_summary.png') plt.close() for i in range(num_rows*num_cols): # テスト用データセットから画像を1枚取り出す img = test_images_ori[i] #print('img.shape', img.shape)例:(28, 28) # 画像1枚の数値データをリスト化(tf.kerasモデルはバッチ処理のため) img = (np.expand_dims(img,0)) # サイズ1の次元を追加する。例:(1, 28, 28) #print('img.shape', img.shape) # 予測 if CNN_flag: img = img.reshape(img.shape[0], img_rows, img_cols, 1) predictions_single = model.predict(img) # 画像出力 #print('predictions_single', predictions_single) plt.figure(figsize=(7,3)) plt.subplot(1,2,1) plot_image(i, predictions_single, test_labels, test_images_ori) plt.subplot(1,2,2) plot_value_array(i, predictions_single, test_labels) _ = plt.xticks(range(10), class_names, rotation=45) # 横軸 plt.tight_layout() #plt.show() plt.savefig(file_name + '_' + str(i) + '.png') plt.close() #print("np.argmax(predictions_single[0])", np.argmax(predictions_single[0])) # メイン関数 def main(): global class_names, model, img_rows, img_cols start_time = time.time() ##### データのロード fashion_mnist = keras.datasets.fashion_mnist (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data() # データセットにはクラス名がないため、後ほど画像出力時に使用するために定義 class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] ##### データの正規化 train_images, test_images = check_data_and_normalization(train_images, train_labels, test_images, test_labels) # データチェック img_rows = train_images[0].shape[0] # 行数 img_cols = test_images[0].shape[1] # 列数 print('img_rows', img_rows) print('img_cols', img_cols) ##### # モデルの構築 if CNN_flag: # 畳み込みニューラルネットワーク:Convolutional Neural Network(CNN) print('CNN_flag', CNN_flag) print(keras.backend.image_data_format()) # 2D畳み込みニューラルネットワークの場合、4次元配列にする # change from (6000,28,28) to (6000,28,28,1) train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, 1) test_images_bp = test_images # 後に可視化するためにバックアップ print("test_images test_images_bp") print(test_images, test_images_bp) test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, 1) model = keras.Sequential([ keras.layers.Conv2D(filters = 32, # 2次元フィルタの枚数 kernel_size = (3,3), # フィルタのサイズ(縦, 横) padding = "same", # ゼロパンディング。フィルタリングによる画素数低下の防止 activation = "relu", # 活性化関数。Noneの場合は線形活性 input_shape = (img_rows,img_cols,1)), # 読み込むデータ。28×28の1チャネル。前処理しないRGBの場合は3チャネル keras.layers.MaxPooling2D(pool_size=(2,2)), # プーリング層(2×2の最大プーリング手法)。座標ずれの感度を低くする keras.layers.Flatten(), # 1次元に変換 keras.layers.Dense(64, activation='relu'), keras.layers.Dropout(drop_rate), # ドロップアウト率 keras.layers.Dense(10, activation='softmax') # ソフトマックス関数で、10個のクラスに分類する。 ]) else: # 普通のニューラルネットワーク:Neural Network(NN) model = keras.Sequential([ keras.layers.Flatten(input_shape=(img_rows, img_cols)), keras.layers.Dense(64, activation='relu'), keras.layers.Dropout(drop_rate), keras.layers.Dense(10, activation='softmax') ]) #最適化アルゴリズムの設定 optimization_algorithm = keras.optimizers.Adam(lr=learning_rate, beta_1=0.9, beta_2=0.999, epsilon=1e-08) # モデルのコンパイル model.compile(loss = 'sparse_categorical_crossentropy', # 損失関数 optimizer = optimization_algorithm, metrics = ['accuracy']) # 評価関数←ウォッチしてるだけ。訓練には使われない。 # モデルのサマリー print('model.summary\n', model.summary) # 訓練データを用いてモデルの訓練 early_stopping = keras.callbacks.EarlyStopping(monitor='loss', patience=10, verbose=1) # 枝刈り hist = model.fit(train_images, train_labels, batch_size = batch_size, epochs = epochs, callbacks = [early_stopping]) # 学習曲線をグラフ化 loss = hist.history['loss'] plt.rc('font', family='serif') fig = plt.figure() if CNN_flag: mycolor = 'red' else: mycolor = 'blue' plt.plot(np.arange(1, len(loss)+1), loss, label='loss', color=mycolor) plt.xlabel('epochs') plt.ylabel('loss') print(len(loss)) print(type(len(loss))) plt.xticks(np.arange(1, int(len(loss)+1), 1)) plt.ylim([0,1]) plt.grid() plt.title(file_name) plt.savefig(file_name + '_epochs.png') plt.close() # テストデータを用いて正解率の評価 test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2) print('\nTest loss:', test_loss) print('Test accuracy:', test_acc) # 予測 predictions = model.predict(test_images) ##### 予測結果を抜き取り検査(可視化) if CNN_flag: visualization(predictions, test_labels, test_images, test_images_bp) else: visualization(predictions, test_labels, test_images, test_images) model.save(file_name + '_model.h5') print('analysis time:{0:.3f} sec'.format(time.time() - start_time)) with open('result.txt', 'a') as f: f.write(file_name + '_test_loss=' + str(test_loss) + '\n') f.write(file_name + '_test_acc=' + str(test_acc) + '\n') f.write(file_name + '_time=' + str(time.time() - start_time) + '\n') if __name__ == '__main__': print(tf.__version__) # parameter CNN_flag = True if CNN_flag: file_name = 'CNN' else: file_name = 'NN' epochs = 10 # 世代数 batch_size = 32 # バッチサイズ default 32 learning_rate = 0.001 # 学習率 default 0.001 drop_rate = 0.5 # ドロップアウト率 # call function main()
以上
<広告>