LoginSignup
5
4

More than 3 years have passed since last update.

畳み込みニューラルネットワーク (CNN)に対する画像の回転・拡大・色などの影響

Last updated at Posted at 2020-01-23

畳み込みニューラルネットワーク (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の一人勝ち!!!」な結果を出したかったのですが、結論は「勾配ブースティングすげええええ!」になってしまいました。(ヽ´ω`)

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4