前面一篇文章中鸽捻,我們簡單的介紹了自然語言處理中最簡單的詞向量 word embedding,這一篇文章我們將介紹如何使用word embedding做自然語言處理的詞語預(yù)測统锤。
N-Gram language Modeling
首先我們介紹一下 N-Gram 模型猎莲。在一篇文章中奔则,每一句話有很多單詞組成,而對于一句話,這些單詞的組成順序也是很重要的驮吱,我們想要知道在一篇文章中我們是否可以給出幾個(gè)詞然后預(yù)測這些詞后面的一個(gè)單詞茧妒,比如'I lived in France for 10 years, I can speak _ .'那么我們想要做的就是預(yù)測最后這個(gè)詞是French。
知道了我們想要做的事情之后左冬,我們就可以引出 N-Gram 模型了嘶伟。先給出其公式
這就是一個(gè)條件概率,也就是我們給定想要預(yù)測的單詞的前面幾個(gè)單詞又碌,然后最大化我們想要預(yù)測的這個(gè)單詞的概率九昧。
Code
數(shù)據(jù)預(yù)處理
首先我們給出了一段文章作為我們的訓(xùn)練集
CONTEXT_SIZE = 2
EMBEDDING_DIM = 10
# We will use Shakespeare Sonnet 2
test_sentence = """When forty winters shall besiege thy brow,
And dig deep trenches in thy beauty's field,
Thy youth's proud livery so gazed on now,
Will be a totter'd weed of small worth held:
Then being asked, where all thy beauty lies,
Where all the treasure of thy lusty days;
To say, within thine own deep sunken eyes,
Were an all-eating shame, and thriftless praise.
How much more praise deserv'd thy beauty's use,
If thou couldst answer 'This fair child of mine
Shall sum my count, and make my old excuse,'
Proving his beauty by succession thine!
This were to be new made when thou art old,
And see thy blood warm when thou feel'st it cold.""".split()
CONTEXT_SIZE表示我們想由前面的幾個(gè)單詞來預(yù)測這個(gè)單詞,這里設(shè)置為2毕匀,就是說我們希望通過這個(gè)單詞的前兩個(gè)單詞來預(yù)測這一個(gè)單詞铸鹰。 EMBEDDING_DIM表示word embedding的維數(shù),上一篇已經(jīng)介紹過了皂岔。
trigram = [((test_sentence[i], test_sentence[i+1]), test_sentence[i+2])
for i in range(len(test_sentence)-2)]
接下來我們需要將數(shù)據(jù)整理好蹋笼,也就是我們需要將單詞三個(gè)分組,每個(gè)組前兩個(gè)作為傳入的數(shù)據(jù)躁垛,而最后一個(gè)作為預(yù)測的結(jié)果剖毯。
vocb = set(test_sentence) # 通過set將重復(fù)的單詞去掉
word_to_idx = {word: i for i, word in enumerate(vocb)}
idx_to_word = {word_to_idx[word]: word for word in word_to_idx}
接下來需要給每個(gè)單詞編碼,也就是用數(shù)字來表示每個(gè)單詞教馆,這樣才能夠傳入word embeding得到詞向量逊谋。
定義模型
class NgramModel(nn.Module):
def __init__(self, vocb_size, context_size, n_dim):
super(NgramModel, self).__init__()
self.n_word = vocb_size
self.embedding = nn.Embedding(self.n_word, n_dim)
self.linear1 = nn.Linear(context_size*n_dim, 128)
self.linear2 = nn.Linear(128, self.n_word)
def forward(self, x):
emb = self.embedding(x)
emb = emb.view(1, -1)
out = self.linear1(emb)
out = F.relu(out)
out = self.linear2(out)
log_prob = F.log_softmax(out)
return log_prob
ngrammodel = NgramModel(len(word_to_idx), CONTEXT_SIZE, 100)
criterion = nn.NLLLoss()
optimizer = optim.SGD(ngrammodel.parameters(), lr=1e-3)
這個(gè)模型需要傳入的參數(shù)是所有的單詞數(shù)胶滋,預(yù)測單詞需要的前面單詞數(shù),即CONTEXT_SIZE悲敷,詞向量的維度。
然后在向前傳播中后德,首先傳入單詞得到詞向量,比如在該模型中傳入兩個(gè)詞瓢湃,得到的詞向量是(2, 100),然后將詞向量展開成(1, 200)箱季,然后傳入一個(gè)線性模型,經(jīng)過relu激活函數(shù)再傳入一個(gè)線性模型藏雏,輸出的維數(shù)是單詞總數(shù)拷况,可以看成一個(gè)分類問題,要最大化預(yù)測單詞的概率赚瘦,最后經(jīng)過一個(gè)log softmax激活函數(shù)粟誓。
然后定義好模型、loss以及優(yōu)化函數(shù)起意。
訓(xùn)練
for epoch in range(100):
print('epoch: {}'.format(epoch+1))
print('*'*10)
running_loss = 0
for data in trigram:
word, label = data
word = Variable(torch.LongTensor([word_to_idx[i] for i in word]))
label = Variable(torch.LongTensor([word_to_idx[label]]))
# forward
out = ngrammodel(word)
loss = criterion(out, label)
running_loss += loss.data[0]
# backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Loss: {:.6f}'.format(running_loss / len(word_to_idx)))
接著進(jìn)行訓(xùn)練鹰服,一共跑100個(gè)epoch,在每個(gè)epoch中揽咕,word代表著預(yù)測單詞的前面兩個(gè)詞悲酷,label表示要預(yù)測的詞,然后記住需要將他們轉(zhuǎn)換成Variable亲善,接著進(jìn)入網(wǎng)絡(luò)得到結(jié)果设易,然后通過loss函數(shù)得到loss進(jìn)行反向傳播,更新參數(shù)蛹头。
訓(xùn)練完100個(gè)epoch后的結(jié)果如下
我們發(fā)現(xiàn)loss已經(jīng)降到了0.37顿肺,也可以通過預(yù)測來檢測我們的模型是否有效
word, label = trigram[3]
word = Variable(torch.LongTensor([word_to_idx[i] for i in word]))
out = ngrammodel(word)
_, predict_label = torch.max(out, 1)
predict_word = idx_to_word[predict_label.data[0][0]]
print('real word is {}, predict word is {}'.format(label, predict_word))
得到的如下結(jié)果
可以發(fā)現(xiàn)我們能夠準(zhǔn)確地預(yù)測這個(gè)單詞。
以上我們介紹了如何通過最簡單的單邊 N-Gram 模型預(yù)測單詞渣蜗,還有一種復(fù)雜一點(diǎn)的N-Gram模型通過雙邊的單詞來預(yù)測中間的單詞屠尊,這種模型有個(gè)專門的名字,叫 Continuous Bag-of-Words model (CBOW)耕拷,具體的內(nèi)容差別不大讼昆,就不再細(xì)講了,代碼的實(shí)現(xiàn)放在了github上面斑胜。
下一章我們將講一下如何使用LSTM來做自然語言處理控淡。
本文代碼已經(jīng)上傳到了github上
歡迎查看我的知乎專欄嫌吠,深度煉丹
歡迎訪問我的博客