PyTorch Ignite 0.4.8 : AI Tutorials : Getting Started (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/03/2022 (0.4.8)
* 本ページは、Pytorch Ignite AI の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
PyTorch Ignite 0.4.8 : AI Tutorials : Getting Started
PyTorch-Ignite のクイック・スタートガイドへようこそ、これは Ignite の基本的なコンセプトをウォークスルーしながらプロジェクトを立ち上げる要点をカバーしています。
数行だけのコードで、下で示されるように貴方のモデルを訓練して検証することができます。完全なコードはガイドの最後で見つけられます。
Prerequisites
このチュートリアルは以下に精通していることを仮定しています :
- Python と深層学習の基本
- PyTorch コードの構造
コード
以下をインポートします :
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.models import resnet18
from torchvision.transforms import Compose, Normalize, ToTensor
from ignite.engine import Engine, Events, create_supervised_trainer, create_supervised_evaluator
from ignite.metrics import Accuracy, Loss
from ignite.handlers import ModelCheckpoint
from ignite.contrib.handlers import TensorboardLogger, global_step_from_engine
利用可能な場合には device を cuda に、そうでない場合には cpu を設定して高速化します。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
貴方のモデルのクラスを定義するか以下のように (MNIST のために変更された) 事前定義された ResNet18 モデルを使用し、それをインスタンス化してデバイスに転送します :
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# Changed the output layer to output 10 classes instead of 1000 classes
self.model = resnet18(num_classes=10)
# Changed the input layer to take grayscale images for MNIST instaed of RGB images
self.model.conv1 = nn.Conv2d(
1, 64, kernel_size=3, padding=1, bias=False
)
def forward(self, x):
return self.model(x)
model = Net().to(device)
そして訓練と検証データセットを定義してそれらを (torch.utils.data.DataLoader として) train_loader と val_loader にそれぞれストアしましょう。理解しやすいように MNIST データセットを使用しました。
data_transform = Compose([ToTensor(), Normalize((0.1307,), (0.3081,))])
train_loader = DataLoader(
MNIST(download=True, root=".", transform=data_transform, train=True), batch_size=128, shuffle=True
)
val_loader = DataLoader(
MNIST(download=True, root=".", transform=data_transform, train=False), batch_size=256, shuffle=False
)
最後に、optimizer と損失関数を指定します :
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.005)
criterion = nn.CrossEntropyLoss()
そしてプロジェクトの重要なパートのセットアップは終了です。PyTorch-Ignite は以下で見るように総ての他のボイラープレートを処理します。次に create_supervised_trainer にモデル、optimizer と損失関数を渡すことにより trainer エンジンを、そして create_supervised_evaluator に out-of-the-box な メトリックス とモデルを渡すことにより 2 つの evaluator エンジンを定義しなければなりません。訓練と検証のために個別の evaluator エンジンと定義したのは、このチュートリアルの後で見るように異なる関数を提供するからです。
trainer = create_supervised_trainer(model, optimizer, criterion, device)
val_metrics = {
"accuracy": Accuracy(),
"loss": Loss(criterion)
}
train_evaluator = create_supervised_evaluator(model, metrics=val_metrics, device=device)
val_evaluator = create_supervised_evaluator(model, metrics=val_metrics, device=device)
オブジェクト trainer、train_evaluator と val_evaluator は総て Engine のインスタンスです – Ignite の主要コンポーネントで、これは本質的には訓練と検証ループの抽象化です。
訓練と検証ループに渡りより多くの制御を必要とする場合、Engine のステップロジックをラップすることにより カスタム trainer, train_evaluator と val_evaluator オブジェクトを作成することができます :
def train_step(engine, batch):
model.train()
optimizer.zero_grad()
x, y = batch[0].to(device), batch[1].to(device)
y_pred = model(x)
loss = criterion(y_pred, y)
loss.backward()
optimizer.step()
return loss.item()
trainer = Engine(train_step)
def validation_step(engine, batch):
model.eval()
with torch.no_grad():
x, y = batch[0].to(device), batch[1].to(device)
y_pred = model(x)
return y_pred, y
train_evaluator = Engine(validation_step)
val_evaluator = Engine(validation_step)
# Attach metrics to the evaluators
for name, metric in val_metrics.items():
metric.attach(train_evaluator, name)
for name, metric in val_metrics.items():
metric.attach(val_evaluator, name)
総ての種類のイベントハンドラを追加することによりコードを更にカスタマイズできます。エンジンは実行の間にトリガーされる様々なイベントに対してハンドラを追加することを可能にします。イベントがトリガーされるとき、装着されたハンドラ (関数) が実行されます。そして、ロギング目的で log_interval-th 反復毎に最後に実行される関数を追加します。
# How many batches to wait before logging training status
log_interval = 100
@trainer.on(Events.ITERATION_COMPLETED(every=log_interval))
def log_training_loss(engine):
print(f"Epoch[{engine.state.epoch}], Iter[{engine.state.iteration}] Loss: {engine.state.output:.2f}")
あるいは同値に、デコレータの代わりに add_event_handler で trainer にハンドラ関数を装着します。
def log_training_loss(engine):
print(f"Epoch[{engine.state.epoch}], Iter[{engine.state.iteration}] Loss: {engine.state.output:.2f}")
trainer.add_event_handler(Events.ITERATION_COMPLETED, log_training_loss)
訓練の間にエポックが終了した後、train_loader 上で train_evaluator を val_loader 上でそして val_evaluator をそれぞれ実行して訓練と検証メトリックスを計算できます。そのため trainer にエポックが完了したときに 2 つの追加ハンドラを装着します :
@trainer.on(Events.EPOCH_COMPLETED)
def log_training_results(trainer):
train_evaluator.run(train_loader)
metrics = train_evaluator.state.metrics
print(f"Training Results - Epoch[{trainer.state.epoch}] Avg accuracy: {metrics['accuracy']:.2f} Avg loss: {metrics['loss']:.2f}")
@trainer.on(Events.EPOCH_COMPLETED)
def log_validation_results(trainer):
val_evaluator.run(val_loader)
metrics = val_evaluator.state.metrics
print(f"Validation Results - Epoch[{trainer.state.epoch}] Avg accuracy: {metrics['accuracy']:.2f} Avg loss: {metrics['loss']:.2f}")
各エポックが完了したときメトリック (ここでは accuracy) により決定された n_saved 個の最良のモデルをセーブするために下で示されるように ModelCheckpoint() を利用できます。val_evaluator に model_checkpoint を装着します、何故ならば訓練データセットではなく検証データセット上で最高精度を持つ 2 つのモデルを望むからです。これが、前に 2 つの別個の evaluator (val_evaluator と train_evaluator) を定義した理由です。
# Score function to return current value of any metric we defined above in val_metrics
def score_function(engine):
return engine.state.metrics["accuracy"]
# Checkpoint to store n_saved best models wrt score function
model_checkpoint = ModelCheckpoint(
"checkpoint",
n_saved=2,
filename_prefix="best",
score_function=score_function,
score_name="accuracy",
global_step_transform=global_step_from_engine(trainer), # helps fetch the trainer's state
)
# Save the model after every epoch of val_evaluator is completed
val_evaluator.add_event_handler(Events.COMPLETED, model_checkpoint, {"model": model})
trainer の損失と、訓練と検証メトリクスを別々にログ記録するために TensorboardLogger() を使用します。
# Define a Tensorboard logger
tb_logger = TensorboardLogger(log_dir="tb-logger")
# Attach handler to plot trainer's loss every 100 iterations
tb_logger.attach_output_handler(
trainer,
event_name=Events.ITERATION_COMPLETED(every=100),
tag="training",
output_transform=lambda loss: {"batch_loss": loss},
)
# Attach handler for plotting both evaluators' metrics after every epoch completes
for tag, evaluator in [("training", train_evaluator), ("validation", val_evaluator)]:
tb_logger.attach_output_handler(
evaluator,
event_name=Events.EPOCH_COMPLETED,
tag=tag,
metric_names="all",
global_step_transform=global_step_from_engine(trainer),
)
最後に、訓練データセット上でエンジンを起動してそれを 5 エポック実行します :
trainer.run(train_loader, max_epochs=5)
Epoch[1], Iter[100] Loss: 0.19 Epoch[1], Iter[200] Loss: 0.13 Epoch[1], Iter[300] Loss: 0.08 Epoch[1], Iter[400] Loss: 0.11 Training Results - Epoch[1] Avg accuracy: 0.97 Avg loss: 0.09 Validation Results - Epoch[1] Avg accuracy: 0.97 Avg loss: 0.08 Epoch[2], Iter[500] Loss: 0.07 Epoch[2], Iter[600] Loss: 0.04 Epoch[2], Iter[700] Loss: 0.09 Epoch[2], Iter[800] Loss: 0.07 Epoch[2], Iter[900] Loss: 0.16 Training Results - Epoch[2] Avg accuracy: 0.93 Avg loss: 0.20 Validation Results - Epoch[2] Avg accuracy: 0.93 Avg loss: 0.20 Epoch[3], Iter[1000] Loss: 0.02 Epoch[3], Iter[1100] Loss: 0.02 Epoch[3], Iter[1200] Loss: 0.05 Epoch[3], Iter[1300] Loss: 0.06 Epoch[3], Iter[1400] Loss: 0.06 Training Results - Epoch[3] Avg accuracy: 0.94 Avg loss: 0.20 Validation Results - Epoch[3] Avg accuracy: 0.94 Avg loss: 0.23 Epoch[4], Iter[1500] Loss: 0.08 Epoch[4], Iter[1600] Loss: 0.02 Epoch[4], Iter[1700] Loss: 0.08 Epoch[4], Iter[1800] Loss: 0.07 Training Results - Epoch[4] Avg accuracy: 0.98 Avg loss: 0.06 Validation Results - Epoch[4] Avg accuracy: 0.98 Avg loss: 0.07 Epoch[5], Iter[1900] Loss: 0.02 Epoch[5], Iter[2000] Loss: 0.11 Epoch[5], Iter[2100] Loss: 0.05 Epoch[5], Iter[2200] Loss: 0.02 Epoch[5], Iter[2300] Loss: 0.01 Training Results - Epoch[5] Avg accuracy: 0.99 Avg loss: 0.02 Validation Results - Epoch[5] Avg accuracy: 0.99 Avg loss: 0.03 State: iteration: 2345 epoch: 5 epoch_length: 469 max_epochs: 5 output: 0.005351857747882605 batch: <class 'list'> metrics: <class 'dict'> dataloader: <class 'torch.utils.data.dataloader.DataLoader'> seed: <class 'NoneType'> times: <class 'dict'>
(訳注 : 実験結果)
Epoch[1], Iter[100] Loss: 0.18 Epoch[1], Iter[200] Loss: 0.16 Epoch[1], Iter[300] Loss: 0.04 Epoch[1], Iter[400] Loss: 0.09 Training Results - Epoch[1] Avg accuracy: 0.95 Avg loss: 0.16 Validation Results - Epoch[1] Avg accuracy: 0.96 Avg loss: 0.14 Epoch[2], Iter[500] Loss: 0.04 Epoch[2], Iter[600] Loss: 0.04 Epoch[2], Iter[700] Loss: 0.09 Epoch[2], Iter[800] Loss: 0.10 Epoch[2], Iter[900] Loss: 0.02 Training Results - Epoch[2] Avg accuracy: 0.95 Avg loss: 0.15 Validation Results - Epoch[2] Avg accuracy: 0.96 Avg loss: 0.13 Epoch[3], Iter[1000] Loss: 0.05 Epoch[3], Iter[1100] Loss: 0.01 Epoch[3], Iter[1200] Loss: 0.06 Epoch[3], Iter[1300] Loss: 0.03 Epoch[3], Iter[1400] Loss: 0.01 Training Results - Epoch[3] Avg accuracy: 0.98 Avg loss: 0.08 Validation Results - Epoch[3] Avg accuracy: 0.98 Avg loss: 0.08 Epoch[4], Iter[1500] Loss: 0.02 Epoch[4], Iter[1600] Loss: 0.06 Epoch[4], Iter[1700] Loss: 0.05 Epoch[4], Iter[1800] Loss: 0.03 Training Results - Epoch[4] Avg accuracy: 0.99 Avg loss: 0.02 Validation Results - Epoch[4] Avg accuracy: 0.99 Avg loss: 0.03 Epoch[5], Iter[1900] Loss: 0.05 Epoch[5], Iter[2000] Loss: 0.01 Epoch[5], Iter[2100] Loss: 0.01 Epoch[5], Iter[2200] Loss: 0.11 Epoch[5], Iter[2300] Loss: 0.01 Training Results - Epoch[5] Avg accuracy: 0.99 Avg loss: 0.04 Validation Results - Epoch[5] Avg accuracy: 0.99 Avg loss: 0.05 State: iteration: 2345 epoch: 5 epoch_length: 469 max_epochs: 5 output: 0.0741286650300026 batch: <class 'list'> metrics: <class 'dict'> dataloader: <class 'torch.utils.data.dataloader.DataLoader'> seed: <class 'NoneType'> times: <class 'dict'>
# Let's close the logger and inspect our results
tb_logger.close()
%load_ext tensorboard
%tensorboard --logdir=.
# At last we can view our best models
!ls checkpoints
'best_model_4_accuracy=0.9856.pt' 'best_model_5_accuracy=0.9857.pt'
'best_model_4_accuracy=0.9901.pt' 'best_model_5_accuracy=0.9870.pt'
完全なコード
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision.models import resnet18
from torchvision.transforms import Compose, Normalize, ToTensor
from ignite.engine import Engine, Events, create_supervised_trainer, create_supervised_evaluator
from ignite.metrics import Accuracy, Loss
from ignite.handlers import ModelCheckpoint
from ignite.contrib.handlers import TensorboardLogger, global_step_from_engine
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.model = resnet18(num_classes=10)
self.model.conv1 = self.model.conv1 = nn.Conv2d(
1, 64, kernel_size=3, padding=1, bias=False
)
def forward(self, x):
return self.model(x)
model = Net().to(device)
data_transform = Compose([ToTensor(), Normalize((0.1307,), (0.3081,))])
train_loader = DataLoader(
MNIST(download=True, root=".", transform=data_transform, train=True), batch_size=128, shuffle=True
)
val_loader = DataLoader(
MNIST(download=True, root=".", transform=data_transform, train=False), batch_size=256, shuffle=False
)
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.005)
criterion = nn.CrossEntropyLoss()
trainer = create_supervised_trainer(model, optimizer, criterion, device)
val_metrics = {
"accuracy": Accuracy(),
"loss": Loss(criterion)
}
train_evaluator = create_supervised_evaluator(model, metrics=val_metrics, device=device)
val_evaluator = create_supervised_evaluator(model, metrics=val_metrics, device=device)
log_interval = 100
@trainer.on(Events.ITERATION_COMPLETED(every=log_interval))
def log_training_loss(engine):
print(f"Epoch[{engine.state.epoch}], Iter[{engine.state.iteration}] Loss: {engine.state.output:.2f}")
@trainer.on(Events.EPOCH_COMPLETED)
def log_training_results(trainer):
train_evaluator.run(train_loader)
metrics = train_evaluator.state.metrics
print(f"Training Results - Epoch[{trainer.state.epoch}] Avg accuracy: {metrics['accuracy']:.2f} Avg loss: {metrics['loss']:.2f}")
@trainer.on(Events.EPOCH_COMPLETED)
def log_validation_results(trainer):
val_evaluator.run(val_loader)
metrics = val_evaluator.state.metrics
print(f"Validation Results - Epoch[{trainer.state.epoch}] Avg accuracy: {metrics['accuracy']:.2f} Avg loss: {metrics['loss']:.2f}")
def score_function(engine):
return engine.state.metrics["accuracy"]
model_checkpoint = ModelCheckpoint(
"checkpoint",
n_saved=2,
filename_prefix="best",
score_function=score_function,
score_name="accuracy",
global_step_transform=global_step_from_engine(trainer),
)
val_evaluator.add_event_handler(Events.COMPLETED, model_checkpoint, {"model": model})
tb_logger = TensorboardLogger(log_dir="tb-logger")
tb_logger.attach_output_handler(
trainer,
event_name=Events.ITERATION_COMPLETED(every=100),
tag="training",
output_transform=lambda loss: {"batch_loss": loss},
)
for tag, evaluator in [("training", train_evaluator), ("validation", val_evaluator)]:
tb_logger.attach_output_handler(
evaluator,
event_name=Events.EPOCH_COMPLETED,
tag=tag,
metric_names="all",
global_step_transform=global_step_from_engine(trainer),
)
trainer.run(train_loader, max_epochs=5)
tb_logger.close()
以上