畳み込みニューラルネットワーク (CNN)と、他の機械学習手法(勾配ブースティング・多層パーセプトロン)の特徴を理解することを目的に、画像に回転・拡大・色の変更などの処理を行った時に分類性能がどう変わるかを試してみました。
画像は コアラとクマの画像を自動生成する の方法で生成しました。コアラさんとクマさんをシルエットで区別できるでしょうか?
コード
画像データ読み込み
from PIL import Image
koalas = []
for i in range(num_data):
koala = Image.open("koala_or_bear/koala_{}.jpg".format(i))
koalas.append(koala)
bears = []
for i in range(num_data):
bear = Image.open("koala_or_bear/bear_{}.jpg".format(i))
bears.append(bear)
先頭データ表示
%matplotlib inline
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10,10))
for i in range(16):
ax = fig.add_subplot(4, 4, i+1)
ax.axis('off')
if i < 8:
ax.set_title('koala_{}'.format(i))
ax.imshow(koalas[i],cmap=plt.cm.gray, interpolation='none')
else:
ax.set_title('bear_{}'.format(i - 8))
ax.imshow(bears[i - 8],cmap=plt.cm.gray, interpolation='none')
plt.show()
説明変数・目的変数
import numpy as np
X = [] # 説明変数
Y = [] # 目的変数
index = 0
for koala in koalas:
resize_img = koala.resize((128, 128))
r, g, b = resize_img.split()
r_resize_img = np.asarray(np.float32(r)/255.0)
g_resize_img = np.asarray(np.float32(g)/255.0)
b_resize_img = np.asarray(np.float32(b)/255.0)
rgb_resize_img = np.asarray([r_resize_img, g_resize_img, b_resize_img])
X.append(rgb_resize_img)
Y.append(0)
index += 1
if index >= num_data:
break
index = 0
for bear in bears:
resize_img = bear.resize((128, 128))
r, g, b = resize_img.split()
r_resize_img = np.asarray(np.float32(r)/255.0)
g_resize_img = np.asarray(np.float32(g)/255.0)
b_resize_img = np.asarray(np.float32(b)/255.0)
rgb_resize_img = np.asarray([r_resize_img, g_resize_img, b_resize_img])
X.append(rgb_resize_img)
Y.append(1)
index += 1
if index >= num_data:
break
X = np.array(X, dtype='float32')
Y = np.array(Y, dtype='int64')
教師セット・テストセットに分離
from sklearn import model_selection
X_train, X_test, Y_train, Y_test = model_selection.train_test_split(
X, Y, test_size=0.1
)
scikit-learn用にデータ型変換
d1, d2, d3, d4 = X_train.shape
X_train_a = X_train.reshape((d1, d2 * d3 * d4))
Y_train_onehot = np.identity(2)[Y_train]
d1, d2, d3, d4 = X_test.shape
X_test_a = X_test.reshape((d1, d2 * d3 * d4))
Y_test_onehot = np.identity(2)[Y_test]
PyTorch用にデータ型変換
import torch
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
X_train_t = torch.from_numpy(X_train).float()
Y_train_t = torch.from_numpy(Y_train).long()
X_train_v = torch.autograd.Variable(X_train_t)
Y_train_v = torch.autograd.Variable(Y_train_t)
X_test_t = torch.from_numpy(X_test).float()
Y_test_t = torch.from_numpy(Y_test).long()
X_test_v = torch.autograd.Variable(X_test_t)
Y_test_v = torch.autograd.Variable(Y_test_t)
train = TensorDataset(X_train_t, Y_train_t)
train_loader = DataLoader(train, batch_size=32, shuffle=True)
勾配ブースティング
%%time
from sklearn.ensemble import GradientBoostingClassifier
classifier = GradientBoostingClassifier()
classifier.fit(X_train_a, Y_train)
print("Accuracy score (train): ", classifier.score(X_train_a, Y_train))
print("Accuracy score (test): ", classifier.score(X_test_a, Y_test))
多層パーセプトロン(中間1層)
%%time
from sklearn.neural_network import MLPClassifier
classifier = MLPClassifier(max_iter=10000, early_stopping=True)
classifier.fit(X_train_a, Y_train)
print("Accuracy score (train): ", classifier.score(X_train_a, Y_train))
print("Accuracy score (test): ", classifier.score(X_test_a, Y_test))
多層パーセプトロン(中間2層)
%%time
from sklearn.neural_network import MLPClassifier
classifier = MLPClassifier(max_iter=10000, early_stopping=True,
hidden_layer_sizes=(100, 100))
classifier.fit(X_train_a, Y_train)
print("Accuracy score (train): ", classifier.score(X_train_a, Y_train))
print("Accuracy score (test): ", classifier.score(X_test_a, Y_test))
畳み込みニューラルネットワーク (CNN)
ネットワークの定義
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = torch.nn.Conv2d(3, 10, 5)
self.conv2 = torch.nn.Conv2d(10, 20, 5)
self.fc1 = torch.nn.Linear(20 * 29 * 29, 50)
self.fc2 = torch.nn.Linear(50, 2)
def forward(self, x):
x = torch.nn.functional.relu(self.conv1(x))
x = torch.nn.functional.max_pool2d(x, 2)
x = torch.nn.functional.relu(self.conv2(x))
x = torch.nn.functional.max_pool2d(x, 2)
x = x.view(-1, 20 * 29 * 29)
x = torch.nn.functional.relu(self.fc1(x))
x = torch.nn.functional.log_softmax(self.fc2(x), 1)
return x
ネットワーク構造の確認
from torchsummary import summary
model = CNN()
summary(model, X[0].shape)
----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 10, 124, 124] 760
Conv2d-2 [-1, 20, 58, 58] 5,020
Linear-3 [-1, 50] 841,050
Linear-4 [-1, 2] 102
================================================================
Total params: 846,932
Trainable params: 846,932
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.19
Forward/backward pass size (MB): 1.69
Params size (MB): 3.23
Estimated Total Size (MB): 5.11
----------------------------------------------------------------
実行のための関数
def learn(model, criterion, optimizer, n_iteration):
for epoch in range(n_iteration):
total_loss = np.array(0, dtype='float64')
for x, y in train_loader:
x = torch.autograd.Variable(x)
y = torch.autograd.Variable(y)
optimizer.zero_grad()
y_pred = model(x)
loss = criterion(y_pred, y)
loss.backward()
optimizer.step()
total_loss += loss.data.numpy()
if (epoch + 1) % 10 == 0:
print(epoch + 1, total_loss)
if total_loss == np.array(0, dtype='float64'):
break
loss_history.append(total_loss)
return model
実行用コード
#%%time
model = CNN()
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
loss_history = []
model = learn(model, criterion, optimizer, 300)
損失履歴表示
ax = plt.subplot(2, 1, 1)
ax.plot(loss_history)
ax.grid()
ax = plt.subplot(2, 1, 2)
ax.plot(loss_history)
ax.set_yscale('log')
ax.grid()
正答率
正答率(教師セット)
Y_pred = torch.max(model(X_train_v).data, 1)[1]
accuracy = sum(Y_train == Y_pred.numpy()) / len(Y_train)
print(accuracy)
正答率(テストセット)
Y_pred = torch.max(model(X_test_v).data, 1)[1]
accuracy = sum(Y_test == Y_pred.numpy()) / len(Y_test)
print(accuracy)
結果
まずは、コアラさんとクマさんを区別する一番簡単な問題です。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 1min 2s | 1.0 | 1.0 |
多層パーセプトロン(中間1層) | 50.9 s | 1.0 | 1.0 |
多層パーセプトロン(中間2層) | 35.1 s | 1.0 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
どの予測法も完璧に答えることができました。
拡大
コアラさんとクマさんをランダムな倍率で拡大してみましょう。このときついでに、少しだけ縦横にずらしてみます。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 5min 11s | 1.0 | 0.975 |
多層パーセプトロン(中間1層) | 1min 4s | 1.0 | 1.0 |
多層パーセプトロン(中間2層) | 1min 4s | 0.977 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
勾配ブースティングの正答率が、ほんの少しだけ落ちましたね。畳み込みニューラルネットワーク (CNN)は完璧な正答率です。
邪魔物あり
背景に、邪魔になりそうなものを描き込みましょう。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 1min 23s | 1.0 | 1.0 |
多層パーセプトロン(中間1層) | 38.3 s | 1.0 | 1.0 |
多層パーセプトロン(中間2層) | 1min 3s | 0.983 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
ほとんど影響ないようです。
背景だけ彩色
背景をカラフルにしてみます。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 1min 30s | 1.0 | 1.0 |
多層パーセプトロン(中間1層) | 43.9 s | 0.9916 | 1.0 |
多層パーセプトロン(中間2層) | 41.6 s | 1.0 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
これもほとんど影響ないようです。
動物だけ彩色
コアラさんとクマさんをカラフルにしてみましょう。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 4min 26s | 1.0 | 0.975 |
多層パーセプトロン(中間1層) | 27.8 s | 0.494 | 0.55 |
多層パーセプトロン(中間2層) | 1min 9s | 0.816 | 0.775 |
畳み込みニューラルネットワーク (CNN) | - | 0.505 | 0.45 |
予測精度がかなり落ちました。畳み込みニューラルネットワーク (CNN)の性能がガタ落ち。あれ?意外にも多層パーセプトロン(中間2層)が健闘してる。そして、勾配ブースティングは、ほとんど性能が落ちてない。すごいぞ。
拡大・彩色・邪魔物あり
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 7min 24s | 1.0 | 0.9 |
多層パーセプトロン(中間1層) | 42.4 s | 0.6861 | 0.6 |
多層パーセプトロン(中間2層) | 1min 50s | 0.925 | 0.75 |
畳み込みニューラルネットワーク (CNN) | - | 0.5 | 0.5 |
難易度が上がったけど、勾配ブースティングはかなり健闘してる。畳み込みニューラルネットワーク (CNN)は全く使い物になってない。
拡大・彩色・動物だけ黒・邪魔物あり
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 6min 12s | 1.0 | 0.975 |
多層パーセプトロン(中間1層) | 1min 1s | 0.9916 | 0.975 |
多層パーセプトロン(中間2層) | 1min 12s | 1.0 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
コアラさん・クマさんの色を黒に統一するだけで、全ての予測性能が回復した。やっぱりそこが判断基準なわけね。
回転
まわるまわるよ コアラは回る クマも回る
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 3min 10s | 1.0 | 0.925 |
多層パーセプトロン(中間1層) | 27.4 s | 0.5 | 0.5 |
多層パーセプトロン(中間2層) | 1min 20s | 0.994 | 1.0 |
畳み込みニューラルネットワーク (CNN) | - | 1.0 | 1.0 |
多層パーセプトロン(中間1層)はダメでしたが他の皆さんは回転についてこれたようです。勾配ブースティングは回転が少し苦手っぽい。
回転・拡大
回転しながら拡大もしてみましょう。
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 5min 28s | 1.0 | 0.775 |
多層パーセプトロン(中間1層) | 1min 33s | 0.825 | 0.7 |
多層パーセプトロン(中間2層) | 30.9 s | 0.65 | 0.675 |
畳み込みニューラルネットワーク (CNN) | - | 0.505 | 0.45 |
回転だけなら平気、拡大だけなら平気。だけど、回転しながら拡大だと、みなさん混乱してしまうようですね。それでも、勾配ブースティング、よく頑張ってる。
回転・拡大・彩色・動物だけ黒・邪魔物あり
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 7min 6s | 1.0 | 0.6 |
多層パーセプトロン(中間1層) | 29.5 s | 0.5194 | 0.325 |
多層パーセプトロン(中間2層) | 33 s | 0.572 | 0.65 |
畳み込みニューラルネットワーク (CNN) | - | 0.5194 | 0.325 |
いろんな要因(回転・拡大・邪魔物・色)が個別で攻めてくると、大した問題ではなくても、それらの要因が混ざって攻めてくると難しくなるようですね。
いろんな要因全部のせ
手法 | 学習時間 | 正答率(教師セット) | 正答率(テストセット) |
---|---|---|---|
勾配ブースティング | 7min 55s | 1.0 | 0.45 |
多層パーセプトロン(中間1層) | 31.8 s | 0.505 | 0.45 |
多層パーセプトロン(中間2層) | 55.7 s | 0.6027 | 0.45 |
畳み込みニューラルネットワーク (CNN) | - | 0.505 | 0.45 |
いろんな要因を全部乗っけました。これはどの方法でもお手上げのようです。
まとめ
畳み込みニューラルネットワーク (CNN)について、感覚を掴みたかったので、「CNNの一人勝ち!!!」な結果を出したかったのですが、結論は「勾配ブースティングすげええええ!」になってしまいました。(ヽ´ω`)