同じ言葉が1つもないページ
埋め込み (エンベディング) とは、文章の意味を表す数のリストです。意味が近い文章は、同じ向きを指すリストになり、プログラムは計算で意味を比べられます。
図書館のサイトで、利用者が「本を持ち帰るルール」を探します。必要なページの題は「貸出の条件」です。同じ言葉が1つもないので、言葉だけの検索では見つかりません。数なら見つけられます。
このレッスンのゴール
メモ
埋め込みとは何かを説明し、2つをコサイン類似度で比べられる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| 埋め込み | 意味を表す、数のリスト | |
| ベクトル | 点や矢印として見る、数のリスト | |
| 内積 | 同じ位置の数を掛けて、全部足したもの | |
| コサイン類似度 | 2本の矢印が、どれだけ同じ向きかの点数 | 0.98 |
| 検索 | 質問に合う文章の断片を見つけること |
学生が入力するセルは、このプレビューでは空白です。
本物の埋め込みと、この科目の代用品
本物の埋め込みは数百個の数でできていて、モデルが作ります。この科目では、1つも作りません。
代わりに、小さな代用品を使います。3つの数でできた作り物のリストと、スクリプトが自分で数える文字の回数です。こうすれば、各手順をここで実行して確かめられます。方法は同じです。
ベクトルは数のリスト
数のリストは、地図の上の点だと考えます。数が2つなら平らな地図の点、3つなら部屋の中の点です。
リストの各位置に意味を付けます。ここでは3つの位置を、動物、乗り物、食べ物とします。数が大きいほど、その言葉はそれに強く関わっています。
例: 作り物の4つのベクトル
| 言葉 | 動物 | 乗り物 | 食べ物 |
|---|---|---|---|
| ねこ | 0.9 | 0.1 | 0.1 |
| こねこ | 0.8 | 0.1 | 0.2 |
| バス | 0.1 | 0.9 | 0.0 |
| おにぎり | 0.1 | 0.0 | 0.9 |
学生が入力するセルは、このプレビューでは空白です。
コサイン類似度は似ている度合いの点数
2つのベクトルを比べるには、3つの小さな手順を踏みます。内積は、同じ位置の数を掛けて足したものです。長さは、自分自身との内積の平方根です。
コサインは、内積を2つの長さで割ったものです。矢印が同じ向きなら1、直角なら0、反対向きなら0より小さくなります。
例: 2つの小さなベクトル
| 手順 | a = [1, 2] と b = [2, 1] の計算 | 結果 |
|---|---|---|
| 内積 | 1 x 2 + 2 x 1 | 4 |
| a の長さ | (1 x 1 + 2 x 2) の平方根 | 2.24 |
| b の長さ | (2 x 2 + 1 x 1) の平方根 | 2.24 |
| コサイン | 4 を (2.24 x 2.24) で割る | 0.8 |
学生が入力するセルは、このプレビューでは空白です。
計算をループで書く
次のスクリプトは、4つのベクトルを持ち、3つの手順を小さな関数にします。すべての組のコサインを表示します。
数えるベクトルも作ります。決めた漢字を並べ、それぞれが文に何回出るかを数えます。日本語は言葉の間に空白がないので、1文字ずつ数えます。
ベクトルのスクリプトを書く(1)
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 小さな作り物のベクトル: [動物, 乗り物, 食べ物]。本物の埋め込みは数百個の数です。' 'const vectors = {' ' \"ねこ\": [0.9, 0.1, 0.1],' ' \"こねこ\": [0.8, 0.1, 0.2],' ' \"バス\": [0.1, 0.9, 0.0],' ' \"おにぎり\": [0.1, 0.0, 0.9],' '};' 'function dot(a, b) {' ' let total = 0;' ' for (let i = 0; i < a.length; i++) total = total + a[i] * b[i];' ' return total;' '}' 'function length(a) { return Math.sqrt(dot(a, a)); }' 'function cosine(a, b) { return dot(a, b) / (length(a) * length(b)); }' > vec.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ベクトルのスクリプトを書く(2)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const names = Object.keys(vectors);' 'for (let i = 0; i < names.length; i++) {' ' for (let j = i + 1; j < names.length; j++) {' ' const score = cosine(vectors[names[i]], vectors[names[j]]);' ' console.log(names[i] + \" と \" + names[j] + \": \" + score.toFixed(2));' ' }' '}' '// 数えるベクトル: 決めた漢字が、文の中に何回出るか。' 'const letters = [\"開\", \"閉\", \"本\", \"遅\"];' 'function counts(sentence) {' ' return letters.map(function (letter) { return sentence.split(letter).length - 1; });' '}' >> vec.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ベクトルのスクリプトを書く(3)
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const one = counts(\"夜は遅くまで開いています。開館は朝9時です。\");' 'const two = counts(\"本は遅れずに返します。閉館は夜8時です。\");' 'console.log(\"数えた結果:\", one.join(\" \"), \"と\", two.join(\" \"));' 'console.log(\"数えた結果のコサイン:\", cosine(one, two).toFixed(2));' >> vec.js"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
実行して点数を読む
docker exec -w /home/node/project agent node vec.jsここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
表示される主な行
| 行 | 意味 |
|---|---|
| ねこ と こねこ: 0.99 | とてもよく似ている |
| ねこ と バス: 0.22 | あまり似ていない |
| バス と おにぎり: 0.01 | 共通点がほとんどない |
| 数えた結果のコサイン: 0.26 | 2つの文は、遅の字だけを共有している |
学生が入力するセルは、このプレビューでは空白です。
よくある間違い
注意
コサインが0.9なら「90%同じ」だと考える人が多くいます。そうではありません。点数は順位を付けるためのものです。0.9は0.5より近い、というだけで、真実の割合ではありません。