Chapter 03

学習

sosa
sosa
2023.01.30に更新
このチャプターの目次

PyTorchチュートリアル(日本語翻訳版)「PyTorch入門 8. クイックスタート」を変更していきます。
こちらのコードと比較しながら試してみてください。

クイックスタートそのまま
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt

あやめデータ抽出

クイックスタート全面入れ替え
# サンプルデータ準備、あらかじめ用意されているデータを利用。
#Iris Dataset:あやめ(花びら/がく片の長さと幅の4項目)の表形式データセット
from sklearn.datasets import load_iris
iris = load_iris()
# 入力値xと目標値tを抽出
x=iris['data']
t=iris['target']
# PytorchのTensor型へ変換
x=torch.tensor(x,dtype=torch.float32)#少数に設定
t=torch.tensor(t,dtype=torch.int64)#整数に設定
# 入力値と目標値をまとめる
dataset=torch.utils.data.TensorDataset(x,t)
# データセット分割、datasetを分割
#学習データ、テストデータを用意する。
# 各データのサンプル数を決定する
# training:test=80%:20%
n_train=int(len(dataset)*0.8)
n_test=int(len(dataset)*0.2)
# データセット150個の分割,ランダムに分割
training_data,test_data=torch.utils.data.random_split(dataset,[n_train,n_test])

あやめのデータの準備ができました。ここからクイックスタートに合わせていきます。クイックスタートと比較しながら見てください。

バッチサイズとエポック数

バッチサイズは学習用のデータを指定した数で分割します。今回は10、10個のデータの学習が終わったあとパラメータを調整しています。この方法をミニバッチ法といいます。
エポック数は学習用のデータ全部を何回学習するかを指定します。今回200にしました。後述。
まとめると学習用データ全120個を10個に分け学習し、10個終わったらパラメータを更新。それを12回繰り返す。これでエポック数1回分が終了です。これをエポック数で指定した200回繰り返します。

クイックスタート書き換え
batch_size = 10#バッチサイズは少なくした
# データローダーの作成
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

モデルの構築
クイックスタートのモデルをあやめ用に変更しても正解率が低いので、こちらのモデルを使わせていただきました。
Pytorchメモ: DatasetとDataLoaderを使ったミニバッチ処理

クイックスタート書き換え
# 訓練に際して、可能であればGPU(cuda)を設定します。GPUが搭載されていない場合はCPUを使用します
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))
# modelを定義します
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        # self.flatten = nn.Flatten()#nn.Flattenレイヤーで、2次元(28x28)の画像を、1次元の784ピクセルの値へと変換するもの。今回は関係ない。
        # nn.Sequentialは、モジュールを順番に格納する箱のような要素です。
        # 入力データはnn.Sequentialに定義された順番に各モジュールを伝搬します。
        self.linear_relu_stack = nn.Sequential(
#*******このモデルを「あやめ」にあてはめてやった(4入力3出力)が、成績が良くないので使わない。*****
            # nn.Linear(28*28, 512),#入力データが28*28の画像データなので28*28入力ノードが必要でした。
            # nn.ReLU(),
            # nn.Linear(512, 512),
            # nn.ReLU(),
            # nn.Linear(512, 10),#出力10種類なので10
            # nn.ReLU()
#*********************************************************************
#********あやめ用の正解率のいいモデルを使う***********
            #Irisの特徴量(がく片の長さ、がく片の幅、花びらの長さ、花びらの幅)が4個で1組なのでノード4

            nn.Linear(4,6),#入力条件4個なので4。2層目の6は変えてもいい。
            nn.Sigmoid(),
            nn.Linear(6,3)#あやめ3種類の判別なのでノード
#**********************************************************************
         )
    def forward(self, x):
        # x = self.flatten(x)#今回二次元画像ではないので関係ない。
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork().to(device)
print(model)

モデルパラメータの最適化

クイックスタート書き換え
loss_fn = nn.CrossEntropyLoss()
# optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)#lr学習係数0.001だと正解率低くなるので0.1にした。
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

batchにはbatch_size分つまり10組のデータが入っているのでxも10個。pred = model(X)
で10個分の結果が出てくる。yには正解10個がはいっているので、pred10個と比較でloss損失誤差を計算している。

クイックスタート書き換え
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X, y = X.to(device), y.to(device)
        
        # 損失誤差を計算
        pred = model(X)
        loss = loss_fn(pred, y)
        
        # バックプロパゲーション
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # if batch % 100 == 0:#バッチ数が少ないので全部表示
        loss, current = loss.item(), batch * len(X)
        print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
クイックスタートそのまま
def test(dataloader, model):
    size = len(dataloader.dataset)
    model.eval()
    test_loss, correct = 0, 0
    with torch.no_grad():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= size
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
クイックスタート書き換え
epochs = 200#training全データセット120個をepoxhs回学習
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(train_dataloader, model, loss_fn, optimizer)
    test(test_dataloader, model)
print("Done!")

モデルの保存

クイックスタートそのまま
torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")

モデルの読み込み

クイックスタートそのまま
model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))