埋め込み

ここまで、テキストを機械学習モデルで扱えるようにするためにbug-of-wordsをはじめとする、一つの単語を一つの次元で表現する特徴ベクトル化の手法について見てきました。

特徴ベクトルに求められる機能はタスクに十分な情報を残した状態で機械学習で取り扱うことができるベクトルに変換することです。

この表現は便利である一方、次の問題点を抱えています。

  1. 高次元であること

  2. 単語の「近さ」を表現できないこと

まず1.の「高次元の表現」であることについて考えてみましょう。 機械学習では 次元の呪い という問題が知られており、特徴ベクトルの次元が増えるにつれて、学習に必要なデータ量も増えます。 例えば、学習データに入っていないテキストを学習済みのモデルに入力して分類するとしましょう。 高次元になればなるほど、入力の特徴ベクトルが、学習データ中に現れるサンプルの特徴ベクトル全てと遠い状況になりやすく、結果予測が正しく行われなくなってしまいます。 これは別の言葉で言うと、学習データに過学習しており、汎化性能が低くなるということです。

bug-of-wordsのような特徴ベクトルはほとんどが0な なベクトルであり、その部分に次元を小さくする余地があります。もとのテキストの情報を落とさずに次元を小さくする手法が求められます。

次に2.の単語の「近さ」を表現できない問題についてみてみましょう。 特徴ベクトルに求められる性質として、タスクに十分な情報を含んでいることがあげられます。 ところが、bug-of-wordsでは単語の近さをはじめとする、単語や文の意味を捉えてはいません。 これは一つの単語につき一つの次元が割り当てられているからで、次元と小さくしてベクトル間の関係をコサイン類似度のような指標で計測できるようにすれば解決できます。

この1.と2.を解決する方法が単語や文の 埋め込み (embedding) と呼ばれる手法で、 ニューラルネットワークの時代では埋め込みが頻繁に使われるようになりました。

手法によっては埋め込みはラベルなしデータから学習することが可能で、学習データが少ない状況でも適用可能です。 ここでは、そのような事前の埋め込みを利用して埋め込みを特徴ベクトルとして利用する方法を見てみましょう。

単語のエンベッディング

SpaCyの日本語モデルのトークンには埋め込みがついています。 埋め込みはchiVe v1.1 mc90 aunitを使っています。

import spacy

nlp = spacy.load("ja_core_news_md")
2022-04-27 11:16:11.255208: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2022-04-27 11:16:11.255902: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
doc = nlp("今日は銀座から池袋へ向かいました。")
for token in doc:
    print(token, token.has_vector, tokben.vector.shape)
今日 True (300,)
は True (300,)
銀座 True (300,)
から True (300,)
池袋 True (300,)
へ True (300,)
向かい True (300,)
まし False (300,)
た True (300,)
。 True (300,)
import numpy as np

tokens = list(doc)
for i in range(len(tokens)-1):
    for j in range(i+1, len(tokens)):
        t1, t2 = tokens[i], tokens[j]
        score = t1.similarity(t2)
        #score = np.dot(t1.vector, t2.vector) / (np.linalg.norm(t1.vector) * np.linalg.norm(t2.vector))
        if score > 0.5:
            print(t1, t2, np.array(score).round(4))
今日 た 0.549
は から 0.7804
は た 0.7607
は 。 0.5274
銀座 池袋 0.7067
から へ 0.6643
から た 0.7289
た 。 0.5702
/tmp/ipykernel_3458/1923281132.py:7: UserWarning: [W008] Evaluating Token.similarity based on empty vectors.
  score = t1.similarity(t2)
doc = nlp("東京と大阪に行く")
tokens = list(doc)
tokens
[東京, と, 大阪, に, 行く]
tokyo, _, osaka, _, go = tokens
tokyo.vector.shape, osaka.vector.shape, go.vector.shape
((300,), (300,), (300,))
tokyo.similarity(osaka)
0.6331307291984558
tokyo.similarity(go)
0.30672687292099

文ベクトルは doc.vector でアクセスできます。 文ベクトルは単語ベクトルの平均として得られます。したがって、単語の順は考慮されていないことに注意してください。

doc.vector.shape
(300,)
nlp("東京に行く").similarity(nlp("大阪に行きます"))
0.7961583740464455
nlp("東京に行く").similarity(nlp("大阪に行きます"))
nlp("東京に行く").similarity(nlp("ご飯を食べます"))
0.680034620070472

単語の順も考慮する文エンベッディング

単語ごとにエンベッディングを取ることで単語ごとの近さを考慮することができるようなりました。 一方で、単語ごとの埋め込みの和をとっているので、単語の順番は考慮できていません。

ここでは単語の順も考慮した文エンベッディングについてみていきましょう。

Universal Sentence Encoderが簡単に利用でき、よいスタートラインに立てます。 https://tfhub.dev/google/universal-sentence-encoder-multilingual/3

import tensorflow_hub as hub
import numpy as np
import tensorflow_text


embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder-multilingual/3")
2022-04-27 05:29:27.594044: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2022-04-27 05:29:27.595306: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
2022-04-27 05:30:06.841760: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory
2022-04-27 05:30:06.841964: W tensorflow/stream_executor/cuda/cuda_driver.cc:269] failed call to cuInit: UNKNOWN ERROR (303)
2022-04-27 05:30:06.842008: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (60353b6b26b9): /proc/driver/nvidia/version does not exist
2022-04-27 05:30:06.843081: I tensorflow/core/platform/cpu_feature_guard.cc:151] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
embed("東京に行きます").shape
TensorShape([1, 512])
import numpy as np


def calc_similarity(text, target):
    return np.inner(embed(text), embed(target))
calc_similarity("東京に行く", "大阪に行きます")
array([[0.6303844]], dtype=float32)
calc_similarity("東京に行く", "ご飯を食べます")
array([[0.26746953]], dtype=float32)