PytorchでCNN-Fashion-MNIST編


PytorchのFashion-MNIST

Fashion-MNISTは、衣類の画像のデータセットです。

画像は、28×28ピクセルで、1チャネル(グレースケール画像)です。
Pytorchのライブラリなので、(データ数, 1チャンネル, 28, 28)のshapeになっている。

  • 訓練データが、60,000枚
  • テストデータが、10.00枚
  • クラス数が、10クラス
  • ラベルと衣類の対応

    ラベル クラス
    0 Tシャツ
    1 ズボン
    2 プルオーバー
    3 ドレス
    4 コート
    5 サンダル
    6 シャツ
    7 スニーカー
    8
    9 アンクルブーツ

    Fashion-MNISTのデータロード

    from torch.utils.data import Dataset, DataLoader, TensorDataset
    from torchvision.datasets import FashionMNIST
    from torchvision import transforms
    
    fashion_mnist_train = FashionMNIST("FashionMNIST", train=True, download=True, transform=transforms.ToTensor())
    
    fashion_mnist_test = FashionMNIST("FashionMNIST", train=False, download=True, transform=transforms.ToTensor())
    
    batch_size = 128
    train_loader = DataLoader(fashion_mnist_train, batch_size=batch_size, shuffle=True)
    test_loader = DataLoader(fashion_mnist_test, batch_size=batch_size, shuffle=True)
    
    print(fashion_mnist_train)
    print(fashion_mnist_test)
    

    Pytorchのサンプルプログラム

    import torch
    from torch import nn, optim
    from torch.utils.data import Dataset, DataLoader, TensorDataset
    
    from torchvision.datasets import FashionMNIST
    from torchvision import transforms
    
    import matplotlib.pyplot as plt
    %matplotlib inline
    
    #データセット
    fashion_mnist_train = FashionMNIST("FashionMNIST", train=True, download=True, transform=transforms.ToTensor())
    
    fashion_mnist_test = FashionMNIST("FashionMNIST", train=False, download=True, transform=transforms.ToTensor())
    
    batch_size = 128
    train_loader = DataLoader(fashion_mnist_train, batch_size=batch_size, shuffle=True)
    test_loader = DataLoader(fashion_mnist_test, batch_size=batch_size, shuffle=True)
    
    print(fashion_mnist_train)
    print(fashion_mnist_test)
    
    #ネットワークの定義
    #ネットワークの定義
    class net(nn.Module):
        def __init__(self):
            super(net,self).__init__()
            #畳み込み層
            self.conv_layers = nn.Sequential(
                nn.Conv2d(in_channels = 1, out_channels = 16, kernel_size = 5, stride=1, padding=0),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=2, stride=2),
                nn.Conv2d(in_channels = 16, out_channels = 32, kernel_size = 5, stride=1, padding=0),
                nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = 5, stride=1, padding=0),
                nn.ReLU(inplace=True),
                nn.MaxPool2d(kernel_size=2, stride=2),
            )
            #全結合層
            self.dence = nn.Sequential(
                nn.Linear( 64* 2 *2, 128),
                nn.Dropout(p=0.2),
                nn.Linear(128, 128),
                nn.Linear(128, 64),
                nn.Dropout(p=0.2),
                nn.Linear(64, 10),
            )
            
        #順伝播
        def forward(self,x):
            
            out = self.conv_layers(x)
            #Flatten
            out = out.view(out.size(0), -1)
            #全結合層
            out = self.dence(out)
            
            return out
        
        #畳み込み層の出力サイズのチェック
        def check_cnn_size(self, size_check):
            out = self.conv_layers(size_check)
            
            return out
    
    #デバイスの選択
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    net = net().to(device)
    
    #全結合層の入力サイズの確認
    size_check = torch.FloatTensor(10, 1, 28, 28)
    size_check = size_check.to(device)
    print(net.check_cnn_size(size_check).size())
    
    #損失関数
    criterion = nn.CrossEntropyLoss()
    
    #最適化
    optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)
    
    num_epochs = 100
    
    train_loss_list = []
    train_acc_list = []
    val_loss_list = []
    val_acc_list = []
    import time
    start = time.time()
    for epoch in range(num_epochs):
        train_loss = 0
        train_acc = 0
        val_loss = 0
        val_acc = 0
        
        #train
        net.train()
        for i, (images, labels) in enumerate(train_loader):
            images, labels = images.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = net.forward(images)
            loss = criterion(outputs, labels)
            train_loss += loss.item()
            train_acc += (outputs.max(1)[1] == labels).sum().item()
            loss.backward()
            optimizer.step()
        
        avg_train_loss = train_loss / len(train_loader.dataset)
        avg_train_acc = train_acc / len(train_loader.dataset)
        
        #val
        net.eval()
        with torch.no_grad():
            for images, labels in test_loader:
                images = images.to(device)
                labels = labels.to(device)
                outputs = net.forward(images)
                loss = criterion(outputs, labels)
                val_loss += loss.item()
                val_acc += (outputs.max(1)[1] == labels).sum().item()
        avg_val_loss = val_loss / len(test_loader.dataset)
        avg_val_acc = val_acc / len(test_loader.dataset)
        
        print ('Epoch [{}/{}], Loss: {loss:.4f}, val_loss: {val_loss:.4f}, val_acc: {val_acc:.4f}' 
                       .format(epoch+1, num_epochs, i+1, loss=avg_train_loss, val_loss=avg_val_loss, val_acc=avg_val_acc))
        train_loss_list.append(avg_train_loss)
        train_acc_list.append(avg_train_acc)
        val_loss_list.append(avg_val_loss)
        val_acc_list.append(avg_val_acc)
    
    end = time.time() - start
    print(end)
    
    plt.plot(train_acc_list, color='orange')
    plt.plot(val_acc_list)
    plt.legend
    plt.savefig('acc.png')
    plt.show
    

    結果

    訓練データとテストデータの正解率が乖離していることから、過学習が起こっていることがわかる。

    参考書