MLPモデルを使用したMNISTの学習


緒論


深度学習画像認識基礎学習で最も多く用いられるのが手書きデータMNISTである.
これは古臭い(?)Fashion-Ministが登場する前に,手書きデータが最も多く用いられた深さ学習例であるという噂がある.
司会はPython PyTorch.

構成コード


(1)奥行き学習モジュールのインポート
(2)CUDA(GPU)の適用
(3)MNISTデータの設定
(4)データチェック
(5)画像の出力を試みる
(6)多層Perceptronモデル設計
(7)初期化、Optimizer、関数設定
(8)学習関数の定義
(9)検証関数の定義
(10)学習進捗と確認

プログラム


(1)奥行き学習モジュールのインポート


コード#コード#

import numpy as np # 선형대수 모듈
import matplotlib.pyplot as plt # 시각화 모듈
import torch # 파이토치
import torch.nn as nn # PyTorch의 모듈을 모아놓은 것. from~~이 아닌 저렇게 임포트를 하는 것이 거의 관습이라고 한다.
import torch.nn.functional as F # torch.nn 중에서 자주 쓰는 함수를 F로 임포트.
import torch.nn.init as init # 초기화 관련 모듈 
import torchvision # TorchVision 임포트
from torchvision import transforms, datasets # 데이터를 다루기 위한 TorchVision 내의 Transforms와 datasets를 따로 임포트

(2)CUDA(GPU)の適用


コード#コード#

DEVICE = torch.device('cuda:0') if torch.cuda.is_available() else torch.device('cpu')

BATCH_SIZE = 32
EPOCHS = 30

print('Using PyTorch version: ', torch.__version__, 'Device: ', DEVICE)
出力内容
Using PyTorch version: 1.9.0+cu102 Device: cuda:0

解説


Google Collabでアプリケーションを整理していますが、事前にノートパソコンの設定にGPUを設定する必要があります.is available()にTrueが表示され、cuda:0に設定されます.
BATCH SIZEは、1回の学習時に使用するデータのサイズを決定する.
ここは32です.メモリが許容される場合は、メモリをある程度拡大することが望ましい.
EPOCHSはすべてのデータを1回学習するのが1 EPOCHで、ここではすべてのデータを30回学習すると言える.EpochとBatch Sizeの関係は、Epoch=反復☓Batch Sizeである.

(3)MNISTデータの設定


コード#コード#

train_dataset = datasets.MNIST(
    root="../data/MNIST",
    train=True,
    download=True,
    transform=transforms.ToTensor()
)
test_dataset = datasets.MNIST(
    root="../data/MNIST",
    train=False,
    download=True,
    transform=transforms.ToTensor()
)
train_loader = torch.utils.data.DataLoader(
    dataset=train_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True
)
test_loader = torch.utils.data.DataLoader(
    dataset=test_dataset,
    batch_size=BATCH_SIZE,
    shuffle=False
)

解説


datasets.MNISTでは、MNISTのデータセットをロードできます.trainオプションをTrueに設定するだけで、Train Data setに設定できます.テストはもちろんtrain=Falseに設定できます.
Transformオプションは、前処理に関連する内容で、各筆画をTenser形式に変換します.各ピクセルを0~1の値に正規化します.
DataLoaderは、上にロードされたデータセットを、ミニ配置のために予め設定されたbatch size(32)に簡単に設定することができる.

(4)データチェック


コード#コード#

for (X_train, y_train) in train_loader:
    print('X_train: ', X_train.size(), 'type: ', X_train.type())
    print('y_train: ', y_train.size(), 'type: ', y_train.type())
    break
しゅつりょく
X_train: torch.Size([32, 1, 28, 28]) type: torch.FloatTensor
y_train: torch.Size([32]) type: torch.LongTensor

解説


train loaderでは、各マイクロレイアウトがデータを出力するため、break文を書かないと、同じ形状の出力が複数回(非常に多い)発生します.どうせ同じなのでbreakで1回印刷します.
28,28は、手書き画像のサイズ(縦横28画素)であり、32個ごとにMini Batchであるため、32,1,28,28のサイズがある.
y trainは1つの画像の正しいTenserであるため、32という長いTenserのみが出力される.

(5)画像の出力を試みる


コード#コード#

def imshow(img):
    # img = img / 2 + 0.5     # denormalize
    npimg = img.numpy()
    plt.axis('off')
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

dataiter = iter(train_loader) # iterator
images, labels = dataiter.next()

imshow(torchvision.utils.make_grid(images))
しゅつりょく

解説


このコードを実行するたびに、32のデジタル画像が追加されます.
誰が書いたのか分からないが、字がおかしい.

(6)多層Perceptronモデル設計

class MLP(nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, 10)
        self.dropout_prob = 0.5 # 50%의 노드에 대한 가중치 계산을 하지 않기 위한 설정
        self.batch_norm1 = nn.BatchNorm1d(512) # 1dimension이기 때문에 BatchNorm1d를 사용함.
        self.batch_norm2 = nn.BatchNorm1d(256)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = self.fc1(x)
        x = self.batch_norm1(x)
        x = F.relu(x) # sigmoid(x)
        x = F.dropout(x, training=self.training, p=self.dropout_prob)
        x = self.fc2(x)
        x = self.batch_norm2(x)
        x = F.relu(x) # sigmoid(x)
        x = F.dropout(x, training=self.training, p=self.dropout_prob)
        x = self.fc3(x)
        x = F.log_softmax(x, dim=1)
        return x

解説

self.dropout_prob = 0.5 # 50%의 노드에 대한 가중치 계산을 하지 않기 위한 설정

x = F.dropout(x, training=self.training, p=self.dropout_prob)
これはニューラルネットワーク学習中に重み付け計算を行わずにランダムDropすることによってGeneralization効果を生成する装置であり,モデルinitで変数宣言を行う.
人間の遺伝アルゴリズムに突然変異の概念を借りたという.
reluの後にp値に上のdrop probを加えてdropdown関数を使用します.
self.batch_norm1 = nn.BatchNorm1d(512) # 1dimension이기 때문에 BatchNorm1d를 사용함.

x = self.batch_norm1(x)
入力値分布の違いによる学習速度の低下を防ぐため,各層にBatch Normalization技術を用いた.Input分布を正規化することで,学習速度を速める.
完全に接続された関数の後に使用します.この場合、それぞれ512256を入れます.

(7)初期化、Optimizer、関数設定


コード#コード#

def weight_init(m):
    if isinstance(m, nn.Linear):
        init.kaiming_uniform_(m.weight.data)

model = Net().to(DEVICE) # 정의한 모델을 GPU로 납치
model.apply(weight_init)
# optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.CrossEntropyLoss()

# print(model)

解説


ここでは、モジュールを最初にインポートしたときに述べたinitを使用します.
ここでinitはニューラルネットワークの初期化を意味する.
初期損失値は、どのように初期化するかによって変化するという.
最適なニューラルネットワーク損失を得るためには,初期損失がどこから始まるかが最も重要であり,ここでそれを捕まえる.
ここで用いた初期化方法はHe Initializationである.
if isinstance(m, nn.Linear):
	init.kaiming_uniform_(m.weight.data)
    
model.apply(weight_init)
パラメータのmm.隣接するオブジェクトのみを初期化します.ここの明瞭な制服は彼の初期化を意味する.
次に、この初期化関数をモデリングします.applyで適用します.
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
深度学習で最もよく使われるOptimizer Adamを使用します.ランニングレートは0.01に指定されています.

(8)学習関数の定義


コード#コード#

def train(model, train_loader, optimizer, log_interval):
    model.train()
    for batch_idx, (image, label) in enumerate(train_loader):
        image = image.to(DEVICE)
        label = label.to(DEVICE)
        optimizer.zero_grad()
        output = model(image)
        loss = loss_fn(output, label)
        loss.backward()
        optimizer.step()

解説


モデルを定義してGPUに誘拐したように、画像とラベルもGPUに誘拐します.
zero gradの場合、以前に計算した勾配を使用しないようにします.
また,モデル(画像)によるfeed‐forwardの1回通過後,Cross Entropyによるロス計算を行い,その後,逆伝搬を行うのは反復である.
optimizer.Step()パラメータ値を更新します.

(9)検証関数の定義


コード#コード#

def evaluate(model, test_loader):
    model.eval()
    test_loss = 0
    correct = 0

    with torch.no_grad():
        for image, label in test_loader:
            image = image.to(DEVICE)
            label = label.to(DEVICE)
            output = model(image)
            test_loss += loss_fn(output, label).item()
            prediction = output.max(1, keepdim=True)[1]
            correct += prediction.eq(label.view_as(prediction)).sum().item()
    
    test_loss /= len(test_loader.dataset)
    test_accuracy = 100. * correct / len(test_loader.dataset)
    return test_loss, test_accuracy

解説

with torch.no_grad():
検証フェーズのコードでは、パラメータは更新できません.だからno gradで防ぐ
output = model(image)
test loaderから抽出した画像をモデル計算出力として使用します.
test_loss += loss_fn(output, label).item()
CrossEntropyを使用して損失を計算し、test lossに追加します.
prediction = output.max(1, keepdim=True)[1]
Outは10サイズのベクトルなので、最大の数値を予測値予測として定義します.
prediction = output.max(1, keepdim=True)[1]
予測が正解と一致するかどうかを確認し、一致する場合は正しい変数に追加します.

(10)学習進捗と確認


コード#コード#

for Epoch in range(1, EPOCHS + 1):
    train(model, train_loader, optimizer, log_interval=100)
    test_loss, test_accuracy = evaluate(model, test_loader)
    print("[EPOCH: {}], \tTest Loss: {:.4f}, \tTest Accuracy: {:.2f} %".format(
        Epoch, test_loss, test_accuracy
    ))

解説


最初に設定した1~31、すなわち30 Epochで学習と検証を行います.

30を大きくすると約98.25%の精度を示すそうです.
終わります.