各種アルゴリズムと理論
深層学習基礎概要に大雑把にまとめました。
モデル構築
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