PyTorchによるニューラルネットワークの実践ガイド

このガイドでは、PyTorchの基本的な概念について学びます:テンソル、自動微分、ニューラルネットワークモジュール、オプティマイザ、カスタムモジュール、および動的計算グラフ。各トピックには実行可能なコードサンプルが含まれており、NumPyから始めてPyTorchの高度な機能に移行する方法を示します。

1. PyTorchの主要な特徴

  • テンソル(Tensor): NumPyのndarrayと似ていますが、GPU上で動作し、計算を高速化します。
  • 自動微分(Autograd): 手動でバックプロパゲーションを書くことなく、勾配を自動的に計算します。

例: 全結合ReLUネットワークを使用してランダムデータをフィッティング

2. NumPyを使ったシンプルな二層ネットワーク

ここでは、ディープラーニングフレームワークを使わずに、前進伝播、損失計算、逆伝播、およびパラメータ更新を手動で実装します。

import numpy as np

# ハイパーパラメータ
N, D_in, H, D_out = 64, 1000, 100, 10

# データ生成
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)

w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)

learning_rate = 1e-6

for t in range(500):
    # 前進伝播
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

    loss = np.square(y_pred - y).sum()
    if t % 100 == 0:
        print(t, loss)

    # 逆伝播
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

    # パラメータ更新
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

3. PyTorchテンソルでの実装

PyTorchテンソルはNumPyと類似した構文を持ちますが、デバイスを指定してGPU上で動作させることができます。

import torch

dtype = torch.float
device = torch.device("cpu")

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6

for t in range(500):
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 0:
        print(t, loss)

    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

4. 自動微分(Autograd)

PyTorchの自動微分機能を使って、手動で逆伝播を書かずに勾配を計算できます。

import torch

dtype = torch.float
device = torch.device("cpu")

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6

for t in range(500):
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 0:
        print(t, loss.item())

    loss.backward()

    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad
        w1.grad.zero_()
        w2.grad.zero_()

5. カスタム自動微分関数

自前のReLU関数を作成します。

import torch

class CustomReLU(torch.autograd.Function):
    @staticmethod
    def forward(ctx, input_tensor):
        ctx.save_for_backward(input_tensor)
        return input_tensor.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        input_tensor, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input_tensor < 0] = 0
        return grad_input

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device)
y = torch.randn(N, D_out, device=device)
w1 = torch.randn(D_in, H, device=device, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    y_pred = CustomReLU.apply(x.mm(w1)).mm(w2)
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 0:
        print(t, loss.item())

    loss.backward()
    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad
        w1.grad.zero_()
        w2.grad.zero_()

6. nn.Sequentialと最適化アルゴリズムの使用

nn.Sequentialと最適化アルゴリズムを使用してモデルを簡潔に定義します。

import torch

N, D_in, H, D_out = 64, 1000, 100, 10

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

loss_fn = torch.nn.MSELoss(reduction='sum')

optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)

for t in range(500):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
    if t % 100 == 0:
        print(t, loss.item())

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

7. nn.Moduleのサブクラス作成

複雑なモデル構造のためにnn.Moduleを継承します。

import torch

class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.fc1 = torch.nn.Linear(D_in, H)
        self.fc2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.fc1(x).clamp(min=0)
        y_pred = self.fc2(h_relu)
        return y_pred

N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = TwoLayerNet(D_in, H, D_out)
loss_fn = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

for t in range(500):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
    if t % 100 == 0:
        print(t, loss.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

タグ: PyTorch Tensor NeuralNetworks autograd optimization

9月1日 00:15 投稿