ブラウザ上でデータ分析が出来る!Clojure/Gorilla入門

f:id:AntiBayesian:20140907214031j:plain
f:id:AntiBayesian:20140907201052j:plain

概要

この記事は、Gorillaという
ブラウザ上でClojureという言語を利用出来るライブラリを利用し、
ブラウザ上でデータ分析環境を構築するための入門記事です。
Clojureの事前知識は一切不要で、
ClojureやGorillaのインストールから、ブラウザ上で各種手法を
コピペだけで実践出来るになるまでを説明しています。
なお、各種分析手法の詳細には言及しておりません。

とにかくGorillaを動かす

「説明は良いから、とにかく動かしたい」
という方向けに要点だけ説明します。

leiningenをインストールし、作業フォルダにてlein new gorillaでプロジェクトを作り、
生成されたgoraillaフォルダ直下にあるproject.cljに下記をコピペし保存、
lein gorillaと打ち込み、少し待つと
Running at http://localhost:XXXXX/worksheet.html
と表示されるので、そのURLを叩くとこんな感じのページが表示されます。
以上。

(defproject gorilla-test "0.1.0-SNAPSHOT"
  :description "A test project for Gorilla REPL."
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [incanter "1.5.5"]
                 [incanter-gorilla "0.1.0"]
                 [cc.artifice/clj-ml "0.6.0-SNAPSHOT"]
                 [org.atilika.kuromoji/kuromoji "0.7.7"]]
  :main ^:skip-aot gorilla-test.core
  :target-path "target/%s"
  :repositories [["Atilika Open Source repository" "http://www.atilika.org/nexus/content/repositories/atilika"]]
  :plugins [[lein-gorilla "0.3.3"]]
  :profiles {:uberjar {:aot :all}})

Clojure/Gorillaとは

Clojureとは、JVM上で動くプログラミング言語の一種です。
Clojureには統計解析ライブラリIncanter機械学習ライブラリclj-mlなど、
継続して開発されているデータ分析用のライブラリが用意されています。
また、同じくJVM上で動くHadoopやHBaseなどのシステムとも親和性が高く、
それらを利用して大規模なデータ分析を行うのに適しています。
しかし、Clojureをいつでもどこでも誰にでも利用して貰うためには、
各端末にJDKのインストールやパスを通すなどの環境構築が必要であり、
各種ライブラリの更新の度全端末にそれを適用し続けるのは
ハードルが高いと言わざるを得ません。
また、データを個々の端末に移動するのも非常にコストがかかります。
勿論これはClojureに限った話ではなく
他の処理系においても同じ問題ではありますが。
そこで、ブラウザ上で誰でもアクセス出来て、
直ちにデータ分析を行えるようなシステムを簡単に構築出来れば
上記のハードルをクリアできます。
つまり、誰でも色んな端末(スマホタブレットでも!)アクセス出来て、
最新のバージョンを利用したければサーバ側で更新を掛ければ済み、
データはサーバ上に置いたままなので一々解析するのに
全データをローカルに落とす必要も無く、
解析用の処理を送り結果を受け取るだけでよくなります。
そのような要望によって生み出されたのがGorillaです。
下記のGollira本家サイト、また、
GorillaにIncanterを組み込んだincanter-gorillaのサイトを
ご覧頂ければどのようなものかがご理解頂けると思います。
Gollira本家
incanter-gorilla
同様のプロジェクトはPythonやRにも存在しています。
特にIPython notebookはかなりの出来栄えですので、
特にJVM環境以外でも問題無く、
かつPythonに慣れ親しんでいる方は
そちらを利用するのも良いと思われます。

Clojureのインストール

主にWindows7向けのインストール方法について説明します。

まず最新のJavaSDK(Javaの開発環境)を入れる必要があります。
下記からご自分の端末に合わせたモノをインストールしてください。
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

次に、下記のページを参考にPath, JAVA_HOMEを設定します(windows向け)。
http://www.javadrive.jp/install/jdk/index4.html

次に、Clojureの開発ツールであるleiningenをインストールします。
公式サイトからlein.batをダウンロードして下さい。
http://leiningen.org/
(あるいはこれを利用)

最後に、パスの通っている作業フォルダ(例:c:\lein)にlein.batを移動して、
コマンドプロンプトから
lein self-install
を実行。
あとはインストール処理が自動で行われますので、終了するまで待ちます。
終了したら
lein -version
と打ち込み、2014/09/07時点では
Leiningen 2.4.3
と表示されればインストール作業終了です。

Gorillaを起動してみる

作業フォルダにて、コマンドプロンプト
lein new <プロジェクト名>
と打ち込むと新規プロジェクトを作成出来ます。
新規プロジェクトには最低限必要なファイルや設定が自動で生成されています。
ここでは
lein new gorilla
と打ち込んでみましょう。
するとgorillaというフォルダ名の直下に
project.cljというファイルが生成されています。
これはこのプロジェクトの設定ファイルです。
設定を変更する場合はこのファイルを編集します。
ここでは下記のように編集して下さい。

(defproject gorilla-test "0.1.0-SNAPSHOT"
  :description "A test project for Gorilla REPL."
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [incanter "1.5.5"]
                 [incanter-gorilla "0.1.0"]
                 [cc.artifice/clj-ml "0.6.0-SNAPSHOT"]
                 [org.atilika.kuromoji/kuromoji "0.7.7"]]
  :main ^:skip-aot gorilla-test.core
  :target-path "target/%s"
  :repositories [["Atilika Open Source repository" "http://www.atilika.org/nexus/content/repositories/atilika"]]
  :plugins [[lein-gorilla "0.3.3"]]
  :profiles {:uberjar {:aot :all}})

ここで重要なのはdependenciesとpluginsです。
dependenciesに利用するライブラリを記載します。
ここに記載されたライブラリをleiningenが
依存関係などを自動解決してインストールしてくれます。
ここではincanterとincanter-gorilla、clj-ml、そしてkuromoji*1
という形態素解析器を指定しています。
形態素解析器についてはこちらをご覧ください。
pluginsにlein-gorillaを記載し、gorillaを使えるようにしています。*2

これらが全部終わったら
lein gorilla
コマンドプロンプトに打ち込むと
Running at http://localhost:xxxxx/worksheet.html
とURLが表示されるので、そのアドレスをブラウザで見ると
Clojure実行環境が表示されます。
f:id:AntiBayesian:20140907202323j:plain

なお、アドレスのxxxxxの部分はランダムで決まりますが、ipやportを指定したい場合は
lein gorilla :ip xxxxx :port yyyyy
とします。詳細なオプションは下記に記載されています。
http://gorilla-repl.org/configuration.html

動作するか確かめてみましょう。
f:id:AntiBayesian:20140907202437j:plain
表示された画面の灰色のブロックに
(+ 1 2 3)
と打ち込み、ShiftとEnterキーを同時押しすると処理が実行されます。
実行結果が6になったら成功!
これにてGorillaの設定作業は終わりです!お疲れ様でした!

Gorillaで統計量を求めてみる

(use '(incanter core stats charts datasets)) ;統計解析ライブラリIncanterを使う指定
(def iris (get-dataset :iris)) ;サンプルデータセットの中からirisを選択し、その内容をirisに束縛
iris ;irisの中身を見てみる。
; :column-names から[:Sepal.Length :Sepal.Width :Petal.Length :Petal.Width :Species]
; という列があることが分かる。
; irisとはあやめのデータで、sepalはがく、petalは花びら、speciesはあやめの種類である
(mean ($ :Petal.Length iris)) ;花びらの長さの平均値を求める
(sd ($ :Petal.Length iris)) ;花びらの長さの標準偏差を求める

f:id:AntiBayesian:20140907202646j:plain

Gorilla上で可視化してみる

(use '(incanter core stats charts datasets))
(use 'incanter-gorilla.render) ;Incanterで可視化したモノをGorilla上で表示するライブラリを指定

(def iris (get-dataset :iris))
(chart-view (histogram :Petal.Width :data iris)) ;chart-view関数でヒストグラムをGorilla上に表示
(view (histogram :Petal.Width :data iris)) ;view関数でヒストグラムをGUIで表示

f:id:AntiBayesian:20140907202748j:plain
こちらに掲載した可視化のviewの部分を
chart-viewに置き換えれば、全てブラウザ上で実行可能です

Gorilla上で機械学習してみる

(use 'clj-ml.classifiers 'clj-ml.utils 'clj-ml.io 'clj-ml.data)
; weka特有のデータ形式arffを利用します。
(def iris  (-> (load-instances :arff "http://repository.seasr.org/Datasets/UCI/arff/iris.arff") (dataset-set-class :class)))
; サーバ上にあるCSV形式を読み込む場合は次のようにします。
; (def iris  (-> (load-instances :csv "iris.csv") (dataset-set-class :class)))
; 決定木(c4.5)を作ってみる
(def decision-tree (-> (make-classifier :decision-tree :c45) (classifier-train iris)))
; 作成した決定木の中身を見てみます
decision-tree
;クロスバリデーションに掛けてみます
(def decision-tree-evaluation (classifier-evaluate decision-tree :cross-validation iris 10))
;クロスバリデーションの結果を確認します
(println (:summary decision-tree-evaluation))
(println (:confusion-matrix decision-tree-evaluation))

f:id:AntiBayesian:20140907203011j:plain
f:id:AntiBayesian:20140907203017j:plain
こちらに掲載した機械学習手法も
全てブラウザ上で実行可能です。RandomForestやK-meansなどもお試しください。
但し、ファイルを読み込む際、サーバ上でGorillaを立ち上げている場合はファイルパスにご注意ください。
ローカルファイル指定は自分の端末ではなくサーバ上で探索します。

Gorilla上で形態素解析してみる

(import [org.atilika.kuromoji Token Tokenizer])
(def tokenizer (.build (Tokenizer/builder)))
(defn ma [sentence] 
  (doseq [^Token token (.tokenize tokenizer sentence)] 
      (println (str (.getSurfaceForm token) "\t" (.getAllFeatures token))))) 
(ma "あきつ丸改二で大発動艇欲しい")
; (ma "あきつ丸改二で大発動艇欲しい")
; の形態素解析結果が望みの結果になってないので
; 形態素解析用の辞書を整備する必要がある

; 以下の内容を記載したファイルを用意し、適当なファイル名を付ける
; ここでは一旦userdict.txtと名付ける。
; -- ファイル内容ここから -- 
; 大発動艇,大発動艇,ダイハツドウテイ,固有名詞
; あきつ丸改二,あきつ丸 改二,アキツマル カイニ,カスタム名詞
; -- ファイル内容ここまで -- 
; 1列目:表層, 
; 2列目:1列目をどう区切るか。区切りたい位置に半角スペースを入れる
; 3列目:読み(カタカナ)
; 4列目:品詞情報
; ※windowsの場合はファイルのエンコードをSJISにする必要有り
; ファイルをc:\\lein\\gorilla\\userdict.txtに置いたとした場合、
; 以下のようにすると望みの結果が得られる。

(def dic "c:\\lein\\gorilla\\userdict.txt")
(def tokenizer (.build (.userDictionary (Tokenizer/builder) dic)))
(defn ma [sentence] 
  (doseq [^Token token (.tokenize tokenizer sentence)] 
      (println (str (.getSurfaceForm token) "\t" (.getAllFeatures token))))) 
(ma "あきつ丸改二で大発動艇欲しい")

f:id:AntiBayesian:20140907203143j:plain
f:id:AntiBayesian:20140907204502j:plain
形態素解析の結果を用いてワードカウントなどをする場合は
こちらをご覧ください。
ここで紹介している内容もブラウザ上で実行可能です。

終わりに

ブラウザ上で様々な分析が出来る便利な時代になってきました。
外出先の貧弱な端末や回線からでも、インタラクティブなデータ分析が可能になり、
例えばMTGの最中に「あのデータってどうなってるんだっけ?」
って話題に上がったその場ですぐ集計・可視化して確認出来るなど夢が広がりまくりです。

実務上データ分析しててかなり面倒だと思うのがデータサイズです。
数十GB単位になるとファイルの移動だけで面倒なことになりますし、
ローカルに持ってくるとそれだけでSSD空き容量死ぬケースがあります。

あと分析ツールの更新管理は多分皆さんが想像するのの27倍ダルいです。
一つのユースケースとして想像して欲しいのですが、
分析チームに30人くらい人がいて、
そのうちの半分がコンサルやマーケター的な方、
残り半分がエンジニアだったとしましょう。
大体前半の方々は「更新したのでアップデートしてくださーい」
とアナウンスしても、面倒なので無視します。
だって現状で使えてるもん。慣れてるのが一番だよね!
大体後半の方々は「更新したのでアップデートしてくださーい」
とアナウンスすると、既に自力拡張してるので、
それが動かなくなると嫌だから無視します。
こういう時システム提供側が取り得る選択肢は
更新しない同僚をハンマーで一人ずつ殴って回るか
あるいはブラウザで実行できるようにするかです。
非常に僅差ですが二個めの方がまだ若干マシかなと思わなくもないです。

ブラウザ上での分析環境構築は、
同時接続数とか認証周りとかにまだまだ機能不足や問題があります。
これはClojure/Gorillaだけではなく、
RのShinyやPythonのIPython notebookでも同じ問題は生じています。*3
とはいえ、ブラウザ上で分析可能なメリットは結構大きい上に、
Clojure/GorillaはJVM環境で簡単に構築できるツールなので
是非チャレンジしていきたい+皆さんもチャレンジして欲しいなと思います。
f:id:AntiBayesian:20140907214043p:plain
それでは皆様、よきGorillaライフを!!

素敵なLispエイリアンイラストをマルタコEX@marutakoEX様からお借りしました!
マルタコEX様、利用のご快諾、誠に有難うございます!!!

*1:ちなみにここで指定しているのは最新版ではありません。これは単に私が今まで利用してて動作が理解出来てるというので、今回も惰性で指定しているだけです。Lucene Kuromojiの最新版を使う方が良いです

*2:私は「何でこれがdependenciesで何でこれがpluginsなの?」っていうのがいまいちわかってないんですが、その切り分けをご存知の方いらっしゃったら教えてください…。まぁとりあえず公式サイトの指示に従って設定すれば動きます

*3:IPython notebookは一応パスワード設定出来ますが、ここでいう認証は「Aさんはこのデータとこのデータにアクセス出来て、Bさんはさらにあのデータにもアクセス出来て、あ、Cさんは異動したからこのデータへのアクセス禁止して」とかやりたいって意味です。まぁ他で認証機能別途付けろと言われたらそりゃそうなんですが