mrknさんが開発しているPyCallを使うと、RubyからPythonオブジェクトを操作できる。 Rubyから、Pythonの機械学習・統計分析のツールを利用することを目的としており、 ネット上にもnumpyやscikit-learnを実行する例があがっている。
Rubyist Magazine - PyCall があれば Ruby で機械学習ができる
このPyCallで、Kerasを叩くことができれば、RubyでもDeep Learningできると思い試してみた。 まずはインストールから。
$ gem install --pre pycall
ちなみに、実行環境をまとめると、Ruby 2.4.0、PyCall 0.1.0.alpha.20170317、Python 3.6.0、Theano 0.9.0、Keras 2.0.2である。 試したのは、MNISTの手書き数字画像を、畳み込みニューラルネットで認識するサンプルコードである。
keras/mnist_cnn.py at master · fchollet/keras · GitHub
これを、細かいところは適宜はぶきながら、Ruby+PyCallで移植すると次のようになる。
require 'pycall/import' include PyCall::Import # Kerasの必要なものを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'] # MNISTは28x28の大きさの手書き数字画像で、各画像が10個のクラスにわけられている. nb_classes = 10 img_rows = 28 img_cols = 28 # MNISTデータセットを読み込む.初回実行時はダウンロードするところから始まる. (x_train, y_train), (x_test, y_test) = mnist.load_data.() # データのreshapeは、元のコードでは... # pyfrom 'keras', import: 'backend' # backend.image_data_format.() # の結果で処理を分けている. # 試してみたところ "channels_last" だったので、 # そちらの処理を移植した. 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) # 型をfloat32にして、要素を[0.0,1.0]にする. 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]))
これを実行すると、問題なくネットワークの学習が動き始める!
$ ruby keras_test.rb Using Theano backend. Using cuDNN version 5005 on context None Mapped name None to device cuda: GeForce GTX 1080 (0000:06:00.0) Train on 60000 samples, validate on 10000 samples Epoch 1/10 60000/60000 [==============================] - 27s - loss: 0.3227 - acc: 0.9030 - val_loss: 0.0730 - val_acc: 0.9760 ... Epoch 10/10 60000/60000 [==============================] - 26s - loss: 0.0410 - acc: 0.9881 - val_loss: 0.0298 - val_acc: 0.9890 Test loss: 0.029782 Test accuracy: 0.989000
Kerasは、ブロックをつなげる感じで、簡単にネットワーク構造を定義できる。 Pythonに詳しくないRubyエンジニアが、Deep Learningを試してみるには、PyCall+Kerasが最高な気がする。 また、Pythonで学習したネットワークを、Ruby+PyCallで読み込んで使うという形にすれば、 容易にDeep Learningな機能をRailsアプリに組み込める、という感じで夢が広がる。