前提・実現したいこと
CNNを用いて自前データセットの画像の2値分類を行おうと思っています。
データセットが不均衡なので(クラス0:クラス1=8:1くらい)、クラス1のデータを画像のデータ拡張によってオーバーサンプリングしています。
該当のソースコード
python
1import os 2import numpy as np 3import keras 4from keras import layers 5from keras import models 6from keras import optimizers 7from keras.preprocessing.image import ImageDataGenerator 8from keras.utils import plot_model 9import tensorflow.keras.backend as K 10 11from sklearn.utils import shuffle 12 13import pandas as pd 14 15import matplotlib 16matplotlib.use('Agg') 17import matplotlib.pyplot as plt 18plt.style.use('ggplot') 19 20from datasets import make_dir, extract_data 21 22def cnn_model(image_size): 23 model = models.Sequential() 24 model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 1))) 25 model.add(layers.normalization.BatchNormalization()) 26 model.add(layers.MaxPooling2D((2, 2))) 27 model.add(layers.Conv2D(64, (3, 3), activation='relu')) 28 model.add(layers.MaxPooling2D((2, 2))) 29 model.add(layers.Conv2D(64, (3, 3), activation='relu')) 30 model.add(layers.Flatten()) 31 model.add(layers.Dropout(0.5)) 32 model.add(layers.Dense(30, activation='relu')) 33 model.add(layers.normalization.BatchNormalization()) 34 model.add(layers.Dense(1, activation='sigmoid')) 35 return model 36 37def train(x_train, y_train, x_val, y_val, image_size, output_dir, fold_num=5): 38 model =cnn_model(image_size) 39 model.summary() 40 plot_model(model, show_shapes=True, to_file=output_dir + '/model.png') 41 history_list = [] 42 csv_dir = os.path.join(output_dir, 'training_result_csv') 43 make_dir(csv_dir) 44 model_dir = os.path.join(output_dir, 'saved_model') 45 make_dir(model_dir) 46 # データの水増し(Data Augmentation) 47 datagen = ImageDataGenerator(rescale=1./255, 48 rotation_range=40, 49 width_shift_range=0.2, 50 height_shift_range=0.2, 51 shear_range=0.2, 52 zoom_range=0.2, 53 horizontal_flip=True) 54 x_0 = x_train[np.where(y_train==0)[0]] 55 x_1 = x_train[np.where(y_train==1)[0]] 56 y_0 = y_train[np.where(y_train==0)[0]] 57 x_1, y_1 = over_sampling(x_1, datagen, 8, 1) 58 x_train = np.concatenate([x_0, x_1]) 59 y_train = np.concatenate([y_0, y_1]) 60 x_train, y_train = shuffle(x_train, y_train, random_state=0) 61 # 水増し画像を訓練用画像の形式に合わせる 62 datagen.fit(x_train, augment=True, rounds=10) 63 filename = os.path.join(csv_dir, 'training_result.csv') 64 callbacks_list = [keras.callbacks.CSVLogger(filename)] 65 model.compile(loss='binary_crossentropy', 66 optimizer=optimizers.Adam(lr=1e-4), 67 metrics=['acc']) 68 history = model.fit_generator(datagen.flow(x_train, y_train, batch_size=32), 69 steps_per_epoch=x_train.shape[0] // 32, 70 epochs=20, 71 verbose=1, 72 validation_data=(x_val, y_val), 73 callbacks=callbacks_list) 74 history_list.append(history) 75 model.save(os.path.join(model_dir, 'saved_model.h5')) 76 return history_list 77 78def draw_graph(history_list, output_dir): 79 for i, history in enumerate(history_list): 80 acc = history.history['acc'] 81 val_acc = history.history['val_acc'] 82 loss = history.history['loss'] 83 val_loss = history.history['val_loss'] 84 85 epochs = range(1, len(acc) + 1) 86 87 plt.figure() 88 plt.plot(epochs, acc, 'bo', label='Training acc') 89 plt.plot(epochs, val_acc, 'b', label='Validation acc') 90 plt.title('Training and validation accuracy') 91 plt.legend() 92 plt.savefig(output_dir + '/acc{}.png'.format(i)) 93 plt.show() 94 95 plt.figure() 96 plt.plot(epochs, loss, 'bo', label='Training loss') 97 plt.plot(epochs, val_loss, 'b', label='Validation loss') 98 plt.title('Training and validation loss') 99 plt.legend() 100 plt.savefig(output_dir + '/loss{}.png'.format(i)) 101 plt.show() 102 103def over_sampling(datas, datagen, num, label_num): 104 imgs = [] 105 label_list = [] 106 for x in datas: 107 x = np.expand_dims(x, axis=0) 108 for d in datagen.flow(x, batch_size=1): 109 d = np.squeeze(d, axis=0) 110 imgs.append(d) 111 label_list.append(label_num) 112 if (len(imgs) % num) == 0: 113 break 114 return np.array(imgs), np.expand_dims(np.array(label_list), axis=-1) 115 116if __name__ == '__main__': 117 train_dir = 'datasets/train_binary/' 118 valid_dir = 'datasets/valid_binary/' 119 output_dir = 'model_out_shuffle' 120 classes = ['0', '1'] 121 image_size = 150 122 123 make_dir(output_dir) 124 x_train, y_train = extract_data(train_dir, classes, image_size) 125 x_val, y_val = extract_data(valid_dir, classes, image_size) 126 history_list = train(x_train, y_train, x_val, y_val, image_size, output_dir) 127 graph_dir = os.path.join(output_dir, 'training_result_graph') 128 make_dir(graph_dir) 129 draw_graph(history_list, graph_dir) 130 df_dir = os.path.join(output_dir, 'df_csv') 131 make_dir(df_dir) 132 for i, history in enumerate(history_list): 133 df = pd.DataFrame(history.history) 134 filename = os.path.join(df_dir, 'training{}_history.csv'.format(i+1)) 135 df.to_csv(filename) 136
発生している問題・エラーメッセージ
実際に学習させてログを見ると、二回目のエポックから訓練データの精度は1.0、検証データの精度は0.87で停止してしまっています。
Epoch 1/20 53/53 [==============================] - 118s 2s/step - loss: 0.0361 - acc: 0.9870 - val_loss: 2.5961 - val_acc: 0.8700 Epoch 2/20 53/53 [==============================] - 118s 2s/step - loss: 0.0029 - acc: 1.0000 - val_loss: 4.4108 - val_acc: 0.8700 Epoch 3/20 53/53 [==============================] - 117s 2s/step - loss: 0.0044 - acc: 0.9994 - val_loss: 7.1868 - val_acc: 0.8700 Epoch 4/20 53/53 [==============================] - 115s 2s/step - loss: 0.0028 - acc: 1.0000 - val_loss: 12.1159 - val_acc: 0.8700 Epoch 5/20 53/53 [==============================] - 115s 2s/step - loss: 0.0018 - acc: 1.0000 - val_loss: 18.7604 - val_acc: 0.8700 Epoch 6/20 53/53 [==============================] - 116s 2s/step - loss: 0.0022 - acc: 1.0000 - val_loss: 27.9707 - val_acc: 0.8700 Epoch 7/20 53/53 [==============================] - 116s 2s/step - loss: 0.0021 - acc: 1.0000 - val_loss: 40.4534 - val_acc: 0.8700 Epoch 8/20 53/53 [==============================] - 115s 2s/step - loss: 0.0014 - acc: 1.0000 - val_loss: 54.9640 - val_acc: 0.8700 Epoch 9/20 53/53 [==============================] - 115s 2s/step - loss: 0.0015 - acc: 1.0000 - val_loss: 72.0444 - val_acc: 0.8700 Epoch 10/20 53/53 [==============================] - 117s 2s/step - loss: 0.0014 - acc: 1.0000 - val_loss: 90.2816 - val_acc: 0.8700 Epoch 11/20 53/53 [==============================] - 120s 2s/step - loss: 0.0012 - acc: 1.0000 - val_loss: 105.6784 - val_acc: 0.8700 Epoch 12/20 53/53 [==============================] - 117s 2s/step - loss: 0.0014 - acc: 1.0000 - val_loss: 121.3914 - val_acc: 0.8700 Epoch 13/20 53/53 [==============================] - 115s 2s/step - loss: 0.0010 - acc: 1.0000 - val_loss: 140.8129 - val_acc: 0.8700 Epoch 14/20 53/53 [==============================] - 116s 2s/step - loss: 0.0012 - acc: 1.0000 - val_loss: 160.8408 - val_acc: 0.8700 Epoch 15/20 53/53 [==============================] - 118s 2s/step - loss: 9.9551e-04 - acc: 1.0000 - val_loss: 169.9402 - val_acc: 0.8700 Epoch 16/20 53/53 [==============================] - 116s 2s/step - loss: 0.0013 - acc: 1.0000 - val_loss: 181.2626 - val_acc: 0.8700 Epoch 17/20 53/53 [==============================] - 116s 2s/step - loss: 0.0012 - acc: 1.0000 - val_loss: 178.5618 - val_acc: 0.8700 Epoch 18/20 53/53 [==============================] - 116s 2s/step - loss: 8.5294e-04 - acc: 1.0000 - val_loss: 183.2046 - val_acc: 0.8700 Epoch 19/20 53/53 [==============================] - 123s 2s/step - loss: 9.1750e-04 - acc: 1.0000 - val_loss: 187.8404 - val_acc: 0.8700 Epoch 20/20 53/53 [==============================] - 123s 2s/step - loss: 7.0443e-04 - acc: 1.0000 - val_loss: 179.0444 - val_acc: 0.8700
試したこと
検証の精度0.87はクラス0とクラス1の比なので、おそらくすべてクラス0と予測して出力しているだろうことはわかりました。しかし、訓練の精度が1.0で停止している理由がわかりません。また、その解決策があればよろしくおねがいします。
回答1件
あなたの回答
tips
プレビュー