Python用機械学習ライブラリscikit-learnと形態素解析IgoをHeroku上で動かしてみる
動機
Python用の機械学習ライブラリscikit-learnは機械学習がさっぱりわからない私のような素人にも機械学習が使えるというすごいライブラリだ。正直大学時代に機械学習概論の単位を落としかけて以来、あまり寄り付かないようにしていたのだけれど、データサイエンティストっていうのは話しによればお金になるらしい。というわけでそういうスケベゴコロ満開で機械学習ライブラリに何かデータを分析させてみようと思う。
今回のモチベーションは
http://blog.parosky.net/archives/2212
でツイートの文章クラスタリングを見たので、ここのところを2chのレスに変えてやってみよう、というだけの話。それだけでは華がないので、PaaSサービス上で動かしてみたいな、というところ。つまり今回目指すのは
- scikit-learnを使っての文章クラスタリング
- scikit-learnに文章を渡すための前段階処理として形態素解析Igo-pythonを使う
- そのアプリケーションをPaaS上にアップロードしてウェブから見えるようにする
という感じだ。
PaaS選び
まずPythonアプリケーションのデプロイ先として候補に上がるのは当然Google App Engineなのだけど、残念ながらscikit-learnは複数のライブラリ、しかもCへの依存が強いScipy/Numpyに依存しているので、動かすことはできないようだ(Is it possible to run scikit-learn on Google App Engine? - Stack Overflow)。
次に上がるのは前回アカウントを開けてWai+WarpのHaskellウェブアプリをデプロイしたOpenShiftだ。
だけど、ここでいろいろ試したのだけどうまくいかない。scikit-learnは動かないようだ。なのでHerokuを試してみる。Herokuなら動作報告もあるみたいだし、できるはず!
普通のPython+DjangoアプリケーションをHerokuにデプロイする
まず最初はひな形になるプレーンバニラのPython+DjangoウェブアプリをHeroku上にデプロイする。まずはHerokuでアカウントを取得して、HerokuのPythonチュートリアル通りにDjangoアプリケーションをデプロイする。 Getting Started with Python on Heroku | Heroku Dev Center
$ heroku login Enter your Heroku credentials. Email: <メールアドレスを入力> Password (typing will be hidden): Authentication successful.
チュートリアルと違ってsshキーをアップロードする必要があるので、アップロードする。終わるとメールが来る Managing Your SSH Keys | Heroku Dev Center
この通りにsshキーをインストールしないとgit pushができないのでそこで気づくはず。 テンプレートプロジェクトをクローンする。
git clone https://github.com/heroku/python-getting-started.git mv python-getting-started <自分の好きなディレクトリ名> cd <上でつけたディレクトリ名>
このディレクトリ内でプロジェクトを作成する。
heroku create
すると適当名前がついたプロジェクトが作成される。好きな名前をつけてもいいが、だいたい取られてる気がする。
git push heroku master
これでとりあえずコードをプッシュしてプロジェクトを動かしてみる。
heroku ps:scale web=1
プロジェクト作成の時点でDyno(実行単位)が割り当てられているはずだけど、念の為に1つ割り当てておく。1つのDynoを1ヶ月動かせる分だけが無料分に含まれているので、Dynoは1つにしておく。それ以上割り当てて1ヶ月まるまる走らせると無料分を超えてしまうようだ。
heroku open
でブラウザにデプロイしたウェブページが開く。ここでエラーが出ないことを確認しよう。
2ch.scを日本語データのソース先として取り込めるようにする
転載の問題とかあるけどとりあえず2ch.scをデータソースとして使ってみる。心配な人はオープン2ちゃんねるを使うのが良いだろう。先に上げた先人のようにツイートを使うのも良いかもしれない。とにかくたくさん日本語の文章があればよい。
def get_with_url(url): page_str = fetch_with_url(url) lines = page_str.split('\n') responses = [] i = 0 for line in lines: i += 1 try: [name, mail, date_time_id, message, smth] = line.split('<>') thread = Response(i, name, mail, date_time_id, message) responses.append(thread) except: pass (a, _) = get_default_name_in_thread(responses) for res in responses: if res.name == a: res.name = '' return responses def fetch_with_url(url): # if not ending "subject.txt", add logging.info('fetching thread : ' + url) try: result = requests.get(url) logging.info('result code : ' + str(result.status_code)) if result.status_code == 200: ret = result.text else: ret = "Error: " + str(result.status_code) return ret except: logging.warning('Unexpected exception occurred')
という感じ。特別なところは特にないと思う。エラー処理は雑なので、適宜足してください。URLのフェッチには人間のためのHTTPを使っている。
Igo-Pythonをインストールして形態素解析を試してみる
まずは最初の肝の部分、Igo-Pythonを使って形態素解析をしてみる。これは簡単で、requirements.txtに
igo-python==0.9.3
をたしてgit pushすると使えるようになっているはずです。
__author__ = 'yoshinori' from igo.Tagger import Tagger def get_tagger(): return Tagger('lib/igo/naist-jdic') def analyzer(text): ret = [] t = get_tagger() l = t.wakati(text) for m in l: ret.append(m) return ret class BagsOfWords(object): def __init__(self, text): self.words = analyzer(text) def __str__(self): ret = '' for s in self.words: ret += s + ', ' return ret
という感じで使えるはず。
という感じにして、naist-jdicを配置してコミットしておく必要がある。
scikit-learnとscipyとnumpyをインストールして機械学習をさせてみる
同様にrequirements.txtに必要なライブラリを指定してコミットしてプッシュすることでインストールできる。
numpy==1.7.0 scipy==0.11.0 scikit-learn==0.13.1
で、書いたコードは以下の様な感じです。主にscikit-learnのサンプルのパクリです。パクリ元は
Clustering text documents using k-means — scikit-learn 0.13.1 documentation
で、実際にコードはこんな感じです。
実際に動かしてみよう!
2014-09-28T07:38:33.817668+00:00 heroku[router]: at=info method=GET path="/thread?server=ai&board=newsplus&dat=1411886294.dat" host=obscure-shelf-3820.herokuapp.com request_id=6813cf4b-1ec2-4859-a357-955a681beba0 fwd="1.75.196.170" dyno=web.1 connect=0ms service=393ms status=200 bytes=180695 2014-09-28T07:38:35.330096+00:00 app[web.1]: 2014-09-28 07:38:35 [INFO] fetching thread : http://ai.2ch.sc/newsplus/dat/1411886294.dat 2014-09-28T07:38:35.614954+00:00 app[web.1]: 2014-09-28 07:38:35 [INFO] result code : 200 2014-09-28T07:38:35.626421+00:00 app[web.1]: 2014-09-28 07:38:35 [INFO] Done in 0.0 sec(s) 2014-09-28T07:38:35.626462+00:00 app[web.1]: 2014-09-28 07:38:35 [INFO] Feature extraction... 2014-09-28T07:38:35.626211+00:00 app[web.1]: 2014-09-28 07:38:35 [INFO] Start analyzing... 2014-09-28T07:39:05.327304+00:00 heroku[router]: at=error code=H12 desc="Request timeout" method=GET path="/alzthread?server=ai&board=newsplus&dat=1411886294.dat" host=obscure-shelf-3820.herokuapp.com request_id=7aff11ad-e1ee-4430-b3f3-34d412b86bde fwd="1.75.196.170" dyno=web.1 connect=3ms service=30001ms status=503 bytes=0
クソワロタ。タイムアウトしてますね。Herokuのリクエストラウターはタイムアウト時間で強制的にタイムアウトさせてしまうので結果が見えない。ローカルで確認したところ5分位で結果返してくれるので、動くのは動くみたいだけど、タイムアウトしちゃうのでサーバー上だと使いものにならないみたいだ。
終わりに
非同期に計算できるようにAjaxかなんかでフロントエンドを書き直すよ。全部きれいにしてGithubに公開したりウェブアプリとして公開できるようにいつの日かしてみます、気が向いたら。
動作写真
2015-02-09 追記
レス数の少ないスレッドだと動作する模様です(目安は200以下)。デモは以下のリンクで見られます。
https://obscure-shelf-3820.herokuapp.com/board?server=ai&board=newsplus
参考文献
http://blog.parosky.net/archives/2212
4.3. Clustering — scikit-learn 0.11 documentation
http://www.cs.kent.edu/~jin/DM08/ClusterValidation.pdf
Clustering text documents using k-means — scikit-learn 0.13.1 documentation