PyTorchをRubyで動かすtorch-rbをやってみた

## はじめに

RubyからPyTorchを動かすTorch-rbを動かすところまでやってみたのでレポートします。
![PyTorch](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/b55b71c3-9234-bdc8-68d5-cda1a152431c.png)![Ruby](https://upload.wikimedia.org/wikipedia/commons/0/0f/Ruby-logo-notext.png)

## Torch-rbとは
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/734ba80d-c2e5-9418-b11b-439b3b5aab1f.png)](https://github.com/ankane/torch-rb)

Torch-rbはAndrew Kane氏が作成・公開している一連のRuby向け機械学習ライブラリの最新作です。ここ数ヶ月、Ankane氏はonnxruntimeのバインディング、Tensorflowのバインディング、LightGBMやxgboostのバインディングと、ものすごい勢いでRuby向けの機械学習ライブラリを制作・公開してきました。なかでも最新作にあたるtorch-rbに関してはREADMEから強い気合が伝わってきます。これまでのAnkaneプロダクツは、いずれもruby-ffiを利用したものですが、今回はriceを利用しているという特徴もあります。

torch-rbは絶賛開発中なので、ここに書いた情報はすぐに古くなる可能性があります。なるべく最新の情報を参照してください。

## PyTorchのインストール
まずはpytorchをインストールします。Macを使っている方は、`brew`コマンドで大丈夫みたいですが、Linuxを使っているのでサイトからダウンロードします。LanguageでC++を選択するのがポイントです。
たまたま手元のPCがノートパソコンでGPUの性能は高くないので、今回はCPUオンリーのものをダウンロードしました。

https://pytorch.org/
https://pytorch.org/get-started/locally/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/65a17e44-4785-83ba-7a79-59d46a879d00.png)

ダウンロードしたら解凍してできた`libtorch`ディレクトリをどこに配置したらいいのか、実はあまりよくわかっていませんが、とりあえず`/usr/local/lib`あたりに配置しましてみました。


つぎに、Githubからtorch-rbのリポジトリーをcloneします。

```bash
git clone https://github.com/ankane/torch-rb
```

まずは`bundle install`しようと試みますが

```bash
bundle install
```

最初からエラーメッセージが出てつまづきます。どうやら、`rice`のインストールでエラーが発生しているようです。[rice は Ruby Interface for C++ Extensions](https://github.com/jasonroelofs/rice) です。

```
Unfortunately Rice does not build against a staticly linked Ruby.
You'll need to rebuild Ruby with --enable-shared to use this library.

If you're on rvm:   rvm reinstall [version] -- --enable-shared
If you're on rbenv: CONFIGURE_OPTS="--enable-shared" rbenv install [version]
```

エラーメッセージを見ると`--enable--shared` オプションをつけてRubyをビルドし直すように表示されますので言われたとおりにします。

```bash
CONFIGURE_OPTS="--enable-shared" rbenv install 2.6.5
```

うまくインストールされました。

`Installed ruby-2.6.5 to /home/kojix2/.rbenv/versions/2.6.5`

気を取り直して、`bundle install`しなおします。今度はうまくいきました。
あとは以下のように`--with-torch-dir` オプションをつけてインストールします。(Macの場合はこれらの過程は不要のようですので詳しくは[README](https://github.com/ankane/torch-rb)をご覧ください)

```bash
bundle exec rake build
gem install pkg/torch-rb-0.1.4.gem -- --with-torch-dir=/usr/local/lib/libtorch/
```

## mnist example を試す

さて、インストールがうまくいったら、お決まりのmnist exampleを試してみます。



```bash
cd examples/mnist
wget https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
gem install npy
ruby main.rb
```

https://github.com/ankane/torch-rb/tree/master/examples/mnist

```ruby
class Net < Torch::NN::Module
  def initialize
    super
    @conv1 = Torch::NN::Conv2d.new(1, 32, 3, stride: 1)
    @conv2 = Torch::NN::Conv2d.new(32, 64, 3, stride: 1)
    @dropout1 = Torch::NN::Dropout2d.new(p: 0.25)
    @dropout2 = Torch::NN::Dropout2d.new(p: 0.5)
    @fc1 = Torch::NN::Linear.new(9216, 128)
    @fc2 = Torch::NN::Linear.new(128, 10)
  end

  def forward(x)
    x = @conv1.call(x)
    x = Torch::NN::F.relu(x)
    x = @conv2.call(x)
    x = Torch::NN::F.max_pool2d(x, 2)
    x = @dropout1.call(x)
    x = Torch.flatten(x, start_dim: 1)
    x = @fc1.call(x)
    x = Torch::NN::F.relu(x)
    x = @dropout2.call(x)
    x = @fc2.call(x)
    output = Torch::NN::F.log_softmax(x)
    output
  end
end
```

![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/f048c7c5-f7be-8943-ab21-7310983256eb.gif)

左のターミナルに`htop`によるリソースの使用状況、右のターミナルに学習の進捗が表示されています。CPUを4コア使って頑張って計算してくれているのがわかりますね。しかしCPUやメモリをカツカツに使っている感じではなくて、バランスが取れていていいですね。

最終結果はこんな感じでした。

```
Train Epoch: 14 [59904/60000 (100%)] Loss: 0.197016
Test set: Average loss: 0.0298, Accuracy: 9909/10000 (99%)
```

素晴らしい。
推論だけでなく学習もPythonと遜色のない速度で実行できるRubyのディープラーニングライブラリは、私が知ってる範囲ではこれがはじめてです。今回はノートPCを使用して、i5-7200U CPUで計算しましたがまあまあ短時間で学習を終えることができました。

```
real	18m39.163s
user	47m20.107s
sys	0m26.818s
```
(ブラウザとか開いていたのでさらに早くなるかも）

ちょうどPFNがChainerをやめてPytorchに行くというニュースが入ってきました。今はちょうどそんな時期ですが、やっとRubyでもディープラーニングを勉強できる環境が整備されつつあるのではないでしょうか。

この記事は以上です。