rubyでjupyter notebookを使う

rubyでjupyter notebookが使えるというのを発見したので,環境構築してみたがはまったので,改めて環境構築方法を記す.

環境

MacBookPro
macOS Sierra 10.12.3

やったこと

※ここでやったことはうまくいかなかった方法で詰まっている方向けなので,これから環境構築する方は後半を見てください.

とりあえず見つけた以下の記事通りにやってみた.

Railsエンジニアに役立つJupyter NotebookとiRuby - クックパッド開発者ブログ

$ pip install jupyter # python, pip環境が整っていない方は別途整えてください
$ gem install iruby
$ ruby register # これが動かない
$ iruby register # たぶんこう
$ jupyter notebook # jupyter notebookが立ち上がり,rubyを選択できるようになってる!

無事,jupyter notebookを立ち上げ,rubyカーネルを選択することでできるようになった.
しかしここで問題が.
rubyカーネルを立ち上げ使おうと思ったらうまく立ち上がらない.
ターミナルをみると以下のようなエラーが.

dyld: lazy symbol binding failed: Symbol not found: _zmq_poll
  Referenced from: /usr/local/lib/libzmq.dylib
  Expected in: flat namespace

dyld: Symbol not found: _zmq_poll
  Referenced from: /usr/local/lib/libzmq.dylib
  Expected in: flat namespace

ググってるといろいろな解決法が挙げられていたが最終的には以下の記事が解決してくれた.

Mac で IRuby Notebook 環境を構築する方法 2017年版 - Qiita

ちなみに EI Capitanであればこの記事を見ると良さそう.

結局どうすればよいのか

いろんな記事でrbczmqをインストールするよう書いてあるがこれがコケてめんどくさい.
上の記事によるとインストールする必要がないようで,以下のようにするとうまくいく.

$ brew uninstall --ignore-dependencies zeromq # installしていたらuninstallする
$ brew uninstall czmq # 上に同じ
$ brew install zeromq --HEAD # --HEADをつけて最新版を取得する必要がある!!!
$ brew install czmq --HEAD
$ gem install cztop # rbczmqの代わり?

次に,cztop supportをインストールする.

$ gem install specific_install
$ gem specific_install https://github.com/SciRuby/iruby.git
$ iruby register --force

これで環境は整った!!

$ jupyter notebook

起動後,rubyカーネルを選択すればOK!
これで快適なrubyライフが送れますね!!

python並列処理による機械学習の高速化の試み

機械学習をしていると処理の遅さに悩まされることが多々ある.
私は画像系のdeep learningを扱うことが多いので,頻繁に悩まされている.

高速化するにあたってNetworkの処理自体はGPUに任せるしかないので,それ以外でGPUの足を引っ張らないようにするのが大事だと思う.
今回は画像の読み込みを並列で行うことで処理時間の短縮ができるのではないかと思い,やってみた.

この記事でやったこと
* multiprocessingモジュールの簡単な使い方の説明
* Poolクラスを用いて,画像読み込みの並列化&速度測定
* Process, Queueクラスを用いたCNNの処理時間高速化の試み

注意点

Desktop(Ubuntu)とMacBookProで試したのですが,結果が異なってしまったので,あっているのか自信がない…
実装の参考までにしてください.
間違っている点があれば教えていただけると幸いです.

環境

Ubuntu 16.04
プロセッサ: 3.0GHz Core i7
コア数: 16

Pythonにおける並列処理

並列処理には大きく,Threadによる並列化(メモリ共有)とProcessによる並列化(共有なし)があるが,pythonではGIL (Global Interpreter Lock)というインタプリタ上で一度に一つのスレッドのみが動作することを保証する機能があり,Threadによる並列化ができないらしい.

そのため基本的にProcessによる並列化を行う.

multiprocessingモジュール

pythonではmultiprocessingモジュールが提供されており,これを用いることで並列化できる.
[document]

使い方

基本的にはPoolというクラスを用いて以下のように使う.
並列化してもちゃんと順番通りに処理してくれる.

画像読み込みの並列化

以下のように実装した.

結論としては,マルチプロセスにすることで処理速度は向上したがそれほど大きな違いはなかったので,”絶対に並列化するべき!”というほどではない.
が,そんなに難しくない処理なので導入しておくにこしたことはないと思う.

ちなみに,画像読み込み枚数を100, 500, 1000としつこいくらいに細かく調べているのは初めにMacBookProで試したときに想定外の結果がでたので,細かく調査していた名残です.
プロセス数を4にしているのも同様の理由.16にするともっと大きな差がついていた.

CNNでの予測処理高速化の試み

process,queueを用いて,画像の読み込みとCNNでの推定処理を並列に行った.
実装は以下のようにした.(CNNでの推定用コードは省略)

結論としては,並列で画像を読み込んだほうが速いということがわかった.
Process, Queueの導入は逆効果である.という結果がでたのだが,ちょっと納得いかない..
もう少しうまく処理する方法はないのだろうか..

まとめ

multiprocessingモジュールのPool, Process, Queueクラスを用いた並列処理の実装サンプルを示した.
結果,並列化することで処理の高速化を実現できることがわかった.
一番良い方法が並列で画像を読み込む方法だったのだが,Process,Queue等他の方法でもっとうまくやれる方法がある気がする.
間違いがあればご指摘いただけると幸いです.

おまけ

Ubuntuで行う前にMacBookProで試した結果.
Macでは並列化することで悪影響を与えていたので,もしかすると環境次第では並列化しないほうが速いかもしれないので,ご注意ください.

Macで行ったときの結果

Macの環境

Mac Book Pro
プロセッサ: 2.7GHz Core i5
コア数: 4

実行時のエラー出力をファイルに書き出す

エラーログが多すぎて流れきってしまってエラーを追えないときに困ったのでメモする

エラーログは以下のコマンドでファイルに出力できる.

$ [command] 2>log.txt

2標準エラー出力1は標準出力.
ちなみに両方出力する場合は,

$ [comannd] 1>log.txt 2>&1 # こうする
$ [comannd] 1>log.txt 2>log.txt # これは上書きしてしまうので間違い

Juliusを使ってSiriのような音声認識BOTを作る

ラズパイにSiriみたいなことをさせたい.
が,マイクとスピーカの購入がまだなのでとりあえず手元にあるMacで似たような処理を再現する.

仕様

今回作るもののイメージはこんな感じ

私「ヘイ,ラズパイ!」
Raspi「ポンポンっ」(コマンド受け付けますよ音)
私「今日の天気は?」
Raspi「今日の天気は晴れです」

これを再現したい

実装

音声認識 => Julius
天気情報取得 => OpenWeatherMap(Ruby)

JuliusはOSS音声認識エンジンで,認識精度自体はDocomo等の音声認識APIには劣るが,ローカルで動き,単語の登録等ができるのが強み.
常時認識しておきたいのでWebAPIを用いるよりこちらのほうがよいと判断した.

手順

Juliusのインストール

ディクテーションキット
- Mac
http://sourceforge.jp/projects/julius/downloads/60416/dictation-kit-v4.3.1-osx.tgz
- Linux
https://ja.osdn.net/projects/julius/downloads/60416/dictation-kit-v4.3.1-linux.tgz

をダウンロードしてくる

$ tar xvzf dictation-kit-v4.3.1-osx.tgz
$ cd dictation-kit-v4.3.1-osx
$ ./run-gmm.sh

これでJuliusが起動するはず (最新版は4.4 https://ja.osdn.net/projects/julius/downloads/66544/dictation-kit-v4.4.zip だが,試してみた感じ精度が悪そうだったのでとりあえず4.3を使う)

適当にしゃべってみると

<<< please speak >>>Warning: strip: sample 0-66 has zero value, stripped
pass1_best:  こんにちは
sentence1:  こんにちは 。
<<< please speak >>>

うまくうごいてそう このdictation-kitの中には必要ないものも多いので,必要なものだけを取り出してくる.
今回はmodel,am-gmm.jconf,bin/julius,yomi2voca.plを取り出してきて適当に新しいフォルダを作る.

$ mkdir julius
$ cp dictation-kit-v4.3.1-osx/model dictation-kit-v4.3.1-osx/am-gmm.jconf dictation-kit-v4.3.1-osx/bin/julius dictation-kit-v4.3.1-osx/bin/yomi2voca.pl julius

単語の登録

動くことが確認できたので次に,自分が認識して欲しい単語の辞書を作る(julius/word.yomi)

#julius/word.yomi
ヘイラズパイ へいらずぱい
今日の天気 きょうのてんき
明日の天気 あしたのてんき
天気 てんき
今何時 いまなんじ

そしてこれらをJuliusが使える辞書形式に変換する. Juliusは文字コードEUC-JPなので変換して,辞書形式に変換する.

$ iconv -f utf-8 -t eucjp julius/word.yomi | ./julius/yomi2voca.pl > ./julius/word.dic

登録した辞書を用いる設定ファイルの作成

単語の登録が完了したので,反映させるため設定ファイル(julius/word.jconf)を作成する.

#julius/word.jconf
-w word.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/hmmdefs_ptm_gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-zmeanframe
-rejectshort 800
-charconv EUC-JP UTF-8

※この際,設定ファイルに記載する相対パスは,設定ファイルを起点とするパスになる.

起動の確認

./julius/julius -C julius/word.jconf -C julius/am-gmm.jconf

上記のコマンドで起動できる.

pass1_best: ヘイラズパイ
pass1_best_wordseq: ヘイラズパイ
pass1_best_phonemeseq: silB h e i r a z u p a i silE
pass1_best_score: -2283.777832
sentence1: ヘイラズパイ
wseq1: ヘイラズパイ
phseq1: silB h e i r a z u p a i silE
cmscore1: 1.000
score1: -2283.777832

こんな感じに今まで登録されていな方単語が認識されるようになった.
※注意すべきなのが,登録した単語ありきの認識をするため,なにかしらの言葉を発すると登録した単語のどれかに割り当ててしまうということ.

Rubyとのつなぎ込み

julius gemを使うと簡単にできる.
その際,Juliusはモジュールモードで起動しておくこと.(julius-server.sh)

# julius-server.sh
./julius/julius -C julius/word.jconf -C julius/am-gmm.jconf -module

Rubyスクリプト(main.rb)は以下のようにした.

# main.rb
SRC_DIR = File.expand_path(File.dirname(__FILE__))

require 'rubygems'
require 'julius'

class HeyRaspi
  def run(julius)
    begin
      julius.each_message do |message, prompt|
        case message.name
        when :RECOGOUT
          prompt.pause

          shypo = message.first
          whypo = shypo.first
          confidence = whypo.cm.to_f

          puts "#{message.sentence} #{confidence}"

          prompt.resume
        end
      end
    rescue REXML::ParseException
      puts "retry…"
      retry
    end
  end
end

puts "接続中…"
julius = Julius.new

puts "準備OK!"
hey_raspi = HeyRaspi.new
hey_raspi.run(julius)

ターミナルを二つ立ち上げ,ひとつでjulius-server.shを実行.
もう一つはmain.rbを実行.

そして,準備OKと表示されたら,話しかける!

接続中…
準備OK!
ヘイラズパイ 1.0
今日の天気 0.997

いい感じに認識できた〜
あとはここにコマンドの中身を追加すれば完了!

天気の取得

はOpenWeatherMapのAPIを使うと簡単にできる. 実装したものの書くほどでもない気がするので気が向くor要望があったみたいなことがない限り書かない気がする.
参考までに,実装の参考にした記事を記載しておく.

無料天気予報APIのOpenWeatherMapを使ってみる - Qiita

まとめ

とりあえず,今回は音声認識BOTの外枠だけ作成した.
これの中身を変えるとそれぞれの好きなBOTが作れるのではないでしょうか〜

ref

Raspberry Pi 3にElasticsearchとKibanaをいれてみた

背景

Raspberry PiでたてたサーバのログをFluentdとElasticSearch,Kibanaで監視,可視化したい.
Fluentdは前回いれたので,今回はElasticSearchとKibanaをいれる.
そこで軽くつまったのでメモ

ElasticSearchのインストール

The Elastic Stack Download · Get Started in Minutes | Elastic
Elasticのホームページから適当にダウンロードしてくる.
今回は2.4.1をダウンロードして,インストールする.(5.0.0が最新だが,Kibanaとの連携がうまくいかなかった.2016/11/02現在)
※elasticsearchにはJavaが必要なのでインストールしておいてください.

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-2.4.1.zip
$ unzip elasticsearch-2.4.1.zip
$ rm elasticsearch-2.4.1.zip
$ mv elasticsearch-2.4.1 elasticsearch
$ cd elasticsearch
$ ./bin/elasticsearch

これで起動する.

Kibanaのインストール

先程と同様にホームページから適当にダウンロードして,実行する.
今回は4.6.2を使用する.(5.0.0が最新だが,./bin/../node/bin/node: bad option: --no-warningsのようなエラーが出て,調べてもよくわからなかったため,とりあえずバージョンは本質じゃないということで,安心安全のバージョン4.6.2に戻した.)

$ wget https://artifacts.elastic.co/downloads/kibana/kibana-4.6.2-linux-x86_64.tar.gz
$ tar xvfz kibana-4.6.2-linux-x86_64.tar.gz
$ rm kibana-4.6.2-linux-x86_64.tar.gz
$ mv kibana-4.6.2-linux-x86_64 kibana
$ cd kibana
$ ./bin/kibana

が,Kibanaを実行すると,以下のエラーが出た.

./bin/../node/bin/node: 1: ./bin/../node/bin/node: ELF: not found
./bin/../node/bin/node: 17: ./bin/../node/bin/node: Syntax error: ")" unexpected

調べるとRaspberry PiではARMアーキテクチャを使用しているため,nodejsバイナリを変更する必要があるようだ.

$ wget http://node-arm.herokuapp.com/node_latest_armhf.deb
$ sudo dpkg -i node_latest_armhf.deb
$ rm node_latest_armhf.deb
$ cd kibana/node/bin
$ mv node node.orig
$ mv npm npm.orig
$ ln -s `which node` node
$ ln -s `which npm` npm
$ cd ../../
$ ./bin/kibana

無事に動いた.
localhost:5601にアクセスするとKibanaのダッシュボードが表示される.

おまけ

最初,実はElasticSearchは最新だった5.0.0を使用していた.
するとRaspberry Piではメモリが限られているため,以下のようなエラーが出た.

Error occurred during initialization of VM
Could not reserve enough space for 2097152KB object heap

つまりメモリが足りていない.
設定ファイルをいじって対応する.
調べるとES_MAX_MEMやら,-XX:MaxHeapSizeやらいろいろあったが,正解はこれ.

$ vim config/jvm.options
- -Xms2g    #2Gの設定になっている
- -Xmx2g
+-Xms256m    #とりあえず256Mにしてみた
+-Xmx256m

これでメモリの問題は解決.
Kibanaとのバージョンが合わなくて,ElasticSearchのバージョンを落としたため使わなかったが,メモとして残す.

Raspberry PiにFluentdをインストール

はじめに

Raspberry PiにFluentdをインストールして,Pythonアプリケーションのログを監視しようと思った.
が,インストールで軽く詰まったのでメモ

つまったこと

よくあるインストール方法で簡単にインストール&動作確認してみた.
[参考] fluentdインストール(丁寧な説明つき) - Qiita

gem install fluentd --no-ri --no-rdoc
fluentd --setup ./fluent
fluentd -c ./fluent/fluent.conf -vv &
echo '{"json":"message"}' | fluent-cat debug.test

が,実行したと同時にエラーログが大量に流れ出した.

[error]: fluent/supervisor.rb:640:rescue in main_process: unexpected error error="undefined method `compress' for nil:NilClass"
[error]: command/fluentd.rb:288:<top (required)>: /home/user/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/fluentd-0.14.8/lib/fluent/plugin/out_forward.rb:145:in `configure'
...

rbenvのせいかなと思ったけど,そうでもなさそうだった.

解決法

簡単な話で,自動で生成されるfluent.confの設定がよくなかったみたい.

<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>
<match **>
  type stdout
</match>

みたいに簡単なものにすると無事動いた.

サーバ起動時に自動でpyenv環境下のuWSGIアプリケーションを実行しようとしたらはまった

前置き

Raspberry Piでサーバを構築している.
せっかくなので起動したら自動的に作成したPythonアプリケーションを実行できるようにしたい.
作成したPythonアプリケーションはpyenvで管理していて,uWSGIサーバを使って動いている.

した

/etc/rc.localのファイルにシェルスクリプトを書くと実行できる.
ここにこんな風に書くと希望のアプリケーションを実行できる.

# run app from shell script
sh run_app.sh

run_app.sh

# nohup uwsgi --ini hoge_app.ini & <-怒られたやつ
nohup /home/hoge/.pyenv/shims/uwsgi --ini hoge_app.ini &

はまったこと

上記のように書くと,実行できるが,run_app.shはrootユーザで実行される.
がuWSGIはhogeユーザのpyenvで管理していたので,rootユーザからは動かすことができなかった.
そのため,そのまま動かすとuWSGIがないと怒られた.
nohup: コマンド `uwsgi' の実行に失敗しました: そのようなファイルやディレクトリはありません

解決策

たぶんよくない方法だと思うのだが,直接hogeユーザの.pyenv内のuwsgiを指定して実行することで,無事動いた.