技術メモ

後で同じ状況が起こった時に思い出せるように技術的なちょっとしたことをメモする。ベストな解ではない。



PyCallを用いてTensorFlowをRubyで使う


PyCallはmrknさんが開発しているPythonのライブラリをRubyで使うgemである。
バージョンが1.0になって大幅に使いやすくなった。
今回は、このPyCallを用いてPython用のライブラリのTensorFlowをRubyで動かしてみる。
今回の環境はWindows10, Ruby2.4, Python3.6を用いた。





 

PyCallのインストール

PyCallを使用するにはPythonをインストールする必要がある。(すでにインストール済みの場合は必要ない)
インストール方法はなんでも良いが、公式のWebページのダウンロードリンクからインストーラーをダウンロードしてインストールするのが楽だろう。
パスを通すのとpipもインストールするのを忘れないように。
Rubyは恐らくインストールされていると思うが、ない場合はRubyInstaller2か何かでインストールしておく。
PyCallのインストールはgem install pycallで可能である。
追加でNumpyを使いたい場合はgem install numpyでインストールすることも出来る。
今回はPythonのTensorFlowライブラリを用いるので、pip install tensorflowでインストールしておく。(TensorFlowはGPU版もあるが、PyCallでは対応できないっぽいのでCPU版を用いる)
インストールに失敗する場合はcmdを管理者権限で開いてみたり再起動してみたり…とエラーメッセージを見れば多分解決はわかると思う。





 

PyCallの基本的な使い方

まず最初にrequire "pycall"をしておく。(Numpyを使う場合はrequire "numpy"も)
PythonのライブラリをインポートするにはPyCall::import_moduleを用いる。(Numpyの場合は不要)
Pythonのimport tensorflow as tftf=PyCall::import_module("tensorflow")となる。(Numpy(import numpy as np)はnp=NumpyでOK。)
後は、tf.~やnp.~などPythonでやっているように書くだけで動く。
Pythonのメソッドの引数や返り値でのオブジェクトの型変換は可能な場合は自動的に行われる(RubyのArrayとPythonのListなど)。
あくまで、文法や構文はRubyなので、PythonのキーワードなどはRuby用に変える必要がある。(Nonenilにするなど)
具体的にどんな風に書くのかは実際のコードで説明する方が分かりやすいと思うので、ここでの説明はこの辺にとどめておく。





 

TensorFlowを用いたクラス分類DNN(ディープニューラルネットワーク)のサンプル

概要

今回のサンプルプログラムの概要は以下のとおりだ。

  • NeuralNetworkクラス, Datasetクラスを用意する
  • NeuralNwtworkクラスはネットワークを定義し、入力を与えると出力を返せ、入力とラベルを入れると、学習や誤差の計算が可能である
  • ファイル名を指定することで学習したモデルの保存・復元が可能
  • NeuralNwtworkクラスはコンストラクタにネットワークを構成するノード数とドロップアウト率を渡す
  • Datasetクラスはコンストラクタにファイル名を渡すとそれを読み込み、ランダムに入力とラベルを返すことが出来る
  • データセットの入力形式はCSVで「属性1,属性2,…,属性N,ラベル」であり, 属性値は実数, ラベルは0から始まる整数とする
  • 今回は汎化誤差の計算(ホールドアウト検証やクロスバリデーション)はせず、テスト誤差のみ出力することにする(ただしホールドアウト検証するのはプログラムを2行ほど追加するだけで可能である)

実際にTensorFlowを用いた時のコードを比較しやすいように同じ実装(アルゴリズム)で、PythonとRubyで記述した。
サンプルプログラムでは10001回学習させて、1000回毎に評価している。
データセットは60次元で2クラス分類のソナー問題を読み込むようにしてある。レイヤーの構造は[60,30,15,7,2]である。
なお、今回はPythonでの記述とRubyでの記述の比較が目的のため、TensorFlowの説明はしない。

Pythonのコード

Pythonには入れ子のリストをFlattenにするメソッドが無いので、84-95行目にRubyで言うflattenメソッドを実装している。

PyCallを用いたRubyのコード

簡単に解説をしよう。

  • 4 require "pycall": PyCallを読み込む。
  • 11 tf=PyCall::import_module("tensorflow")": import tensorflow as tfと同等
  • 23 z.push(tf.placeholder(tf.float32,[nil,layer[0]])): Pythonと表記はほぼ同等だがNonenilにしている
  • 29 w.push(tf.Variable.new(tf.truncated_normal([layer[i-1],layer[i]]),name:"w#{i}")): tf.Variableはクラスなので、Rubyではnewメソッドでインスタンス化する必要がある。Pythonでのキーワード引数keyword=valueはRubyでの記法keyword:valueに修正している。
  • 42 @loss=-1*tf.reduce_sum(@t*@y): -tf.rresuce_sum(@t*@y)ではエラーが出たので-1*にした。
  • 71 return tmp.tolist.to_a: @yはnp.ndarray型なのでtolistメソッドでPythonのリスト型に変換し、そのままだとRubyで扱えないのでto_aでArrayに変換している。

これ以外は特にPythonとRubyでの書き方の違いに大きな差はなく、特に解説する必要はないと思う。

動かしてみる

実行の仕方は特に変わらない。
上記のファイルをmain.rbとしたなら、そのディレクトリでruby main.rbとすれば良い。同じディレクトリにdataset.csvが存在する必要がある。
サンプルで用いたデータセットは以下からダウンロードできる。

実行結果は次のようになった(ランダムシードが実行毎に変わるため、必ずしも同じ結果にならない)。
accuracyが1に近づいてきちんと学習できていることがわかる。

> ruby main.rb
loading dataset: dataset.csv
18008 data load. (dim: 60, class: 2)
–Neural Network–
layer: [60, 30, 15, 7, 2]
dropout_rate: 0.8
depth: 5

start training!
0: loss=-0.463793869391 accuracy=0.463794
1000: loss=-0.463793869391 accuracy=0.463794
2000: loss=-0.79221783591 accuracy=0.791648
3000: loss=-0.85574949545 accuracy=0.85673
4000: loss=-0.888409621765 accuracy=0.888605
5000: loss=-0.918586964821 accuracy=0.91898
6000: loss=-0.933551884926 accuracy=0.934418
7000: loss=-0.946889164712 accuracy=0.94769
8000: loss=-0.958797825966 accuracy=0.95924
9000: loss=-0.963946579298 accuracy=0.964238
10000: loss=-0.966777569344 accuracy=0.967126
saved model: D:/model

finished.

なお、モデルを復元したい場合はコメントアウトされているnn.restore(PATH+"model")のコメントを外せば良い。
実行した時のメモリの消費量はPyCallを用いたほうが多いが、結果や実行速度に関してはPyCallを用いても変わることはなかった。
PyCallを使えばPythonの様々な便利なライブラリがRubyで使えるので非常に便利で使いやすい。開発者様様である。





 



タグ: , , ,
投稿日: 2017年11月10日
最終更新日:





コメントを残す

回答をお約束することは出来ませんので予めご了承ください。
コメントは承認されると表示されるようになります。
コメントを送信する前に「私はロボットではありません」にチェックを入れて下さい。