blog.katsuma.tv

TensorFlow / TF Learn v0.9のDNNClassifier / TensorFlowDNNClassifierの罠

(2016.07.18 19:00 追記: 誤解のある表現が多かったのでタイトル含め加筆しています)

唐突ですが、1年ぶりの更新を機に、最近興味を持って触っているTensorFlow / TF Learn(skflow) の話をします。

背景

2016.06.27にTensorFlow v0.9がリリースされています。

「モバイルサポートが充実したよ〜」が今回のウリなのですが、v0.8から本体に梱包されてるTF Learn(skflow)において、 シンプルなDeep Neural NetworkモデルのClassifierを扱うときに便利なDNNClassifier周辺で 罠が多いことが分かったので備忘録としてメモを残しておきます。 ちなみに、下記のコードや調査はすべてTensorFlow Learn(TF Learn)ベースのものです。 いろいろな罠の話をしていますが、TensorFlow本体の話では無いのでご注意ください。

結論からいうとv0.9は実用段階では無さそうです。

v0.9はmodelをsave/restoreできない

いきなり致命的な問題です。要するに学習したmodelを使いまわせないというもの。どうしてこうなったのか。。 当然のように、IssueやStackOverflowでは同様の質問が乱立しています。

これはv0.8まであったTensorFlowDNNClassifierがv0.9からDeprecatedになって DNNClassifierの利用を推奨される流れで入ったバグのようです。問題を整理すると、

  • v0.9のDNNClassifierTensorFlowDNNClassifierにあったsave, restoreメソッドが落ちてる
  • v0.9のTensorFlowDNNClassifiersave, restoreメソッドはv0.8から引き続き生きている
    • ただし、v0.9のTensorFlowDNNClassifier.saveはcheckpointファイルを作るものの、modelファイルを保存しないバグがある
    • 結果としてv0.9のTensorFlowDNNClassifier.saveの結果をrestoreすることができない
    • 詰む

というわけで、modelのsave, restoreをする必要がある場合、2016/07/18時点ではv0.8の利用が必要になります。 ただし、いろんなIssueで話題になってるので、この問題は近いうちに修正されるでしょう。

v0.9のDNNClassifierはパフォーマンスが悪い

じゃぁsave, restoreはおいといて、ひとまずコードをTensorFlowDNNClassifierからDNNClassifierに 移そうか、、と思うのですが、v0.9のDNNClassifierはパフォーマンスがかなり悪いです。

DNNClassifier, TensorFlowClassifierで学習とテストデータ、およびstepを固定し、 fitにかかる時間とclassification_reportを計測してみると以下のような結果に。

DNNClassifier

             precision    recall  f1-score   support
          0       0.77      0.96      0.86      4265
          1       0.86      0.45      0.59      2231
avg / total       0.80      0.79      0.76      6496

elapsed_time: 7359.658695936203 [sec]

TensorFlowDNNClassifier

             precision    recall  f1-score   support
          0       0.77      0.92      0.84      4265
          1       0.75      0.47      0.58      2231
avg / total       0.76      0.76      0.75      6496

elapsed_time: 68.16537308692932 [sec]

まず、DNNClassifierTensorFlowDNNClassifierと比較してprecisionが10%ほど向上。 おそらくデフォルトのハイパーパラメータが一部異なるのでしょう。原因は不明確ですが、ひとまず結果が良くなる分はまだ良いです。

問題は、elapsed_timeが68秒から7359秒とハチャメチャに長くなってる点。なんだこれは。。 これだと使い物にならなさすぎるので、Stackoverflowに投げてみたものの、2016/07/18時点ではまだ回答はありません。

このとおり、TensorFlowDNNClassifierは近い将来DNNClassifierに乗り換える必要があるものの、 このパフォーマンス差はだと乗り換えは辛いです。100倍以上遅くなってるけど、これどうなるんでしょ・・・? ちなみに、DNNClassifierは、CPU使用率もTensorFlowDNNClassifierと比較すると20%くらい高くて、 正直何もいいことが無い印象です。

v0.8のTensorFlowDNNClassifierはv0.9と比較すると遅い

これまでの通り、v0.9は辛い状態かつmodelをsaveできない状態なので、 v0.9でチューニングしたハイパーパラメータでv0.8を利用してmodelをsaveさせることにします。

ところが、v0.8のTensorFlowDNNClassifierはv0.9と比較すると 約3倍遅い結果に。

つまり、v0.9では実はTensorFlowDNNClassifierはDeprecatedになりながらも 内部では全体的にパフォーマンスが向上してるようですね。 もう、ここまでくるとDeprecaedにするのもやめてくれ、、、と思い始めます。

v0.8のTensorFlowDNNClassifierはOptimizerを指定しているとmodelをsaveできない

v0.8でsave/restoreの調査を進めてて気づいた問題なのですが、

optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate)
classifier = learn.TensorFlowDNNClassifier(hidden_units=units, n_classes=n_classes, steps=steps, optimizer=optimizer)
classifier.fit(features, labels)
classifier.save(model_dir)

こんなかんじのコードをv0.8で実行すると、最後のsave時にJSONのシリアライズに失敗して ValueError("Circular reference detected")が出てコケます。 ちなみにsaveを呼び出さない場合はコケないのでこれも地味に辛いです。

回避方法としては、「Optimizerは使わない」を選ぶしか無いのかな。この場合、learning rateを調整できないのが辛いですね。

classifier = learn.TensorFlowDNNClassifier(hidden_units=units, n_classes=n_classes, steps=steps)

ちなみにv0.9ではOptimizerを指定したTensorFlowDNNClassifiersaveを呼び出してもエラーにはなりません。 ただし、modelデータの保存もできてないので、このバグが修正されたときにOptimizerのバグも治っているかどうかは不明です。

まとめ

トラップだらけなのですが、modelのsave/restoreが必要な場合、

  1. v0.8 + TensorFlowDNNClassifier を利用
  2. save/restoreのバグが修正されるであろう、v0.9.1相当のリリースを待つ
  3. DNNClassifierのパフォーマンスが上がってたらTensorFlowDNNClassifierから乗り換える。上がってなかったらしばらく使う

のような感じでしょうか。

まだまだTensorFlowの知識は少ないので、誤った情報がある場合はぜひ教えていただきたいです。

KeynoteをRubyから操作するkeynote-clientを作りました

  • 2015年8月10日 00:35
  • ruby

唐突の約1年ぶりのエントリーになりますが、itunes-clientの親戚みたいなかんじのkeynote-clientを作ってみました。

これは何?

itunes-clientと同様、高レベル(のはず)なAPIでKeynoteを操作できます。たとえばこんなかんじ。

require 'keynote-client'

# インストール済のテーマ一覧を取得
themes = Keynote::Theme.find_by(:all)

# 特定の名前のテーマを取得
theme = Keynote::Theme.find_by(name: 'cookpad').first

# テーマを指定して新しいドキュメントを作成
doc = Keynote::Document.new(theme: theme, file_path: '/path/to/slide_name.key')

# 現状のスライド一覧
doc.slides

# 利用可能なマスタースライド一覧
doc.master_slides

# マスタースライドを指定して、新規スライドを追加
doc.append_slide("タイトル & 箇条書き")

# 保存
doc.save

スライドを追加するあたりのAPIがかなり微妙なんですけど、Keynoteのマスタースライドを一意に指定する方法が名前しか無く、これも言語環境によって名前が同じドキュメントでも変わってしまうという、かなり辛い仕様なので対応方法が結構難しい感じです。頑張ればどうにかできそうか。。本当はシンボルを指定したい。

あとスライドはappendするとき、またはappendした後にタイトルや本文を更新できるようにすれば、シンプルなスライドであればひと通りの自動生成できそうかな。

背景

@k0kubunがLTのスライド作らずにスライドを作るツールを夜な夜な作ってるのを見てゲラゲラ笑ってたのですが、AppleScriptで疲弊してそうな印象が強かったので、itunes-clientの知見をどうにか活かせないかなーと思ったのと、JXAを一度使ってみたいなーと思ってたので、その練習がてらがっと書いてみました。

JXA、謎仕様が依然多いですが、少なくともAppleScriptよりだいぶ書きやすいので、感覚つかむまでの速度はだいぶ速くいけました。md2keyもJXAに切り替えちゃってもいい気もしますね。。

今後

スライドのタイトル、本文くらはいじれるようになると実用段階になると思うので、そうしたらmd2keyの下地をいい感じにしてあげられないかなーと企んでます。まだまだ実装雑なので、PRもお待ちしています!

iTunesのプレイリストのトラック情報を整形してクリップボードに保存する

たまに社内のイベントなんかでBGM担当になることがあって、後日「こういう曲をかけたよ」を伝えるのに毎回丁寧にエディタで書いてたのですが、さすがに2時間分のプレイリストなんかを作ると写経が面倒くさくなったのでAutomatorで整形データを作れるようにしてみました。サービス化したので、iTunesから実行できる。どうでもいいけど、Automatorいじったの初めて。

iTunesでプレイリスト選択して、iTunesのメニューから「サービス」> 「Copy tracks from playlist」を選択すれば、「トラック名 / アーティスト名」のフォーマットでクリップボードに追加してくれます。あとは、エディタなり何なりにペーストしてよしなにやればOK。

本当は、iTunesのコンテキストメニューに追加して、「プレイリスト選択」 > 「右クリック」> 「Copy tracks from playlist」でいけるようにしたかったけど、Automatorで実現する方法がよくわからなかった。。多分、無理そう。 というか、Finder以外のアプリってコンテキストメニュー操作できるのかな??

middleman 3.2系から3.3系へupgrade時、wrap_layoutに気をつける

  • 2014年9月 6日 23:25
  • ruby

ALOHA FISHMANSのサイトで使ってるmiddlemanのバージョンは、長らく3.2.0を使っていたのですが、いい加減そろそろ最新のバージョンにあげておくか。。と思ったところ、結構ハマったのでメモ。

空白のページがrenderされる

バージョンの変更はGemfileを書き換えてbundle updateすると、基本的にはOK。Gemfileのdiff的にはこんなかんじ。

 gem "redcarpet", '~> 3.1.1'
-gem "middleman", "~> 3.2.0"
+gem "middleman", "~> 3.3.5"
 gem "middleman-blog", '~> 3.5.2'
 gem "middleman-minify-html", '~> 3.1.1'
 gem "middleman-deploy", '~> 0.2.3'

renderされるページの内容を確認していたところ、基本的に問題ないように見えつつ、blogページだけ何も出力されません。(bodyがカラ)

何か仕様が変わったんだろうと思いつつ、ChangeLog見てもよくわからないな。。。と途方に暮れて検索検索。

wrap_layout

の、ところ気になるIssueを発見。

見事これでした。テンプレートエンジンにhaml/slim を使っていて、wrap_layoutでレイアウトを上書きしているとき、

- wrap_layout :layout do
  ...

= wrap_layout :layout do
  ...

にしないとダメな模様。これで手元でも正常にbodyが出力されるようになりました。

で、よくよくChangeLog見直すと

Update Padrino to 0.12.1. Introduces BREAKING CHANGE for Haml. Helpers which take blocks used to require - instead of = to work correctly. Now, all helpers which output content should use =.

って、書いてるのが該当する話の模様。(気づかねぇ。。。)

TravisCIの結果を光で通知するtravis-blink1を作りました

  • 2014年9月 3日 01:12
  • ruby

これは何?

blink(1)という、USB接続で発光するガジェットをhmskさんからいただいたので、遊びでつくってみたgemです。

指定したgithubのプロジェクトについて、TravisCIの結果を発光して通知してくれるものです。CIが実行中のときは黄色で点滅、テストに失敗したときは赤色で点滅、テストが通ったときは緑で発光します。 動作の様子はこんなかんじ。

テスト失敗時

テスト成功時

blink(1)がおもしろいのは、プログラマブルに発光できること。色や点滅時間など、ことこまかく調整できるので、任意のイベントの通知として発光させることが可能になります。

いろんな言語からblink(1)のAPIを叩くクライアントがあるのですが、rubyだとrb-blink1がひとまずよさそう。travis-blink1でも内部で利用しています。

とりあえずチカチカさせるには、これだけでOK。

require 'blink1'

Blink1.open do |blink1|
  blink1.blink(255, 255, 0, 5)
end

使い方

お約束の

gem install travis-blink1

でインストール後、手元のディレクトリがプロジェクトをforkした場所でgit remoteの設定がされている場合は

travis-blink1

だけ実行すれば、git remoteの結果から、TravisCIの状態を監視。 また、任意のディレクトリから実行するときは

travis-blink1 katsuma/itunes-client

なんかのように「ユーザ名/プロジェクト名」を指定すればOKです。

雑感

blink(1)は、できることがシンプルなので、すぐに試せるのはいいですね。いっぱい天井からぶら下げるなどして、hueみたいに照明代わりにしみても楽しそうです。

Index of all entries

Home

Search
Feeds

Return to page top