自然言語処理をやっていると文章のベクトルが欲しいことが多々あります。
BoWやtf-idf,トピックモデルや、word2vecの平均、一時期流行ったSCDVなどいろいろ方法はあるのですが、これが決定版というのがなかなか無く、毎回悩ましい問題です。
学習済みモデルの活用なども考えるのですが、日本語に対応しているものは珍しかったりします。
そんな状況の中、Googleさんから多言語に対応した、Universal Sentence Encoderというものが公開されているのでこれを試してみることにしました。
元の論文はこちら: Multilingual Universal Sentence Encoder for Semantic Retrieval
学習済みモデルは Tensorflow Hubの universal-sentence-encoder-multilingual のページで配布されています。
現在は Version 3 が出てるようです。
Tensorflow Hub そのものの使い方にまだ慣れていないのですが、このモデルのページのコードだけで動かすことができたので、それを紹介します。
英語、イタリア語、日本語で、それぞれ3種類の文章をベクトル化し、類似度を図ります。
とりあえず、ライブラリを読み込んで、データを準備します。
tensorflow_text はコード中で使わないのですが、importしておかないといけないようです。
# ライブラリのインポートと、サンプルテキストの準備
import tensorflow_hub as hub
import numpy as np
import tensorflow_text
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 = ["犬", "子犬はいいです", "私は犬と一緒にビーチを散歩するのが好きです"]
さて、実際にモデルを読み込んで、データをベクトル化してみます。すごく手軽ですね。
# モデルの読み込み
url = "https://tfhub.dev/google/universal-sentence-encoder-multilingual/3"
embed = hub.load(url)
# 埋め込みの計算
en_result = embed(english_sentences)
it_result = embed(italian_sentences)
ja_result = embed(japanese_sentences)
埋め込んだ結果は TensorflowのTensorで戻ってきます。
Shapeを確認すると、3この文章がそれぞれ 512次元のベクトルに変換されていることがわかります。
print(type(ja_result))
#
print(ja_result.shape)
# (3, 512)
サンプルでは次のようにして、英語の3文と、イタリア語日本語のそれぞれの類似度を計算していました。
np.inner()
は内積を計算する関数なのですが、実は埋め込まれたベクトルはもともとノルムが1になるように正規化されているので、
これでコサイン類似度が計算できています。
# Compute similarity matrix. Higher score indicates greater similarity.
similarity_matrix_it = np.inner(en_result, it_result)
similarity_matrix_ja = np.inner(en_result, ja_result)
ノルムが1であることも確認しておきます。
print(np.linalg.norm(ja_result, axis=1))
# [1. 1. 1.]
結果を表示しておきましょう。これをみると、近い意味の文章は違う言語であっても近い位置に埋め込まれてるのが確認できます。
print(similarity_matrix_it.round(3))
"""
[[0.958 0.331 0.302]
[0.388 0.734 0.248]
[0.236 0.218 0.928]]
"""
print(similarity_matrix_ja.round(3))
"""
[[0.917 0.512 0.316]
[0.443 0.659 0.309]
[0.267 0.254 0.767]]
"""
さて、テンソル型で帰ってきてるデータですが、普通の numpyのArrayにしたい場合は、 .numpy()
というメソッドが使えます。
print(ja_result)
"""
tf.Tensor(
[[ 0.10949969 -0.02602168 0.04610093 ... 0.05233185 0.00311097
0.01985742]
[ 0.03606617 -0.00969927 0.04294628 ... 0.02523113 -0.00969072
0.05069916]
[-0.02916382 -0.00816513 -0.02910488 ... 0.00125965 -0.00689579
0.0103978 ]], shape=(3, 512), dtype=float32)
"""
print(ja_result.numpy())
"""
[[ 0.10949969 -0.02602168 0.04610093 ... 0.05233185 0.00311097
0.01985742]
[ 0.03606617 -0.00969927 0.04294628 ... 0.02523113 -0.00969072
0.05069916]
[-0.02916382 -0.00816513 -0.02910488 ... 0.00125965 -0.00689579
0.0103978 ]]
"""
とても便利ですね。
言語としては 16言語に対応していて、しかも可変長の文章を全て512次元にエンコードしてくれます。
かなり活用の場がありそうです。