每天五分钟机器学习算法模型实战:通过文本中当前词预测下一个

每天五分钟机器学习算法模型实战:通过文本中当前词预测下一个
2019年10月09日 13:27 千千子心

本专栏代码

github代码

本文重点

语言模型的简单来说就是判断一句话是正常的概率是多少?那么如何计算一句话的概率呢?我们可以使用下面的公式:

那么现在的问题就变成了,我们如何预测

一种方法是使用n-gram方法

这是一种基于词统计的语言模型,我们可以通过语料数据的统计从而算出来P,但是n-gram有一些问题

n-gram的问题

问题一:不可以解决长期依赖的问题

它上程序课,然后它需要电脑,programming和computer之间是相互依赖的,但是距离比较远,所以这个解决不了长期依赖的问题

问题二:相同的词不能共享权重

bought和purchased具有相同的上下文,但是n-gram不能让二者共享权重,所以这也是n-gram的问题

问题三:中心词不能作为上下文的条件

n-gram可以使用前面的词预测后面的词,但是不可以使用中心词预测上下文的词

如果我们在数据集中见到了Dr.Gertrude Smith的时候,我们使用n-gram不可以共享我们之前见到的Dr.Jane Smith

解决问题三

假如现在给定上下文为giving a然后预测后面的单词的概率,其中b表示偏置项(每个词的先验可能),然后w1,a表示在a条件下,每一个词的可能性,w2,giving表示在giving的条件下,每一个词的可能性,然后加起来就是在giving a条件下后一个词的可能性,加起来就使得giving a绑定在了一起,这样可以使得giving a共同的发挥作用,我们可以看到最可能的就是gift,我们可以把它变成网络的形式,如下所示

giving下面的蓝色向量(可以看作是特征),对应于w2,giving,a下面的蓝色向量(可以看作是特征),对应于w1,giving。

相对于n-gram基于统计的方式,这里使用特征的方式,特征化模型的计算概率用不同的办法,它们会计算上下文的特征,并基于特征来计算概率,对于n-gram来说Dr.jane和Dr Gertrude完全是两个不同的东西,而基于特征的方式,可以最大限度的使得二者的scores相近,这样就当Dr.Gertrude Smith和Dr .jane Gertrude之间共享。

中心词w嵌入在上下文h中,为和w相似的w'应该也可以嵌入到h中,但是n-gram只是统计了w的情况,很难泛化到w'中。

问题二

上面的模型是线性模型,我们知道线性模型没有特征组合,所以上面的模型没有办法解决问题二,那么如何才能解决问题二呢?那么我们可以使用非线性的方式,从而完成特征组合

我们将giving和a通过并列连接,然后通过非线性激活,从而得到紫色的特征组合,然后完成最终的分类,此时就可以解决问题二了,这样的方式就会出现下面的情况:

当我们使用这个模型的时候,就会出现相近的词具有相同的词编码,这是因为我们并没有将二者加起来,而是连起来了,这样然后预测的下一个词相同,这就会导致相近的词会有相同的词向量编码。

这是n-gram所不能处理的,原因是:bought和purchased 是意思相同的词,然后car和bicyle是类型相同的词,但是n-gram仅仅进行表面的统计,所以bought和purchased没有共享任何的参数,这个意思就是说二者的意思相近,但是n-gram却把它当成了完全不同的东西,而使用这样的模型可以使得相近的词具有相同的向量

代码实现

模型搭建

参数

生成文本

数据处理

准备

训练

测试

根据训练模型生成句子

{!-- PGC_COLUMN --}import torch from torch import nn from collections import defaultdict import math import random class My_net(nn.Module): def __init__(self,nwords,emb_size,hid_size,num_hist): super(My_net,self).__init__() self.embedding=nn.Embedding(nwords,emb_size) self.fnn=nn.Seuential( nn.Linear(num_hist*emb_size,hid_size), nn.Dropout(True), nn.Linear(hid_size,nwords) ) def forward(self,x): emb=self.embedding(x) feat=emb.view(emb.size(0),-1) logit=self.fnn(feat) return logit N=2#两个单词预测下一个词 EMB_SIZE=128 H_SIZE=128 MAZ_LEN=50 w2i=defaultdict(lambda:len(w2i)) S=w2i[''

'']#既做开始又做结束 UNK=w2i[''

''] MAX_LEN=100 def generate_sent(): hist=[S]*N sent=[] while True: new_hist=torch.LongTensor([hist]) logits=model(new_hist) prob = nn.functional.softmax(logits) next_word = prob.multinomial(1).data[0,0] if next_word == S or len(sent) == MAX_LEN: break sent.append(next_word) hist = hist[1:] + [next_word] return sent def read_dataset(filename): with open(filename,''r'') as f: for line in f: yield[w2i[x] for x in line.strip().split('' '')] train=list(read_dataset(''datalab/39811/train.txt'')) w2i=defaultdict(lambda : UNK,w2i) dev=list(read_dataset(''datalab/39811/valid.txt'')) i2w={v:k for k,v in w2i.items()} nwords=len(w2i) model=My_net(nwords=nwords,emb_size=EMB_SIZE,hid_size=H_SIZE,num_hist=N) optimizer=torch.optim.Adam(model.parameters(),lr=0.001) criterion=nn.CrossEntropyLoss() for epoch in range(10): random.shuffle(train) model.train() train_words,train_loss=0,0.0 for sent_id,sent in enumerate(train):#遍历每一条文本 hist=[S]*N#[0,0] all_histories=[]#一条文本的所有特征[[x1,x2],[x2,x3],..] all_targets=[]#一条文本的所有的标签[y1,y2,y3...] for next_word in sent+[S]: all_histories.append(hist) all_targets.append(next_word) hist=hist[1:]+[next_word] all_histories=torch.LongTensor(all_histories) tag=torch.tensor(all_targets) logits=model(all_histories) loss = criterion(logits,tag) train_loss+=loss.item() train_words+=len(sent)#样本的个数 optimizer.zero_grad() loss.backward() optimizer.step() print(''epoch %s ,loss %.4f''%(epoch,train_loss/train_words)) model.eval() dev_words,dev_loss=0,0.0 for sent_id,sent in enumerate(dev): hist=[S]*N all_histories=[] all_targets=[] for next_word in sent+[S]: all_histories.append(list(hist)) all_targets.append(next_word) hist=hist[1:]+[next_word] all_histories=torch.LongTensor(all_histories) tag=torch.tensor(all_targets) logits=model(all_histories) loss=criterion(logits,tag) dev_loss+=loss.item() dev_words+=len(sent) print(''devepoch %s ,loss %.4f''%(epoch,dev_loss/dev_words)) for _ in range(5): sent=generate_sent() print('' ''.join([i2w[x.item()] for x in sent]))

代码解析

这里往模型中的输入的格式为[[a,b],[a,b],[]...]它的维度为[batch,2],每个样本有两个单词,因为这里n=2,然后经过embedding层之后,它的维度变为[batch,2,词向量的维度],然后我们把它放到全连接神经网络中,全连接神经网络为了可以接收它,输入层的维度应该是2*词向量的维度,也就是num_hist*emb_size。

数据集中一句话是一个样本,这里我们对一句话前后添加

一句话

,

表示一句话的开始和结束,当然也要把它放到字典中,第一个样本是

,标签是word1,最后一个样本是wordn-1,wordn标签是

,一句话的样本数是n

在写程序的时候,我们要注意list和tensor之间的处理,二者必须变成相同的类型才可以处理,个人建议,先处理成网络模型喜欢的格式[[a,b],[a,b],[]...],此时是list,然后再转成tensor,这样效果比较好,也就是只有放到网络模型的时候再使用tensor,平时就使用list就挺好的

程序的最后是根据训练好的模型生成文本,生成5句话,每句话100个词,具体来说就是从

开始输入到模型中,不断的预测,然后将预测的作为输入继续预测,最终生成一个100个词的文本,为了具有随机性,我们使用

next_word = prob.multinomial(1).data[0,0]

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部