GCP Cloud FunctionsでMeCab + Neologdの分かち書きAPIをデプロイする(Python)


背景

Neologdの辞書が重すぎて、Cloud Functionsに乗らない。
でも、サーバ料金を押さえたいので、なんとかして乗せたい。お金ない。

・環境
Mac macOS Mojave(10.14.2)

・Cloud Functions
言語はPython3、トリガーはhttpです。

参考記事

方針

参考記事に書かれているように、neologd辞書を--eliminate-redundant-entryで縮小し、zip化することで100MB以下に納め、デプロイするという作戦です。

--eliminate-redundant-entry オプションとは
mecab-ipadic-NEologd

"--eliminate-redundant-entry" オプションを指定した場合は、正規化済みの日本語テキストを単語分割するための別称・異表記・表記揺れなどを一切考慮できない辞書を、512MByte 程度の空きメモリ領域があればインストールできます。

Functionのデプロイ方法

  1. APIのコードとneologd辞書をzip化(mecab.zip)してCloud Storageに配置
  2. Storageのパスを指定してFunctionを作成します

デプロイコマンドはこんな感じ。

gcloud beta functions deploy mecab \
  --source=gs://<バケット名>/mecab.zip \
  --stage-bucket=<バケット名> \
  --trigger-http \
  --memory=512MB \
  --runtime=python37 \
  --region=asia-northeast1 \
  --project=<プロジェクト名>

mecab.zip

mecab.zipは下記ファイルとフォルダを一つのzipにしたものです。
このmecab.zipは、上記デプロイコマンドを打つ前にStorageにデプロイしておきます。

$ tree
.
├── main.py  # Functionのコード
├── neologd-light  # 辞書
│   ├── char.bin
│   ├── dicrc
│   ├── left-id.def
│   ├── matrix.bin
│   ├── pos-id.def
│   ├── rewrite.def
│   ├── right-id.def
│   ├── sys.dic
│   └── unk.dic
└── requirements.txt

下記コマンドでmecab.zipを作成します。

$ zip -r mecab.zip *
$ tree
.
├── main.py
├── mecab.zip  # 作成されたmecab.zip
├── neologd-light
│   ├── char.bin
│   ├── dicrc
│   ├── left-id.def
│   ├── matrix.bin
│   ├── pos-id.def
│   ├── rewrite.def
│   ├── right-id.def
│   ├── sys.dic
│   └── unk.dic
└── requirements.txt

各ファイルの説明

neologd-light(辞書)

Mac上でMeCabとNeologdをインストールし、作られた辞書データを使用します。
環境依存だと思っていたのでダメ元だったのですが、Cloud Functions上でそのまま動作しました。

ここでは、MeCabのインストール方法は割愛させてください。

Neologdを--eliminate-redundant-entryオプション付きでビルドします。

$ 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-light -n --eliminate-redundant-entry

ホームディレクトリにneologd-lightが作成されました。

main.py

import os
import json
import MeCab

# MeCab準備
# リクエスト毎ではなく、インスタンスが立った時にだけ読み込まれる
neologd_path = os.path.join(os.path.abspath(
    os.path.dirname(__file__)), "neologd-light")
mcb = MeCab.Tagger('-d ' + neologd_path)
mcb.parse('')


def mecab(request):
    if request.method == 'GET':
        if request.args and 'text' in request.args:
            text = request.args.get('text')
            parsed_text = mcb.parse(text)
            return json.dumps({'p_text': parsed_text}, ensure_ascii=False)
        else:
            print('No text.')
            return ('Bad Request', 400)

requirements.txt

mecab-python3==0.996.2