Ahogrammer

Deep Dive Into NLP, ML and Cloud

Googleが開発した多言語の埋め込みモデル「LaBSE」を使って多言語のテキスト分類

自然言語処理において、テキストをその意味を考慮しつつ固定長のベクトルに変換する埋め込みモデルは重要です。文の意味をよく表現したベクトルを作ることができれば、テキスト分類や情報検索、文類似度など、さまざまなタスクで役立ちます。本記事では、Googleが開発した多言語の埋め込みモデル「LaBSE」を使って、テキスト分類をする方法を紹介します。単なるテキスト分類では面白くないため、学習には英語のデータセットを使い、評価には日本語とフランス語のデータセットを使います。

記事では要点だけを紹介するので、コードについては以下のノートブックを参照してください。

LaBSE

LaBSEは、Googleが「Language-agnostic BERT Sentence Embedding」という論文で提案したモデルです。109の言語に対応しており、これらの言語を与えることで、文の埋め込み表現を生成できます。モデルは170億の単言語の文と60億の2言語の文ペアによって訓練されています。訓練したモデルを多言語の情報検索タスクで評価したところ、m~USEやLASERといったモデルと比べて良い結果を示すことが明らかになりました。以下は単言語の文と2言語の文ペアの数を表しています。

f:id:Hironsan:20200916145324p:plain
単言語の文と2言語の文ペアの数。画像はLanguage-Agnostic BERT Sentence Embeddingより。

文類似度

理屈的なところはともかく、実際にLaBSEを試した結果を紹介しましょう。LabseはTensorFlow Hubで公開されているので、まずはこちらを使って、多言語の文類似度を計算してみます。類似度計算には、下記のページ内のコードを利用しています。

以下の英語、イタリア語、日本語文の類似度を計算してみます。日本語と英語のペアを見るのがわかりやすいと思うのですが、各言語の同一インデックスの文は同じ意味を持ちます。たとえば、英語の1番目の文と日本語の1番目の文の意味は同じだとみなしています。したがって、LaBSEで文を埋め込んで類似度を計算した結果が、同じ意味の文は他よりも高い類似度になって欲しいということになります。

english_sentences = [
  "dog",
  "Puppies are nice.",
  "I enjoy taking long walks along the beach with my dog."
]
italian_sentences = [
  "cane",
  "I cuccioli sono carini.",
  "Mi piace fare lunghe passeggiate lungo la spiaggia con il mio cane."
]
japanese_sentences = [
  "犬",
  "子犬はいいです",
  "私は犬と一緒にビーチを散歩するのが好きです"
]

類似度を計算した結果は以下のようになりました。左から英語と日本語、イタリア語と日本語、英語とイタリア語のペアとなっています。結果を見ると、行列の対角成分の値が他より大きくなっていることがわかります。つまり、同じ意味の文は、たとえ言語が異なっていても、類似度が高くなるように文を埋め込めていることを示唆しています。

f:id:Hironsan:20200916145423p:plain
各言語間の文類似度

テキスト分類

LaBSEで文を埋め込んで類似度を計算できることは確認したので、次はテキスト分類をしてみましょう。今回はAmazon Customer Reviews Datasetを使ってモデルの学習と評価を行います。このデータセットには、Amazonの商品レビューやその評価が含まれています。評価は星1から5までの5段階なのですが、今回は簡単のために、星3のレビューを取り除いた後、星1と2を0に、星4と5を1にマッピングして使うことにします。つまり、2分類です。

モデルのアーキテクチャは以下の通りです。LaBSEの上にDropoutと全結合層を足しただけです。

__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_word_ids (InputLayer)     [(None, 128)]        0                                            
__________________________________________________________________________________________________
input_mask (InputLayer)         [(None, 128)]        0                                            
__________________________________________________________________________________________________
segment_ids (InputLayer)        [(None, 128)]        0                                            
__________________________________________________________________________________________________
keras_layer (KerasLayer)        [(None, 768), (None, 470926849   input_word_ids[0][0]             
                                                                 input_mask[0][0]                 
                                                                 segment_ids[0][0]                
__________________________________________________________________________________________________
dropout (Dropout)               (None, 768)          0           keras_layer[0][0]                
__________________________________________________________________________________________________
dense (Dense)                   (None, 2)            1538        dropout[0][0]                    
==================================================================================================
Total params: 470,928,387
Trainable params: 470,928,386
Non-trainable params: 1
__________________________________________________________________________________________________

学習データと評価データの数は以下の通りです。冒頭で述べたように、学習データには英語、テストデータには日本語とフランス語を使って評価します。実際のところ、日本語やフランス語のデータの中にも英語のレビューが含まれているので取り除くべきですが、大勢に影響はないと考えられるので、今回はそのまま使うことにしました。ちなみに、2クラスの割合は等分です。

en fr ja
train 32000
test 8000 8000 8000

実験結果(acc)は以下のようになりました。以下は、何度か試した中での最高値であることに留意してください。英語でしか学習させていないにもかかわらず、フランス語や日本語に対しても、まずまずの予測をすることができています。実験する前は、英語のデータだけでFine-tuneして大丈夫か心配だったのですが、どうやらそれなりには予測できるようです。ここにはLaBSEの重みを固定して使った結果は載せていませんが、学習させた方が良い結果となりました。

en fr ja
test 0.9324 0.8943 0.8506

今回はテキスト分類に使いましたが、固有表現認識のような系列ラベリングに使ったらどうなるのかは気になるところです。

参考資料

Lambdaレイヤーを公開するためのシェルスクリプト

最近、AWS Lambdaをよく使っているが、その機能の一つとしてLambdaレイヤーがある。レイヤーにパッケージを取り込むことで、複数のLambda関数から使用可能になる。ビジネスロジックを共通化する、ビルドの手間を省く等の恩恵があるが、私的にはデプロイするファイルのサイズを小さくするのに重宝している。Lambdaの制限は結構きつくて、デプロイするファイルのサイズが大きくなり、デプロイに失敗するということが起きる。

Lambdaレイヤーにパッケージを取り込むには、パッケージをzip化してアップロードすればいいのだが、それがまた面倒くさい。pipディレクトリを指定してパッケージをインストールし、zipコマンドで圧縮した後S3にアップロードし、Lambdaコンソールから取り込む。また、単にどこでもいいからパッケージをインストールすればいいというわけでもなく、Lambdaの実行環境に合わせた環境でビルドしてやらないと実行に失敗する。手動でやるのは実に面倒くさい。

そういうわけで、一連の作業を一つのシェルスクリプトにしてみた。以下のスクリプトでは、Docker Lambdaを使ってパッケージをインストールし、それをzip化した後、Lambdaレイヤーにアップロードしている。不要なパッケージを除去してパッケージのサイズを小さくするような機能はないが、面倒くさいことをせずに、とにかくパッケージをアップロードしたいときに使える。

gist.github.com

以下のようにオプションを指定して実行するようになっている。nにはパッケージ名、rにはリージョン名、pにはPythonのランタイムを指定する。

sh publish_lambda_layer.sh -n spacy -r us-east-1 -p python3.7

質問応答におけるパッセージ検索 BERT vs BM25

最近、文章を書く機会が少なくなっているので、リハビリがてら以下の論文を紹介しよう。

この論文は、BERTによるパッセージ検索がBM25と比べて、どのような状況で強いのか分析している。パッセージ検索とは、検索クエリに関連するパッセージを検索するタスクである。情報検索システムで重要な技術だが、この論文の文脈では質問応答システムを想定している。つまり、質問文をクエリとした時に、対応する回答が含まれるパッセージが検索結果の上位に来ると嬉しい。このようなパッセージさえ得られれば、あとは機械読解の技術を使って回答を抽出すればよい。

f:id:Hironsan:20200627194858p:plain
情報検索ベースの質問応答システムのアーキテクチャ(Speech and Language Processing 25章より引用)

具体的には、以下の4つの仮説を検証している。

  • 仮説1: BM25はBERTと比べて高頻度のクエリ語にバイアスがかかった結果になる
  • 仮説2: 高頻度語がBM25の性能に悪影響を及ぼしている
  • 仮説3: BERTはより新語に強い
  • 仮説4: BERTはBM25と比べて長いクエリに強い

仮説1では、BM25の検索結果は、クエリに出現する語が繰り返し現れる文書なのではないかという仮説を検証している。その検証のために、FQTと呼ばれる指標を計算している。定義については論文を参照してもらいたいが、要するに検索した文書中にクエリ語が現れた割合を計算している。たとえば、10単語の文書でクエリ語が4回現れたらFQT=4/10=0.4になる。結果として、BM25の方がFQTが高かったので、よりクエリ語の頻度に影響を受けた文書が得られることがわかった。

仮説2では、高頻度語がBM25の性能に悪影響を及ぼしているか検証するために、FQTの値をいくつかの区間に分けたときの検索性能について調査している。その結果、FQTの値が大きいほど性能が低下していることがわかった。つまり、平均的には、クエリ語が含まれる割合が高い文書には回答が含まれる可能性が低いということになる。この傾向は、BERTとBM25の両方の場合で見られたが、BM25の方がより顕著であった。

f:id:Hironsan:20200627200234p:plain
FQTと検索性能の関係

仮説3では、BERTは新語が含まれる文書の検索に強いのかを検証している。その検証のために、FNTと呼ばれる指標を計算している。これは要するに、文書中の語のうちクエリに現れない割合を計算している。たとえば、10単語の文書で、そのうちクエリに現れない語が8単語であればFNT=8/10=0.8となる。要するに、クエリに含まれない単語を多く含む文書を検索できるのかを測っている。結果として、BERTの方がBM25よりFNTが高いという結果になった。

仮説4では、BERTはBM25と比べて長いクエリに強いか調査している。そのために、クエリ長ごとの性能を検証している。どのクエリ長でもBERTの方が性能が良かったが、クエリ長が長くなるほど、双方のモデルで性能が低下するという結果になった。しかも、BERTの方がより大きく性能が低下していたので、この仮説は正しくなかった。

f:id:Hironsan:20200627202204p:plain
クエリ長と検索性能の関係