<jupyter_start><jupyter_text>ニューラルネットワークの訓練: 重みの更新回数と出力値 参考コード：https://github.com/pytorch/examples/blob/master/snli/train.py<jupyter_code>from google.colab import drive
drive.mount('/content/drive')
import numpy as np
import time

import pandas as pd
import matplotlib.pyplot as plt

import torch as t
import torch.optim
import torch.nn as nn
import torch.nn.functional as F
import torchvision as tv<jupyter_output><empty_output><jupyter_text>各種関数を定義<jupyter_code>device = t.device("cuda:0" if t.cuda.is_available() else "cpu")
device
class MLP(nn.Module):

    def __init__(self):
        super(MLP, self).__init__()
        self.l1 = nn.Linear(784, 1000) 
        self.l2 = nn.Linear(1000, 1000)
        self.l3 = nn.Linear(1000, 10)

    def forward(self, x):
        h = x.view(-1, 28*28) # (N, 1, 28, 28) -> (N, 784)
        # １層目
        h = F.relu(self.l1(h))
        # ２層目
        h = F.relu(self.l2(h))
        # 出力層
        return self.l3(h)
def dev_env(tensor):
    return "cuda" if tensor.is_cuda else "cpu"<jupyter_output><empty_output><jupyter_text>今回は`running_loss`をコードの可読性のためリストとして定義した。  だが、各lossをメモリに展開しておく理由も特にないので、普通のfloat変数として定義して毎回加算してしまって問題ない。<jupyter_code>def train(model, trainset, criterion, lr, batchsize, iterations):
    """
    指定されたモデルを指定回数分だけ訓練させる関数。
    """
    model.train()
    train_loader = t.utils.data.DataLoader(trainset, batch_size=batchsize)
    optimizer = t.optim.Adam(model.parameters(), lr=lr)
    running_loss = []
    correct = 0
    axis = 1
    if iterations is None:
        iterations = len(train_loader.dataset.data)
    criterion = criterion.to(device)
    model = model.to(device)
    for i, (inputs, labels) in enumerate(train_loader):
        # Move device
        inputs = inputs.to(device)
        labels = labels.to(device)
        optimizer.zero_grad()
        # Forward + Backward
        p = model(inputs)
        loss = criterion(p, labels)
        loss.backward()
        optimizer.step()
        # Calc statistics
        running_loss.append(loss.item())
        predicted_labels = p.max(axis)[1]
        correct += (predicted_labels == labels).sum().item() # 一致数の算出
        # Skip code
        if iterations < (i +1):
            break
    train_loss = np.array(running_loss).mean()
    train_acc = float(correct) / trainset.data.shape[0]
    return train_loss, train_acc
def valid(model, testset, criterion, batchsize):
    """
    指定されたモデルでデータを検証する関数
    """
    model.eval()
    model = model.to(device)
    criterion = criterion.to(device)
    correct = 0
    total = 0
    axis = 1
    running_loss = []
    test_loader = t.utils.data.DataLoader(testset, batchsize)
    with t.no_grad(): # Disable to calc gradient
        for i, (inputs, labels) in enumerate(test_loader):
            inputs = inputs.to(device)
            labels = labels.to(device)
            # Forward + Backward
            p = model(inputs) # p is one-hot vector
            loss = criterion(p, labels)
            # Calc statistics
            running_loss.append(loss.item())
            predicted_labels = p.max(axis)[1]
            correct += (predicted_labels == labels).sum().item()
    val_loss = np.array(running_loss).mean()
    val_acc = float(correct) / testset.data.shape[0]
    return val_loss, val_acc<jupyter_output><empty_output><jupyter_text>ポイント1```pythoncorrect =  (predicted == labels).sum().item()````(predicted_labels == labels)`でラベルが一致したかどうかの配列を生成している。  一致している場合は`True`が格納されて、そうでない場合は`False`が格納される。  その`True`の数を総和して取得している。> itemメソッドは`Tensor`型に含まれている単一数字を取得できる。 ポイント2```pythontotal += labels.size(0)````labels`は`torch.tensor`型なので`labels.size()`を実行しても`Tensor([n])`が取得できてしまって数値演算できない。  よって、0次元目の値を取り出すために`tensor.size(0)`でアクセスしている。  基本的に`labels.size(0)`は`DataLoader`が返す`Enumerarte`のサイズなので**基本的には**バッチサイズと一致する。  ただし、batchsizeによっては端数が出る可能性があるので、`tensor.size(0) * i`として`O(1)`計算するのは難しい。> 今回はデータセット数10,000に対してbatchsize=100でアクセスしているので端数が出ることなくアクセスできている。  > しかし、batchsize=128などでアクセスした場合は余りが発生するのでそうはならない。最後の`labels.size(0)`は16となる。<jupyter_code>csv_path = '/content/drive/My Drive/ColabNotebooks/PyTorchBeginner/loss.csv'
def train_and_eval(model, trainset, testset, lr=0.001, epochs=1, batchsize=100, iterations=None):
    results = []
    criterion = nn.CrossEntropyLoss()
    for epoch in range(epochs):
        start = time.time()
        train_loss, train_acc = train(model, trainset, criterion, lr, batchsize, iterations)
        valid_loss, valid_acc = valid(model, trainset, criterion, batchsize)
        end = time.time()
        results.append([epoch, train_loss, train_acc, valid_loss, valid_acc, end - start])
        print('epoch %d, train_loss: %.4f train_acc: %.4f val_loss: %.4f val_acc: %.4f' % (epoch, train_loss, train_acc, valid_loss, valid_acc))
    out = pd.DataFrame(results, columns=["epoch", "train_loss", "train_accuracy", "valid_loss", "valid_accuracy", "elapsed_time"])
    out.to_csv(csv_path, index=False)
    return out<jupyter_output><empty_output><jupyter_text>データをロードして準備しましょう。<jupyter_code>preprocess = tv.transforms.Compose([
                                  tv.transforms.ToTensor()
                                  ])
trainset = tv.datasets.MNIST('~/tmp/mnist', 
                                           train=True,
                                           download=True,
                                           transform=preprocess)
testset = tv.datasets.MNIST('~/tmp/mnist',
                                          train=False,
                                          download=True,
                                          transform=preprocess)<jupyter_output><empty_output><jupyter_text>トレーニングを繰り返す<jupyter_code>batchsize=100
result = train_and_eval(model=MLP(), trainset=trainset, testset=testset, epochs=20)
result.head()<jupyter_output><empty_output><jupyter_text>結果のグラフ化 各損失関数の値(loss)を表示<jupyter_code>result[['train_loss', 'valid_loss']].plot()<jupyter_output><empty_output><jupyter_text>accuracyの表示<jupyter_code>result[['train_accuracy', 'valid_accuracy']].plot()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1264559068219634, 'fraction_numerical': 0.015307820299500832, 'mean_word_length': 3.1830201809324983, 'pattern_counts': {'":': 0, '<': 34, '<?xml version=': 0, '>': 37, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '21896', 'license': 'MIT', 'n_tokens_mistral': 2449, 'n_tokens_neox': 2302, 'n_words': 333, 'path': '23_MNIST_loss_accuracy.ipynb', 'repo_name': 'Riliumph/PyTorchBeginner', 'size': '6010'}