- Word2Vec
- How achieve
- Lookup table
- Coding
- Pre-dataing
- Model
- Negative sameple
Word2Vec
單詞與單詞之間的向量往往不在同一個向量空間,例如,傳統的編碼方式:one-hot編碼,不同單詞
[1, 0, 0]
和[0, 1, 0]
之間的餘弦相似度為0。因此,Word2Vec希望能夠透過訓練得到一個新的詞向量表達方式,從而豐富向量的語義資訊。主要目標如圖所示,從一個稀疏的one-hot向量透過訓練得到一個豐富稠密的新向量。
How achieve
word2vec透過神經網路模型訓練新的詞向量表達
-
模型中引數的定義:
one-hot
:[1, 7] 表示一共有七個單詞;
Embedding
:表示輸入層到隱藏層的權重矩陣,是從one-hot向量到Embedding向量的關鍵,[7, 3]表示訓練完成的每一個embedding向量維度為3;
WeightLogits
:表示隱藏層到輸出層的權重矩陣,是模型損失計算的關鍵;
Logits
:表示最後每個單詞輸出的機率,與目標標籤做損失進行模型訓練;
Lookup table
語料庫十分巨大,每個單詞都採用one-hot輸入訓練會大大增加儲存和計算開銷,因此,在輸入的過程,僅僅輸入單詞的索引值,例如在上述例子中,直接採用索引4進行輸入,同樣也可以得到相同的詞向量。
Coding
Word2Vec有兩種模型結構:CBOW和Skip-gram,本質上的模型架構的不同:輸入和輸出一對多(Skip-gram)和多對一(CBOW)。
CBOW:透過前t個單詞預測後一個單詞
Skip-gram:透過周圍的單詞預測中間的單詞 (一般來說,效果較好)
Pre-dataing
- 構建詞彙表(Lookup-table)
def build_vocab(corpus):
word_counts = Counter(chain(*corpus))
vocab = {word: i for i, (word, _) in enumerate(word_counts.items())}
return vocab
- 構建不同模型的輸入、輸出
# Skip-gram 資料集生成
def generate_skipgram_data(corpus, vocab, window_size):
data = []
for sentence in corpus:
for i in range(len(sentence)):
target = sentence[i]
context = [sentence[j] for j in range(max(0, i - window_size), min(len(sentence), i + window_size + 1)) if j != i]
for context_word in context:
data.append((vocab[target], vocab[context_word]))
return data
# cbow 資料集生成
def generate_cbow_data(corpus, vocab, window_size):
data = []
for sentence in corpus:
for i in range(len(sentence)):
target = sentence[i]
context = [sentence[j] for j in range(max(0, i - window_size), min(len(sentence), i + window_size + 1)) if j != i]
data.append(([vocab[word] for word in context], vocab[target]))
return data
Model
可以發現兩個結構的程式碼只有輸入和輸出的大小不同,其他類似
- CBOW
class CBOW(nn.Module):
def __init__(self, vocab_size, embedding_dim):
super(CBOW, self).__init__()
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
self.linear = nn.Linear(embedding_dim, vocab_size)
def forward(self, context_words):
embedded = self.embeddings(context_words).mean(dim=1) # 平均上下文詞向量
logits = self.linear(embedded)
return logits
- Skip-gram
class SkipGram(nn.Module):
def __init__(self, vocab_size, embedding_dim):
super(SkipGram, self).__init__()
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
self.linear = nn.Linear(embedding_dim, vocab_size)
def forward(self, target_word):
embedded = self.embeddings(target_word) # (Batch, embedding_dim)
logits = self.linear(embedded)
return logits
Negative sameple
提出動機:每次模型訓練都需要計算所有詞向量的損失,透過只更新負樣本的權重,避免整個詞彙表的計算
Word2Vec模型本質是一個多分類問題,最後需要透過softmax
啟用函式判斷哪一個單詞的機率最大,因此需要計算所有單詞的機率大小。而負取樣最佳化是指將原來的任務退化為二分類問題,只對正負樣本進行判斷,在詞彙表隨機選取\(N_{neg}\)個樣本作為負樣本集合。