WSLとJupyter-notebookでCNN、その2、CNNを試す
前回の続き
mecobalamin.hatenablog.com
環境ができたので手持ちのデータセットを試してみる
こちらのサイトを参考にコードを作成した
kerasでCNN 自分で拾った画像でやってみる - Qiita
主な変更点は以下の3点
- 画像の読み込み
- mnist_cnn.pyのモデルを使用
- label、predict、画像ファイル名を保存する
こちらの環境ではlist_picturesが動かなかったので
ファイルを読み込む関数Image_Listを定義した
globは引数で指定したパスのファイル名を再帰的に取得する
ファイル名だけ取得したいのでrelpathで処理している
モデルはmnist_cnn.pyのと同じモデルを使ってみた
画像の分類は手動で行って
negativeとpositiveのディレクトリに保存してある
それぞれの画像がどのように分類されているかを知りたかったので
画像のファイル名とそれぞれがどのようにpredictされているかを
csvファイルに保存した
fit()で出力されるlistのindexも
バージョン違いのせいか"accu"ではエラーが出るので
"accuracy"に修正してある
実際のコードは以下の通り
# ライブラリのインポート import keras from keras.utils import np_utils from keras.layers.convolutional import Conv2D, MaxPooling2D from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation, Flatten from keras.preprocessing.image import array_to_img, img_to_array, load_img from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt import pandas as pd import numpy as np from glob import glob from os.path import relpath # ファイル名の取得 def Image_List(Path_Images, Ext): List_Images = glob(Path_Images + "/*." + Ext) List_Names = [] for i in List_Images: List_Names.append(relpath(i, Path_Images)) return List_Names # 画像の拡張子 Ext_Type = "png" # 画像のパス Path_Negative = '/path/to/negative/' Path_Positive = '/path/to/positive/' X = [] Y = [] # ネガティブ画像の読み込み Image_Negative = Image_List(Path_Negative, Ext_Type) for picture in Image_Negative: img = img_to_array(load_img(Path_Negative + picture, color_mode = "grayscale", target_size=(256, 256))) X.append(img) Y.append(0) # ポジティブ画像の読み込み Image_Positive = Image_List(Path_Positive, Ext_Type) for picture in Image_Positive: img = img_to_array(load_img(Path_Positive + picture, color_mode = "grayscale", target_size=(256, 256))) X.append(img) Y.append(1) # arrayに変換 # Zはファイル名のリスト X = np.asarray(X) Y = np.asarray(Y) Z = pd.DataFrame(Image_Negative + Image_Positive) # 画素値を規格化 X = X.astype('float32') X = X / 255.0 # one-hot表現に変換 Y = np_utils.to_categorical(Y, 2) # 学習用データとテストデータに分割 # ファイル名のリストも分割 X_train, X_test, y_train, y_test, indices_train, indices_test = train_test_split(X, Y, Z, test_size=0.33, random_state=111) # モデルの構築 model = Sequential() model.add(Conv2D(20, kernel_size=(3, 3), activation='relu', input_shape=X_train.shape[1:])) model.add(Conv2D(40, (3, 3), activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(2)) # クラスは2個 model.add(Activation('softmax')) # コンパイル model.compile(loss='categorical_crossentropy', optimizer='SGD', metrics=['accuracy']) # 実行 # 出力有り(verbose=1)。 history = model.fit(X_train, y_train, batch_size=300, epochs=4[f:id:mecobalamin:20200608101008p:plain]0, validation_data = (X_test, y_test), verbose = 1) plt.plot(history.history['accuracy']) plt.plot(history.history['val_accuracy']) plt.title('model accuracy') plt.xlabel('epoch') plt.ylabel('accuracy') plt.legend(['acc', 'val_acc'], loc='lower right') plt.show() # テストデータに適用 predict_classes = model.predict_classes(X_test) # マージ。yのデータは元に戻す mg_df = pd.DataFrame({'predict': predict_classes, 'class': np.argmax(y_test, axis=1)}) # confusion matrix pd.crosstab(mg_df['class'], mg_df['predict']) # 結果の保存 # テストに使った画像ファイルのリストも保存 df_h = pd.concat([pd.DataFrame(y_test), indices_test], axis = 1) pd.DataFrame(y_test).to_csv("/path/to/y_test.csv") indices_test.to_csv("/path/to/indices_test.csv") pd.DataFrame(predict_classes).to_csv("/path/to/predict_test.csv")
jupyter-notebookの複数のcellをまとめたが
gistを使うとそのまま貼り付けられるらしい
計算結果は以下の通り
Core i5-7200UのノートPCで
大体6時間ぐらいかかった
Train on 3287 samples, validate on 1619 samples Epoch 1/40 3287/3287 [==============================] - 522s 159ms/step - loss: 0.6425 - accuracy: 0.5117 - val_loss: 0.5973 - val_accuracy: 0.5374 Epoch 2/40 3287/3287 [==============================] - 494s 150ms/step - loss: 0.5793 - accuracy: 0.6693 - val_loss: 0.5340 - val_accuracy: 0.7739 Epoch 3/40 3287/3287 [==============================] - 493s 150ms/step - loss: 0.5429 - accuracy: 0.7478 - val_loss: 0.5146 - val_accuracy: 0.7573 ~~~ 省略 ~~~ Epoch 38/40 3287/3287 [==============================] - 501s 152ms/step - loss: 0.4346 - accuracy: 0.8059 - val_loss: 0.4734 - val_accuracy: 0.7900 Epoch 39/40 3287/3287 [==============================] - 493s 150ms/step - loss: 0.4001 - accuracy: 0.8190 - val_loss: 0.5238 - val_accuracy: 0.7622 Epoch 40/40 3287/3287 [==============================] - 497s 151ms/step - loss: 0.4388 - accuracy: 0.8026 - val_loss: 0.4895 - val_accuracy: 0.7857
Epochが増えると学習データに対するAccuracyは増えているが
テストデータに対するAccuracyはあまり増えていない
Jupyter Notebookをはてなブログに貼り付ける方法 - akatak’s blog
画像の分類は以下の通り
classは学習時に使った画像のラベルで
0はnegative、1はpositiveを表す
predictはcnnで分類された画像で
0/1はやはりnegative/positiveを表す
predict | |||
---|---|---|---|
0 | 1 | ||
class | 0 | 652 | 104 |
1 | 243 | 620 |
10%-30%程度間違っている
グラフにしてないけれど損失関数も
mnistのデータを使ったときほど減っていない
使ったモデルが合っていないのか
教師データの分類が不十分なのか
そもそもデータの数が足りないのか
分類できた画像を確認したら
見た目にも分類が不十分に感じる
この画像がネガティブに分類?みたいな
計算はできたけど
ここからの修正はどうしたらいいだろうか