bert-as-serviceを使って日本語の文章を埋め込み表現ベクトルに変換してみる

学習済みの汎用言語表現モデルBERTを使って文章を埋め込み表現(数値ベクトル)に変換することができるbert-as-serviceを触ってみました。

テキストを埋め込み表現に変換するサーバーモジュールを用意するのは、BERTのモデルを読み込んで、必要なレイヤーを抜き出し、テキストをトークン化して入力して、処理を順次捌くためにキューに渡して、複数のワーカープロセスを制御して...と結構大変です。bert-as-serviceはこういった処理をラップしてくれているので、専用のサービスを起動して、クライアントモジュールからリクエストを投げるだけでテキストの埋め込みが出来ます。簡単。

ではbert-as-serviceを使った文章の埋め込み表現への変換について紹介していきます。

BERT(Bidirectional Encoder Representations from Transformers)

BERTはGoogleが開発した汎用言語表現モデルです。機械学習で言語表現を学習させることができるため、言語に関わる様々な問題に使う事ができます。 今回はモデルの構造や学習の方法といったBERT自体には触れません。興味のある方は以下の解説記事や論文が参考になると思います。

bert-as-service

bert-as-serviceは、BERTを文章の埋め込み表現を獲得できるサービスとして使う事ができ、以下の特徴があります。

  • 🔭 State-of-the-art: build on pretrained 12/24-layer BERT models released by Google AI, which is considered as a milestone in the NLP community.
  • 🐣 Easy-to-use: require only two lines of code to get sentence/token-level encodes.
  • ⚡️ Fast: 900 sentences/s on a single Tesla M40 24GB. Low latency, optimized for speed. See benchmark.
  • 🐙 Scalable: scale nicely and smoothly on multiple GPUs and multiple clients without worrying about concurrency. See benchmark.
  • 💎 Reliable: tested on multi-billion sentences; days of running without a break or OOM or any nasty exceptions.

といった感じです。紹介した特徴の中で言及されているベンチマークはこちらです。

そのほかにも次のような特徴があります。

  • サーバーとクライアントに分かれている
  • 基本はpython3.xに対応していて、クライアントサイドのみpython 2.xにも対応
  • zeromqをバックエンドとして使用
  • CPUにも対応

イメージ

ざっくりとした動作イメージは、学習済みのBERTのモデルを読み込ませたサーバーを動かし、変換したい文章をサーバーにリクエストとして投げる事で、その文章の埋め込み表現がレスポンスとして返ってくる、という感じです。

※ テキストのトークン化は自分で行うことも可能です。

使ってみる

bert-as-serviceを使ってBERTのモデルで文章を埋め込み表現に変換し、t-SNEで3次元に圧縮した埋め込み表現を可視化してみます。

モデルのダウンロード

BERTのリポジトリに記載されてるURLから使いたい事前学習済みモデルをダウンロードします。

今回はベースサイズの多言語対応モデルを使用します。

BERT-Base, Multilingual Cased (New, recommended): 104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters

ダウンロードしたモデルはzip形式で圧縮されているので、圧縮し、使いたい場所に移動させます。

サーバーの起動

bert-as-serviceのサーバーを起動します。

  • model_dir: ホストするモデルが配置されたフォルダのパス
  • num_worker: ワーカープロセスの数

その他の引数や詳細についてはドキュメントをご覧ください。

bert-serving-start -model_dir ./models/multi_cased_L-12_H-768_A-12 -num_worker=2

起動すると色々表示されますが、次のように表示されると入力を受けつける準備が出来ていそうです。

文章を埋め込み表現に変換

bert-as-serviceクライアントを使って、文章を埋め込み表現に変換します。

今回変換する言葉は次の通りです。

  • こんにちは
  • おはよう
  • こんばんは
  • お腹すいた
  • ご飯食べたい
  • 明日晴れてると良いな
  • 明日の天気はどうだろうか
  • 雨降ったら嫌やな

まずテキストを先ほどの起動したbert-as-serviceのサーバーに投げて、埋め込み表現を受け取り、csvに書き出すスクリプトを作成します。

from bert_serving.client import BertClient
import numpy as np

text_list = [
    'こんにちは',
    'おはよう',
    'こんばんは',
    'お腹すいた',
    'ご飯食べたい',
    '明日晴れてると良いな',
    '明日の天気はどうだろうか',
    '雨降ったら嫌やな'
]

with BertClient(port=5555, port_out=5556) as bc:
    text_vecs = bc.encode(text_list)

np.savetxt('text_vecs.csv', text_vecs, delimiter=',')

めっちゃ簡単です。

作成したスクリプトを実行します。テキストが埋め込み表現に変換されて、csvに吐き出されます。 今回はベースモデルを使用しているため、MacBook Proで動かしても一瞬で処理が終わりました。

python encode_text.py

一行だけ確認してみます。

head -1 text_vecs.csv

何か数値列がちゃんと入ってるってことだけはわかりました。

可視化

埋め込み表現ベクトルそのままでは何も分からなかったので、Embedding Projectorで可視化してみたいと思います。

元々のベクトルは768次元あるので、t-SNEという次元圧縮法を使って3次元に圧縮したベクトルを描画します。各点が一つの文章になっています。 "明日の天気はどうだろうか"という文章の周辺には天気に関する文章があることが分かります。同じ天気の話題は近くに来るという直感的なイメージと合いますね。

"おはよう"という言葉の近くには"こんにちは"といった挨拶系ではなく、"ご飯食べたい"と"お腹すいた"という2つの文章が並んでいます。こちらはよくわからないですね。 今回使ったモデルが使用したテキストデータセット(wikipedia)に、朝はお腹が減っていることを意味した文章が含まれていたんでしょうか...?

さいごに

bert-as-serviceを使う事でBERTを用いた文章の埋め込みが簡単にできました。 bert-as-sercieをラップしたコンテナイメージを作成し、Amazon SageMakerのエンドポイントとして起動させる事でアプリケーションのバックエンドに組み込んだり、パイプライン処理に埋め込んだりできそうです。また試してみたいと思います。

お読みくださり、ありがとうございました〜!