AWSのlambda上でMeCabを実行する (他のバイナリへも応用可)!!
日本語の解析をする場合、とりあえず形態素解析を実施することになると思います。
手っ取り早く動かすのであれば、ローカルで動作させれば良いですが、サーバ側で処理することも多いかと思います。
AWS lambda + APIGateway で動作させることが出来れば、便利なので、その事前準備として、lamdab上で形態素解析を行うと言う話です。
日本語の形態素解析といえば、MeCabが定番なのではないでしょうか。
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
ただ、mecabはc/c++で実装されており、lambdaで動作させるためには、lambdaで利用されているCPUが解釈できるバイナリを準備する必要があります。
lambdaが動作する環境は、AMIとしてAWSが公開しています。
Lambda 実行環境と利用できるライブラリ - AWS Lambda
要は、このAMIからEC2インスタンスを一旦起動し、立ち上げたEC2以上でmecabをバイナリへコンパイルし、その成果物をlambdaで利用すれば良いことになります。
また、lambdaでプログラムを実行した場合、/var/task配下で実行されることになります。
今回は、/var/task/mecab配下へmecabのバイナリや辞書をおくことにします。
以下に具体的な手順を示します。
- lambdaのAMIからEC2インスタンスを起動します。
- ログイン後、取り敢えず環境をアップデートしておきます。
- sudo yum update
- Mecabのコンパイルに必要なソフトウェアをインストールします。
- MeCabのダウンロード (ここはEC2内でも外でも大丈夫です。適宜DLしてEC2インスタンスへ持っていきましょう。)
- MeCabをインストールするためのディレクトリを作成します。
- MeCabのビルド
- IPAdicのビルド
- できたバイナリ(/var/task/mecab配下)を、一旦ローカルのコンピュータへコピーしておきます。
- 確認します。
あらゆる 連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル 現実 名詞,一般,*,*,*,*,現実,ゲンジツ,ゲンジツ を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 全て 名詞,副詞可能,*,*,*,*,全て,スベテ,スベテ 自分 名詞,一般,*,*,*,*,自分,ジブン,ジブン の 助詞,連体化,*,*,*,*,の,ノ,ノ 方 名詞,非自立,一般,*,*,*,方,ホウ,ホー へ 助詞,格助詞,一般,*,*,*,へ,ヘ,エ ねじ曲げ 動詞,自立,*,*,一段,連用形,ねじ曲げる,ネジマゲ,ネジマゲ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ の 名詞,非自立,一般,*,*,*,の,ノ,ノ だ 助動詞,*,*,*,特殊・ダ,基本形,だ,ダ,ダ 。 記号,句点,*,*,*,*,。,。,。 EOS
lambda上での実行
- AWS lambda functionを作成します。
- 作成手順は、AWSの公式ドキュメントなどを参考にしてください。
- 作成したlambda functionのコードをローカルへコピーします。
- index.jsを編集します。
- サンプルを下の方に置いておきます。
- ファイルをlambdaへ転送します。
- 実行する
- 今までの手順が正しくできていると、以下の結果がlambdaのログに吐かれていると思います。
- 「あらゆる 連体詞,*,*,*,*,*,あらゆる,アラユル,アラユル」
- 今までの手順が正しくできていると、以下の結果がlambdaのログに吐かれていると思います。
- 以下にindex.jsのサンプルを置いておきます(後述の注意事項も合わせて参照願います)。
'use strict'; const exec = require('child_process').exec; const fs = require('fs') exports.handler = (event, context, callback) => { /* OSコマンドインジェクションは避けたいので、 以下のように、変動要素(解析文字列)を渡すのは危険 const cmd = 'echo "あらゆる" | /var/task/mecab/bin/mecab' */ fs.writeFileSync("/tmp/input.txt","あらゆる"); const cmd = '/var/task/mecab/bin/mecab /tmp/input.txt'; /* lambda実行時は、/tmp配下以外は書き込めないので注意 */ exec(cmd , (err, stdout, stderr) => { if (err) { console.error(err); callback(null, "error"); return; } console.log(stdout); //ここでmecabの実行結果が表示される。 callback(null, "done"); }); };
- 注意事項
execコマンドは、便利ですが、最終的に sh -c "/var/task/mecab/bin/mecab" と言うように入力がシェルに渡ります。
つまり OS コマンド インジェクションに注意しないといけません。
今回は、固定文字列(「あらゆる」)なので問題ないですが、実際は、任意の文字列を渡したいと思います。
その場合、外部からの入力がコマンドラインのパラメータに渡らないように注意してください。
例えば、形態素解析したい文字列は、ファイルへ書き出しておき(input.txt)、execコマンドで実行するのは、「mecab input.txt 」と言うように固定してしまうことです。
もちろん、正攻法で、サニタイズするのもありです。
mecabを実行しようとしてエラーが出た場合
- lambdaを実行しようとして、libmecab.so.2 がない(no such file or directory)と言って怒られた場合
- それは、EC2で、make installした時に、しれっとリンク (ln -s -f libmecab.so.2.0.0 libmecab.so.2) が作成されていたのに、lambdaでは作成していないからです。