MLP と AlexNet の違い
全結合のニューラルネットワーク (例: MLP)
3次元データも1次元に変換し、全結合層への入力データとする為、 3次元データが持つ形状を無視してしまう
畳込みニューラルネットワーク (例: AlexNet)
3次元データを受け、畳込み(≒フィルタ)演算し、3次元を出力する為、形状を保つ. また、プーリングとは縦横の空間を小さくする演算.
pytorch for pythonによる CIFAR10 に対する AlexNet 画像分類
pytorch for pythonによる CIFAR10 に対する画像分類 - end0tknr's kipple - 新web写経開発
上記urlにある先日のエントリはMLPによるものですが、 これをAlexNetで実装すると以下の通り。
#!/usr/local/python3/bin/python3 # -*- coding: utf-8 -*- import torch import torchvision import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import torchvision.transforms as transforms import numpy as np # from matplotlib import pyplot as plt import matplotlib matplotlib.use('Agg') import matplotlib.pylab as plt NUM_CLASSES = 10 # CIFAR10データは、10種類のdata DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu' DATA_DIR = './data/' IMG_SIZE_AND_CHANNEL = 0 TRAIN_NUM_EPOCHS = 30 # 学習回数 #TRAIN_NUM_EPOCHS = 50 # 学習回数 def main(): (train_loader, test_loader) = get_train_test_data() print("train_loader: ", train_loader) print("test_loader: ", test_loader) net = AlexNet(NUM_CLASSES).to(DEVICE) # net = MLPNet().to(DEVICE) (criterion, optimizer) = get_net_criterion_optimizer(net) (train_loss_list, train_acc_list, val_loss_list, val_acc_list) = \ train(train_loader, test_loader, net, criterion, optimizer) output_to_file(train_loss_list, train_acc_list, val_loss_list, val_acc_list) # torch.save(net.state_dict(), 'net.ckpt') # net2 = MLPNet().to(device) # net2.load_state_dict(torch.load('net.ckpt')) # net2.eval() # with torch.no_grad(): # total = 0 # test_acc = 0 # for images, labels in test_loader: # images, labels = images.view(-1, 32*32*3).to(device), labels.to(device) # outputs = net2(images) # test_acc += (outputs.max(1)[1] == labels).sum().item() # total += labels.size(0) # print('精度: {} %'.format(100 * test_acc / total)) def get_train_test_data(): # CIFAR10とは、ラベル付済の5万枚の訓練画像と1万枚のテスト画像のデータセットで # 以下で、STEP1) www.cs.toronto.edu から自動的にダウンロード & 解凍します # STEP2) 画像と正解ラベルのペアを返却 が行われます train_dataset = torchvision.datasets.CIFAR10(root=DATA_DIR, train=True, transform=transforms.ToTensor(), download=True) test_dataset = torchvision.datasets.CIFAR10(root=DATA_DIR, train=False, transform=transforms.ToTensor(), download=True) # 試しに1つの訓練用データの内容を見ると、3チャネル , 32x32size だと分かります image, label = train_dataset[0] # print( image.size() ) # print(label) global IMG_SIZE_AND_CHANNEL IMG_SIZE_AND_CHANNEL = image.size()[0] * image.size()[1] * image.size()[2] # DataLoader() は、batch_size分だけ、画像と正解ラベルを返します train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True, num_workers=2) test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=64, shuffle=False, num_workers=2) return train_loader, test_loader # MLP ネットワークの定義 # (多層パーセプトロン, 入力層,隠れ層,出力層が全結合である最も単純なdeep learning) class MLPNet (nn.Module): def __init__(self): super(MLPNet, self).__init__() # 32x32size & 3チャネル, 隠れ層のunit数は、600 self.fc1 = nn.Linear(IMG_SIZE_AND_CHANNEL, 600) self.fc2 = nn.Linear(600, 600) self.fc3 = nn.Linear(600, NUM_CLASSES) self.dropout1 = nn.Dropout2d(0.2) self.dropout2 = nn.Dropout2d(0.2) def forward(self, x): x = F.relu(self.fc1(x)) x = self.dropout1(x) x = F.relu(self.fc2(x)) x = self.dropout2(x) return F.relu(self.fc3(x)) class AlexNet(nn.Module): def __init__(self, num_classes): super(AlexNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=5), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(64, 192, kernel_size=5, padding=2), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(192, 384, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), ) self.classifier = nn.Linear(256, num_classes) def forward(self, x): x = self.features(x) x = x.view(x.size(0), -1) x = self.classifier(x) return x def get_net_criterion_optimizer(net): criterion = nn.CrossEntropyLoss() # 以下のparameterの妥当性は理解していません optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4) return criterion, optimizer def train(train_loader, test_loader, net, criterion, optimizer): #最後にlossとaccuracyのグラフを出力する為 train_loss_list = [] train_acc_list = [] val_loss_list = [] val_acc_list = [] for epoch in range(TRAIN_NUM_EPOCHS): #エポックごとに初期化 train_loss = 0 train_acc = 0 val_loss = 0 val_acc = 0 net.train() #訓練モードへ切り替え #ミニバッチで分割し読込み for i, (images, labels) in enumerate(train_loader): # AlexNetの場合、view()での1次元化変換を行わない images, labels = images.to(DEVICE), labels.to(DEVICE) # #viewで縦横32x32 & 3channelのimgを1次元に変換し、toでDEVICEに転送 # images, labels = \ # images.view(-1,IMG_SIZE_AND_CHANNEL).to(DEVICE),labels.to(DEVICE) optimizer.zero_grad() # 勾配をリセット outputs = net(images) # 順伝播の計算 loss = criterion(outputs, labels) #lossの計算 train_loss += loss.item() #lossのミニバッチ分を溜め込む #accuracyをミニバッチ分を溜め込む #正解ラベル(labels)と予測値のtop1(outputs.max(1))が一致時、1が返る train_acc += (outputs.max(1)[1] == labels).sum().item() #逆伝播の計算 loss.backward() #重みの更新 optimizer.step() #平均lossと平均accuracyを計算 avg_train_loss = train_loss / len(train_loader.dataset) avg_train_acc = train_acc / len(train_loader.dataset) net.eval() #評価モードへ切り替え #評価するときに必要のない計算が走らないようtorch.no_gradを使用 with torch.no_grad(): for images, labels in test_loader: # AlexNetの場合、view()での1次元化変換を行わない images, labels = images.to(DEVICE), labels.to(DEVICE) # images, labels = \ # images.view(-1,IMG_SIZE_AND_CHANNEL).to(DEVICE),labels.to(DEVICE) outputs = net(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) # 訓練データのlossと検証データのlossとaccuracyをログ出力. print ("Epoch [{}/{}], Loss: {loss:.4f},val_loss: {val_loss:.4f},val_acc: {val_acc:.4f}" .format(epoch+1, TRAIN_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) return train_loss_list,train_acc_list,val_loss_list,val_acc_list def output_to_file(train_loss_list,train_acc_list,val_loss_list,val_acc_list): plt.figure() plt.plot(range(TRAIN_NUM_EPOCHS), train_loss_list, color='blue', linestyle='-', label='train_loss') plt.plot(range(TRAIN_NUM_EPOCHS), val_loss_list, color='green', linestyle='--', label='val_loss') plt.ylim([0,0.04]) plt.legend() plt.xlabel('epoch') plt.ylabel('loss') plt.title('Training and validation loss') plt.grid() plt.savefig( 'test_4_1.png' ) plt.figure() plt.plot(range(TRAIN_NUM_EPOCHS), train_acc_list, color='blue', linestyle='-', label='train_acc') plt.plot(range(TRAIN_NUM_EPOCHS), val_acc_list, color='green', linestyle='--', label='val_acc') plt.ylim([0,1]) plt.legend() plt.xlabel('epoch') plt.ylabel('acc') plt.title('Training and validation accuracy') plt.grid() plt.savefig( 'test_4_2.png' ) if __name__ == '__main__': main()
MLP , AlexNet の実行結果を比較すると、 以下のように CNNである AlexNet の精度が高いことが分かります。
MLP
CNN (AlexNet)