PolyglotとGoogle Cloud Functionで形態素解析apiを作る

Thursday, March 21, 2019

現在大学の授業で行なっているプロジェクトで英語の文章を形態素に分割することがあったため、pythonのpolyglot を使用し、ついでにgoogle cloud functionでベータ版として提供されているpythonランタイム で形態素解析apiを作成しました。

Polyglotとは

Polyglotは英語で多言語話者のことを指すのですが、pythonのpolyglot はその名の通り多数の自然言語を処理するツールを提供するパッケージです。

GoogleのCloud Natural Language Api の構文解析のように文章を品詞分解してくれるパッケージはよくあるのですが、polyglotのように英語の文章を形態素に分解する機能があるものは珍しいのではないでしょうか。

👁️形態素(英: morpheme)とは、言語学の用語で、意味をもつ表現要素の最小単位のことを指します。

形態素解析

Polyglotには様々な機能がありますが、今回は形態素解析だけを使用します。

例えば、以下のコードで"This is a sample text"というテキストを形態素に分解すると、

from polyglot.text import Text, Word

text = "This is a sample text"
words = text.replace(",", "").split()

for w in words:
    word = Word(w, language="en")
    print(word.morphemes)

以下のように一つ一つの単語を形態素に分解した結果が得られます。

['Thi', 's']
['is']
['a']
['s', 'ample']
['text']

API作成

今回はこのpolyglotの機能をフロントのjavascriptで利用したかったため、google cloud functionのpythonランタイムでpolyglotを使って形態素解析apiを作成しブラウザからアクセスできるようにしました。

以下が作成したコードです。

from polyglot.text import Text, Word
from polyglot.downloader import downloader
from flask import Flask, jsonify, request, Response
import simplejson as json

def parse_morpheme(request):
    request_json = request.get_json()
    response = Response()
    response.headers.add('Access-Control-Allow-Origin', '*')
    response.headers.add('Access-Control-Allow-Methods', 'POST')
    try:
        if request.method == 'OPTIONS':
            headers = request.headers.get('Access-Control-Request-Headers')

            if headers:
                response.headers['Access-Control-Allow-Headers'] = headers
                response.status_code = 200
                return response

        if request_json and 'text' in request_json:
            downloader.download("morph2.en")
            text = request_json['text'].replace(" ", "")
            parsedText = Text(text)
            parsedText.language = "en"
            response.set_data(json.dumps({ "result": parsedText.morphemes }))
            response.status_code = 200
            return response

        else:
            response.set_data(json.dumps({ "error": 'invalid request' }))
            response.status_code = 400
            return response

    except:
        response.set_data(json.dumps({ "error": 'internal server error' }))
        response.status_code = 500
        return response

以下のrequest methodがOPTIONSか判定するブロックで、CORSに対応しています。

if request.method == 'OPTIONS':
  headers = request.headers.get('Access-Control-Request-Headers')

  if headers:
    response.headers['Access-Control-Allow-Headers'] = headers
    response.status_code = 200
    return response

テスト

書いた関数を毎度デプロイしてテストするのも時間がかかるので、以下のようにflaskを使用してlocalで先ほど定義したparse_morpheme関数をテストします。

from flask import Flask, jsonify, request, Response
from main import parse_morpheme

if __name__ == "__main__":
    app = Flask(__name__)

    @app.route('/', methods=['POST'])
    def index():
        return parse_morpheme(request)

    app.run('127.0.0.1', 8000, debug=True)

上のコードをpythonコマンドで実行し、“this is an apple"をデータにhttp://127.0.0.1:8000/へPOSTリクエストを送信すると、

curl -X POST --data '{"text":"this is an apple" }' -H "Content-Type: application/json" http://127.0.0.1:8000/ | jq .

以下のように結果が返ってきました。良さそうですね。

{
  "result": [
    "th",
    "is",
    "is",
    "an",
    "apple"
  ]
}

参考記事

デプロイ

parse_morpheme関数を以下のコマンドでgcpにデプロイします。

gcloud functions deploy parse_morpheme --runtime python37 --trigger-http

結果

試しに、“this is an apple"というテキストをparseしてみると、

curl -X POST --data '{"text":"thisisanapple" }' -H "Content-Type: application/json" https:cloud-functions-endpoint/parse_morpheme | jq .
{
  "result": [
    "th",
    "is",
    "is",
    "an",
    "apple"
  ]
}

とリスポンスが返ってきました。うまく行ってそう。

感想

普段cloud functionはjavascriptで書いてますが、pythonの方が数倍楽に書ける気がしました。同期処理がdefaultの言語は直感的で良いですね。

programmingpolyglotpythongoogle-cloud-functions

Seita Uchimura

Software Engineer in Tokyo

PuppeteerをFirebase Cloud Functionsで動かす

Firebase Cloud Functionsの環境変数にJSONをサクッと設定する方法