この記事はNextremer Advent Calendar 2018の2日目の記事です。
表題の通り、mecab-ipadic-NEologd を AWS Lambda で使えるようにしたという内容です。
困難
- NEologd 辞書が標準で 900 MB 程度ある
- Lambda にはデプロイパッケージを zip で上げることが可能であるが、unzip 後、250 MB 以下でなければいけないという制限がある
- Lambda には実行時、一時ファイル領域(/tmp)が用意されているが、容量 512 MB までである
- NEologd を縮小版(
--eliminate-redundant-entry
オプション)で生成しても 400 MB 程度になる
既存のソリューション: s3 に辞書を置いておく
こちらに、s3 使った方法がまとまっています。
https://speakerdeck.com/satorukadowaki/aws-apigateway-plus-python-lambda-plus-neologddezuo-rusabaresuri-ben-yu-xing-tai-su-jie-xi-api
が、この記事では違う方法を取ります。
この記事でのやりかた
縮小版 neologd 辞書を zip で固めると 100MB 以下になるため、デプロイパッケージに含めることができると考えました。
手順
デプロイパッケージはAmazon が公開している Lambda 用の AMI上で作ります。
(この記事執筆時点で amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2
を使いました。)
EC2 インスタンスを上記の AMI で立ち上げます。
以下は EC2 インスタンス上での作業になります。
いろいろインストール。
$ sudo yum install gcc gcc-c++ git patch
$ sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
# 以下は NEologd インストール時に必要
$ sudo yum install perl-App-cpanminus
$ sudo cpanm autodie
MeCab をビルド。
$ mkdir ~/mecab-service
$ curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE" -o mecab-0.996.tar.gz
$ tar zxvf mecab-0.996.tar.gz
$ cd mecab-0.996
$ ./configure --prefix=$HOME/mecab-service/local --enable-utf8-only
$ make
$ make install
mecab-ipadic もビルド。
$ cd ~
$ curl -L "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM" -o mecab-ipadic-2.7.0-20070801.tar.gz
$ tar zxvf mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ export PATH=$HOME/mecab-service/local/bin:$PATH
$ export LD_LIBRARY_PATH=$HOME/mecab-service/local/lib:$LD_LIBRARY_PATH
$ ./configure --prefix=$HOME/mecab-service/local --enable-utf8-only --with-charset=utf8
$ make
$ make install
mecab-ipadic-NEologd をビルド。
$ cd ~
$ git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ ./bin/install-mecab-ipadic-neologd -y -p $HOME/neologd -n --eliminate-redundant-entry
ここで、正しくビルドできたかチェック。
$ echo 東京スカイツリー | ~/mecab-service/local/bin/mecab -d ~/neologd
東京スカイツリー 名詞,固有名詞,一般,*,*,*,東京スカイツリー,トウキョウスカイツリー,トーキョースカイツリー
EOS
必要なファイルを zip 化します。
$ cd ..
$ zip -r mecab-service/neologd.zip neologd
$ zip -r ~/mecab-service.zip mecab-service
ここからローカルでの作業になります。
mecab-service.zip
を scp でローカルに落としてきてください。
これで EC2 は不要になります。
mecab-service.zip
を解凍してください。
解凍した mecab-service
フォルダに index.js
を追加します。内容は以下の通り。
const exec = require('child_process').exec;
const MeCab = new require('mecab-async');
const fs = require('fs');
const mecab = new MeCab();
mecab.command = '/var/task/lambda_neologd/local/bin/mecab -d /tmp/neologd/';
exports.handler = async (event) => {
try {
fs.accessSync('/tmp/neologd');
} catch(e) {
await new Promise((resolve, reject) => {
exec('unzip -d /tmp/ /var/task/lambda_neologd/neologd.zip', (err, stdout, stderr) => {
if (err) {
console.error(err);
reject(err);
}
console.log(stdout);
resolve(stdout);
});
});
}
const res = mecab.parseSync(normalize("僕の名前はラルセイです。"));
const response = {
statusCode: 200,
body: JSON.stringify(['Hello', res])
};
return response;
};
function normalize(text) {
return text.normalize("NFKC").toLowerCase();
}
そして再び mecab-service
を mecab-service.zip
に圧縮してください。
zip を s3 へ上げます。
AWS Lambda コンソールで新しい関数を追加します。ランタイムは Node.js 8.10
を指定。
関数コードとして s3 上に上げた mecab-service.zip
を指定して保存します。
環境変数を以下のように設定。
LD_LIBRARY_PATH=/var/task/local/lib
MECABRC=/var/task/local/etc/mecabrc
タイムアウトを10秒以上に設定してください。
保存してテストすると、5秒くらい経って成功すると思います。
一度実行すれば、/tmp/
に辞書が展開されるので、2度目からは早いです。
おわりに
これで、s3 に辞書置かずに NEologd を使うことができました。
s3 からの転送は8秒程かかり、unzip は5秒かかるので若干のアドバンテージがあります。といっても、時間がかかるのは初回呼び出しだけですが…。
あと、--eliminate-redundant-entry
オプションで辞書を縮小しているので、表記ゆれが吸収されません。MeCab に掛ける前にテキストの正規化が必要なことに注意してください。
このように NFKC 正規化と小文字化が必要です。
text.normalize("NFKC").toLowerCase();
Lambda で動かす以上、こればかりはどうしようも無いですね…。