Ruby で TensorFlow === 2017/02/11 名古屋Ruby会議03 antimon2(後藤 俊介 / 機械学習名古屋) --- ## デモ ---- [Sinatra](http://www.sinatrarb.com/) + [tensorflow.rb](https://github.com/somaticio/tensorflow.rb) で動いていま~~す~~++した++。 <!-- .element: class="fragment" --> <aside class="notes">このサイトは今回のデモ用に臨時に用意したものです。<S>週が明けたら閉じる予定</s><ins>閉じました</ins>。あとタイムテーブルで「Rails に組み込んだ…」と言っていましたがあれは嘘だ(時間がなかった)。</aside> --- ここから本題…。 <aside class="notes">ここまでは枕</aside> --- <!-- .slide: data-transition="convex-in slide-out" --> # Ruby で TensorFlow してみたけれど辛かった 2017/02/11 名古屋Ruby会議03 antimon2(後藤 俊介 / 機械学習名古屋) <aside class="notes">Ruby で TensorFlow してみたけれどなかなか大変だった</aside> --- ## 自己紹介 ---- + 名前:後藤 俊介 + 所属:有限会社 来栖川電算 + コミュニティ:**[機械学習名古屋](https://machine-learning.connpass.com/)**(主催の1人), [Ruby東海](https://rubytokai.doorkeeper.jp/), [Python東海](https://connpass.com/series/292/), … + 言語:Python, Julia, **Ruby**, Scala(勉強中), … + twitter: [@antimon2](https://twitter.com/antimon2) + Facebook: [antimon2](https://www.facebook.com/antimon2) + GitHub: [antimon2](https://github.com/antimon2/) + Qiita: [@antimon2](http://qiita.com/antimon2) <aside class="notes">Rubyist としては、誰も使わないような gem を2つほど公開しています。機械学習とは、コンピュータにデータを学習させて自分の判断で色々仕事をさせること。自然言語処理とか画像認識とか。あと amazon の「この商品を買った人はこんな商品も」とか。今日の発表でも須藤さんの発表中にちらっと出てきましたね。</aside> ---- ### 機械学習名古屋 [![mlngy_201703.png](https://qiita-image-store.s3.amazonaws.com/0/30400/deab3775-657e-bcc3-55bd-a3b234fe1017.png)](https://machine-learning.connpass.com/event/49365/) [第9回勉強会](https://machine-learning.connpass.com/event/49365/) 2017/03/11 開催(ちょうど1ヶ月後) <aside class="notes">機械学習名古屋は、「ビジネスで実用的に使える機械学習」を1つのテーマとしています。</aside> --- ## TensorFlow ---- ### TensorFlow とは + https://www.tensorflow.org/ + Google 製の「データフローグラフを用いた数値計算ライブラリ」(公式の説明を私訳) + 要するに、**機械学習ライブラリ**。 + 最新 [v0.12](https://www.tensorflow.org/versions/r0.12/)(2017/02/11 現在)。[v1.0](https://github.com/tensorflow/tensorflow/tree/r1.0) が開発中。 ---- ### tensorflow.rb + https://github.com/somaticio/tensorflow.rb + TensorFlow を Ruby から操作するためのラッパーライブラリ。 + [Docker イメージ](https://github.com/somaticio/tensorflow.rb/blob/master/README.md#docker) が用意されているので、手軽に試すことができる。 --- ## tensorflow.rb のインストール ---- ### `gem install tensorflow`… ```shell $ gem install tensorflow ERROR: Could not find a valid gem 'tensorflow' (>= 0) in any repository ... ``` <!-- .element: class="fragment" --> ---- ### 人類にはまだ早かった <aside class="notes">まだコマンド一発でインストールできるよう整備されているわけではありません。</aside> ---- ## 自分でビルド 公式 [README](https://github.com/somaticio/tensorflow.rb/blob/master/README.md#outside-of-docker "Outside of Docker - Installation - tensorflow.rb") を参考に > + 要 [Bazel](http://www.bazel.io/docs/install.html) > + 要 [Swig](http://www.swig.org/download.html) <aside class="notes">macOS なら brew で、Linux系なら apt-get/yum 等でインストールできると思います。</aside> ---- ### 〜中略〜 <aside class="notes">libtensorflow.so のビルドに1時間くらい(READMEには10-15分とあるけれど)と時間かかる…。</aside> ---- ### 確認 ```shell $ bundle exec rake spec : 《中略》 dyld: lazy symbol binding failed: Symbol not found: _TF_NewStatus Referenced from: /path/to/gems/tensorflow-0.0.1/lib/sciruby/Tensorflow.bundle Expected in: flat namespace dyld: Symbol not found: _TF_NewStatus Referenced from: /path/to/gems/tensorflow-0.0.1/lib/sciruby/Tensorflow.bundle Expected in: flat namespace : 《後略》 $ ``` <aside class="notes">libtensorflow.so のビルドがなんかおかしい…</aside> ---- ## 人類にはまだ早かった <aside class="notes">素直に Docker 使った方が取り敢えず幸せになれそう。</aside> --- ## Docker で tensorflow.rb ---- ### Docker + https://www.docker.com/ + (ry <aside class="notes">コンテナ型仮想化</aside> ---- ### tensorflow.rb の Docker イメージ ```shell $ docker pull nethsix/ruby-tensorflow-ubuntu:0.0.1 ``` ---- ### コンテナ実行 ```shell $ docker run --rm -it -v `pwd`:`pwd` --expose 4567 -p 8000:4567 \ nethsix/ruby-tensorflow-ubuntu:0.0.1 /bin/bash ``` <aside class="notes"><code>-v `pwd`:`pwd`</code> は、カレントディレクトリを同じ名前で docker 内にマウントする設定、<code>--expose 4567 -p 8000:4567</code> は、Sinatra を起動してポート番号 8000 でアクセスできるようにするための設定。あと便宜上改行しています。</aside> ---- ### Gemfile ```ruby= source 'https://rubygems.org' gem 'tensorflow', path: '/repos/tensorflow.rb' gem 'sinatra' # gem 'sinatra-contrib' gem 'thin' ``` ---- ### `bundle install` ```shell $ bundle install : #《中略》 Bundle complete! 3 Gemfile dependencies, 11 gems now installed. Use `bundle show [gemname]` to see where a bundled gem is installed. ``` ---- ### 動作確認 <big><big><big><big>(ry</big></big></big></big> <aside class="notes">先人たちの戦果を <a href="https://www.google.co.jp/search?q=tensorflow.rb&oq=tensorflow.rb" target="_blank">tensorflow.rb でググっ</a>て見てみてください。</aside> --- ## 学習 ---- ### してません! <aside class="notes">時間的な問題で。その代わり学習済モデルを読み込む方針で。本当はワンストップで全部Rubyで、っていうデモができればと思ってたんですけど…</aside> --- ## 推測 ---- ### 学習済モデル準備 Python で以前学習したモデルを [Protocol Buffers](https://developers.google.com/protocol-buffers/)形式 で保存。 (詳細略)<!-- .element: class="fragment" --> <aside class="notes"><a href="http://workpiles.com/2016/07/tensorflow-protobuf-dump/" target="_blank">TensorFlowで学習済みグラフを保存する方法</a> を参考にしました。なお Python のコードは出しません。Ruby 会議なので。</aside> ---- ### 学習済モデル読込 ```ruby= require 'tensorflow' sess = Tensorflow::Session.new graph = Tensorflow::Graph.new graph.read("trained_graph.pb") sess.extend_graph(graph) ``` <aside class="notes"><strong>trained_graph.pb</strong> が学習済モデルを保存したファイルです。</aside> ---- #### 参考 .pb のフォーマットが間違っていると… ```shell E tensorflow/core/common_runtime/executor.cc:334] Executor failed to create kernel. Invalid argument: NodeDef mentions attr 'Tshape' not in Op<name=Reshape; signature=tensor:T, shape:int32 -> output:T; attr=T:type>; NodeDef: Reshape = Reshape[T=DT_FLOAT, Tshape=DT_INT32, _device="/job:localhost/replica:0/task:0/cpu:0"](_recv_Placeholder_0, Reshape/shape) [[Node: Reshape = Reshape[T=DT_FLOAT, Tshape=DT_INT32, _device="/job:localhost/replica:0/task:0/cpu:0"](_recv_Placeholder_0, Reshape/shape)]] E tensorflow/core/client/tensor_c_api.cc:485] NodeDef mentions attr 'Tshape' not in Op<name=Reshape; signature=tensor:T, shape:int32 -> output:T; attr=T:type>; NodeDef: Reshape = Reshape[T=DT_FLOAT, Tshape=DT_INT32, _device="/job:localhost/replica:0/task:0/cpu:0"](_recv_Placeholder_0, Reshape/shape) [[Node: Reshape = Reshape[T=DT_FLOAT, Tshape=DT_INT32, _device="/job:localhost/replica:0/task:0/cpu:0"](_recv_Placeholder_0, Reshape/shape)]] /repos/tensorflow.rb/lib/tensorflow/session.rb:103: [BUG] Segmentation fault at 0x00000000000017 ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-linux] -- Control frame information ----------------------------------------------- c:0007 p:---- s:0040 e:000039 CFUNC :tensor_size c:0006 p:0015 s:0036 e:000035 METHOD /repos/tensorflow.rb/lib/tensorflow/session.rb:103 c:0005 p:0009 s:0029 e:000028 BLOCK /repos/tensorflow.rb/lib/tensorflow/session.rb:42 [FINISH] : (ry ``` <!-- .element: class="fragment" --> <aside class="notes">Segmentation fault で Ruby ごと落ちます。</aside> ---- #### <big><big>辛い</big></big> <aside class="notes">辛い。</aside> ---- ### 推測 ```ruby=8 # x: 要素数 784 の数値の配列(を期待) input1 = Tensorflow::Tensor.new(x, :float) res = sess.run( { 'Placeholder' => input1.tensor }, ['Softmax'], nil ) print res[0][0], "\n" # => [7.959904735344026e-09, 2.4618149532806832e-11, 9.080870950128883e-05, 1.0324711918752172e-10, 2.9449205296572245e-09, 1.312004671571998e-10, 1.6875362218726764e-09, 5.2551704307624014e-12, 0.9999091625213623, 3.465244091671593e-08] ``` ---- #### 参考 ちょっとでも間違ったモノを渡したりすると… ```shell E tensorflow/core/client/tensor_c_api.cc:485] Input to reshape is a tensor with 783 values, but the requested shape requires a multiple of 784 [[Node: Reshape = Reshape[T=DT_FLOAT, _device="/job:localhost/replica:0/task:0/cpu:0"](_recv_Placeholder_0, Reshape/shape)]] /repos/tensorflow.rb/lib/tensorflow/session.rb:103: [BUG] Segmentation fault at 0x00000000000017 ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-linux] -- Control frame information ----------------------------------------------- c:0007 p:---- s:0040 e:000039 CFUNC :tensor_size c:0006 p:0015 s:0036 e:000035 METHOD /repos/tensorflow.rb/lib/tensorflow/session.rb:103 c:0005 p:0009 s:0029 e:000028 BLOCK /repos/tensorflow.rb/lib/tensorflow/session.rb:42 [FINISH] : (ry ``` <!-- .element: class="fragment" --> <aside class="notes">Segmentation fault で Ruby ごと落ちます。</aside> ---- #### <big><big><big>辛い</big></big></big> <aside class="notes">本当に辛い。</aside> --- ## Web アプリ組込 ---- ### 仕様 + canvas を表示し、マウスもしくはタッチで、手書きで数字を書き込める。 + 「Classify」ボタンを押すと、それを「0」〜「9」のどの数字か推測して結果を表示する。 ---- ### mnist.rb ```ruby= # mnist.rb require 'tensorflow' class MNIST def initialize(model) sess = Tensorflow::Session.new graph = Tensorflow::Graph.new graph.read(model) sess.extend_graph(graph) @sess = sess end ``` ---- ```ruby=13 def classify(x) input1 = Tensorflow::Tensor.new(x, :float) res = @sess.run( { 'Placeholder' => input1.tensor }, ['Softmax'], nil ) res[0][0] end end ``` ---- ### app.rb(抜粋) ```ruby= # app.rb require 'sinatra' require 'thin' require 'json' require_relative 'mnist' configure do # 略:IPの設定、Thin Backend の設定 ``` ---- ```ruby=41 def classify(x) @@mnist ||= MNIST.new("trained_graph.pb") @@mnist.classify(x) end ``` <aside class="notes"><code>@@mnist</code>(クラス変数)を使ってて行儀悪い(Warning が出る)けれど取り敢えずやっつけで。</aside> ---- ```ruby=69 post '/classify' do content_type :json begin x = JSON.parse(params["x"]) res = classify(x) res.to_json rescue Exception => e "[]" end end ``` <aside class="notes"><code>x</code>という名前でパラメータ(長さ784の数値配列(JSON文字列))を受け取り、結果を配列(JSON文字列)で返す。例外が起きたら(ハンドリングできたら)空の配列を返している。</aside> ---- ### HTML(+JavaScript)(抜粋) ```htmlmixed=15 <script> // (ry function classify() { var myCanvas = document.getElementById("myCanvas"); var ctx = myCanvas.getContext("2d"); var data = []; // (ry: canvas からピクセルデータを読み込む処理 jQuery.ajax("classify", { dataType: "json", data: "x=" + JSON.stringify(data), type: "POST", success: function (res) { // (ry: 整形して表示する処理 } }); } // (ry </script> ``` ---- ```htmlmixed=138 <div id="board"> <canvas id="myCanvas" width="112px" height="112px"></canvas> <p> <button id="classify" onclick="classify()">Classify</button> <button id="clear" onclick="myClear()">Clear</button> </p><p> Result: <input type="text" id="result_output" size="5" value=""><br> <textarea id="result_detail" cols="30" rows="10"></textarea> </p> </div> ``` <aside class="notes">idとか関数名とかが<code>myXxxx</code>になっていて、いかにもサンプルっぽいですよね!(名前付けをさぼっただけ…)</aside> ---- ### 結果 <img width="414" alt="IMG_1426.png" src="https://qiita-image-store.s3.amazonaws.com/0/30400/2c4f8f2b-a7ea-d4c0-cd79-aa8c8ba9e39f.png"> <aside class="notes">なんとか動くところまで漕ぎ着けました…</aside> ---- #### 辛かった --- ## 結論 + **人類にはまだ早い**<!-- .element: class="fragment" data-flagments-index="1" --> + **辛い**<!-- .element: class="fragment" data-flagments-index="2" --> <aside class="notes"><ul> <li>「誰でも手軽に組み込める」レベルはまだ遠い。</li> <li>頑張れば動くものはできるけれど、結構大変。</li> </ul></aside> --- # おしまい ご清聴ありがとうございます。
{"metaMigratedAt":"2023-06-14T12:23:28.343Z","metaMigratedFrom":"YAML","title":"Ruby で TensorFlow","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"80062a4b-8dad-49ac-95bf-848ce0686e9e\",\"add\":36,\"del\":0}]"}
    3391 views