kaggle の Digit Recognizer に挑戦する (2)
前回 kaggle の Digit Recognizer というコンペティションに挑戦しましたが、今回は、ニューラルネットワークの構成を変えて、畳み込みニューラルネットワークで学習をしたいと思います。畳み込みニューラルネットワークについては、私が理解するのに参考にした動画やサイトを、この記事の最後にリンクしておきましたので、そちらを参照してください。
データ
データの準備は前回とほぼ同じなので、記載を適宜割愛しますが、畳み込みニューラルネットワークへの入力を 28 * 28 * 1 にしないといけないので、その部分を調整していきます。
import keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten from keras.utils import to_categorical import matplotlib.pyplot as plt import seaborn as sns import numpy as np import pandas as pd
# データ読み込み train = np.loadtxt('/root/practice/digit/csv/train.csv', skiprows=1, delimiter=',') x_test = np.loadtxt('/root/practice/digit/csv/test.csv', skiprows=1, delimiter=',') # print(train.shape) # print(x_test.shape) # 1列目と2〜785列目を分ける y_train, x_train = np.split(train, [1], 1) # print(x_train.shape) # print(y_train.shape) # 形状を 枚数 × 28 × 28 × 1 に変形する x_train = x_train.reshape(42000, 28, 28, 1) x_test = x_test.reshape(28000, 28, 28, 1) # print(x_train.shape) # print(x_test.shape) # 1枚分を取り出し表示させてみる # plt.imshow(x_train[0]) # ラベル確認 # print(y_train[0]) # 0 - 1 の間の数値にする x_train = x_train.astype('float32') x_test = x_test.astype('float32') x_train /= 255 x_test /= 255 # one-hot 表現にする y_train = to_categorical(y_train, 10) # print(y_train[0])
畳み込みニューラルネットワーク構築
それでは、畳み込みニューラルネットワークを構築していきます。
model = Sequential() # 畳み込み層 model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))) # プーリング層 model.add(MaxPooling2D(pool_size=(2, 2))) # 出力を1次元にする model.add(Flatten()) # 出力層 model.add(Dense(10, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary()
Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 26, 26, 32) 320 max_pooling2d (MaxPooling2D (None, 13, 13, 32) 0 ) flatten (Flatten) (None, 5408) 0 dense (Dense) (None, 10) 54090 ================================================================= Total params: 54,410 Trainable params: 54,410 Non-trainable params: 0 _________________________________________________________________
Conv2Dが畳み込み層になります。各引数の意味は下記の通りです。
- filters: 出力空間の次元=出力フィルタの数
- kernel_size: 2次元畳み込みウィンドウの大きさ
- activation: 活性化関数
- input_shape: 入力次元
MaxPooling2Dはプーリング層になります。引数 pool_size はプーリングするウィンドウの大きさです。
それでは学習していきます。
history = model.fit(x_train, y_train, epochs=5)
結果をグラフで表示します。
sns.lineplot(data=history.history["loss"])
sns.lineplot(data=history.history["accuracy"])
学習が進むにつれて、誤差が減少し、精度が増加していくことがわかります。具体的な誤差と精度の数値も求めておきます(結果は後述)。
loss, accuracy = model.evaluate(x_train, y_train) print(loss) print(accuracy)
設定を変える
上で紹介した畳み込みニューラルネットワークの構成をベースとして、いろいろと一つずつパラメータや構成を変えて誤差と精度がどう変わるのか試してみます。変えたパラメータや構成以外はベースと同じです。今回は下記のようにパラメータ・構成を変えてみました。
(1) Conv2D の kernel_size を (4, 4) にする
(2) Conv2D の kernel_size を (2, 2) にする
(3) MaxPooling2D の pool_size を (3, 3) にする
(4) epochs を 10 にする
(5) 全結合層を追加する
(6) Conv2D, MaxPooling2D の層を追加する
「(5) 全結合層を追加する」は、具体的に下記のように設定しています。
model = Sequential() model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) # この行を追加 model.add(Dense(10, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary()
「(6) Conv2D, MaxPooling2D の層を追加する」は、具体的に下記のように設定しています。
model = Sequential() model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu')) # この行を追加 model.add(MaxPooling2D(pool_size=(2, 2))) # この行を追加 model.add(Flatten()) model.add(Dense(10, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary()
それぞれの結果は下記の通りになりました。
loss | accuracy | |
---|---|---|
ベース | 0.0354999378323555 | 0.9896904826164246 |
(1) Conv2D の kernel_size を (4, 4) にする | 0.03126463294029236 | 0.9907143115997314 |
(2) Conv2D の kernel_size を (2, 2) にする | 0.04609178006649017 | 0.9873571395874023 |
(3) MaxPooling2D の pool_size を (3, 3) にする | 0.05607537552714348 | 0.9817143082618713 |
(4) epochs を 10 にする | 0.015390428714454174 | 0.9960476160049438 |
(5) 全結合層を追加する | 0.012460934929549694 | 0.9962618947029114 |
(6) Conv2D, MaxPooling2D の層を追加する | 0.016651742160320282 | 0.9953095316886902 |
まず、畳み込み層の畳み込みウィンドウのサイズですが、学習する画像のサイズが28 × 28だと、3 × 3 よりも少し大きくした 4 × 4 の方が良いようです。学習回数は少なかったようで、epoch=10にしたら、良い結果が出ました。全結合層、Conv2D・MaxPooling2D の層を増やした方が良い結果が出ています。
この結果を踏まえて、ベースの設定から下記の設定を加えたもので再度学習し直してみます。
(1) Conv2D の kernel_size を (4, 4) にする
(4) epochs を 10 にする
(5) 全結合層を追加する
(6) Conv2D, MaxPooling2D の層を追加する
具体的には下記のような設定になります。
model = Sequential() model.add(Conv2D(filters=32, kernel_size=(4, 4), activation='relu', input_shape=(28, 28, 1))) # kernel_size を (4, 4) にする model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=64, kernel_size=(4, 4), activation='relu')) # この行を追加 kernel_size を (4, 4) にする model.add(MaxPooling2D(pool_size=(2, 2))) # この行を追加 model.add(Flatten()) model.add(Dense(64, activation='relu')) # この行を追加 model.add(Dense(10, activation='softmax')) model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) model.summary() history = model.fit(x_train, y_train, epochs=10) # epochs を 10 にする
上記の設定の結果は下記のようになりました。今までで一番良い結果になりましたね。
loss | accuracy | |
---|---|---|
(1) + (4) + (5) + (6) | 0.008217111229896545 | 0.9972618818283081 |
予測
それでは、上記構成で予測します。前回と同様のソースです。
predicts = model.predict(x_test) # 最も値の大きいものが予測された値なので、それを取り出す predicts_label = np.argmax(predicts, axis=1) # CSVを出力するためのデータを生成 df = pd.DataFrame({ 'imageId': list(range(1, len(predicts)+ 1)), 'Label': predicts_label }) # CSV出力 df.to_csv("/root/practice/digit/csv/predictions.csv", index=False)
CSVが出力されたので kaggle に提出します。
おおおおおおおおおおおお!前回よりもスコアとランキングが上がりました!!