1
1

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 1 year has passed since last update.

てきと〜にPyTorchでニューラルネット

Last updated at Posted at 2024-03-24

各種アルゴリズムと理論

深層学習基礎概要に大雑把にまとめました。

モデル構築

import torch
import torch.nn as nn
import torch.nn.functional as F

class Network(nn.Module):
    def __init__(self, dim_in, dim_out, dim_inner=512):
        super().__init__()

        self.netIN = nn.Sequential(
            nn.Conv2d(dim_in, dim_inner, kernel_size=3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(dim_inner),
            nn.ReLU(inplace=True)
        )
            
        self.netInner = nn.Sequential(
            nn.Conv2d(dim_inner, dim_inner, kernel_size=3, stride=1, padding=1, bias=False),
            nn.BatchNorm2d(dim_inner),
            nn.ReLU(inplace=True)
        )
        
        self.netOUT = nn.Conv2d(dim_inner, dim_out, kernel_size=3, stride=1, padding=1)

    def forward(self, x):
        x = self.netIN(x)
        x = self.netInner(x)
        x = self.netOUT(x)
        x = F.adaptive_avg_pool2d(x, 1).view(x.size(0), -1) # Global Average Pooling
        return x

こんな感じ。

  • nn.Conv2d: 畳み込み演算(2次元パーセプトロン)
  • nn.BatchNorm2d: バッチノーマライゼーション(データの正規化)
  • nn.ReLU: 活性化関数
  • Global Average Pooling: 全レイヤの平均

動作確認

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

x = torch.randn(1, 3, 32, 32).to(device)
network = Network(dim_in=3, dim_out=5).to(device)
print(network(x).shape)

Output

torch.Size([1, 5])

データセット準備

import os
from torchvision.datasets import ImageFolder
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader

dataset = ImageFolder(path, transform=transforms.Compose([
            transforms.Resize(int(size)),
            transforms.RandomCrop(size),
            transforms.ToTensor()
        ]))
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True, num_workers=os.cpu_count(), pin_memory=True)

とか。

  • ImageFolder: ラベル毎の複数のディレクトリに画像データの入ったディレクトリから画像を読み込みDatasetを返す(Datasetを自分で実装する事も多いです)
  • transforms: 画像をリサイズ等してマップする
  • DataLoader: バッチサイズに分割しながらデータセットからデータを読み込む

誤差逆伝播

import torch.optim as optim
from tqdm import tqdm

network = Network(dim_in=3, dim_out=len(dataset.classes)).to(device)
optimizer = optim.AdamW(network.parameters())
criterion = nn.CrossEntropyLoss()

network.train()
for data, label in tqdm(dataloader):
    data = data.to(device, non_blocking=True)
    label = label.to(device, non_blocking=True)
    
    y_eval = network(data)
    loss = criterion(y_eval, label)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    print(f'Loss: {loss.item()}')

こんな感じ。

  • optim.Adam: 勾配降下法の最適化アルゴリズムの一つ
  • nn.CrossEntropyLoss: ソフトマックス交差エントロピー誤差関数
  • tqdm: 進捗状況のプログレスバーを表示する

誤差逆伝播-AMP(Automatic Mixed Precision) ver.

import torch_optimizer
from torch.cuda.amp import GradScaler
from tqdm import tqdm

network = Network(dim_in=3, dim_out=len(dataset.classes)).to(device)
optimizer = torch_optimizer.Lamb(network.parameters())
scaler = GradScaler()
criterion = nn.CrossEntropyLoss()

network.train()
for data, label in tqdm(dataloader):
    data = data.to(device, non_blocking=True)
    label = label.to(device, non_blocking=True)

    with torch.autocast(device_type='cuda' if str(device).startswith('cuda') else 'cpu'):
        y_eval = network(data)
        loss = criterion(y_eval, label)
    
    optimizer.zero_grad()
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
    
    print(f'Loss: {loss.item()}')

こんな感じ。

  • torch.autocast: 倍精度浮動小数点数から単精度へのキャスト
  • GradScaler: 単精度浮動小数点数により勾配消失が起きやすいので、誤差逆伝播で勾配計算を行う時に誤差を大きくして計算し、それを元に戻して最適化アルゴリズムを適用する処理を行う
  • Lamb: AMPでミニバッチ数を大きくすると、イテレーションの大数の法則やバッチサイズの大きさの問題が出てくる。そこで、モデル全体ではなくレイヤー毎に学習率を最適化する手法のLARS、更にモーメンタムを追加考慮した最適化手法のLAMBがある

評価

from sklearn.metrics import accuracy_score

network.eval()
y_pred = network(eval_data).argmax(dim=1)
y_pred = y_pred.cpu().detach().numpy()
accuracy_score(y_true, y_pred)

こんな感じ。

保存と読み込み

保存

network.cpu()
torch.save(network.state_dict(), 'weight.pth')
network.to(device)

読み込み

network.load_state_dict(torch.load('weight.pth', map_location=device))

Note

  • Batch Normalization
    Batch Normalizationで正規化する時、その前の層にバイアスパラメータがあると、その分計算手順が増えて実行時間が遅くなるらしいです。
  • DataLoader
num_workers=os.cpu_count()

とすると並列処理が出来て高速化するらしいです。

pin_memory=True

とするとメモリ領域がページングされなくなって高速化するらしいです。

  • PyTorch JIT
    テンソルへの個別操作の関数を作る時は、JIT(C++実行形式)にして高速化するといいらしいです。
@torch.jit.script

とデコレータを関数の前に付けるだけです。

  • non_blocking=True
    テンソルデータをGPUに転送する際、ブロッキングしないように、non_blocking=Trueを付けるといいらしいです。
data = data.to(device, non_blocking=True)
  • GANにおけるAdamの学習率と一次及び二次モーメンタム
    経験則としてGANの場合、beta1=0, beta2=0.9、Generator-lr=1e-4, Discriminator-lr=4e-4などですが、
    Generator及びDirscriminatorで、beta1=0.5, beta2=0.9, lr=2e-4を推奨する論文があるらしいです。1
    また、Adamの実装に理論的ミスがあった様で、それを訂正したAdamWが発表公開されています。2

参考

PyTorchでの学習・推論を高速化するコツ集

  1. 敵対的生成ネットワーク

  2. AdamWにおける改善点をきちんと理解する

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?