Chainer の Framework Migration Guideを DeepL で翻訳しました。
一般情報
このドキュメントでは、Chainer から PyTorch への移行に関する技術情報を提供します。
両方のフレームワークの概念とコンポーネント
配列ライブラリ
Chainer は NumPy/CuPy (xp.ndarray) を配列ライブラリとして使用し、autograd
をサポートするために chainer.Variable
としてラップしています。同様に、PyTorch は ATen (at::Tensor (C++)) を配列ライブラリ (PyTorch 用語では「テンソルライブラリ」) として使用し、それを torch::Tensor
(C++ API) / torch.Tensor
(Python API) としてラップして autograd
をサポートしています。
コアフレームワークとトレーニングループ
どちらのフレームワークも define-by-run というコンセプトを共有しているので、PyTorch で書かれたコードのルック&フィールは Chainer によく似ています。以下に機能のハイレベルなマッピングを示します。
Chainer | PyTorch | Notes |
---|---|---|
Variable chainer.Variable |
Tensor torch.Tensor |
|
Function chainer.FunctionNode (chainer.functions.*) |
Function torch.autograd.Function (torch.nn.functional.*) |
`torch.*` もまた NumPy に似た操作を提供します (互換性はありません)。 |
Link / Chain chainer.{Link, Chain} (chainer.links.*) |
Module torch.nn.Module (torch.nn.*) |
|
Sequential chainer.Sequential |
Sequential torch.nn.Sequential |
関数モジュールをメンバとして使用することができます(例:torch.nn.functional.reluの代わりにtorch.nn.ReLU)。 |
Dataset chainer.dataset.DatasetMixin (chainer.datasets.*) |
Dataset torch.utils.data.Dataset |
PyTorchにはTransformDatasetはありません(CPMにはcpm.TransformDatasetとして存在します)。 |
Iterator chainer.iterators.* |
DataLoader torch.utils.data.DataLoader |
Chainer's Iterator とは異なり、DataLoader はデフォルトではすべてのサンプルを自動的に 1 つの Tensor に照合します。
DataLoader 自体はマルチプロセスの反復処理をサポートしています (num_workers オプションを使用します)。 |
Optimizer chainer.Optimizer (chainer.optimizers.*) |
Optimizer torch.optim.Optimizer (torch.optim.*) |
|
Trainer chainer.training.Trainer |
Engine ignite.Engine |
ignite.engine.create_supervised_trainer() |
Updater (with converter) chainer.training.Updater |
Engine ignite.Engine |
上述したように、Iterator はデフォルトでサンプルを連結します。デバイスへの転送は Engine (Ignite を使用しない場合はカスタムループコード) で処理されます。 |
Evaluator chainer.training.extensions.Evaluator |
Engine ignite.Engine |
ignite.engine.create_supervised_evaluator() |
Extension chainer.training.Extension (chainer.training.extensions.*) |
Handler (ignite.handlers.*, ignite.contrib.handlers.*) |
各コンポーネントの違いの詳細については、ポーティングガイドの項を参照してください。
移行のシナリオ
ChainerスクリプトをPyTorchに一歩一歩移植したい
おそらくモデルは、トレーニングの結果に影響を与えずに移植するのが最も難しい部分です。
この順番で移植する方が簡単かもしれません。
1. トレーニングスクリプト (オプティマイザ / アップデータ / 評価器 / ...)
PyTorchオプティマイザを使ってChainerモデルを学習するには、 cpm.LinkAsTorchModel
が必要です。
2. データセット / 前処理
Datasetは一般的にChainerとPyTorchの間で互換性があります。この部分は後回しにしてもいいですが、簡単にできるはずです。
3. モデル
本ドキュメントでは、以下の関数/モジュールのマッピングを参照してください。
ChainerコードにPyTorchモデルを学習させたい
cpm.TorchModule
を使って、PyTorch モジュールを Chainer モデルとしてラップすることができます。
移行ツール (cpm)
chainer-pytorch-migration
Python モジュール (このドキュメントでは cpm
と呼ばれています) は、Chainer から PyTorch への移行を支援するための様々なユーティリティを提供しています。
コード例では、以下のように cpm
がインポートされていると仮定しています。
import chainer_pytorch_migration as cpm
import chainer_pytorch_migration.ignite
cpm.TorchModule
PyTorchモジュールをChainerリンクとしてラップします。これにより、ChainerのトレーニングスクリプトでPyTorchモデルを学習することができます。グラフ(前進/後退)はPyTorchで構築し、走査する必要があります。
model = torchvision.models.resnet50()
model.cuda()
w_model = cpm.TorchModule(model)
w_model.to_gpu(device) # Just synchronizes the metadata, does not transfer data
cpm.ChainerParameter
ChainerパラメータをPyTorchパラメータとしてラップします。これにより、PyTorchのトレーニングスクリプト(torch.optimizer
を使って)でChainerモデル(chainer.Link
)を学習することができます。グラフ(前進/後退)は Chainer で構築し、走査する必要があります。
# initialized parameter
arr = numpy.full(shape, 17, 'float32')
chainer_param = chainer.Parameter(arr)
torch_param = cpm.ChainerParameter(chainer_param)
cpm.LinkAsTorchModel
このクラスは、与えられたChainer linkのすべての cpm.ChainerParameter
オブジェクトを自動的に作成し、pytorch オプティマイザや horovod などのツールが必要とする parameters()
、named_parameters()
、state_dict()
などのメソッドを提供します。
model = ChainerModel()
model.to_device(ch_device)
# Initialize parameters before converting to `ChainerParameter`s.
model(ch_device.xp.zeros((1, 784)).astype('f'))
# Convert parameters to `ChainerParameter`s to share memory with PyTorch.
torched_model = cpm.LinkAsTorchModel(model)
optimizer = optim.SGD(torched_model.parameters(), lr=args.lr)
cpm.ignite.add_trainer_extension
この関数は、igniteで使用するChainerトレーナーエクステンションを登録します。
関数呼び出しには、パラメータとして ignite トレーナー、Torchオプティマイザー、Chainerエクステンションが必要です。
optimizer.target = model
trainer.out = 'path to store extension results'
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.ExponentialShift('lr', 0.9, 1.0, 0.1))
cpm.use_torch_in_cupy_malloc
この関数は、PyTorchのメモリプールをCuPyに利用させます。CuPyを使う操作をする前に呼び出す必要があります。
# Enable using PyTorch memory allocator in CuPy.
cpm.use_torch_in_cupy_malloc()
# Revert back to CuPy's default memory pool.
cpm.use_mempool_in_cupy_malloc()
移植ガイド
データセットとデータの前処理/後処理
PyTorchのデータセット(pytorch.utils.data.Dataset
)は基本的にChainerのものと互換性があります。ほとんどの場合、両方向で互換性があります。
負の歩み
PyTorch 1.2.0 以降、PyTorch は負のストライドを持つデータ配列を扱うことができません(例えば、numpy.flip
や chainercv.transform.flip
の結果になる可能性があります)。
この問題を回避する最も簡単な方法は、データセットを numpy.ascontiguousarray
でラップすることです。
def avoid_negative_strides(in_data):
data, label = in_data
data = numpy.ascontiguousarray(data)
return data, label
dataset = cpm.TransformDataset(dataset, avoid_negative_strides)
data_loader = torch.utils.data.DataLoader(dataset, ...)
もう一つの方法は、torch.utils.data.DataLoader
の collate_fn
引数で照合関数をカスタマイズすることです。
def collate(batch):
data = numpy.stack([d for d, l in batch])
label = numpy.stack([l for d, l in batch])
data_tensor = torch.from_numpy(data)
label_tensor = torch.from_numpy(label)
return data_tensor, label_tensor
data_loader = torch.utils.data.DataLoader(dataset, ..., collate_fn=collate)
カスタムコンバータ関数を collate_fn に書き換える
Chainerでは、training.updaters.StandardUpdater(training_iter, optimizer, device=device, converter=_converter)
を使用して、バッチごとにカスタムコンバータを指定することができます。PyTorchでは、同様の機能をデータローダで実現することができます。DataLoader(..., collate_fn=_converter)
.
しかし、マルチプロセッシングと併用する場合には、重要な違いがあります。Chainerでは、_converter
はメインプロセスで実行されるので、マルチプロセシングのfork
モードを使う場合は、関数内でCUDAにアクセスしても問題ありません。しかし、PyTorchでは _converter
はデータローダの各ワーカープロセス内で実行されます。これでは、CUDAのinitエラーが出ないとCUDAにアクセスできない。PyTorchでは、_convert
の内部でCPU関連の処理だけを行い、データローダからテンソルを取得した後にGPUにテンソルを送信するのが正しい使い方のようです。以下はPyTorchの正しい使い方の例です。
it = DataLoader(..., collate_fn=_converter)
for img, label, metadata in it:
img = img.cuda()
label = label.cuda()
# metadata is still on CPU
...
上記のシナリオは、_converter
がメインプロセスで呼び出される Chainer で期待されるものとは異なることに注意してください。
上記のユースケースでは、(img, label)
の CPU から GPU への転送を高速化するために、_convert
は pin_memory
も使用しなければならないことに注意してください: https://discuss.pytorch.org/t/when-to-set-pin-memory-to-true/19723
NumPy ブリッジ
torch.DataLoader
は NumPy 配列を自動的に PyTorch テンソルに変換しますが、手動で変換したい場合は NumPy Bridge を参照してください。
CuPy ブリッジ
DLPack は CuPy と torch.Tensor
の間のブリッジとして使用できます。DLPack は所有権を処理しないので、変換された tensor/配列が使用されている間は元のバッファ (元の cupy.ndarray
オブジェクトや toDlpack()
で返された dltensor
カプセルオブジェクト) が生き残っていることを確認しなければならないことに注意してください。
所有権の共有を含めて CuPy/PyTorch を完全に変換するには、cpm.asarray
と cpm.astensor
を使用します。
また、CuPy を使う前に cpm.use_torch_in_cupy_malloc
をコールして、PyTorch とアロケータを共有することをお勧めします。
注意: PyTorch 1.3.0 の時点で、__cuda_array_interface__
が壊れています (pytorch/pytorch/#24947)。この問題を修正したら、cpm
の代わりに cupy.asarray
と torch.as_tensor
を使うだけで、これらの間で変換できるようになります。
PyTorchとNumPy/CuPyの違い
事業部行動
Python 3 の除算ルールを尊重する NumPy/CuPy とは挙動が異なります。PyTorchで明示的にfloat
にキャストする必要があります(議論)。
>>> x = numpy.arange(5)
>>> x
array([0, 1, 2, 3, 4])
>>> x / 5
array([0. , 0.2, 0.4, 0.6, 0.8])
>>> torch.from_numpy(x) / 5
tensor([0, 0, 0, 0, 0])
>>> torch.from_numpy(x).float() / 5
tensor([0.0000, 0.2000, 0.4000, 0.6000, 0.8000])
フィーチャーマッピング
比較表は PyTorch for Numpy を参照してください。
トレーニングループ
これはトレーニングループの例のコードです。model.train()
に注意。
device = torch.device('cuda:0')
for i_epoch in range(args.epoch):
train_loss = 0
train_correct = 0
model.train()
for x, t in data_loader:
x = x.to(device)
t = t.to(device).long()
optimizer.zero_grad()
y = model(x)
loss = F.nll_loss(y, t)
loss.backward()
optimizer.step()
train_loss += loss.sum().item()
_, pred = torch.max(y, 1)
train_correct += (pred == t).sum().item()
train_loss /= len(data_loader.dataset)
train_accuracy = train_correct / len(data_loader.dataset)
print('Train average loss: {:.03f}'.format(train_loss))
print('Train accuracy : {:.03f} %'.format(train_accuracy * 100))
評価ループ
これは評価ループの例です。model.eval()
とtorch.no_grad()
に注目してください。
device = torch.device('cuda:0')
total_loss = 0
total_correct = 0
model.eval()
with torch.no_grad():
for x, t in data_loader:
x = x.to(device)
t = t.to(device).long()
y = model(x)
total_loss += F.nll_loss(y, t, reduction='sum').item()
_, pred = torch.max(y, 1)
total_correct += (pred == t).sum().item()
average_loss = total_loss / len(loader.dataset)
accuracy = total_correct / len(loader.dataset)
Igniteを使ったトレーニングと評価
IgniteはChainerのChainer.training.Trainer
に相当するものです。
このChainerのコード
updater = chainer.training.StandardUpdater(
train_iter,
optimizer,
device=device)
trainer = chainer.training.Trainer(updater, (100, ‘epoch’))
trainer.extend(
extensions.Evaluator(
val_iter,
model,
device=device),
trigger=(1, ‘epoch’))
trainer.run()
はIgniteを使ってPyTorchで書くことができます。
trainer = ignite.engine.create_supervised_trainer(
model,
optimizer,
F.nll_loss,
device=device)
evaluator = ignite.engine.create_supervised_evaluator(
model,
metrics={
'accuracy': ignite.metrics.Accuracy(),
'loss': ignite.metrics.Loss(F.nll_loss),
},
device=device)
@trainer.on(ignite.engine.Events.EPOCH_COMPLETED)
def validation(engine):
evaluator.run(val_loader)
average_accuracy = evaluator.state.metrics[‘accuracy’]
average_loss = evaluator.state.metrics[‘loss’]
print(average_accuracy, average_loss)
trainer.run(train_loader, max_epochs=100)
サポートされているメトリクスのリストについては、https://pytorch.org/ignite/metrics.html を参照してください。
IgniteでChainerエクステンションを使う
cpm.ignite.add_trainer_extension
を使用すると、Ignite トレーニングループ内で呼び出される Chainer 拡張機能を登録することができます。
サポートされている拡張機能のリストは以下の通りです。
-
動くもの:
ExponentialShift
,FailOnNonNumber
,InverseShift
,LinearShift
,LogReport
,MicroAverage
,MultistepShift
,ParameterStatistics
,PlotReport
,PolynomialShift
,PrintReport
,ProgressBar
,snapshot
(read docs),StepShift
,observe_lr
,VariableStatisticsPlot
,WarmupShift
-
動かないもの:
DumpGraph
,Evaluator
,unchain_variables
いくつかの欠点は、モデルやリンクに関連付けられたメトリクスがデフォルトではアクセスできないことにあります。
例えば、これはChainerモデルの内部で行われているため、ユーザーはIgniteコールバックを使用して反復ごとの損失や精度を報告する必要があります。
また、いくつかの拡張機能を動作させるためには、ユーザがTorchまたはChainerモデルをオプティマイザのターゲット属性とLogReport、プロッタ、スナップショット拡張機能の出力ディレクトリパスに割り当てる必要があります。
from chainer import reporter
@trainer.on(Events.ITERATION_COMPLETED)
def report_loss(engine):
reporter.report({'loss':engine.state.output})
複数の拡張子を登録する方法の一例です。
# Torch optimizer
optimizer = SGD(model.parameters(), lr=lr, momentum=momentum)
# Ignite trainer
trainer = create_supervised_trainer(model, optimizer, F.nll_loss, device=device)
# Add the model to the target attribute of the optimizer
optimizer.target = model
# Set the output dir for some of the extensions
trainer.out = 'result'
# Restore the snapshot
cpm.ignite.load_chainer_snapshot(trainer, optimizer, 'result/snapshot_iter_4691')
# Add a bunch of extensions
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.ProgressBar())
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.observe_lr())
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.MicroAverage('loss','lr','mav',(1, 'iteration')))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.LogReport())
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.FailOnNonNumber())
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.ExponentialShift('lr', 0.9, 1.0, 0.1))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.ParameterStatistics(model, prefix='model'))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.VariableStatisticsPlot(model))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.PrintReport(
['epoch', 'iteration', 'loss', 'lr', 'mav', 'model/fc2/weight/grad/percentile/1']))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.PlotReport(['loss'],
'epoch', filename='loss.png'))
cpm.ignite.add_trainer_extension(trainer, optimizer, extensions.snapshot(writer=writer), trigger=(1, 'epoch')) # writer is a SimpleWriter
スナップショット
Igniteトレーナーでスナップショット拡張子を使用すると、PyTorchオブジェクトは出力フォルダ内の追加の snapshot-torch
ファイルに保存されます。これにより、移行が終了した後もこれらのスナップショットを使用し続け、これらのファイルから直接pytorchモデルやオプティマイザの状態をロードすることができます。
さらに、igniteとpytorchでChainerモデルやオプティマイザを混合している場合、これらのオブジェクトはChainerスナップショットファイルに保存されます。
スナップショットを復元する正しい方法は、cpm.ignite.load_chainer_snapshot(engine, optimizer, snapshot_path)
に Chainer スナップショットのパスを指定して使用することです。
以前に取得した Chainer スナップショットは互換性がないことに注意してください。
Igniteを使ったカスタムアップデータの移植
Igniteエンジンにステップ関数を渡すことができます。
- DCGANの例
既存のChainerモデルの書き換え
関数とリンクのマッピングを使って、PyTorch の対応する関数を見つけて置き換えることができます。また、既存のモデルの実装は
- PyTorchの例
-
torchvision.models
モジュール (CV モデル) (API リファレンス) - PyTorch Hub (コミュニティからのモデル) (API リファレンス)
よくある落とし穴
- 画像フォーマット。RGBまたはBGR
- 画像の正規化 0,1] または [0,255] です。
- 古いPyTorchの例やコミュニティプロジェクトの中には、非推奨のインターフェイスである
torch.autograd.Variable
を使用しているものがあります。 -
resnet
のような有名なモデルは、ChainerCVとtorchvisionでは異なる動作をするかもしれません。例えば、chainercv.links.ResNet50
は出力にsoftmax
を適用しますが、torchvision.models.resnet50
は適用しません。
関数とリンク
Chainer の関数の PyTorch と同等のものは、以下の表にあります。
注意点。
- NumPy/CuPy とは異なり、PyTorch Tensor は勾配計算をサポートしています (
torch.*
やtorch.nn.functional.*
をtorch.Tensor
上で使用しても問題ありません)。 - キーワード引数の規約: PyTorch では、Chainer/NumPy の
axis
とkeepdim
の代わりにdim
とkeepdim
を使用します。 - Chainerとは異なり、PyTorchは各関数のモジュール版を提供しています(例:
F.relu
の場合はnn.ReLU
)ので、Sequential
コンテナで関数を使ってモデルを定義する場合はモジュール版を使うことができます。
関数
F は chainer.functions (Chainer) / torch.nn.functional (PyTorch) を参照しています。
算術関数
Chainer | PyTorch | Notes |
---|---|---|
F.add | torch.add | バッチ加算(1回の呼び出しで複数のテンソルを蓄積する)はサポートされていません。 |
活性化関数
Chainer | PyTorch | Notes |
---|---|---|
F.clipped_relu | Rewrite as: x.clamp(0, z) |
|
F.crelu | Rewrite as: torch.cat((F.relu(x), F.relu(-x))) |
|
F.elu | F.elu | |
F.hard_sigmoid | Rewrite as: torch.clamp(x * 0.2 + 0.5, 0, 1) |
|
F.leaky_relu | F.leaky_relu | デフォルトのスロープ値が異なります。 |
F.log_softmax | F.log_softmax | |
F.lstm | L.LSTMを参照のこと。 | |
F.maxout | 手動で実装する必要があります。https://github.com/pytorch/pytorch/issues/805 を参照してください。 | |
F.prelu | F.prelu | |
F.rrelu | F.rrelu | Chainer の `train` 設定の代わりに `training` オプションを明示的に指定する必要があります。 |
F.relu | F.relu | |
F.relu6 | F.relu6 | |
F.selu | F.selu | |
F.sigmoid | F.sigmoid | |
F.slstm | OSSの実装がいくつかあります(例:https://github.com/reachtarunhere/S-LSTM-PyTorch)。 | |
F.softmax | F.softmax | |
F.softplus | F.softplus | PyTorch はデフォルトでは線形関数にフォールバックします。 |
F.swish | Rewrite as: x * F.sigmoid(beta * x) |
|
F.tanh | F.tanh | |
F.tree_lstm | OSSの実装がいくつかあります(例: https://github.com/dasguptar/treelstm.pytorch)。 |
配列操作
Chainer | PyTorch | Notes |
---|---|---|
F.as_strided | torch.as_strided | |
F.broadcast | torch.broadcast_tensors | PyTorch の操作は NumPy のように自動的にブロードキャストされます: https://pytorch.org/docs/stable/notes/broadcasting.html |
F.broadcast_to | 該当なし: https://github.com/pytorch/pytorch/pull/17160 | |
F.cast | Tensor.to | |
F.concat | torch.cat | |
F.copy | Tensor.to | |
F.depth2space | F.pixel_shuffle | |
F.diagonal | torch.diagonal | |
F.dstack | Rewrite as: torch.cat([a,b],dim=2) |
|
F.expand_dims | Rewrite as: torch.unsqueeze(a, dim) |
|
F.flatten | torch.flatten | Notes |
F.flip | torch.flip | |
F.fliplr | torch.flip | dims=1を使用します。 |
F.flipud | torch.flip | dims=0を使用します。 |
F.get_item | 直接インデックスを使用する: `x[indexes]`. 負のストライドはサポートされていません。 | |
F.hstack | Rewrite as: torch.cat([a,b],dim=1) |
|
F.im2col | F.unfold | NCHWのみ対応しています。 |
F.moveaxis | Tensor.permute | 参照:https://discuss.pytorch.org/t/swap-axes-in-pytorch/970/2 |
F.pad | F.pad | 引数 `constant_values` を `value` に置き換えてください。constant`以外のモードも利用可能である。 |
F.pad_sequence | nn.utils.rnn.pad_squence | 長さは指定できませんが、入力の中で最大の長さを使用します。 |
F.permutate | Tensor.permute | |
F.repeat | Tensor.repeat | F.repeatとは動作が異なります。F.tileの方が似ています。 |
F.reshape | torch.reshape | |
F.resize_images | F.interpolate | Notes |
F.rollaxis | Tensor.premute | https://discuss.pytorch.org/t/swap-axes-in-pytorch/970/2 を参照してください。 |
F.scatter_add | Tensor.scatter_add | |
F.select_item | Rewrite as: torch.gather(x, 1, t[:, None])[:, 0] |
|
F.separate | torch.split | 分離された機能の一部を実現するためには、結果を手動で操作する必要があります。 |
F.space2depth | 自分で実装する必要があります。参考にしてください。 https://discuss.pytorch.org/t/is-there-any-layer-like-tensorflows-space-to-depth-function/3487/14 | |
F.spatial_transformer_grid | F.affine_grid | 第2引数 `size` は出力画像のサイズ (N, C, H, W) を表す `torch.Size` オブジェクトを取り、`F.spatial_transformer_grid` は (H, W) のタプルを取ります。返されるテンソルのサイズも異なる。(N x 2 x H x W x 2)の代わりに(N x H x W x 2)が返される。また、v1.3.0 の align_corners に関する変更点にも注意してください (https://github.com/pytorch/pytorch/releases/tag/v1.3.0)。 |
F.spatial_transformer_sampler | F.grid_sample | グリッド形状はChainerでは(N, 2, H, W)ですが、PyTorchでは(N, H, W, 2)です。 |
F.split_axis | torch.split | `force_tuple`がない。 |
F.squeeze | torch.squeeze | |
F.stack | torch.stack | torch.stack または torch.cat([a,b],dim=axis) を使用します。 |
F.swapaxes | 代わりにpermuteを使用してください: https://discuss.pytorch.org/t/swap-axes-in-pytorch/970/7 | |
F.tile | F.repeat | |
F.transpose | torch.t | 軸なしバージョンにはTensor.permuteまたはtorch.tを使用します。 |
F.transpose_sequence | 該当なし | |
F.vstack | Rewrite as: torch.cat([a,b],dim=0) |
|
F.where | torch.where |
ニューラルネットワーク接続
Chainer | PyTorch | Notes |
---|---|---|
F.bilinear | F.bilinear | |
F.convolution_1d | F.conv1d | `cover_all`はありません。 |
F.convolution_2d | F.conv2d | `cover_all`はありません。 |
F.convolution_3d | F.conv3d | `cover_all`はありません。 |
F.convolution_nd | https://discuss.pytorch.org/t/is-there-a-way-to-realize-4d-convolution-using-the-convnd-function/5999 を参照してください。 | |
F.deconvolution_1d | F.conv_transpose1d | |
F.deconvolution_2d | F.conv_transpose2d | |
F.deconvolution_3d | F.conv_transpose3d | |
F.deconvolution_nd | 該当なし、https://discuss.pytorch.org/t/is-there-a-way-to-realize-4d-convolution-using-the-convnd-function/5999 を参照 | |
F.depthwise_convolution_2d | F.conv2d | 引数 `groups` を使用します; https://discuss.pytorch.org/t/depthwise-and-separable-convolutions-in-pytorch/7315/2 を参照してください。 |
F.deformable_convolution_2d_sampler | 未実装: https://github.com/pytorch/pytorch/issues/2260 | |
F.dilated_convolution_2d | F.conv2d | 引数 `dilation` を用いる。 |
F.embed_id | F.embedding | |
F.linear | F.linear | `n_batch_axes` のオプションはありません。 |
F.local_convolution_2d | https://github.com/pytorch/pytorch/pull/1583 を参照してください。 | |
F.n_step_bigru | ドキュメント化されていない _C._VariableFunctions 関数 torch.gru? link」が https://pytorch.org/docs/stable/nn.html#torch.nn.GRU にありますが、これはおそらく予想される使い方です。 | |
F.n_step_bilstm | L.NStepBiLSTMを参照のこと。 | |
F.n_step_birnn | L.NStepBiRNNTanhまたはL.NStepBiRNNReLUを参照のこと。 | |
F.n_step_gru | L.NStepBiGRUを参照してください。 | |
F.n_step_lstm | L.NStepLSTMを参照のこと。 | |
F.n_step_rnn | L.NStepRNNTanhまたはL.NStepRNNReLUを参照のこと。 | |
F.shift | https://github.com/pytorch/pytorch/issues/16408 を参照してください。 |
評価関数
Chainer | PyTorch | Notes |
---|---|---|
F.accuracy | 該当なし、Igniteには実装があります: https://pytorch.org/ignite/metrics.html#ignite.metrics.Accuracy | |
F.binary_accuracy | 該当なし、Igniteには実装があります: https://pytorch.org/ignite/metrics.html#ignite.metrics.Accuracy | |
F.classification_summary | 該当なし | |
F.f1_score | https://discuss.pytorch.org/t/calculating-precision-recall-and-f1-score-in-case-of-multi-label-classification/28265/1 を参照してください。 | |
F.precision | 該当なし、Igniteには実装があります: https://pytorch.org/ignite/metrics.html#ignite.metrics.Precision | |
F.r2_score | 利用できません。差別化できない評価指標です。Igniteで実装されているので、参考にしてみてください。 https://github.com/pytorch/ignite/pull/496 | |
F.recall | 該当なし、Igniteには実装があります: https://pytorch.org/ignite/metrics.html#ignite.metrics.Recall |
損失関数
Chainer | PyTorch | Notes |
---|---|---|
F.absolute_error | 該当なし | |
F.bernoulli_nll | 可能性: -torch.distributionions.Bernoulli(y).log_prob(x).sum() | |
F.black_out | ||
F.connectionist_temporal_classification | F.ctc_loss | |
F.contrastive | https://github.com/adambielski/siamese-triplet を参照してください。 | |
F.crf1d | 利用できません: https://github.com/pytorch/pytorch/issues/11134 | |
F.argmax_crf1d | 利用できません: https://github.com/pytorch/pytorch/issues/11134 | |
F.cross_covariance | ||
F.decov | ||
F.discriminative_margin_based_clustering_loss | 入手できません。再現作品は https://github.com/Wizaron/instance-segmentation-pytorch を参照してください。 | |
F.gaussian_kl_divergence | 該当なし | |
F.gaussian_nll | 該当なし | |
F.hinge | F.hinge_embedding_loss | |
F.huber_loss | F.smooth_l1_loss | reduction='sum' を使用して reduction メソッドを維持する |
F.mean_absolute_error | F.l1_loss | 参照: ignite.metrics.MeanAbsoluteError |
F.mean_squared_error | F.mse_loss | |
F.negative_sampling | https://github.com/kefirski/pytorch_NEG_loss, https://github.com/theeluwin/pytorch-sgns を参照してください。 | |
F.sigmoid_cross_entropy | F.binary_cross_entropy_with_logits | |
F.softmax_cross_entropy | nn.CrossEntropyLoss | |
F.squared_error | ||
F.triplet | F.triplet_margin_loss |
数学関数
Chainer | PyTorch | Notes |
---|---|---|
F.absolute | torch.abs | |
F.arccos | torch.acos | |
F.arcsin | torch.asin | |
F.arctan | torch.atan | |
F.arctan2 | torch.atan2 | |
F.arctanh | 該当なし: https://github.com/pytorch/pytorch/issues/10324 | |
F.argmax | torch.argmax | |
F.argmin | torch.argmin | |
F.average | F.meanを参照してください。 | |
F.batch_inv | torch.inverse | linalg の操作バッチは進行中で、逆はすでにマージされています。 |
F.batch_l2_norm_squared | Rewrite as: x.reshape(len(x), -1).norm(dim=1) ** 2 |
|
F.batch_matmul | torch.matmul | |
F.bias | Rewrite as x + y[(...,) + (None,) * (x.ndim - y.ndim - axis)] |
|
F.ceil | torch.ceil | |
F.clip | torch.clamp | |
F.cos | torch.cos | |
F.cosh | torch.cosh | |
F.cumprod | torch.cumprod | |
F.cumsum | torch.cumsum | |
F.det | torch.det | |
F.batch_det | torch.det | 任意の数のバッチ軸をサポートしています。 |
F.digamma | torch.digamma | |
F.einsum | torch.einsum | |
F.erf | torch.erf | |
F.erfc | torch.erfc | Notes |
F.erfcinv | 使えません。erfと似たような実装、erfinv? https://github.com/pytorch/pytorch/pull/2799 | |
F.erfcx | 該当なし | |
F.exp | torch.exp | |
F.expm1 | torch.expm1 | |
F.fft | torch.fft | インターフェイスがかなり違います。 |
F.fix | 該当なし | |
F.fmod | torch.fmod | Notes |
F.floor | torch.floor | |
F.identity | nn.Identity | |
F.ifft | torch.ifft | |
F.inv | torch.inverse | |
F.lgamma | torch.lgamma | 現在は未登録https://github.com/pytorch/pytorch/pull/27812 |
F.linear_interpolate | 普通の算数で十分です。 | |
F.log | torch.log | |
F.log10 | torch.log10 | |
F.log1p | torch.log1p | |
F.log2 | torch.log2 | |
F.log_ndtr | 該当なし | |
F.logsumexp | torch.logsumexp | |
F.matmul | torch.matmul | |
F.max | torch.max | |
F.maximum | torch.max | |
F.mean | torch.mean | 加重平均はサポートされていません。
rewrite as (without keepdims): torch.tensordot(x, weights / weights.sum(), ([axis], [0])) |
F.min | torch.min | |
F.minimum | torch.min | Notes |
F.ndtr | F.gelu(x)はx * F.ndtr(x)に対応しています。 | |
F.ndtri | 該当なし | |
F.prod | torch.prod | |
F.polygamma | torch.polygamma | 文書化されていない: https://github.com/pytorch/pytorch/issues/25347 n>=2はサポートされていません: https://github.com/pytorch/pytorch/blob/1a92b225db8519ab4697954a5559674f58e91aa7/aten/src/ATen/native/cpu/UnaryOpsKernel.cpp#L187 |
F.rsqrt | torch.rsqrt | |
F.scale | Rewrite as: x * y[(...,) + (None,) * (x.ndim - y.ndim - 1)] |
|
F.sin | torch.sin | |
F.sinh | torch.sinh | |
F.sign | torch.sign | |
F.sparse_matmul | torch.sparse.mm | 密-疎積のみをサポートします。疎密積の場合は、オペランドと出力を転置します。 |
F.sqrt | torch.sqrt | |
F.square | Rewrite as: x * x |
|
F.squared_difference | 該当なし | |
F.sum | torch.sum | |
F.sum_to | 該当なし | |
F.tanh | torch.tanh | |
F.tan | torch.tan | |
F.tensordot | torch.tensordot |
ノイズ注入
Chainer | PyTorch | Notes |
---|---|---|
F.dropout | F.dropout | マスクはサポートされず、要素はランダムにゼロになります。 |
F.gaussian | torch.distributions.normal.Normal | |
F.gumbel_softmax | F.gumbel_softmax | タウのデフォルト値は1ですが、Chainerの関数は0.1を取ります。 |
F.simplified_dropconnect | 利用できません。ウエイトにF.dropoutを使うか、torchnlp.nn.WeightDropを試してみてください。 | |
F.zoneout | 該当なし |
正規化関数
Chainer | PyTorch | Notes |
---|---|---|
F.batch_normalization | F.batch_norm | |
F.batch_renormalization | 該当なし | |
F.decorrelated_batch_normalization | 該当なし | |
F.fixed_batch_normalization | F.batch_norm | training=False? |
F.fixed_batch_renormalization | Batch Renormalizationは実装されていません: https://discuss.pytorch.org/t/support-for-batch-renormalization/2965, https://discuss.pytorch.org/t/batch-renormalization-implementation-in-thcunn/5144 | |
F.fixed_decorrelated_batch_normalization | 該当なし | |
F.group_normalization | F.group_norm | 現在、書類送検されていません。 |
F.layer_normalization | layer_norm | `gamma = weight` & `beta= bias` |
F.local_response_normalization | F.local_response_norm | |
F.normalize | F.normalize | PyTorchの`F.normalize`はL2正規化だけではない。しかし、デフォルトの振る舞いはL2正規化用であり、第2引数 `p` のデフォルト値は2に設定されている。 |
空間プーリング
Chainer | PyTorch | Notes |
---|---|---|
F.average_pooling_1d | F.avg_pool1d | |
F.average_pooling_2d | F.avg_pool2d | Chainerの対極にあるもののスーパーセット。 |
F.average_pooling_3d | F.avg_pool3d | |
F.average_pooling_nd | 該当なし | |
F.max_pooling_1d | F.max_pool1d | 引数 `return_indices` は、`nn.MaxPool1D` で説明した引数に加えて、アンプールするためのインデックスを取得することができます。 |
F.max_pooling_2d | F.max_pool2d | 同上。 |
F.max_pooling_3d | F.max_pool3d | 同上。 |
F.max_pooling_nd | 該当なし | |
F.roi_average_align_2d | torchvision.ops.roi_align | Torchvisionを必要とします。 torchvisionには2つのROI関数しかありません。ROI_ALIGNはピクセルの平均値を使い、ROI_POOLは最大値を使います。 |
F.roi_average_pooling_2d | 該当なし | |
F.roi_max_align_2d | 該当なし | |
F.roi_max_pooling_2d | torchvision.ops.roi_pool | Torchvision の `roi_pool` 関数は、ソースによると roi max プーリングを意味しています: https://github.com/pytorch/vision/blob/ccd1b27d2b7312ebddb4d51b3a4f8ade1ba8fa8b/torchvision/csrc/cpu/ROIPool_cpu.cpp#L65
APIについてですが、RoI座標の各集合の一括インデックスの渡し方が異なります。 |
F.roi_pooling_2d | torchvision.ops.roi_pool | Torchvisionが必要です。 |
F.spatial_pyramid_pooling_2d | 利用できません。実装は難しくない?Chainerの既存の機能を組み合わせたものです。 | |
F.unpooling_1d | F.max_unpool1d | F.max_pool1dから返された `indices` を渡します。 |
F.unpooling_2d | F.max_unpool2d | 同上。 |
F.unpooling_3d | F.max_unpool3d | 同上。 |
F.unpooling_nd | 該当なし | |
F.upsampling_2d | 該当なし |
ユーティリティ関数
Chainer | PyTorch | Notes |
---|---|---|
F.forget | https://pytorch.org/docs/stable/checkpoint.html を参照してください。 |
リンク
L
は chainer.links
(Chainer)、nn
は torch.nn
(PyTorch) を参照しています。
学べるつながり
Chainer | PyTorch | Notes |
---|---|---|
L.Bias | ||
L.Bilinear | nn.Bilinear | |
L.ChildSumTreeLSTM | 該当なし。リファレンスユーザーの実装は https://github.com/ttpro1995/TreeLSTMSentiment にあります。 | |
L.Convolution1D | nn.Conv1d | |
L.Convolution2D | nn.Conv2d | |
L.Convolution3D | nn.Conv3d | |
L.ConvolutionND | 該当なし | |
L.Deconvolution1D | nn.ConvTranspose1d | |
L.Deconvolution2D | nn.ConvTranspose2d | |
L.Deconvolution3D | nn.ConvTranspose3d | |
L.DeconvolutionND | ||
L.DeformableConvolution2D | 該当なし: https://github.com/pytorch/pytorch/issues/2260 | |
L.DepthwiseConvolution2D | nn.Conv2d | 引数 `groups` を使用します; https://discuss.pytorch.org/t/depthwise-and-separable-convolutions-in-pytorch/7315/2 を参照してください。 |
L.DilatedConvolution2D | nn.Conv2d | 引数 `dilation` を用いる。 |
L.EmbedID | nn.Embedding | |
L.GRU | nn.GRU | |
L.Highway | 該当なし | |
L.Inception | torchvision.models.inception.InceptionA` はChainerの `L.Inception` に対応するモジュールのようだが、ドキュメント化されていない。 | |
L.InceptionBN | Inception v3 については torchvision.models.inception を参照してください。 | |
L.Linear | nn.Linear | |
L.LocalConvolution2D | 該当なし | |
L.LSTM | nn.LSTM | |
L.MLPConvolution2D | ||
L.NaryTreeLSTM | 該当なし | |
L.NStepBiGRU | nn.GRU | 双方向性=True, 明示的な活性化なし, スタッキングなし |
L.NStepBiLSTM | nn.LSTM | 双方向性=True, 明示的な活性化なし, スタッキングなし |
L.NStepBiRNNReLU | nn.RNN | 双方向性=True, 明示的な活性化なし, スタッキングなし |
L.NStepBiRNNTanh | nn.RNN | 双方向性=True, 明示的な活性化なし, スタッキングなし |
L.NStepGRU | nn.GRU | |
L.NStepLSTM | nn.LSTM | |
L.NStepRNNReLU | nn.RNN | |
L.NStepRNNTanh | nn.RNN | Notes |
L.Parameter | torch.nn.modules.ParameterListを1つの要素で使うことができます。 | |
L.Scale | 該当なし | |
L.StatefulGRU | nn.GRU | |
L.StatelessGRU | 該当なし | |
L.StatefulMGU | https://github.com/jpeg729/pytorch_bits を参照してください。 | |
L.StatelessMGU | 該当なし | |
L.StatefulPeepholeLSTM | https://github.com/pytorch/pytorch/issues/630 を参照してください。 | |
L.StatefulZoneoutLSTM | 該当なし: https://github.com/pytorch/pytorch/pull/4838 | |
L.StatelessLSTM |
パラメータによる活性化/損失/正規化関数
Chainer | PyTorch | Notes |
---|---|---|
L.BatchNormalization | nn.BatchNorm1d
nn.BatchNorm2d nn.BatchNorm3d |
PyTorchの実装では、引数 momentum はChainerのリンクの 1 - decay と同等のようである。
引数 |
L.BatchRenormalization | 該当なし | |
L.DecorrelatedBatchNormalization | 使えません。参考になる実装(あまり実装されていない?) https://github.com/huangleiBuaa/IterNorm-pytorch/blob/master/extension/normailzation/dbn.py。それ以外はTorch luaの公式実装を見てください https://github.com/princeton-vl/DecorrelatedBN。 | |
L.GroupNormalization | nn.GroupNorm | affine=True |
L.LayerNormalization | nn.LayerNorm | elementwise_affine=True |
L.BinaryHierarchicalSoftmax | ||
L.BlackOut | 該当なし | |
L.CRF1d | ||
L.SimplifiedDropconnect | 該当なし | |
L.PReLU | nn.PReLU | |
L.Swish | https://blog.ceshine.net/post/pytorch-memory-swish/ を参照してください。 | |
L.Maxout | ||
L.NegativeSampling |
機械学習モデル
Chainer | PyTorch | Notes |
---|---|---|
L.Classifier | 該当なし |
トレーニング済みモデル
Chainer | PyTorch | Notes |
---|---|---|
L.VGG16Layers | torchvision.models.vgg* | トーチビジョンのChainerのVGGバリエーションのスーパーセット。 |
L.VGG19Layers | torchvision.models.vgg19* | 同上 |
L.model.vision.vgg.prepare | 該当なし | |
L.GoogLeNet | torchvision.models.googlenet | |
L.model.vision.googlenet.prepare | torchvision.models.googlenet の transform_input=True | |
L.model.vision.resnet.ResNetLayers | ||
L.ResNet50Layers | torchvision.models.resnet101 | トーチビジョンのみ、pretrained=True |
L.ResNet101Layers | torchvision.models.resnet101 | トーチビジョンのみ、pretrained=True |
L.ResNet152Layers | torchvision.models.resnet152 | トーチビジョンのみ、pretrained=True |
L.model.vision.resnet.prepare | 該当なし | |
L.TheanoFunction | 該当なし | |
L.caffe.CaffeFunction | https://github.com/marvis/pytorch-caffe または https://github.com/Microsoft/MMdnn を参照してください。 |
構成
Chainer (chainer.config.*
) と PyTorch の設定のマッピングを以下に示します。
Chainer | PyTorch | Notes |
---|---|---|
autotune | torch.backends.cudnn.benchmark | スレッドローカルではありません。 |
cudnn_deterministic | torch.backends.cudnn.deterministic | スレッドローカルではありません。 |
cudnn_fast_batch_normalization | 該当なし | 機種によっては精度が低いので意図的にサポートしていません。 |
debug | 該当なし | torch.autograd.detect_anomaly() コンテキストマネージャを使用して、バックワード中のNaNをチェックし、バックワードでエラーが発生した場合には、対応するフォワードスタックトレースを表示します。 |
dtype | torch.set_default_dtype(dtype) | 混合精度のサポートはApexを介して行われます。スレッドローカルではない。 |
enable_backprop | torch.no_grad() torch.enable_grad() |
コンテキストマネージャやデコレータとして使うことができます。Backprop モードも参照してください。 |
is_recomputing | 該当なし | F.forget と同等のものは torch.utils.checkpoint.checkpoint を参照してください(RNG もサポートしています)。 |
keep_graph_on_report | 該当なし | |
lazy_grad_sum | 該当なし | |
train | 該当なし | このモードはモジュールごとに設定されます (Module.train() および Module.eval() を使用)。Train/Test モードも参照してください。 |
type_check | 該当なし | |
use_cudnn | torch.backends.cudnn.enabled | デフォルトでは有効になっています。スレッドローカルではありません。 |
use_cudnn_tensor_core | 該当なし | テンソルコアを無効にすることはできません。 |
use_ideep | 該当なし | PyTorch 自体は MKL-DNN をサポートしています。可用性は torch.backends.mkldnn.is_available() で確認できます。 |
use_static_graph | 該当なし | |
warn_nondeterministic | 該当なし |
再現性(種を固定するステップを含む)については、再現性を参照してください。
フック
関数フック
PyTorch にはこれに相当する関数はありません。
Chainerの組み込みフックの代替
- CUDAProfileHook: torch.autograd.profiler.profile + torch.autograd.profiler.emit_nvtx
- CupyMemoryProfileHookを使用しています。N/A (アロケータのステータスを取得可能)
- PrintHook. 該当なし
- TimerHook: torch.autograd.profiler.profile
リンクフック
モジュールごとにモジュールフックを登録することができます。特定のスコープの下で呼び出されるリンクごとにフックを注入する方法はありません。
Chainer の組み込みフックを置き換えるものです。
- SpectralNormalization: torch.nn.utils.spectral_norm / torch.nn.utils.remove_spectral_norm
- タイマーフック。該当なし
オプティマイザーフック
PyTorchには直接の等価なものはありませんが、Tensor / Moduleごとに後方フックを登録してグラデーションを変更することができます。
Chainerの組み込みフックの代わりになります。
- WeightDecay: 各オプティマイザ(例: Adam)への weight_decay 引数として指定します。
- Lasso:該当なし
- GradientClipping: torch.nn.utils.clip_grad_norm_.GradientClipping
- GradientHardClipping: torch.nn.utils.clip_grad_value_.
- GradientNoise. N/A
- GradientLARS。N/A (pytorch #18414 と pytorch-lars を参照)
Chainerを用いたPyTorchモデルの学習
Chainer を使ったトレーニングスクリプトで PyTorch モデルを素早く試すには、cpm.TorchModule を使います。Chainerを使ったトレーニングスクリプトがあると仮定して、以下の手順を試してみてください。
- 学習するモデルを cpm.TorchModule(module_you_want_to_use) で置き換えます。to_gpu を使って変数を GPU デバイスに転送する。
- 損失計算とbackprop呼び出しをPyTorchで書き換えます。
- StandardUpdater を使っている場合は、そのサブクラスを作成して update_core をオーバーライドします。損失計算とbackpropコールをPyTorchで書き換えます。
- 注意: PyTorchで勾配を計算すると、自動的にChainerのパラメータに反映されるので、その後はoptimizer.update()を呼び出すだけで有効です。
- カスタムトレーニングループを使っている場合は、PyTorchでグラデーションの計算部分を書き換えてください。上のnoteも参照してください。
- StandardUpdater を使っている場合は、そのサブクラスを作成して update_core をオーバーライドします。損失計算とbackpropコールをPyTorchで書き換えます。
分散型トレーニング
この記事を書いている時点で、分散型ディープラーニングアプリケーションを実行するには、大きく分けてtorch.distributedとHorovodの2つの方法があります。最初の選択肢としてtorch.distributedを推奨する理由は以下の通りです。
-
torch.distributedはPyTorchの標準モジュールの一部である。
-
Torch.distributed は、Horovod にはない高度な機能をサポートしています。
このドキュメントでは、ChainerMN プログラムを PyTorch に移行するための両方のアプローチについて説明します。
torch.distributedを用いたPytorchモデル
Torch.distributedはPyTorchの分散ディープラーニングの標準モジュールです。
Torch.distributedは3つのバックエンドをサポートしています。"nccl"、"mpi"、"gloo "の3つのバックエンドをサポートしています。ChainerやChainerMNから移行していて、今までMPIを使ってNCCLを使っていたユーザーにとっては、"nccl "バックエンドを使うのが一番わかりやすい方法です。このセクションでは、分散ディープラーニングプログラムを実行するために NCCL と MPI を使用していることを想定しています。特にここで使用する MPI 実装として Open MPI を想定していますが、それは ChainerMN で推奨されているからですが、他の MPI 実装についても言及しています。
呼び出し
ChainerMN では、プロセスの呼び出しは MPI ランタイムによって完全に調整されます。しかし、PyTorch や torch.distributed では、分散型のディープラーニングプロセスを呼び出すために、いくつかのステップが必要になるかもしれません。最も単純な初期化方法は、環境変数の初期化かもしれません。
以下の環境変数が必要です(MPIを含め、スクリプトを呼び出すのに使用するシステムが何であれ)。その他の変数であるWORLD_SIZEとRANKは、以下のスニペットの中から設定します。
MASTER_ADDR : ランク0のプロセスが動作する計算ノードのアドレス。
MASTER_PORT : MASTER_ADDRマシンの空きポート。このポートはランク 0 のプロセスによって使用されます。
プロセスの起動はシステム依存度が高いことに注意してください。PyTorch は TCP 初期化や共有ファイルシステム初期化などのオプションをサポートしています。詳細は公式ドキュメントを参照してください。
初期化
以下のコードスニペットは、torch.distributedモジュールの初期化方法を示しています。
# setup env for torch.distributed
comm_world_size = int(os.environ["OMPI_COMM_WORLD_SIZE"])
comm_rank = int(os.environ["OMPI_COMM_WORLD_RANK"])
comm_local_rank = int(os.environ['OMPI_COMM_WORLD_LOCAL_RANK'])
os.environ["WORLD_SIZE"] = str(comm_world_size)
os.environ["RANK"] = str(comm_rank)
torch.cuda.set_device(comm_local_rank)
torch.distributed.init_process_group(backend='nccl', init_method='env://')
torch.distributed は対応するランク情報を提供していないので、ChainerMN の communicator.intra_rank の代わりに、MPI ランタイムで設定された環境変数がここにあります。MVAPICH2 を使用する場合は、それぞれ MV2_COMM_WORLD_SIZE, MV2_COMM_WORLD_RANK, MV2_COMM_WORLD_LOCAL_RANK を使用してください。
データセット散乱
各ノードはDistributedSamplerを使ってグローバルに共有されたデータセットのスライスを取得することができます。
sampler = torch.utils.data.distributed.DistributedSampler(dataset,
num_replicas=comm_world_size,
rank=comm_rank)
loader_kwargs = {'num_workers': 1, 'pin_memory': True} # Assuming we use GPUs
loader = torch.utils.data.DataLoader(train_dataset,
batch_size=args.batch_size,
sampler=sampler, **loader_kwargs)
これにより、各ワーカーはデータセットのスライスのみを読み込むようになり、このサンプラーは通常DataLoaderに供給されます。
また、エポック数を調整するにはDistributedSampler.set_epoch()を呼び出す必要があります。 このように、典型的な訓練ループは以下のようになります。
for epoch in range(1, args.epochs + 1):
train_sampler.set_epoch(epoch)
train(args, model, device, train_loader, optimizer, epoch)
test(args, model, device, test_loader, len(test_dataset))
scheduler.step()
デバイスへのデータ転送
データを転送するデバイスをcomm_local_rankで指定する必要があります。
class MyNN(nn.module):
...
device = torch.device("cuda:{}".format(comm_local_rank) if use_cuda else "cpu")
model = MyNN().to(device)
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[comm_local_rank])
オプティマイザのラッピング
Horovodとは対照的に、非分散実行と同じオプティマイザを使用することができます。
初期値のブロードキャスト
パラメータ値はDistributedDataParallelクラスによって自動的に同期されます(すなわち、最初のブロードキャストと反復毎のallreduce)ので、それ以上の修正は必要ありません。
メトリクスの平均値と削減量
https://pytorch.org/docs/stable/distributed.html#multi-gpu-collective-functions
同期化
データレースなどのバグを回避するために、データの読み込みやアプリケーションの終了前や終了後に torch.distributed.barrier() を使ってプロセスを同期させる必要があるかもしれません。
Horovodを使ったPyTorchモデル
データはノード間で分散され、オプティマイザはHorovodでラップインされ、複数のMPIプロセスの勾配を自動的に平均化します。
Horovod初期化
次のスニペットは、horovodをインポートして、現在のワーカーIDとワーカーの総数を取得する方法を示しています。
import horovod.torch as hvd
hvd.init()
print(‘My rank is {} of {} workers‘.format(hvd.rank(), hvd.size()))
hvd.local_rank() は単一のノード内のランクを取得するために使用されますが、これは ChainerMN の intra_rank() と同様に GPU を割り当てるのに便利です。
torch.cuda.set_device(hvd.local_rank())
データセット散乱
各ノードはDistributedSamplerを使ってグローバルに共有されたデータセットのスライスを取得することができます。
torch.utils.data.distributed.DistributedSampler(dataset, num_replicas=hvd.size(),
rank=hvd.rank())
これにより、すべてのワーカーがデータセットのスライスのみをロードするようになります。
オプティマイザのラッピング
オプティマイザは、次の構成パラメータを持つ hvd.DistributedOptimizer オブジェクトでラップされます。
compression : {hvd.Compression.fp16, hvd.Compression.none}内の値
圧縮はオプティマイザによって実行されるallreduce操作のサイズを縮小するために使用されます。
backward_passes_per_step : int デフォルト値は通常1
グラディエント交換を実行する前にローカルで実行されるバッチの数。
optimizer = hvd.DistributedOptimizer(
optimizer, named_parameters=model.named_parameters(),
compression=compression,
backward_passes_per_step=args.batches_per_allreduce)
ドキュメントから。
DistributedOptimizer は synchronize() メソッドを公開しており、これは実行を継続する前に全てのreduce操作を強制的に終了させる。これは、グラデーションクリッピングや、step()が実行される前にグラデーションを変更する他の操作と併用すると便利である。コード中でsynchronize()を呼び出す場合は、必ずoptimizer.skip_synchronize()を使用してください。
初期値のブロードキャスト
トレーニングループを開始する前に、初期モデルパラメータとオプティマイザの状態をすべてのワーカーにブロードキャストする必要があります。
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
hvd.broadcast_optimizer_state(optimizer, root_rank=0)
メトリクスの平均値と削減量
損失や精度などのメトリクスを計算する際には、複数の作業員の値を明示的に交換して平均値を計算することができます。
self.sum += hvd.allreduce(val.detach().cpu(), name=metric_name)
Horovodは、他のMPIコレクションを使ったデータ交換をサポートしています。
- horovod.torch.allgather
- horovod.torch.broadcast
3 つの関数には _async バージョンがあり、返されたハンドラに poll() を使用して問い合わせることもできますし、完了するまで待つために synchronize() を使用することもできます。
ホロヴォッドコード構造
import torch
import horovod.torch as hvd
…
…
def main():
# Initialize horovod
hvd.init()
torch.cuda.set_device(hvd.local_rank())
# Read the dataset and create the iterators
dataset = datasets.ImageFolder(…)
train_sampler = torch.utils.data.distributed.DistributedSampler(dataset, num_replicas=hvd.size(),
rank=hvd.rank())
loader = torch.utils.data.DataLoader(dataset, sampler=train_sampler, …)
# Set up the model, checkpoints, …
…
# Create the optimizer
optimizer = optim.SGD(model.parameters(), …)
optimizer = hvd.DistributedOptimizer(
optimizer, named_parameters=model.named_parameters(),
compression= hvd.Compression.none,
backward_passes_per_step=args.batches_per_allreduce)
# Broadcast initial state
broadcast parameters & optimizer state.
hvd.broadcast_parameters(model.state_dict(), root_rank=0)
hvd.broadcast_optimizer_state(optimizer, root_rank=0)
# Start training
for epoch in range(epochs):
train_sampler.set_epoch(epoch)
…
パフォーマンスを測定するためのホロポッドトレースの取得
Horovod通信を示す通信トレースは、環境変数HOROVOD_TIMELINEを設定することで取得できます。
mpirun -bind-to-none -np 8 -x HOROVOD_TIMELINE=timeline.json ...
ブラウザに内蔵されているchrome://トレース機能を使用して、結果のトレースをChromeで可視化することができます。
ホロヴォッドパフォーマンスのチューニング
ホロヴォッドは性能向上のためにいくつかのツマミを持っています。
-
TensorFusionは、小さなテンソル上で行うべき演算が多数ある場合に、ネットワークの利用率を向上させます。horovodは、複数の小テンソルを熱心に開始するのではなく、バッファ上の小テンソルを結合してからバッチ処理を行うことで、より効率的な通信/計算の重複処理を行うようにしています。HOROVOD_FUSION_THRESHOLDは、バッファのサイズをバイト単位で制御します(デフォルトは64MB)。HOROVOD_CYCLE_TIME は、テンソルを削減する前に、テンソルがバッチ処理されるのを待つ時間を ms 単位で指定します。
-
階層的リデュース。HOROVOD_HIERARCHICAL_ALLREDUCEは、ノード内のallreduceをNCCLで行い、ノード間のallreduceをMPIで行うハイブリッドアプローチを行います。
-
HOROVOD_AUTOTUNEは、学習の最初のエポックで設定可能なすべてのパラメータに対してベイズ最適化を実行するために、HOROVODで使用することができます。詳細はこちら
アペックスでホロポッドを使う
Horovodは後方計算と並行してall-reduceを起動し、apexは後方計算後に勾配をアンスケーリングします。
競合状態を避けるために、スケーリングを解除する前にall-reduceの完了を待たなければならない。
from apex import amp
...
with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()
optimizer.synchronize() # Wait for all-reduce completion
with optimizer.skip_synchronize():
optimizer.step()
また、Horovodとapexを使用する場合、backward_passes_per_stepを1にする必要があります。現在のHorovodaとapexの実装では、backward_passes_per_stepが1でないと期待通りに動作しません。
Horovodにおけるマルチノードバッチ正規化
Horovodはまだ公式にはMNBNをサポートしていませんが(https://github.com/horovod/horovod/issues/1384)、非公式な実装があります: https://github.com/atranitell/Synchronized-BatchNorm-PyTorch-Horovod/blob/master/sync_bn.py。Apexにも実装があります: https://nvidia.github.io/apex/parallel.html#apex.parallel.SyncBatchNorm
Horovodとmpi4pyを使った任意のオブジェクトの収集
Horovod は mpi4py (https://github.com/horovod/horovod#mpi4py) との同時使用をサポートしています。mpi4pyを使って、例えばChainerMNのcomm.gather_objを書き換えることができます。
import horovod.torch as hvd
# Initialize Horovod
hvd.init()
# Verify that MPI multi-threading is supported.
assert hvd.mpi_threads_supported()
from mpi4py import MPI
mpi_comm = MPI.COMM_WORLD
assert hvd.size() == mpi_comm.Get_size()
mpi_comm.gather(obj, root=0) # This is equal to ChainerMN’s comm.gather_obj
ホロボドの代替品
Horovodがここで紹介されているのは、ChainerMNに非常に似ており、私たちのコンピューティングインフラストラクチャですぐに使用できるからです。代替案は以下の通りです。
- Pytorch 分散 API
- ライトニング
ホロボドを用いたChainerモデル
Horovodを使って分散環境でChainerモデルを学習するには、cpm.LinkAsTorchModelを使ってChainerリンクをラップする必要があります。PyTorchオプティマイザの使用が必要です。
model = ChainerModel()
model.to_device(ch_device)
# Initialize parameters before converting to `ChainerParameter`s.
model(ch_device.xp.zeros((1, 784)).astype('f'))
# Convert parameters to `ChainerParameter`s to share memory with PyTorch.
torched_model = cpm.LinkAsTorchModel(model)
optimizer = optim.SGD(torched_model.parameters(), lr=args.lr)
optimizer = hvd.DistributedOptimizer(
optimizer, named_parameters=torched_model.named_parameters())
hvd.broadcast_parameters(torched_model.state_dict(), root_rank=0)
hvd.broadcast_optimizer_state(optimizer, root_rank=0)
ChainerMN を使った PyTorch モデル
cpmツールを使えば、ChainerMNを使ってPyTorchモデルを学習することも可能です。
現在のサポートはデータ並列訓練に限定されています。
from chainer_pytorch_migration import chainermn
…
comm = chainermn.create_communicator('pure_nccl')
# Set up standard ResNet-50 model.
model = models.resnet50()
model.cuda()
w_model = links.TorchModule(model)
w_model.to_gpu(device)
optimizer = optim.SGD(model.parameters(), lr=lr)
optimizer = chainermn.create_multi_node_optimizer(optimizer, comm)
optimizer.setup(w_model)
計算グラフを編集するコードの移植
ノードの鎖を外す
計算グラフから変数の鎖を外す方法の違いを説明します。
-
チェインします。Variable.{data,array} または Variable.unchain()。
-
PyTorchを使っています。Tensor.detach()。このメソッドは、requires_grad を False に設定した状態で、計算グラフから鎖を外した新しい Tensor を返します。これは Variable.unchain() とは対照的にインプレース操作ではなく、副作用もありません(新しい Tensor は同じメモリを共有しますが)。しかし、Tensor.detach_()のようなインプレースのバリアントも提供されています。
-
PyTorch. Tensor.dataは推奨されておらず、将来的には非推奨になるかもしれません(フォーラムやGitHubでのコメントによると)。
-
ChainerのVariable.unchain_backward()のPyTorchでのカウンターパートが使えない?
バックプロップモード
backpropモードの切り替え方法の違いを説明します。
-
Chainer: no_backprop_mode, force_backprop_modeはPyTorchでは以下のように対応しています。
-
PyTorch: torch.autograd.no_grad(), torch.autograd.enable_grad()。
-
no_grad()は[....]を使って、既に確保されているTensorに直接データを書き込めるようにすることもできます。
-
Chainerでは、これも設定(config.config.enable_backprop)ですが、PyTorchにはありません。
-
ChainerもPyTorchもデフォルトではbackpropモードが有効になっています。
列車/テストモード
列車/試験モードの切り替え方法の違いを説明します。
-
Chainer.Chainer.Chainer.Chainer.Chainer.Chainer.Chainer このモードは設定(config.config.train)で制御します。
-
PyTorch: このモードはモジュールの状態であり、torch.nn.Module.train.train を使って変更する必要があります。このモードはモジュールの状態であり、torch.nn.Module.train(mode)、torch.nn.Module.eval()を使って変更する必要があります。これは、torch.nn.functional.dropout のような関数呼び出しに "train" 引数を渡さなければならないことも意味しますが、そうでなければステートフルな torch.nn.Dropout モジュールを使用します。
-
Chainer と PyTorch の両方で、train/test モードは、ドロップアウトやバッチ正規化などの特定の関数/リンク/モジュールの動作に影響を与えます。
-
Chainer と PyTorch のデフォルトは train モードです。
エコシステム
ここでは、PyTorchのGitHub組織の下にある、より大きなリポジトリの一部を紹介します。また、PyTorch が認めている他のエコシステムライブラリの公式リストも参照してください。
PyTorch
まとめ:GPUアクセラレーションに強いPythonでテンソルとダイナミックニューラルネットワーク
GitHub: https://github.com/pytorch/pytorch
-
インストールします。インストール方法は Start Locally を参照してください。PyTorch 1.2.0 以降、PyPI でホストされている torch/torchvision パッケージは CUDA 10.0 用になっています。
-
ナイトリービルド。ナイトリービルドが提供されているので、新しい機能をマージしたビルド済みのバイナリを翌日に試すことができます。各ナイトリービルドは日ごとにタグ付けされているので、特定のビルド日のバージョンのロックされた pip wheel やビルド済み libtorch をダウンロードすることができます。pip wheelについては、ローカルで起動(CPUページ、CUDA 9.2ページ、CUDA 10.0ページ)を参照して最適なバージョンを入手してください。
Ignite
要約: トレーニングループの抽象化などの高レベルなユーティリティ。
GitHub: https://github.com/pytorch/ignite
torchvision
要約: PyTorch for CV.
GitHub: https://github.com/pytorch/vision
pytorchと一緒にインストールする公式インストールガイドで推奨されている
ドメインにとらわれない(CVに限定されない)データ増強機能を提供します。
動画データ用のローダーを提供します。ffmpegのため遅いが、将来的には改善されるかも?
torchtext
要約: PyTorch for NLP。
GitHub: https://github.com/pytorch/text
torchaudio
まとめ: オーディオデータのための PyTorch
GitHub: https://github.com/pytorch/audio
Fairseq
まとめ: Seq2seqモデル。
GitHub: https://github.com/pytorch/fairseq
翻訳などのSeq2seqモデル。TransformerやBERTライクなモデルを含む。
その他
PyTorchエコシステムに含まれるライブラリの公式リストがあります(上記のドメイン固有のライブラリ以外にも)。