Chainer ⇒ PyTorch移行を試してみた

今週、PFNよりChainerの開発を停止するという発表があって驚きました。

[Chainer/CuPy v7のリリースと今後の開発体制について](https://chainer.org/announcement/2019/12/05/released-v7-ja.html)

深層学習・機械学習・Pythonの素人でしたが、深層学習ブームの中、2015年の秋くらいから使い始めて、Chainer meetup(#3,#8)でも発表させて頂きました。

新しい機能もあまり使っていなかったので、しらばくこのままChainerを使い続けることもできますが、これを機にPyTorchを一度試してみました。

公式の移行ドキュメントを見ると、関数の対応など書いてあり、とても参考になります。

[Chainer ⇒ PyTorchへの移行ドキュメント(公式)](https://chainer.github.io/migration-guide/)

以下は個人的な移行時のメモとして置いておきます。

## PyTorch インストール

* OS: Windows 10, ubuntu 18.04
* GPU: RTX 2080
* python 3.7(anaconda)
* Cuda 10.1
* Chainer 7.0.0a1
* Cupy-cuda101 7.0.0a1


[PyThorch公式](https://pytorch.org/)からOSやPython,Cudaバージョンなどを選択すると以下のようなコマンドが表示されるので、それを実行するとPyTorchのインストールが5分足らずで完了します。

![20191207_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109458/003b92f6-dd3e-0f7f-7a9d-58aaeb3fbfbb.png)


```
$ conda install pytorch torchvision cudatoolkit=10.1 -c pytorch
```

Windows・ubuntu両方、GPUでそのまま動いたので、Chainerで使っているCudaのバージョンが合えば、インストールはとても簡単なようです。

## コードの移行

ChainerとPytorchはとても似ているので、だいたい対応するクラス・関数があります。
Trainerとかは使ってないので、そのあたりの変換については触れていません。

細かい関数の対応は[公式の対応表](https://chainer.github.io/migration-guide/#h.mkuuagm60br0)が便利です。

以下のimportから学習までの簡単な[全体コードはこちら](https://github.com/samacoba/Mytest/blob/master/No_06_sample_chainer_pytorch.ipynb)に置きました



### import 関係

``` python
# import chainer
# import chainer.links as L
# import chainer.functions as F
# from chainer import optimizers

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
```

### データ型の変換

ChainerのValiableにあたるものは、Pytorchではtensorのようです。

* Numpy ⇒ Variable, tensor

```python
# x = chainer.Variable(x_array)
x = torch.tensor(x_array)
```

* Variable, tensor　⇒ Numpy

```python
# y_array = y.data
y_array = y.detach().numpy()
```

※グラフ形成後はy.detach()が必要なようです

* CPU(Variable, tensor)　⇒ GPU(Variable, tensor)

```python
# x.to_gpu()
x = x.to("cuda")
```

* GPU(Variable, tensor)　⇒ CPU(Variable, tensor)


```python
# y.to_cpu()
y = y.to("cpu")
```

### ネットワーク

```python
#class MLP(chainer.Chain):
#    def __init__(self):
#        super(MLP, self).__init__()
#        with self.init_scope():
#            self.l1 = L.Linear(None, 1000)  
#            self.l2 = L.Linear(None, 1000)  
#            self.l3 = L.Linear(None, 10)  
#
#    def forward(self, x):
#        h1 = F.relu(self.l1(x))
#        h2 = F.relu(self.l2(h1))
#        return self.l3(h2)


class MLP(nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.l1 = nn.Linear(784, 1000)  
        self.l2 = nn.Linear(1000, 1000)  
        self.l3 = nn.Linear(1000, 10)  

    def forward(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        return self.l3(h2)
```

関数名が同じ名前の場合もあれば、L.Convolution2D ⇒ nn.Conv2d のように違う場合もあります。あまり調べていませんが、Noneにあたるものはつかえない？のかもしれません。

### モデル・Optimizerセットアップ

```python
# model = MLP()
# model.to_gpu()
# optimizer = optimizers.SGD(lr=0.01)
# optimizer.setup(model)

model = MLP()
model = model.to("cuda")
optimizer = optim.SGD(model.parameters(), lr=0.01)
```

### 学習

```python
# y = model(x)
# loss = F.mean_squared_error(y, t)
# model.cleargrads()
# loss.backward()
# optimizer.update()

y = model(x)
loss = F.mse_loss(y, t)
model.zero_grad()
loss.backward()
optimizer.step()
```

## 最後に

今回とりあえず、[今研究で使っている](https://github.com/samacoba/ObjectCounter/tree/master/researchs/No04_2classify)ChainerのコードをPyTorchに手で書き換えを試して動かしてみました。Pytorchは全然使ったことはなかったですが、公式の移行ドキュメントやWebで都度調べるくらいで、1日で動くまでできました。基本的に似ているので、関数の対応を確認して、複数あるところは置換使ってというのを地道に続ければOKでした。

だた、動かしてみるとwindowsで約2割、ubuntuで約1割程度遅くなったので、いつ完全移行するかはゆっくり考えてみようと思います。

Chainerを使って4年経ちますが、Chainerがなかったら深層学習の世界に入ることはなく、普通のサラリーマンを続けていたかもしれません。良かったかはまだわかりませんが、研究生活はそれなりに楽しく過ごせております。

Chainerの皆様、ありがとうございました！


## 参考

* [実践Pytorch Qiita @perrying](https://qiita.com/perrying/items/857df46bb6cdc3047bd8)
* [Pytorch Tensorについて Qiita @illumination-k](https://qiita.com/illumination-k/items/0a5620d0773fc8d70b0b
)