ローリングコンバットピッチなう!

AIとか仮想化とかペーパークラフトとか

【PyTorch始めました】ChainerからPyTorchに移植した畳み込みニューラルネットワークでFashion MNISTを分類する+汎化性能改善を試す

[technology]Fashion MNISTをPyTorch版の畳み込みニューラルネットワークに掛けてみる

Fashion MNISTというのはMNISTと同じデータ形式で、画像を手書き数字ではなく、Tシャツやバッグ等の絵に置き換えたものです。
詳細を下記あたりの記事を参考にしてください。
qiita.com
github.com

弊ブログの記事、
rc30-popo.hatenablog.com
で、Chainer用に書いたMNISTの畳み込みニューラルネットワークによる学習スクリプトをPyTorchに移植しました。
このスクリプトを使って、Fashion MNISTを学習、分類してみます。

上記の記事に載せたコードはChainer用に用意したデータセットをPyTorchで記述したニューラルネットワークに入力する変則コードになっています。(もともとChainer用に書いたコードをPyTorchに移植、移行する実験のため)
MNISTのデータセットはchainer.datasets.get_mnist()で取得出来ますが、Fashion MNISTのデータセットもchainer.datasetsで取得できるので、下記の様に1行だけコードを書き換えます。

#train, test = chainer.datasets.get_mnist()
train, test = chainer.datasets.get_fashion_mnist()

これで実行してみると。

$ python f_mnist_cnn_test_torch_gpu.py
MNIST Deep Learning sample by PyTorch
 PyTorch Version:  1.3.1
 Device:  cuda
epoch: 0, mean loss: 1.602173089981079
epoch: 1, mean loss: 0.7937408685684204
epoch: 2, mean loss: 0.6959575414657593
epoch: 3, mean loss: 0.6460543274879456
epoch: 4, mean loss: 0.6101723313331604
epoch: 5, mean loss: 0.5832085609436035
epoch: 6, mean loss: 0.5586143136024475
epoch: 7, mean loss: 0.5385422706604004
epoch: 8, mean loss: 0.5222655534744263
epoch: 9, mean loss: 0.5040879249572754
epoch: 10, mean loss: 0.4896630644798279
epoch: 11, mean loss: 0.4790268540382385
epoch: 12, mean loss: 0.46714669466018677
epoch: 13, mean loss: 0.4546925723552704
epoch: 14, mean loss: 0.44705063104629517
epoch: 15, mean loss: 0.4386482834815979
epoch: 16, mean loss: 0.4348416328430176
epoch: 17, mean loss: 0.4228558838367462
epoch: 18, mean loss: 0.41755232214927673
epoch: 19, mean loss: 0.413984477519989
accuracy: 0.8429

こんな感じで正解率は84%。MNISTが割と簡単に96〜98%とか出るのに比べると正解率低めです。
エポック数を100まで増やしてみます。

$ python f_mnist_cnn_test_torch_gpu.py
MNIST Deep Learning sample by PyTorch
 PyTorch Version:  1.3.1
 Device:  cuda
epoch: 0, mean loss: 1.4492143392562866
epoch: 1, mean loss: 0.7108485698699951
epoch: 2, mean loss: 0.6192865967750549
epoch: 3, mean loss: 0.5673171281814575
epoch: 4, mean loss: 0.5317571759223938
〜中略〜
epoch: 95, mean loss: 0.18008953332901
epoch: 96, mean loss: 0.17909875512123108
epoch: 97, mean loss: 0.1781744360923767
epoch: 98, mean loss: 0.17554377019405365
epoch: 99, mean loss: 0.17671321332454681
accuracy: 0.8907

エポック数100でも正解率90%を超えられません。

[technology]ニューラルネットワークの汎化性能改善

そこで畳み込みニューラルネットワークのチャンネル数と中間層のノード数を増やしてみます。



前回記事のネットワーク強化したネットワーク

class DNN(nn.Module):
    def __init__(self):
        super(DNN,self).__init__()
        self.cv1 = nn.Conv2d(1,5,4)
        self.cv2 = nn.Conv2d(5,10,4)
        self.l1 = nn.Linear(10 * 4 * 4,100)
        self.l2 = nn.Linear(100,10)
    def forward(self,x):
        c = F.max_pool2d(F.relu(self.cv1(x)),2)
        c = F.max_pool2d(F.relu(self.cv2(c)),2)
        c = c.view(-1,10 * 4 * 4)
        h = F.relu(self.l1(c))
        h = self.l2(h)
        return h

class DNN(nn.Module):
    def __init__(self):
        super(DNN,self).__init__()
        self.cv1 = nn.Conv2d(1,16,4)
        self.cv2 = nn.Conv2d(16,32,4)
        self.l1 = nn.Linear(32 * 4 * 4,500)
        self.l2 = nn.Linear(500,10)
    def forward(self,x):
        c = F.max_pool2d(F.relu(self.cv1(x)),2)
        c = F.max_pool2d(F.relu(self.cv2(c)),2)
        c = c.view(-1,32 * 4 * 4)
        h = F.relu(self.l1(c))
        h = self.l2(h)
        return h

畳み込みとMAX POOLINGを2段掛けていますが、1段目の畳み込みの出力を5チャンネル→16チャンネル、2段目の畳み込みの出力を10チャンネル→32チャンネルに増やしています。 合わせて、それを受ける中間層のノード数を100→500ノードに増やしました。 これで試します。

$ python f_mnist_cnn_test_torch_gpu.py
MNIST Deep Learning sample by PyTorch
 PyTorch Version:  1.3.1
 Device:  cuda
epoch: 0, mean loss: 1.0667693614959717
epoch: 1, mean loss: 0.6207329034805298
epoch: 2, mean loss: 0.5368128418922424
epoch: 3, mean loss: 0.4838525950908661
epoch: 4, mean loss: 0.44583263993263245
〜中略〜
epoch: 45, mean loss: 0.16773974895477295
epoch: 46, mean loss: 0.1631971299648285
epoch: 47, mean loss: 0.1589423418045044
epoch: 48, mean loss: 0.15731170773506165
epoch: 49, mean loss: 0.1543945074081421
accuracy: 0.9053

50エポック学習して正解率90%を超えました。同じネットワークで150エポック回してみると、

$ python f_mnist_cnn_test_torch_gpu.py
MNIST Deep Learning sample by PyTorch
 PyTorch Version:  1.3.1
 Device:  cuda
epoch: 0, mean loss: 1.0521917343139648
epoch: 1, mean loss: 0.6028405427932739
epoch: 2, mean loss: 0.5300793051719666
epoch: 3, mean loss: 0.48052194714546204
epoch: 4, mean loss: 0.4372819662094116
〜中略〜
epoch: 145, mean loss: 0.011501261964440346
epoch: 146, mean loss: 0.00985863246023655
epoch: 147, mean loss: 0.008198190480470657
epoch: 148, mean loss: 0.009277496486902237
epoch: 149, mean loss: 0.008205198682844639
accuracy: 0.9054

こんな感じで損失は減るのですが、正解率はほとんど変化しません。単に学習データを過学習しているだけの様です。
精度を上げるにはネットワークの構成自体をチューニングする必要がありそうです。

色々試行錯誤の上、下記の様なモデルを作りました。

class DNN(nn.Module):
    def __init__(self):
        super(DNN,self).__init__()
        self.cv1 = nn.Conv2d(1,32,3)
        self.cv2 = nn.Conv2d(32,64,3)
        self.l1 = nn.Linear(64 * 5 * 5,500)
        self.l2 = nn.Linear(500,10)
    def forward(self,x,train=True):
        c = F.dropout(F.max_pool2d(F.relu(self.cv1(x)),2),training = train)
        c = F.dropout(F.max_pool2d(F.relu(self.cv2(c)),2),training = train)
        # Chainerと異なり、全結合層に入力する前にshape変換が必要
        c = c.view(-1,64 * 5 * 5)
        h = F.relu(self.l1(c))
        h = F.dropout(h,training = train)
        h = self.l2(h)
        return h

変更点は以下の通りです。

このモデルで200エポック回した時の結果は以下の通り。

$ python f_mnist_cnn_test_torch_gpu.py
MNIST Deep Learning sample by PyTorch
 PyTorch Version:  1.3.1
 Device:  cuda
epoch: 0, mean loss: 1.005088210105896
epoch: 1, mean loss: 0.6146973967552185
epoch: 2, mean loss: 0.5396274328231812
epoch: 3, mean loss: 0.4939133822917938
epoch: 4, mean loss: 0.46335190534591675
〜中略〜
epoch: 195, mean loss: 0.16906757652759552
epoch: 196, mean loss: 0.16961127519607544
epoch: 197, mean loss: 0.16971048712730408
epoch: 198, mean loss: 0.16554950177669525
epoch: 199, mean loss: 0.16842873394489288
accuracy: 0.9278

正解率92.78%まで上がりました。ネットで探すと94%ぐらい出している人も居るのですが、なかなか精度上げるの難しいです。
畳み込みを3段にしたモデルも試しましたが最適化が進まなくなり、2段の方が簡単に精度が出る感じです。