この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
木戸です。
前回ご紹介した「AWS Lambda で MeCab を動かす」のエントリーですが、通りすがりの shogo82148 さんに素敵なプルリクエストをいただきましたので、今回はそのご紹介です。
改善点
改善点は以下の2点です。
- 「外部プロセス起動をやめて高速化」
- MeCabは非常に高速なのですが、外部プロセス起動をやめることでさらに高速化しています。
- 「OSコマンドインジェクションの危険性の改善」
- Amazon API Gateway などで外部に公開した場合のOSコマンドインジェクションの危険性を改善しています。
ソースの解説
前回ご紹介したサンプルコードでは、LD_LIBRARY_PATH
を使って、import MeCab
時に libmecab.so
ライブラリを見つけられるようにするため、外部プロセスとして起動していました。(そもそも、Lambda handler 起動する前に LD_LIBRATY_PATH
設定できればこんなことしなくても良いのですが。。)
改善後は、import MeCab
する前に、ctypes.cdll.LoadLibrary
を使って libmecab.so
ライブラリを直接読み込むことで、LD_LIBRARY_PATH
が設定されていなくても MeCab のモジュールとそのライブラリがリンクできるようになっています。そのため上記の改善と、MeCab を実行するモジュールを外部に切り出す必要も無くなり、lambda_function.py
と tokenize.py
モジュールが統合され、コード的にもかなりシンプルになっています!
lambda_function.py(tokenize.py 統合)
# coding=utf-8
import os
import settings
import logging
logger = logging.getLogger(__name__)
logger.setLevel(settings.LOG_LEVEL)
# preload libmecab
import ctypes
libdir = os.path.join(os.getcwd(), 'local', 'lib')
libmecab = ctypes.cdll.LoadLibrary(os.path.join(libdir, 'libmecab.so'))
import MeCab
# prepare Tagger
dicdir = os.path.join(os.getcwd(), 'local', 'lib', 'mecab', 'dic', 'ipadic')
rcfile = os.path.join(os.getcwd(), 'local', 'etc', 'mecabrc')
default_tagger = MeCab.Tagger("-d{} -r{}".format(dicdir, rcfile))
unk_tagger = MeCab.Tagger("-d{} -r{} --unk-feature 未知語,*,*,*,*,*,*,*,*".format(dicdir, rcfile))
DEFAULT_STOPTAGS = ['BOS/EOS']
def lambda_handler(event, context):
sentence = event.get('sentence', '').encode('utf-8')
stoptags = event.get('stoptags', '').encode('utf-8').split(',') + DEFAULT_STOPTAGS
unk_feature = event.get('unk_feature', False)
tokens = []
tagger = unk_tagger if unk_feature else default_tagger
node = tagger.parseToNode(sentence)
while node:
feature = node.feature + ',*,*'
part_of_speech = get_part_of_speech(feature)
reading = get_reading(feature)
base_form = get_base_form(feature)
token = {
"surface": node.surface.decode('utf-8'),
"feature": node.feature.decode('utf-8'),
"pos": part_of_speech.decode('utf-8'),
"reading": reading.decode('utf-8'),
"baseform": base_form.decode('utf-8'),
"stat": node.stat,
}
if part_of_speech not in stoptags:
tokens.append(token)
node = node.next
return {"tokens": tokens}
def get_part_of_speech(feature):
return '-'.join([v for v in feature.split(',')[:4] if v != '*'])
def get_reading(feature):
return feature.split(',')[7]
def get_base_form(feature):
return feature.split(',')[6]
まとめ
AWS Lambda Python で LD_LIBRARY_PATH
の設定が必要な時は、ctypes.cdll.LoadLibrary
で直接読み込んでしまおう!ということで。
Github: aws-lambda-ja-tokenizer マージ済みです。