Keita_Nakamoriです。TensorFlowに疲れてきました。
早くwebアプリの部分にいきたい・・・。
今回は、転移学習を試してみます。学習済みのCNNであるVGG16を使って、その後ろに中間層と全結合層をマニュアルで挿入して最終的にsoftmax関数を通して結果を出力します。
中間層は256node、全結合層は2クラスしかないので2nodeです。
スクリプト:VGG16_trans.py
前回まで、import keras をして kerasを使用していましたが、今どきはTensorFlowのクラスとして存在しているので使ってみます。
というのもfrom keras.models import Modelでエラーが出てしまいどうしようもないので調べていたら見つけました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
import numpy as np import matplotlib.pyplot as plt # keras は tensorflowのクラスとして存在する from tensorflow import keras from tensorflow.keras.models import Sequential, Model from tensorflow.keras.layers import Dense, Dropout, Flatten from tensorflow.keras.layers import Conv2D, MaxPooling2D from tensorflow.keras.optimizers import SGD, Adam from tensorflow.python.keras.utils import np_utils from tensorflow.python.keras.applications import VGG16 # 出力結果のクラスを定義 classes = ["car", "motorbike"] num_classes = len(classes) # クラス数を定義 # 画像サイズを定義(VGG16に合うように) image_size = 224 # npyデータを読み込み、訓練用・検証用データに分割する X_train, X_test, y_train, y_test = np.load( "./image_files_224.npy", allow_pickle=True ) # one hot エンコーディング y_train = np_utils.to_categorical(y_train, num_classes) y_test = np_utils.to_categorical(y_test, num_classes) # ここで入力データの正規化を行う X_train = X_train.astype("float") / 255.0 X_test = X_test.astype("float") / 255.0 # VGG16モデルを定義する model = VGG16(weights='imagenet', include_top=False, input_shape=(image_size,image_size,3)) # 以降のニューラルネットワーク層を定義する。top_model # VGG16の出力がSequentialで出てくるので、追加する全結合層もシーケンシャルで作成しておく。 top_model = Sequential() top_model.add(Flatten(input_shape=model.output_shape[1:])) top_model.add(Dense(256, activation='relu')) top_model.add(Dropout(0.5)) top_model.add(Dense(num_classes, activation='softmax')) # VGG16と追加層を結合する # VGG16の出力を追加層の入力に入れる。 model = Model(inputs=model.input, outputs=top_model(model.output)) # モデルのサマリーを確認する model.summary() # トレーニングしないようにパラメータをフリーズ(固定化)する # 必ずcompileの前に入れる for layer in model.layers[:15]: layer.trainable = False # オプティマイザーを定義する opt = Adam(lr=0.0001) model.compile(loss='categorical_crossentropy', optimizer=opt,metrics=['accuracy']) # 訓練をする history = model.fit(X_train, y_train, batch_size=32, epochs=17) # 評価する score = model.evaluate(X_test, y_test, batch_size=32) print('Test loss:', score[0]) print('Test accuracy:', score[1]) # モデルを保存する model.save("./vgg16_trans.h5") # 可視化する loss = history.history['loss'] nb_epoch = len(loss) plt.plot(range(nb_epoch), loss, marker='.', label='loss') plt.legend(loc='best', fontsize=10) plt.grid() plt.xlabel('epoch') plt.ylabel('loss') plt.show() |
構造
スクリプト内の# モデルのサマリーを確認する model.summary() までを実行するとニューラルネットワークの構造が確認できます。
最後に、sequential (Sequential) (None, 2) が新しく生成されました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ sequential (Sequential) (None, 2) 6423298 ================================================================= Total params: 21,137,986 Trainable params: 21,137,986 Non-trainable params: 0 _________________________________________________________________ |
結果
VGG16層 全結合2層 17エポックまでやってみましたが、数十分かかってしまいました。流石のディープさです。
しかし、結果は素晴らしいい。トレーニング100% テスト99%の精度。
そして、モデルファイルである192MB のvgg16_trans.h5 ファイルも上手く生成できていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
600/600 [==============================] - 226s 376ms/sample - loss: 0.2884 - acc: 0.8750 Epoch 2/17 600/600 [==============================] - 241s 402ms/sample - loss: 0.0544 - acc: 0.9833 Epoch 3/17 600/600 [==============================] - 1048s 2s/sample - loss: 0.0109 - acc: 0.9983 Epoch 4/17 600/600 [==============================] - 235s 392ms/sample - loss: 0.0085 - acc: 0.9967 Epoch 5/17 600/600 [==============================] - 252s 421ms/sample - loss: 0.0012 - acc: 1.0000 Epoch 6/17 600/600 [==============================] - 242s 403ms/sample - loss: 4.5241e-04 - acc: 1.0000 Epoch 7/17 600/600 [==============================] - 235s 391ms/sample - loss: 3.2469e-04 - acc: 1.0000 Epoch 8/17 600/600 [==============================] - 235s 391ms/sample - loss: 1.6192e-04 - acc: 1.0000 Epoch 9/17 600/600 [==============================] - 227s 378ms/sample - loss: 1.1878e-04 - acc: 1.0000 Epoch 10/17 600/600 [==============================] - 228s 380ms/sample - loss: 4.5246e-05 - acc: 1.0000 Epoch 11/17 600/600 [==============================] - 228s 381ms/sample - loss: 1.3106e-04 - acc: 1.0000 Epoch 12/17 600/600 [==============================] - 234s 390ms/sample - loss: 6.3007e-05 - acc: 1.0000 Epoch 13/17 600/600 [==============================] - 229s 382ms/sample - loss: 2.4614e-05 - acc: 1.0000 Epoch 14/17 600/600 [==============================] - 230s 383ms/sample - loss: 3.7191e-05 - acc: 1.0000 Epoch 15/17 600/600 [==============================] - 229s 381ms/sample - loss: 3.3313e-05 - acc: 1.0000 Epoch 16/17 600/600 [==============================] - 235s 392ms/sample - loss: 3.7981e-05 - acc: 1.0000 Epoch 17/17 600/600 [==============================] - 230s 383ms/sample - loss: 7.3269e-06 - acc: 1.0000 200/200 [==============================] - 67s 333ms/sample - loss: 0.0636 - acc: 0.9900 |
スクリプト:vgg16_predict.py
では、転移学習済みのvgg16_trans.h5モデルをロードして、Anacondaプロンプトからサンプル画像を入力することによってcar なのか motorbikeなのか予測してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import numpy as np import matplotlib.pyplot as plt from PIL import Image import sys # keras は tensorflowのクラスとして存在する from tensorflow import keras from tensorflow.keras.models import load_model '''トレーニング済みなので 不要 from tensorflow.keras.layers import Dense, Dropout, Flatten from tensorflow.keras.layers import Conv2D, MaxPooling2D from tensorflow.keras.optimizers import SGD, Adam from tensorflow.python.keras.utils import np_utils from tensorflow.python.keras.applications import VGG16 ''' # 出力結果のクラスを定義 classes = ["car", "motorbike"] num_classes = len(classes) # クラス数を定義 # 画像サイズを定義(VGG16に合うように) image_size = 224 # 画像ファイルをコマンドラインの第2引数から開く image = Image.open(sys.argv[1]) # 画像ファイルをRGBに変換する image = image.convert('RGB') # 画像を同じサイズに揃える image = image.resize((image_size, image_size)) # RGB値をnumpy配列に変換する data = np.asarray(image) # データを正規化する data = data / 255.0 # リストにappendしてnumpy配列にする X = [] X.append(data) X = np.array(X) # VGG16_transモデルをロードする model = load_model('./vgg16_trans.h5') # VGG16_transモデルにデータを入れて予測結果を返す # スコアが配列で帰ってくるので[0]番目をつける result = model.predict([X])[0] # 最も大きい数字を採用する predicted = result.argmax() percentage = int(result[predicted] * 100) # プリントする print(classes[predicted], percentage) print('==== script is done ====') |
予測
1.車の画像データの一つをcar1.jpgにリネームしてdjangoaiフォルダ直下に移動します。
(djangoai) C:\Users\keita\anaconda_projects\djangoai>python vgg16_predict.py car1.jpg
結果:car 100
2.次にバイクの画像データの一つをbike1.jpgにリネームしてdjangoaiフォルダ直下に移動します。
(djangoai) C:\Users\keita\anaconda_projects\djangoai>python vgg16_predict.py motorbike1.jpg
結果:motorbike 100
3.試しにビキニ画像データを入力してみましたが・・・バイク100%になりました。(笑)
次回
Web Application: 第8回 はじめてのwebアプリ