3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【PyTorchチュートリアル⑥】What is torch.nn really?

Last updated at Posted at 2020-10-10

はじめに

前回に引き続き、PyTorch 公式チュートリアル の第6弾です。
今回は What is torch.nn really? を進めます。

What is torch.nn really?

このチュートリアルでは、torch.nn、torch.optim、Dataset、DataLoader を説明します。 (torch.nn、torch.optimは前回でも説明しましたが、いろいろな人がチュートリアルを記述しているため、重複する部分もあります。)

使用するデータセットはMNISTデータセットです。
MNISTデータセットは0から9までの手書き数字画像のデータセットです。
理解を深めるため、最初に上記のパッケージを利用せずにモデルを構築します。
次に、torch.nn、torch.optim、Dataset、DataLoader の順で、一つずつコードを置き換えながら進めていきます。

1. MNIST data setup

まずはMNISTデータセット(手書き数字画像データセット)をダウンロードします。

from pathlib import Path
import requests

DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"

PATH.mkdir(parents=True, exist_ok=True)

URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"

if not (PATH / FILENAME).exists():
        content = requests.get(URL + FILENAME).content
        (PATH / FILENAME).open("wb").write(content)

このデータセットはnumpy配列です。
pickle 形式で保存されています。

import pickle
import gzip

with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
        ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")

一つのデータ(x_train[0])は28×28サイズの画像ですが、784列の1行として保持されています。
pyplot.imshow で閲覧するには、28×28に変換する必要があります。

from matplotlib import pyplot
import numpy as np

pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")
print(x_train.shape)
out
(50000, 784)

ダウンロード.png

以降では PyTorch の Tensor を利用します。
numpy配列から Tensor に変換します。

import torch

x_train, y_train, x_valid, y_valid = map(
    torch.tensor, (x_train, y_train, x_valid, y_valid)
)
n, c = x_train.shape
x_train, x_train.shape, y_train.min(), y_train.max()
print(x_train, y_train)
print(x_train.shape)
print(y_train.min(), y_train.max())
out
tensor([[0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        ...,
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.]]) tensor([5, 0, 4,  ..., 8, 4, 8])
torch.Size([50000, 784])
tensor(0) tensor(9)

学習データの件数は5万件(×784)、教師データは 0 から 9 までの数字であることが分かります。

2. Neural net from scratch (no torch.nn)

最初に torch.nn を利用せず Tensor のみでニューラルネットワークを作成します。
作成するモデルは、単純な線形モデル
$ y = w \times x + b$
です。

PyTorch の ランダムメソッド randn で重み $w$ を初期化します。randn は標準化(平均 0、標準偏差 1)されたランダム値です。
初期化するときの勾配を含めたくないため、初期化後に requires_grad_() を行い、requires_grad=True にします。
重みの初期化は「Xavier initialization」を利用しています。
(とありますが計算式が少し違うような気がします)
bias はゼロで初期化します。

import math

weights = torch.randn(784, 10) / math.sqrt(784)
weights.requires_grad_()
bias = torch.zeros(10, requires_grad=True)

活性化関数も必要ですので、log_softmax 関数を作成します。
PyTorch には損失関数や活性化関数が多数用意されていますが、このように自分で関数を作成することもできます。

def log_softmax(x):
    return x - x.exp().sum(-1).log().unsqueeze(-1)

def model(xb):
    return log_softmax(xb @ weights + bias)

@は内積演算を表します。
バッチサイズ(今回は64個の画像)単位でこの関数が呼び出されます。

bs = 64  # batch size

xb = x_train[0:bs]  # ミニバッチ
preds = model(xb)  # モデルで予想する
preds[0], preds.shape
print(preds[0], preds.shape)
out
tensor([-2.8486, -2.2823, -2.2740, -2.7800, -2.1906, -1.3280, -2.4680, -2.2958,
        -2.8856, -2.8650], grad_fn=<SelectBackward>) torch.Size([64, 10])

予測値 preds を出力すると Tensor に勾配関数(grad_fn)が含まれていることが確認できます。
後でこの勾配関数を利用して逆伝播を計算します。
損失関数として、教師データと予測値の負の対数尤度を実装します。
負の対数尤度は、一般的に交差エントロピー誤差関数と呼ばれます。

def nll(input, target):
    return -input[range(target.shape[0]), target].mean()

loss_func = nll

予測値と教師データで損失を計算し、学習後のパラメータを確認します。

yb = y_train[0:bs]
print(loss_func(preds, yb))
out
tensor(2.4101, grad_fn=<NegBackward>)

モデルの精度を計算する評価関数も実装します。
out には、手書き数字 0 から 9 までの確率が配列で保持されていますので、argmax の最大値の値が一番確率が高い手書き数字ということになります。
その値と教師データの一致した平均をとることで正解率を計算します。

def accuracy(out, yb):
    preds = torch.argmax(out, dim=1)
    return (preds == yb).float().mean()
print(accuracy(preds, yb))
out
tensor(0.0781)

これで学習する準備ができました。
次を繰り返し、学習を行います。

  • ミニバッチ単位で学習データを取得します。
  • モデルを使って、学習データから予測します。
  • 損失を計算します。
  • loss.backward() でモデルの勾配(重みとバイアス)を更新します。

重みとバイアスを更新した後に grad.zero_() で勾配を初期化しています。
これは loss.backward() で勾配を計算するときに、既に保存されているものに追加されるためです。
※set_trace() のコメントを外すことで、各ステップで変数の値を確認できます。

from IPython.core.debugger import set_trace

lr = 0.5  # learning rate
epochs = 2  # how many epochs to train for

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        #set_trace()
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        with torch.no_grad():
            weights -= weights.grad * lr
            bias -= bias.grad * lr
            weights.grad.zero_()
            bias.grad.zero_()

学習後に精度が向上していることが確認できます。

print(loss_func(model(xb), yb), accuracy(model(xb), yb))
out
tensor(0.0822, grad_fn=<NegBackward>) tensor(1.)

学習前は正解率が 7% でしたが、学習後は 100% になっています。
※ 学習データに xb が含まれているため学習後の正解率が高くなっています。 検証データ(x_valid)で試すと正解率は 9% ⇒ 95% くらいでした。

以上で、ゼロから単純なニューラルネットワークが構築できました。
今回の隠れ層がないソフトマックス関数を利用したネットワークはロジスティック回帰と呼ばれます。

3. Using torch.nn.functional

ここから PyTorch の nn パッケージを利用してコードをリファクタリングしていきます。
最初のステップでは、活性化関数と損失関数を置き換えていきましょう。
torch.nn.functional には、log_softmax関数と負の対数尤度を組み合わせた F.cross_entropy があります。
損失関数を F.cross_entropy に置き換えます。
F.cross_entropy には log_softmax関数が含まれていますので、活性化関数として定義した def log_softmax(x) も削除できます。

import torch.nn.functional as F

loss_func = F.cross_entropy

def model(xb):
    return xb @ weights + bias

model で呼び出していた log_softmaxも不要になります。(cross_entropyに含まれます)
損失と精度が以前と同じであることを確認しましょう。

print(loss_func(model(xb), yb), accuracy(model(xb), yb))
out
tensor(0.0822, grad_fn=<NllLossBackward>) tensor(1.)

4. Refactor using nn.Module

次に、nn.Module と nn.Parameter を利用してリファクタリングしていきます。
nn.Module は Pytorch のニューラルネットワークの基底クラスです。
nn.Module はサブクラスとして実装します。
作成したサブクラスに重みとバイアスのパラメータを定義します。また、forward メソッドに入力から出力まで順につなげる処理を記述します。
nn.Module には モデルのパラメータを返却する parameters() もあらかじめ用意されています。

from torch import nn

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.weights = nn.Parameter(torch.randn(784, 10) / math.sqrt(784))
        self.bias = nn.Parameter(torch.zeros(10))

    def forward(self, xb):
        return xb @ self.weights + self.bias

関数を使用する代わりにオブジェクトを使用しているので、最初にモデルをインスタンス化する必要があります。

model = Mnist_Logistic()

これでリファクタリング前と同じように学習できます。
nn.Module オブジェクトは関数のように呼び出して利用することができます。

print(loss_func(model(xb), yb))
out
tensor(2.3918, grad_fn=<NllLossBackward>)

ここまでの実装では、以下のように重みとバイアスの更新をそれぞれ計算し、勾配を手動でゼロにしていました。

  with torch.no_grad():
      weights -= weights.grad * lr
      bias -= bias.grad * lr
      weights.grad.zero_()
      bias.grad.zero_()

重みとバイアスの更新は、nn.Module で定義された parameters() と zero_grad() で置き換えて簡潔にできます。

  # 説明用コードのため実行できません(実行時エラーになります)
  with torch.no_grad():
      for p in model.parameters(): p -= p.grad * lr
      model.zero_grad()

学習のループを fit 関数として定義し、呼び出しできるようにしておきます。

def fit():
    for epoch in range(epochs):
        for i in range((n - 1) // bs + 1):
            start_i = i * bs
            end_i = start_i + bs
            xb = x_train[start_i:end_i]
            yb = y_train[start_i:end_i]
            pred = model(xb)
            loss = loss_func(pred, yb)

            loss.backward()
            with torch.no_grad():
                for p in model.parameters():
                    p -= p.grad * lr
                model.zero_grad()

fit()

損失が減っていることを再確認しましょう。

print(loss_func(model(xb), yb))
out
tensor(0.0796, grad_fn=<NllLossBackward>)

5. Refactor using nn.Linear

最初に weights と bias を自分で定義し、線形関数 $ w \times x + b$ も実装していましたが、nn.Linear (線形レイヤー)に置き換えてみましょう。

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin = nn.Linear(784, 10)

    def forward(self, xb):

        return self.lin(xb)

先ほどと同じようにモデルをインスタンス化し、損失を計算します。

model = Mnist_Logistic()
print(loss_func(model(xb), yb))
out
tensor(2.3661, grad_fn=<NllLossBackward>)

関数化した fit を呼び出して学習します。

fit()
print(loss_func(model(xb), yb))
out
tensor(0.0813, grad_fn=<NllLossBackward>)

loss値が 2.3661 から 0.0813 になり、学習できていることが確認できます。

6. Refactor using optim

次に最適化アルゴリズムをリファクタリングします。
Pytorch の torch.optim パッケージにはさまざまな最適化アルゴリズムがあります。
また、torch.optim の各クラスは手動でパラメータを更新する代わりに、 step メソッドを実行することでパラメータを更新してくれます。

  with torch.no_grad():
      for p in model.parameters(): p -= p.grad * lr
      model.zero_grad()

上記のコードを以下に書き換えることができます。
※ opt.zero_grad() で次のミニバッチの計算前に勾配を 0 にする必要があります。

  # 説明用コードのため実行できません
  opt.step()
  opt.zero_grad()
from torch import optim

モデルとオプティマイザの生成を関数化するとコードが簡潔になります。

def get_model():
    model = Mnist_Logistic()
    return model, optim.SGD(model.parameters(), lr=lr)

model, opt = get_model()
print(loss_func(model(xb), yb))

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))
out
tensor(2.3423, grad_fn=<NllLossBackward>)
tensor(0.0819, grad_fn=<NllLossBackward>)

7. Refactor using Dataset

PyTorchには抽象 Dataset クラスがあります。
Dataset を利用することでトレーニング中に訓練用データ(x_train)と教師データ(y_train)が扱いやすくなります。
Dataset は、要素数を返却する __len__関数と、インデックスを指定して要素を返却する __getitem__関数を実装する必要があります。
TensorDatasetは、データセットを Tensor にラップします。

from torch.utils.data import TensorDataset

TensorDataset 作成時に x_train と y_train を指定して作成します。

train_ds = TensorDataset(x_train, y_train)

先ほどまでは、訓練用データ(x_train)と教師データ(y_train)を個別に反復処理していました。

    xb = x_train[start_i:end_i]
    yb = y_train[start_i:end_i]

TensorDataset を利用すると、まとめて処理できます。

    xb,yb = train_ds[i*bs : i*bs+bs]
model, opt = get_model()

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        xb, yb = train_ds[i * bs: i * bs + bs]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))
out
tensor(0.0803, grad_fn=<NllLossBackward>)

8. Refactor using DataLoader

DataLoaderを使用すると、Dataset を利用したループ処理を簡潔にできます。
Dataset を元に DataLoader を作成します。

from torch.utils.data import DataLoader

train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs)

最初のコードでは、バッチサイズごとにスタート位置を指定し、スライスしていました。

for i in range((n-1)//bs + 1):
    xb,yb = train_ds[i*bs : i*bs+bs]
    pred = model(xb)

DataLoader を利用すると(xb、yb)が DataLoader から自動的に順次読み込まれるため、ループがより簡潔になります。

for xb,yb in train_dl:
    pred = model(xb)
model, opt = get_model()

for epoch in range(epochs):
    for xb, yb in train_dl:
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))
out
tensor(0.0802, grad_fn=<NllLossBackward>)

ここまでで、nn.Module、nn.Parameter、Dataset、DataLoader を利用しました。
コードを簡潔に理解しやすく記述できました。
次に効果的なモデルを作成するために必要な基本機能を追加してみます。

9. Add validation

ここまででは、学習データのみで学習を進めてきましたが、実際の学習では検証データ(validation data)を利用して、過学習していないか、学習が進んでいるかをチェックします。
以下で 検証用のデータセットを設定します。

  • 検証データセットはシャッフルしません。(検証データ全件が検証対象のため、シャッフルする意味はありません。)
  • バッチサイズを2倍にします。検証データでは勾配を計算する必要がないためメモリ消費が少なく、効率化のためバッチサイズを大きくします。
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)

valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)

各エポックの終わりに検証データ(validation data)を利用して損失を計算します。
学習の前に model.train() で学習モードにし、検証の前に model.eval() で評価モードにします。これは nn.Dropout などをトレーニング中のみに有効にさせるためです。

model, opt = get_model()

for epoch in range(epochs):
    model.train()
    for xb, yb in train_dl:
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

    model.eval()
    with torch.no_grad():
        valid_loss = sum(loss_func(model(xb), yb) for xb, yb in valid_dl)

    print(epoch, valid_loss / len(valid_dl))
out
0 tensor(0.3679)
1 tensor(0.2997)

10. Create fit() and get_data()

次に、学習と検証をどちらも行える関数 loss_batch を作成します。
loss_batch にオプティマイザを渡すと 逆伝播を計算し、パラメータを更新します。
検証時はオプティマイザを渡さないことで逆伝播が計算されません。

def loss_batch(model, loss_func, xb, yb, opt=None):
    loss = loss_func(model(xb), yb)

    if opt is not None:
        loss.backward()
        opt.step()
        opt.zero_grad()

    return loss.item(), len(xb)

fit 関数を定義します。
fit 関数は、各エポックで学習と検証を繰り返し、損失を表示します。

import numpy as np

def fit(epochs, model, loss_func, opt, train_dl, valid_dl):
    for epoch in range(epochs):
        model.train()
        for xb, yb in train_dl:
            loss_batch(model, loss_func, xb, yb, opt)

        model.eval()
        with torch.no_grad():
            losses, nums = zip(
                *[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]
            )
        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)

        print(epoch, val_loss)

get_data は 学習データと検証データの DataLoader を返却します。

def get_data(train_ds, valid_ds, bs):
    return (
        DataLoader(train_ds, batch_size=bs, shuffle=True),
        DataLoader(valid_ds, batch_size=bs * 2),
    )

これで、DataLoader を取得して学習を実行する処理を3行のコードで記述できるようになりました。

train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
out
0 0.45953697173595426
1 0.3061695278286934

3行のコードをリファクタリングすることで、さまざまなモデルを構築することができます。
畳み込みニューラルネットワーク(CNN)を構築できるか試してみましょう!

11. Switch to CNN

ここからは、3つの畳み込み層でニューラルネットワークを構築します。ここまでに作成した関数はモデルの制約は特にありませんので、変更を加えることなくCNNをに切り替えることができます。

Pytorch で用意された Conv2d クラスを畳み込み層として使用します。3つの畳み込み層でCNNを定義します。各畳み込み層の活性化関数は ReLU です。
最後に、平均プーリング層を追加します。 (ビューはnumpyの変形のPyTorchバージョンです)

class Mnist_CNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1)
        self.conv2 = nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1)
        self.conv3 = nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1)

    def forward(self, xb):
        xb = xb.view(-1, 1, 28, 28)
        xb = F.relu(self.conv1(xb))
        xb = F.relu(self.conv2(xb))
        xb = F.relu(self.conv3(xb))
        xb = F.avg_pool2d(xb, 4)
        return xb.view(-1, xb.size(1))

lr = 0.1

Momentum は、確率的勾配降下法のバリエーションで、直前の更新値も考慮され、一般により高速なトレーニングにつながります。

model = Mnist_CNN()
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
out
0 0.7808464194297791
1 0.6988550303936004

12. nn.Sequential

torch.nnには、コードを単純にするために使用できるもう1つの便利なクラス、Sequential があります。 Sequentialオブジェクトは、その中に含まれる各モジュールを順次実行します。ネットワークを簡単に記述できるのが特徴です。

Sequential を利用するためには、カスタムレイヤーが必要な場合があります。PyTorch にはネットワーク(層)の次元を変換するレイヤーがないため、独自でビューレイヤーを作成する必要があります。
以下の Lambda は Sequential で扱う入出力層を定義します。

class Lambda(nn.Module):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def forward(self, x):
        return self.func(x)


def preprocess(x):
    return x.view(-1, 1, 28, 28)

Sequential は、以下のように簡単にネットワークを記述することができます。

model = nn.Sequential(
    Lambda(preprocess),
    nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.AvgPool2d(4),
    Lambda(lambda x: x.view(x.size(0), -1)),
)

opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
out
0 0.4288556560516357
1 0.2115058801174164

13. Wrapping DataLoader

作成した CNN はかなり簡潔ですが、以下の制約があるため MNISTデータ(手書き数字画像)でのみ機能します。

  • 入力は28 * 28のデータである必要があります。
  • 最終的なグリッドサイズは4 * 4であると想定しています(カーネルサイズ 4 の2次元平均プーリングを利用しているため)

これらの2つの仮定を取り除いて、モデルが任意の2Dシングルチャネル画像(単色画像)で機能するようにします。
まず、最初のLambdaレイヤーを削除し、データの前処理をDataLoaderに移動します。

def preprocess(x, y):
    return x.view(-1, 1, 28, 28), y


class WrappedDataLoader:
    def __init__(self, dl, func):
        self.dl = dl
        self.func = func

    def __len__(self):
        return len(self.dl)

    def __iter__(self):
        batches = iter(self.dl)
        for b in batches:
            yield (self.func(*b))

train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)

次に、nn.AvgPool2dをnn.AdaptiveAvgPool2dに置き換えます。
これにより、入力テンソルではなく、必要な出力テンソルのサイズを定義できます。
その結果、平均プーリング層は任意のサイズの入力で動作します。

model = nn.Sequential(
    nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.AdaptiveAvgPool2d(1),
    Lambda(lambda x: x.view(x.size(0), -1)),
)

opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

試してみましょう。

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
out
0 0.3351769802570343
1 0.2583931807518005

14. Using your GPU

CUDA対応のGPUが利用できる場合(ほとんどのクラウドプロバイダーから1時間あたり約0.50ドルでGPUが利用できます)、学習を高速化できます。
まず、GPUがPytorchで動作していることを確認します。
※ Google Colaboratory でGPUを利用するには、「ランタイム」 > 「ランタイムのタイプを変更」から「ハードウェア アクセラレータ」を「GPU」にします。

print(torch.cuda.is_available())
out
True

次に、デバイスオブジェクトを作成します。
デバイスオブジェクトには GPUが利用できる場合は「cuda」、利用できない場合は「cpu」がセットされます。

dev = torch.device(
    "cuda") if torch.cuda.is_available() else torch.device("cpu")

バッチをGPUに移動する前処理を書き加えます。

def preprocess(x, y):
    return x.view(-1, 1, 28, 28).to(dev), y.to(dev)

train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)

最後に、モデルをGPUに移動します。

model.to(dev)
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)

処理速度が速くなったことが確認できます。

fit(epochs, model, loss_func, opt, train_dl, valid_dl)
out
0 0.1938392831325531
1 0.18594802458286286

Google Colaboratory で確認すると、CPUだと15秒くらいかかっていた上記の処理が5秒くらいで完了しました。

15. Closing thoughts(まとめ)

このチュートリアルで、モデルに依存しないデータ処理と学習処理が作成できました。
データ拡張(Data Augmentation)、ハイパーパラメータ調整、モニタリングト​​レーニング、転移学習など、追加したいことがたくさんあります。
これらの機能はfastaiライブラリで利用できます。fastaiライブラリは、このチュートリアルで示したものと同じ設計アプローチを使用して開発されており、機械学習をさらに習得する人には、よいステップになるでしょう。

このチュートリアルでは、torch.nn、torch.optim、Dataset、DataLoader を利用しました。
これまでに見てきたことをまとめてみましょう。

  • torch.nn
    • Module:関数のように呼びだせるオブジェクトで、ニューラルネットワーク層や重みを保持します。パラーメータも保持していて、勾配を計算したり、重みを更新する反復処理を実行します
    • Parameter:勾配の計算中に更新が必要な重みがあることをモジュールに通知する tensor のラッパー。 require_grad 属性が設定されたテンソルのみが更新されます
    • functional:活性化関数、損失関数や畳み込み層や線形層などの層を含むモジュール
  • torch.optim:SGD(確率的勾配降下法)などのオプティマイザーが含まれ、勾配計算中にパラメーターの重みを更新します
  • Dataset:TensorDataset などの クラスを含む、__len__および__getitem__を持つ抽象インターフェース
  • DataLoader:任意の Dataset を受け取り、データのバッチを返すイテレータを作成します

16. 最後に

以上が「What is torch.nn really?」です。
前回と似たような内容でしたが、Pytorch とニューラルネットワークの理解を深めることができました。
次回は「Visualizing Models, Data, and Training with TensorBoard」を進めたいと思います。

#履歴
2020/10/10 初版公開
2020/10/15 次回のリンク追加

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?