はじめに
みなさまお久しぶりです、くろすです。
新卒を名乗れるのもあと数日になりましたが、気持ち的にはあと3年くらい新卒扱いでひたすら勉強できる機会が欲しいなって思います。
めちゃくちゃ今更感ありますが、RubyからPyCallを使ってtensorflowを動かしてみました。
学生時代自分でガリガリ微分してMATLABのコードに起こしてDNN作っていたのがアホらしく感じてしまいます。
Rubyでデータサイエンス
かなり前どこかで読んで覚えていたのですがRubyでデータサイエンスをするためには3つほど手段があるそうです。
一つ目は「巨人の肩に乗る」
二つ目は「既存のgemを何とかする」
三つ目は「Rubyのための仕組みを作る」
今回はこの「巨人の肩に乗る」という方法でRubyから機械学習をやりたいと思います。
環境
$ ruby -v ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16] $ gem list | grep pycall pycall (1.0.3) $ python3 -V Python 3.6.4 $ pip3 freeze | grep tensorflow tensorflow==1.6.0
Rubyのコード
今回やったチュートリアルはtensorflowのversion r1.2のチュートリアルのうちMNISTを使ったやつです。
https://www.tensorflow.org/versions/r1.2/get_started/mnist/beginners
tensorflowのバージョンが1.6.0でもちゃんと動きました。
require 'pycall' require 'pycall/import' module Python extend PyCall::Import class << self def from(path, import: nil) pyfrom path, import: import self.send import end def method_missing(method_name) pyimport method_name self.send method_name end end end
require_relative 'python' # Python library import Python.from('tensorflow.examples.tutorials.mnist', import: :input_data) tf = Python.tensorflow input_data = Python.input_data # Download MMIST dataset mnist = input_data.read_data_sets('MNIST_data/', one_hot: true) # Input, weight, bias x = tf.placeholder(tf.float32, [nil, 784]) w = tf.Variable.new(tf.zeros([784, 10])) b = tf.Variable.new(tf.zeros([10])) # Output y = tf.nn.softmax(tf.matmul(x, w) + b) y_ = tf.placeholder(tf.float32, [nil, 10]) # Error function cross_entropy = tf.reduce_mean(-1 * tf.reduce_sum(y_ * tf.log(y), reduction_indices: [1])) # Train Model train_step = tf.train.GradientDescentOptimizer.new(0.5).minimize(cross_entropy) sess = tf.InteractiveSession.new tf.global_variables_initializer.run # Train start 1000.times do batch = mnist.train.next_batch(100) batch_xs, batch_ys = batch[0], batch[1] sess.run(train_step, feed_dict: {x => batch_xs, y_ => batch_ys}) end # normalize correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) # Result accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) puts sess.run(accuracy, feed_dict: { x => mnist.test.images, y_ => mnist.test.labels })
わざわざPythonモジュールとして切り出しているのはmath.rbとか作って確認してた名残なので、実際はtensorflow_tutorial.rbにベタ書きしても問題ないです。
結果
$ ruby tensorflow_tutorial.rb 0.9185
This should be about 92%.
確かに正答率92%くらいになりました。
詰まった所
pycallが読みに行くpythonが/usr/bin/python
環境変数PYTHONを使いたいpythonにすればいい
$ export PYTHON=path/to/python3
データセットをダウンロードしようとしてSSL関係でエラー
$ /Applications/Python\ 3.6/Install\ Certificates.command
で解決
参考( https://github.com/tensorflow/tensorflow/issues/10779 )
tf.matmul(x, w)でなんかエラーでた
Exception: PyCall::PyError: <class 'TypeError'>: _as_graph_element() missing 1 required positional argument: 'self' File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/ops/math_ops.py", line 2004, in matmul with ops.name_scope(name, "MatMul", [a, b]) as name: File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 5616, in __enter__ g = _get_graph_from_inputs(self._values) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 5277, in _get_graph_from_inputs graph_element = _as_graph_element(op_input) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/tensorflow/python/framework/ops.py", line 118, in _as_graph_element return conv_fn()
直前の部分をrubyコードに読み替えるのをミスっていた
pythonの元コード: tf.Variable(tf.zeros([10])) 間違えたrubyコード: tf.Varialbe(tf.zeros([10])) よく見るrubyコード: tf.Variable.(tf.zeros([10]))
よくPyCall使ってrubyで…って記事には tf.Variable.(tf.zeros([10]))
のように書いてありますが、これはtf.Variableクラスの()メソッドを呼んでいる、つまりコンストラクタの呼び出しなのでruby的に書くならinitializerを呼び出してあげれば良いんですね。
じゃあちょっと冗長ですがnewしましょう。
正解のrubyコード: tf.Variable.new(tf.zeros([10]))
連想配列の違いで手間取った
元のPythonコード: sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys}) Ruby間違いコード : sess.run(train_step, feed_dict: {x: batch_xs, y_: batch_ys}) Ruby正解コード : sess.run(train_step, feed_dict: {x => batch_xs, y_ => batch_ys})
終わりに
PyCallを使ってRubyからtensorflowを使ってMNISTのチュートリをやった的な記事がぱっと見当たらなかったので書いてみましたが、ほぼほぼチュートリアルのコードを書き写したみたいになってあまり面白くはなかったです。
今回はわざわざPyCallを使ってRubyからtensorflowを触りましたが、実は tensorflow.rb というRubyAPIがあったりするので、次はそっち使って遊んでみます。