CNN始めました
初めてCNNの実装を行ったのでメモしておきます。
きっかけ
今取り組んでいる研究でCNNを使おうとしています。CNNの理論はある程度理解していましたが、実装したことがなかったので7層のCNNの実装を行いました。
CNNの構築を行う上で、以下のkernelを参考にしました。
Introduction to CNN Keras - Acc 0.997 (top 8%): https://www.kaggle.com/yassineghouzam/introduction-to-cnn-keras-0-997-top-6/notebook
実装
前処理
最初に画像を読み込んだ上で正規化をします。
train = pd.read_csv("../data/mnist_train.csv") test = pd.read_csv("../data/mnist_test.csv") X_train = train.drop(columns='label') y_train = train['label'] X_train = X_train/255.0 test = test/255.0
次に画像を28×28で1チャンネルの画像として読み込ませます。 reshape内の28は画像の縦横、1はチャンネル数を示します。 -1は任意を示しているようですがまだよく分かってないです……
X_train = X_train.values.reshape(-1,28,28,1) test = test.values.reshape(-1,28,28,1)
次に、ラベルエンコーディングをして0~9まであるラベルを1つのベクトルに変換します。
例: 2 -> [0,0,1,0,0,0,0,0,0,0]
y_train = to_categorical(y_train, num_classes=10)
あとはデータ分割を行い、学習データの方で回転させたりランダムにノイズを混ぜた画像を入れることで学習データを増加させて過学習を起こしにくくしました(詳しくは参考ページ)。
CNNのモデル構築と実行
今回はちょうど読んでいた論文で使われていた畳み込み層が7つあるCNNの構造に近い形で構築しました。
構造: In -> 4(Conv2D -> ReLU) -> MaxPool2D -> 2(Conv2D -> ReLU) -> MaxPool2D -> (Conv2D -> ReLU) -> MaxPool2D -> Flatten -> Dence -> Out
padding: 'Same' or 'Valid' (zero_paddingの場合はSame)
Flatten: 1次元データに変形
Dropout: 括弧内の割合でネットワークをランダムに切る
Dense: 全結合層
model = Sequential() model.add(Conv2D(filters=32, kernel_size=(3,3), padding='Same', activation='relu', input_shape=(28,28,1))) model.add(Conv2D(filters=32, kernel_size=(3,3), padding='Same', activation='relu')) model.add(Conv2D(filters=32, kernel_size=(3,3), padding='Same', activation='relu')) model.add(Conv2D(filters=32, kernel_size=(3,3), padding='Same', activation='relu')) model.add(MaxPool2D(pool_size=(2,2))) model.add(Conv2D(filters=64, kernel_size=(3,3), padding='Same', activation='relu')) model.add(Conv2D(filters=64, kernel_size=(3,3), padding='Same', activation='relu')) model.add(MaxPool2D(pool_size=(2,2), strides=(2,2))) model.add(Conv2D(filters=128, kernel_size=(3,3), padding='Same', activation='relu')) model.add(MaxPool2D(pool_size=(2,2), strides=(2,2))) model.add(Flatten()) model.add(Dense(512, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(10, activation='softmax'))
最適化関数はRMSpropを用いました。他の方の記事を見ると、MNISTではこの手法が精度が良いことが多いようです。これ以外にも確率的勾配降下法 (SGD) やAdamなど様々な方法があります。
optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)
それ以外の設定は以下の通りで行いました。
epoch数は5、バッチサイズは100で行いました。
model.compile(optimizer = optimizer , loss = "categorical_crossentropy",metrics=["accuracy"]) learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc', patience=3, verbose=1, factor=0.5, min_lr=0.00001) epochs = 5 batch_size = 100 history = model.fit_generator(datagen.flow(X_train,y_train, batch_size=batch_size), epochs = epochs, validation_data = (x_test,y_test), verbose = 2, steps_per_epoch=X_train.shape[0] // batch_size , callbacks=[learning_rate_reduction]) print("\nfinish!")
結果は以下の通りです。
Epoch 1/5 - 225s - loss: 0.4281 - acc: 0.8587 - val_loss: 0.0491 - val_acc: 0.9838 Epoch 2/5 - 238s - loss: 0.0918 - acc: 0.9723 - val_loss: 0.0356 - val_acc: 0.9879 Epoch 3/5 - 256s - loss: 0.0644 - acc: 0.9798 - val_loss: 0.0279 - val_acc: 0.9910 Epoch 4/5 - 237s - loss: 0.0521 - acc: 0.9844 - val_loss: 0.0333 - val_acc: 0.9917 Epoch 5/5 - 250s - loss: 0.0467 - acc: 0.9864 - val_loss: 0.0385 - val_acc: 0.9902 finish!
また、今回のモデルでKaggleのDigit Recognizerに投稿してみたところ、0.99128とそこそこのスコアを出すことができました。
ただ、このモデルの学習を自分のi5-6200UのCPUを積んだノートPCで回すとおおよそ20分程度かかり、かなり高温になりました(汗)。この前にGPUを積んだPCで実行したときやKaggleのkernelでGPUを使って実行した時は30~60秒位で終わったのでやはりGPUを使った方がいいようです(当たり前)。
可視化
結果を見てみると、誤って4や8と認識したものが多かったようです。
まとめ
無理矢理CPUでやろうとしたせいで無駄に苦労しました……次からGPU使ってやります……
また、モデルそのものや各関数で多くのパラメータがあり理解できてないところもあるので、他のデータでも試しつつ覚えていこうかと思います。
今回のソースコード: