NNLM初學習
NNLM
在瞭解NNLM之前先了解一下詞向量
詞向量
我們人學過單詞,漢字等等,能明白一句話。但是計算機只認識0和1,如何把語言讓計算機看懂。將文字轉化為向量。
詞向量的方法是「one-hot(獨熱編碼)表示法」
是最早的表示詞向量的方法, 首先我們有一個詞表,裡面包括了我們可能會用到的所有詞,每個詞佔據一個位置。那麼詞向量就是一個該詞表維度大小的向量,詞所在位置取值1,其它位置取值0。例如我們的詞表有下面9個詞:
你,我,他,是,誰,哪,裡,來,自
“我”表示為向量[0, 1, 0, 0, 0, 0, 0, 0, 0],“是”表示為[0, 0, 0, 1, 0, 0, 0, 0, 0]
這樣會可能會產生【維度災難】和【語義鴻溝】
【維度災難】:是因為如果詞表過大,向量維度也過大,而且向量特別稀疏
【語義鴻溝】:因為每一個詞向量1的位置不同,可以認為彼此相互正交(內積為0),任何兩個內積為0,沒有任何差別,體現不出來相似性。
為了彌補one-hot向量的這些缺陷,分散式向量(distributed representation) ,相似的詞向量相似。這個與word enbedding關係, Representation → Distributed Reprensentation → word embedding ,詞在基於神經網路的分佈表示,好像同一個意思。
參考連結 (7 封私信 / 80 條訊息) word2vec和word embedding有什麼區別? - 知乎 (zhihu.com)
NNLM
Yoshua Bengio等人於2003年發表的《A Neural Probabilistic Language Model》針對N-gram模型的問題進行了解決。這是第一篇提出神經網路語言模型的論文,它在得到語言模型的同時也產生了副產品詞向量。
論文連結:《A Neural Probabilistic Language Model》
輸入層:
透過前n個詞,來預測第n個單詞
詞向量W:是一個one-hot向量,大小=[10W,1],W(t)表示第t個詞語的one hot(一個元素為1,其餘全為0
投影矩陣C:維度[D*V],V=10W,引數D根據文字大小不同來設定:谷歌測試時選取D=300。
計算過程:
- 投影矩陣C[300 * 10W] X 詞向量W(t)[10W *1] 得到= 矩陣[300 * 1]
- 比如根據前3個詞來預測第4個詞語,那麼上述操作會重複三次,得到3個[300*1]的矩陣
- 將這3個[300*1]的矩陣按行拼接,得到[900x1]的矩陣。
形式類似:
隱藏層:
存在一個向量矩陣[Hx1],H根據文字集合情況設定(谷歌測試時選取H=500)
該層完成的功能主要是全連線!
說通俗一些:把輸入層計算得到的矩陣[900x1],轉換為矩陣[Hx1],完成輸入層到隱藏層的資料傳輸,並且在全連線的過程中存在計算的權重。
最終得到矩陣[500x1]
輸出層:
我們的詞語大小為V=10W,隱藏層計算得到矩陣[500x1],要將這[500x1]的計算結果轉化為[10Wx1],以此來預測第4個詞語是什麼?
得到矩陣[10Wx1],也就是所謂第4個詞ont-hot,最終經過SoftMax啟用函式,選取行向量最大值,就是預測詞語。
# code by Tae Hwan Jung @graykode
import torch
import torch.nn as nn
import torch.optim as optim
def make_batch():
input_batch = []
target_batch = []
for sen in sentences:
word = sen.split() # space tokenizer
input = [word_dict[n] for n in word[:-1]] # create (1~n-1) as input
target = word_dict[word[-1]] # create (n) as target, We usually call this 'casual language model'
input_batch.append(input)
target_batch.append(target)
return input_batch, target_batch
# Model
class NNLM(nn.Module):
def __init__(self):
super(NNLM, self).__init__()
self.C = nn.Embedding(n_class, m)
self.H = nn.Linear(n_step * m, n_hidden, bias=False)
self.d = nn.Parameter(torch.ones(n_hidden))
self.U = nn.Linear(n_hidden, n_class, bias=False)
self.W = nn.Linear(n_step * m, n_class, bias=False)
self.b = nn.Parameter(torch.ones(n_class))
def forward(self, X):
X = self.C(X) # X : [batch_size, n_step, m]
X = X.view(-1, n_step * m) # resize [batch_size, n_step * m]
tanh = torch.tanh(self.d + self.H(X)) # [batch_size, n_hidden]
output = self.b + self.W(X) + self.U(tanh) # [batch_size, n_class]
return output
if __name__ == '__main__':
n_step = 2 # number of steps, n-1 in paper,
n_hidden = 2 # number of hidden size, h in paper
m = 2 # embedding size, m in paper
sentences = ["i like dog", "i love coffee", "i hate milk"]
word_list = " ".join(sentences).split()
word_list = list(set(word_list))
word_dict = {w: i for i, w in enumerate(word_list)}
number_dict = {i: w for i, w in enumerate(word_list)}
n_class = len(word_dict) # number of Vocabulary
model = NNLM()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
input_batch, target_batch = make_batch()
input_batch = torch.LongTensor(input_batch)
target_batch = torch.LongTensor(target_batch)
# Training
for epoch in range(5000):
optimizer.zero_grad()
output = model(input_batch)
# output : [batch_size, n_class], target_batch : [batch_size]
loss = criterion(output, target_batch)
if (epoch + 1) % 1000 == 0:
print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
loss.backward()
optimizer.step()
# Predict
predict = model(input_batch).data.max(1, keepdim=True)[1]
# Test
print([sen.split()[:2] for sen in sentences], '->', [number_dict[n.item()] for n in predict.squeeze()])
nn.Embedding( num_embeddings , embedding_dim )詳解:
num_embeddings - 詞嵌入字典大小,即一個字典裡要有多少個詞
embedding_dim - 每個詞嵌入向量的大小。
input_batch[3:2 ]------>C(X)------->[3,2,2],input_batch每個不相同數都有一個2維向量,相同數的向量相同。
nn.Linear(in_features,out_features,bias=False)詳解:
in_features指的是輸入的二維張量的大小,即輸入的[batch_size, size]中的size。
out_features指的是輸出的二維張量的大小,即輸出的二維張量的形狀為[batch_size,output_size],當然,它也代表了該全連線層的神經元個數。
從輸入輸出的張量的shape角度來理解,相當於一個輸入為[batch_size, in_features]的張量變換成了[batch_size, out_features]的輸出張量。
input = [1 , 100]
nn.Linear(in_features = 100, out_features = 1)
output = [1,1]
參考連結: