14
10

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 3 years have passed since last update.

ChainerからPyTorchに書き換えるFramework Migration GuideをDeepLで翻訳

Last updated at Posted at 2020-04-23

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.flipchainercv.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.DataLoadercollate_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 への転送を高速化するために、_convertpin_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.asarraycpm.astensor を使用します。

また、CuPy を使う前に cpm.use_torch_in_cupy_malloc をコールして、PyTorch とアロケータを共有することをお勧めします。

注意: PyTorch 1.3.0 の時点で、__cuda_array_interface__ が壊れています (pytorch/pytorch/#24947)。この問題を修正したら、cpm の代わりに cupy.asarraytorch.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 の axiskeepdim の代わりに dimkeepdim を使用します。
  • 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 を参照してください。

リンク

Lchainer.links (Chainer)、nntorch.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 と同等のようである。

引数 eps のデフォルト値 (1e-5) は Chainer のデフォルト値 (2e-5) とは異なる。

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も参照してください。

分散型トレーニング

この記事を書いている時点で、分散型ディープラーニングアプリケーションを実行するには、大きく分けてtorch.distributedとHorovodの2つの方法があります。最初の選択肢としてtorch.distributedを推奨する理由は以下の通りです。

  1. torch.distributedはPyTorchの標準モジュールの一部である。

  2. 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エコシステムに含まれるライブラリの公式リストがあります(上記のドメイン固有のライブラリ以外にも)。

14
10
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
14
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?