文書を数の列にして、向きを比べる
コサイン類似度とは、2つの文書を数の列(ベクトル)にして、その向きの近さで、似ている度合いを測る方法です。長さのちがう文書でも、公平に比べられます。
検索語も、短い1つの文書だと考えます。検索語に近い向きの文書ほど、上に並べれば、検索結果の順位になります。
このレッスンのゴール
メモ
文書と検索語をベクトルにして、コサイン類似度で、文書の順位をつけることができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 語彙 | 数える語を決めた、語の並び | 電車、遅れ、雨、駅 |
| ベクトル | 語彙の順に、語ごとの数を並べた数の列 | [1, 1, 0, 2] |
| 内積 | 同じ位置の数どうしをかけて、全部足した数 | |
| ベクトルの長さ | 各数を2乗して足し、平方根をとった数 | |
| コサイン類似度 | 内積を、2つの長さの積で割った数。0から1 |
学生が入力するセルは、このプレビューでは空白です。
語彙の順に、語の数を並べるとベクトルになる
まず、数える語を決めます。これが語彙です。文書ごとに、語彙の順に、語の回数を並べると、ベクトル(数の列)ができます。
検索語も、同じ語彙で、同じようにベクトルにします。ここでは回数を使いますが、tf-idf の重みを入れても、考え方は同じです。
例: 3枚の文書と検索語
| もとの文 | [電車, 遅れ, 雨, 駅] |
|---|---|
| 文書1「電車 遅れ 駅 駅」 | [1, 1, 0, 2] |
| 文書2「雨 電車」 | [1, 0, 1, 0] |
| 文書3「駅 雨」 | [0, 0, 1, 1] |
| 検索語「電車 遅れ」 | [1, 1, 0, 0] |
学生が入力するセルは、このプレビューでは空白です。
内積を長さで割ると、向きの近さになる
2つのベクトルの内積は、同じ位置の数をかけて足した数です。長さは、各数を2乗して足し、平方根をとります。
コサイン類似度は、内積を、2つの長さの積で割った数です。向きが同じなら1、共通の語がなければ0になります。
例: 検索語と3枚の文書
| 文書 | 内積 | 長さの積 | コサイン類似度 |
|---|---|---|---|
| 文書1 [1, 1, 0, 2] | 2 | √6 × √2 | 0.577 |
| 文書2 [1, 0, 1, 0] | 1 | √2 × √2 | 0.5 |
| 文書3 [0, 0, 1, 1] | 0 | √2 × √2 | 0.0 |
学生が入力するセルは、このプレビューでは空白です。
文書1の類似度を計算
docker exec jupyter python3 -c "import math; print(round(2 / (math.sqrt(6) * math.sqrt(2)), 3))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
倍にしても、向きは同じ
import math
a = [1, 1, 0]
b = [1, 0, 1]
dot = 0
sum_a = 0
sum_b = 0
for n in range(len(a)):
dot = dot + a[n] * b[n]
sum_a = sum_a + a[n] * a[n]
sum_b = sum_b + b[n] * b[n]
print("内積:", dot)
print("コサイン類似度:", round(dot / (math.sqrt(sum_a) * math.sqrt(sum_b)), 3))
# b を [2, 0, 2] に変えて、もう一度実行してください。値は変わりますか。
よくある間違い
注意
内積が大きいほど、似ていると考えがちです。長い文書は、内積が大きくなりやすいのです。長さで割ったコサイン類似度で比べれば、長さの影響は消えます。
例: 内積と、コサイン類似度の順位
| 文書 | 内積 | コサイン類似度 |
|---|---|---|
| A [1, 1, 0, 0](短い) | 2 | 1.0 |
| B [3, 3, 3, 3](長い) | 6 | 0.707 |
学生が入力するセルは、このプレビューでは空白です。
さらに挑戦: tf-idf の重みで比べる
課題の to_vector が返す回数を、前の回の tf_idf に置きかえると、どうなるでしょうか。「の」のように、どの文書にもある語の重みは 0 になります。
検索語が「駅 の 近く」のとき、順位が変わるか調べてください。