NNLM初認識以及相關程式碼

夢彷徨發表於2023-04-14

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。

計算過程:

  1. 投影矩陣C[300 * 10W] X 詞向量W(t)[10W *1] 得到= 矩陣[300 * 1]
  2. 比如根據前3個詞來預測第4個詞語,那麼上述操作會重複三次,得到3個[300*1]的矩陣
  3. ​ 將這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]

參考連結:

  1. 【語言模型】NNLM(神經網路語言模型)
  2. 神經網路語言模型NNLM
  3. 入門通俗易懂的神經網路語言模型(NNLM)詳解
  4. 程式碼專案地址
  5. NN.linear

相關文章