令和元年なのでRubyでDeep Learningする方法をリストアップしてみた。

タイトルは釣りです。すみません。令和も2年目に突入しました。

　RubyでもDeepLearningを試してみたいなって思いませんか？　そこで今までネットで見聞きした範囲でRubyでDeepLearningする手段をまとめてみました。数年前のように全く手も足も出ない状況からは少しずつ進歩しているなぁという印象です。

## ruby-dnn
https://github.com/unagiootoro/ruby-dnn
ruby-dnn は Ruby + NArrayで全てのアルゴリズムが記述されています。注目すべきは、その記述法ですね。たとえばMNISTのサンプルを見てください。

```ruby
model = Model.new

model << InputLayer.new([28, 28, 1])

model << Conv2D.new(16, 5)
model << BatchNormalization.new
model << ReLU.new

model << MaxPool2D.new(2)

model << Conv2D.new(32, 5)
model << BatchNormalization.new
model << ReLU.new

model << Flatten.new

model << Dense.new(256)
model << BatchNormalization.new
model << ReLU.new
model << Dropout.new(0.5)

model << Dense.new(10)
model << SoftmaxWithLoss.new

model.compile(Adam.new)

model.train(x_train, y_train, 10, batch_size: 100, test: [x_test, y_test])
```
なんだかワクワクしませんか？ 後述するRed Chainerの弱点として、Chainerとの互換性を保っているため、どうしても記述がRubyっぽくないところがあります。その点ruby-dnnの書き方はシンプルで楽しい感じがします。またruby-dnnは完全にRubyだけで書かれた珍しいdeep learningライブラリです。なのであえて一番最初に紹介しました。

* [ruby-dnnとディープラーニングで建物の画像を生成(Pix2pix)](https://qiita.com/unagiootoro8388/items/63c5edff8f2861e90845)

## ONNX Runtime
https://github.com/ankane/onnxruntime
Microsoftの作っているONNXRuntimeのバインディングです。chartkick, searchkick, pgheroなど有名Gemを多数作成しているAndrew Kane氏が作成しています。学習済みモデルを使うことが前提で、モデルの入手やトレーニング自体は別のマシンでやります。すでにトレーニング済みのモデルがあって、Deep Learningを自分のアプリの中に組み込みたいという場合は、今回紹介したライブラリの中で一番実用性が高いと思います。ONNXファイルを読み込んで使います。

```ruby
# load ONNX file
model = OnnxRuntime::Model.new("model.onnx")
```

```ruby
# execute inference
model.predict(x: [1,2,3])
```

* [TensorFlow Object Detection in Ruby](https://ankane.org/tensorflow-ruby) （英語)
* [ONNXRuntime の Ruby バインディングを動かしてみた](https://qiita.com/kojix2/items/32998de72d4aa0200e56)


## Torch-rb
https://github.com/ankane/torch-rb
本命候補。今後のライブラリの発展に期待。

* [PyTorchをRubyで動かすtorch-rbをやってみた](https://qiita.com/kojix2/items/c19c36dc1bf73ea93409)

## ankane/tensorflow
https://github.com/ankane/tensorflow

## Red Chainer
https://github.com/red-data-tools/red-chainer
ChainerをRubyで再実装したプロジェクトです。このプロジェクトのすごい点は、Deep Learningのアルゴリズムが全てRubyで記述されており、しかもGPUで動作するところです。弱点はChainerそのものが海外ユーザーへのアピールにとぼしい点でしょうか。

```ruby
class MLP < Chainer::Chain
  L = Chainer::Links::Connection::Linear
  R = Chainer::Functions::Activation::Relu

  def initialize(n_units, n_out)
    super()
    init_scope do
      @l1 = L.new(nil, out_size: n_units)
      @l2 = L.new(nil, out_size: n_units)
      @l3 = L.new(nil, out_size: n_out)
    end
  end

  def call(x)
    h1 = R.relu(@l1.(x))
    h2 = R.relu(@l2.(h1))
    @l3.(h2)
  end
end
```
[Red Chainer and Cumo: Practical Deep Learning in Ruby at RubyKaigi 2019](https://speakerdeck.com/sonots/red-chainer-and-cumo-practical-deep-learning-in-ruby-at-rubykaigi-2019)
[【Google Colab】Red Chainer を使ってみた](https://qiita.com/kojix2/items/18808d6691166e1ae55a)


## menoh-ruby
https://github.com/pfnet-research/menoh-ruby
[Menoh](https://github.com/pfnet-research/menoh)はChainerを作っているPreferred NetworksがC++で開発したライブラリです。インストールには[MKL-DNN](https://github.com/intel/mkl-dnn)が必要です。Menohはさまざまな言語にバインディングして使うことが想定されています。menoh-rubyはRubyのバインディングです。ONNX Runtimeが登場するまでMenohはRubyでONNXを動かせる唯一の方法でしたが今後の動きが注目されます。

```ruby
# load ONNX file
onnx_obj = Menoh::Menoh.new './data/mnist.onnx'
```
```ruby
# execute inference
inference_results = model.run image_set
```
[RubyでONNXフォーマットのDNNの推論を可能にするExtensionを開発](https://qiita.com/colspan/items/36b2e7cf25c6c6a9e534)
[Menohを使ってRubyでDeepLearningしてみた](https://qiita.com/7rpn/items/eec83e60fa4f695ffa5a)

## Tensorflow.rb
https://github.com/somaticio/tensorflow.rb


## mxnet.rb
https://github.com/mrkn/mxnet.rb



## PyCall + Pythonのライブラリ(Kerasなど)
https://github.com/mrkn/pycall.rb

はい、最後になりましたが、世間で最も有名で、もっとも実践されているRubyでディープラーニングする方法はこちらでしょう。欠点は、やってる途中にRubyを書いている意味がだんだんわからなくなって、直にPythonを書こうとしはじめてしまうところだと思います。

 [PyCallを使えばRubyでもKerasでDeep Learningができる - 洋食の日記](https://yoshoku.hatenablog.com/entry/2017/03/30/003000) 様より引用

```ruby
require 'pycall/import'
include PyCall::Import

pyimport 'keras'
pyfrom 'keras.datasets', import: 'mnist'
pyfrom 'keras.models', import: 'Sequential'
pyfrom 'keras.layers', import: ['Dense', 'Dropout', 'Flatten']
pyfrom 'keras.layers', import: ['Conv2D', 'MaxPooling2D']

nb_classes = 10
img_rows = 28
img_cols = 28

(x_train, y_train), (x_test, y_test) = mnist.load_data.()

x_train = x_train.reshape.(x_train.shape[0], img_rows, img_cols, 1)
x_test = x_test.reshape.(x_test.shape[0], img_rows, img_cols, 1)
x_train = x_train.astype.('float32')
x_test = x_test.astype.('float32')
x_train /= 255
x_test /= 255

y_train = keras.utils.to_categorical.(y_train, nb_classes)
y_test = keras.utils.to_categorical.(y_test, nb_classes)

model = Sequential.()
model.add.(Conv2D.(32, kernel_size: [3, 3], activation: 'relu', 
                   input_shape: [img_rows, img_cols, 1]))
model.add.(Conv2D.(64, kernel_size: [3, 3], activation: 'relu'))
model.add.(MaxPooling2D.(pool_size: [2, 2]))
model.add.(Dropout.(0.25))
model.add.(Flatten.())
model.add.(Dense.(128, activation: 'relu'))
model.add.(Dropout.(0.5))
model.add.(Dense.(nb_classes, activation: 'softmax'))

model.compile.(loss: keras.losses.categorical_crossentropy,
              optimizer: keras.optimizers.Adadelta.(),
              metrics: ['accuracy'])

model.fit.(x_train, y_train,
          batch_size: 128,
          epochs: 10,
          verbose: 1,
          validation_data: [x_test, y_test])

score = model.evaluate.(x_test, y_test, verbose: 0)
print(sprintf("Test loss: %.6f\n", score[0]))
print(sprintf("Test accuracy: %.6f\n", score[1]))
```

## 番外 mruby-tflite
https://github.com/mattn/mruby-tflite
@mattnさんが作らているmrubyとTensorflow Liteのバインディングです。（mrubyなので番外としました）

* [mruby + TensorFlow Liteで画像分類する](https://qiita.com/akuroda/items/e3d509f75713a55ae22e)
* [mruby から TensorFlow Lite を操りブラックホールとポンデリングとオニオンフライを見分ける](https://qiita.com/mattn/items/0da60e9b133aad2632bf)
* [Big Sky :: MRuby の TensorFlow Lite バインディングを書いた。](https://mattn.kaoriya.net/software/lang/c/20190417102631.htm)

## 番外 TensorStream
https://github.com/jedld/tensor_stream
こちらは、GitHubスター数は多いけど、内容は？？な感じのライブラリです。READMEの紹介文をざっと見るとNArrayやCupyに相当する部分もすべて１人で開発しようとしている？？本当にそんなことが可能なのでしょうか。詳しく見ていないですが、OpenCLを使うと書いてある部分と、マーケティングの巧みさに独自性を感じたので挙げました。

## 番外 ruby-cntk
残念ながら開発が止まっているみたい…
https://github.com/tamuratak/ruby-cntk

## おわりに
　RubyでDeep Learningできそうなライブラリをリストアップしてみました。
　おそらく、これ以外にもRubyのディープラーニングする方法はあると思います。ライブラリを探すにしても、NMatrixやNArrayに依存しているライブラリは逆依存を調べることで発見できますが、それ以外はなかなか見つけられません。たとえば誰かがdarknetや、それよりずっと無名のCで書かれたディープラーニングライブラリに対して、Ruby用のバインディングを作っていても、Gem化していなかったり、Deep Learningといったキーワードを使っていなければ、捕捉するのは難しいです。たとえば、海外の人がmenoh-rubyを発見するのは相当むずかしいのではないでしょうか。そんな感じで埋もれているものもあると思います。なにか情報をお持ちでしたらコメント欄にお寄せください。

　この記事は以上です。令和元年おめでとうございます。ますますRubyで楽しくdeep learningできるようになるといいなと思います。