CBOW | 単語の分散表現
ゼロから作る Deep Learning 2 斎藤康毅著 — 第3章・第4章 のメモです。
コーパス:you say goodbye and i say hello.
第3章:シンプルな CBOW モデル(多値分類)
概要
CBOW( Continuous Bag of Words )は、周囲の単語(コンテキスト)から中心単語を予測するモデルです。
単語 ID の対応表
| 単語 | ID |
|---|---|
| you | 0 |
| say | 1 |
| goodbye | 2 |
| and | 3 |
| i | 4 |
| hello | 5 |
| . | 6 |
単語サイズは 7 です。
モデルの構造(コンテキストサイズ=1、前後1単語)
入力層 中間層(隠れ層) 出力層
[you] → W_in → | |
| 平均ベクトル | → W_out → Softmax → 全単語の確率分布
[goodbye] → W_in → | |
学習データの例
コンテキスト [you, goodbye] → 正解:say
入力:you (one-hot: [1,0,0,0,0,0,0])
入力:goodbye (one-hot: [0,0,1,0,0,0,0])
出力(Softmax後):
you → 0.05
say → 0.70 ← 正解!ここが高くなるよう学習
goodbye → 0.05
and → 0.05
i → 0.05
hello → 0.05
. → 0.05
問題点:コーパスが巨大になると破綻する
語彙数が 100万語の場合:
W_in のサイズ:1,000,000 × 隠れ層次元
W_out のサイズ:隠れ層次元 × 1,000,000
Softmax の計算:全100万語について exp を計算する必要があり非常に重い
第4章:Embedding レイヤ + Negative Sampling(2値分類)
アイデアの転換
【3章】多値問題
「you と goodbye が与えられたとき、次の単語は何か?」
→ 全単語の確率分布を出力(語彙数が大きいと計算コスト爆発)
【4章】2値問題
「you と goodbye が与えられたとき、say は正しい単語か?」
→ YES / NO の1値だけ出力(Sigmoid)
Embedding レイヤとは
one-hot ベクトルと重み行列の積は、重み行列の該当行を取り出すだけです。
# 3章:非効率(one-hot との行列積)
one_hot = [0, 0, 1, 0, 0, 0, 0] # goodbye の one-hot
result = one_hot @ W_in # 実質 W_in[2] を取り出しているだけ( @ は行列のドット積)
# 4章:Embedding レイヤ(直接インデックスで取り出す)
word_id = 2 # goodbye の ID
result = W_in[word_id] # W_in[2] を直接参照 → 高速・省メモリ
Negative Sampling の仕組み
コーパス中の全コンテキストに対して、正例1つ + 少数の負例を組み合わせて学習します。
コーパス:you say goodbye and i say hello.
─────────────────────────────────────────────────
コンテキスト=[you, goodbye] ← 中心語 say の前後
─────────────────────────────────────────────────
正例:(コンテキスト=[you, goodbye], 単語=say) → ラベル=1(正しい)
負例:(コンテキスト=[you, goodbye], 単語=and) → ラベル=0(誤り)
負例:(コンテキスト=[you, goodbye], 単語=i) → ラベル=0(誤り)
─────────────────────────────────────────────────
コンテキスト=[say, and] ← 中心語 goodbye の前後
─────────────────────────────────────────────────
正例:(コンテキスト=[say, and], 単語=goodbye) → ラベル=1
負例:(コンテキスト=[say, and], 単語=you) → ラベル=0
負例:(コンテキスト=[say, and], 単語=hello) → ラベル=0
─────────────────────────────────────────────────
コンテキスト=[goodbye, i] ← 中心語 and の前後
─────────────────────────────────────────────────
正例:(コンテキスト=[goodbye, i], 単語=and) → ラベル=1
負例:(コンテキスト=[goodbye, i], 単語=say) → ラベル=0 ← say も負例になりうる
負例:(コンテキスト=[goodbye, i], 単語=hello) → ラベル=0
─────────────────────────────────────────────────
コンテキスト=[and, say] ← 中心語 i の前後
─────────────────────────────────────────────────
正例:(コンテキスト=[and, say], 単語=i) → ラベル=1
負例:(コンテキスト=[and, say], 単語=you) → ラベル=0
負例:(コンテキスト=[and, say], 単語=goodbye) → ラベル=0
─────────────────────────────────────────────────
コンテキスト=[i, hello] ← 中心語 say の前後
─────────────────────────────────────────────────
正例:(コンテキスト=[i, hello], 単語=say) → ラベル=1
負例:(コンテキスト=[i, hello], 単語=and) → ラベル=0
負例:(コンテキスト=[i, hello], 単語=you) → ラベル=0
なぜ say が正例とわかるのか? 正例は「予測」ではなく、コーパスのテキストから機械的に取り出した既知の答えです。
you ? goodbyeの?に何が入るかはテキストを見れば自明です。
モデルの計算(1ペアあたり)
コンテキストベクトル = Embedding(you) + Embedding(goodbye)
↓
スコア = コンテキストベクトル ・ Embedding(say) (内積)
↓
確率 = Sigmoid(スコア) → 0〜1 の1値
↓
損失 = CrossEntropyLoss(確率, ラベル)
負例のサンプリング方法
負例はコーパス中の単語出現頻度に基づいて選びます。
# 単語の出現頻度(コーパス例)
頻度:say=2, you=1, goodbye=1, and=1, i=1, hello=1
# 0.75乗でスムージング(高頻度語を少し抑える)
P(w) = (freq(w) ** 0.75) / Σ(freq(w') ** 0.75)
# say は頻度が高いので負例に選ばれやすい
# ただし 0.75乗により偏りすぎを防ぐ
第3章 vs 第4章 の比較まとめ
| 項目 | 第3章(多値) | 第4章(2値) |
|---|---|---|
| 問いの形 | 「次の単語は何か?」 | 「この単語は正しいか?」 |
| 出力 | 全語彙の確率分布 | 0〜1 の1値(Sigmoid) |
| 計算コスト | 語彙数に比例して爆発 | 負例数に比例(少数でよい) |
| 重みの参照 | one-hot との行列積 | Embedding レイヤで直接インデックス参照 |
| 語彙数100万での対応 | ❌ 困難 | ✅ 対応可能 |
学習で何が育つのか
学習前:各単語のベクトルはランダム
学習後:
・say のベクトル ≈ [you, goodbye] のコンテキストベクトルに近い
・and のベクトル ≈ [goodbye, i] のコンテキストベクトルに近い
・意味的に似た単語(i と you など)は近いベクトルを持つようになる
これが単語の分散表現(Word Embedding) です。 Negative Sampling は「正解と間違いを繰り返し判定させること」で、 副産物として意味を捉えたベクトルを生み出します。