nlp-tutorial代码注释1-1,语言模型、n-gram简介

Gretel ·
更新时间:2024-09-21
· 573 次阅读

本文知识点介绍来自斯坦福大学CS224N课程lecture6语言模型部分

语言模型

语言模型可以预测一个序列接下来会出现什么词。即给定一个单词序列,语言模型计算出下一个单词是词汇表中各个词的概率分布。
还有一种理解是语言模型可以计算一个句子出现的概率,计算公式如下(条件概率): 在这里插入图片描述

n-gram语言模型:

含义:通过前(n-1)个词去预测某个单词。
n-gram有一个基本的假设:假设某个词的出现仅取决于它前面的n-1个单词。根据条件概率公式,某个词的出现概率计算公式为:
在这里插入图片描述
计算上图n-gram和(n-1)-gram的比例是通过计算他们在大型语料库中出现的次数的比例:
在这里插入图片描述
举个例子:As the proctor started the clock, the students open their ___. 预测这句话空格处的词,假设这里是一个4-gram的语言模型。那么就是通过前三个词:students open their去预测下一个词,语料库中students open their books出现的次数最多,故4-gram模型预测的空格处的词就应该是books。
在这里插入图片描述
然而回顾整个句子,前文出现了proctor,所以这个空是exams的概率应该最大,而4-gram模型只考虑前三个词,忽略了前面的词proctor,故导致预测错误。n-gram语言模型的一个缺点:每次预测时只考虑前n-1个词,忽略了再之前的上下文。

sparsity problem:

n-gram模型对于下图的计算式会出现两个问题:
在这里插入图片描述
1、分子为0,即语料库中没有相应的n元组,一个解决方法是在一开始给每一个单词增加很小的次数,这个方法叫smoothing;
2、分母为0,即语料库中没有相应的n-1元组,一个解决方法是退化成n-1-gram,即考虑所预测单词前n-2个单词出现的次数。
更糟的是,对于上一个问题,即每次只考虑前n-1个词而忽略了更之前的上下文,可能有个解决方法是增大N。而增大N会导致更严重的sparsity problem。通常N不超过5。

storage problem:

增加N会增加模型大小。
在这里插入图片描述

语言模型可以用作文本生成,以下是一个3-gram的语言模型生成的一段文本。但显然,这段文本非常的不连贯且奇怪,因为它的每个词仅仅是依据前两个词生成的,增大N可以改善这个问题,而增大N又会带来很多副作用:sparsity、storage problem。
在这里插入图片描述
利用n-gram训练词向量的代码详细注释:
(源代码为github上nlp-tutorial项目,项目地址:nlp-tutorial)

import numpy as np #引入numpy库 import torch #引入torch import torch.nn as nn #torch.nn是torch的神经网络库 import torch.optim as optim #torch.optim是优化库,包含很多优化函数 from torch.autograd import Variable #现在的pytorch版本variable已经回归tensor了,直接用tensor即可 dtype = torch.FloatTensor sentences = [ "i like dog", "i love coffee", "i hate milk"] #训练集 word_list = " ".join(sentences).split() #先用" ".join(),以空格为分隔,将sentences中的句子连接起来,再用split()以空格为分割点,将每个词分出来 word_list = list(set(word_list)) #先用set合并重复的单词,再用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 # NNLM Parameter n_step = 2 # n-1 in paper #步长,即根据多少个词来预测下一个词,这里是根据前两个词预测第三个词 n_hidden = 2 # h in paper #隐藏层神经元个数 m = 2 # m in paper #词向量维度 def make_batch(sentences): #作用是将训练集中的句子的最后一个词和前面的词分开 input_batch = [] #空列表,用来存放输入 target_batch = [] #存放一次输入对应的输出 for sen in sentences: #对于训练集中的每个句子 word = sen.split() #先将句子中每个单词按空格分开 input = [word_dict[n] for n in word[:-1]] #[:-1]切片,即输入是一直到最后一个词,最后一个词不要 target = word_dict[word[-1]] #target是最后一个词 input_batch.append(input) #将分离出来的input添加到输入列表 target_batch.append(target) #将分离出来的target添加到标记列表 return input_batch, target_batch #返回输入和标记列表 # Model class NNLM(nn.Module): def __init__(self): super(NNLM, self).__init__() #文章中计算式为y = b + Wx + U * tanh(d + Hx),这里设置各个参数 self.C = nn.Embedding(n_class, m) #C是词向量矩阵,行数是单词的数量,列数是词向量的维度 #H是输入层到隐层的权重矩阵,维数是(输入的个数 * 隐层单元个数),输入的个数是(输入的单词个数 * 词向量维数),即n_step * m self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(dtype)) #W是一个直连输入层和输出层的权重矩阵,维数是(输入的个数 * 输出层单元个数) self.W = nn.Parameter(torch.randn(n_step * m, n_class).type(dtype)) #d是输入层到隐层的偏置常数,维数是(n_hidden * 1) self.d = nn.Parameter(torch.randn(n_hidden).type(dtype)) #U是隐层到输出层的权重矩阵,维数是(隐层单元个数 * 输出层单元个数),要输出词典中每一个单词是下一个词的概率,输出的个数应该是单词的个数 self.U = nn.Parameter(torch.randn(n_hidden, n_class).type(dtype)) #b是隐层到输出层的偏置常数,维数是(输出层单元个数 * 1) self.b = nn.Parameter(torch.randn(n_class).type(dtype)) def forward(self, X): #前向传播,计算公式y = b + Wx + U * tanh(d + Hx) X = self.C(X) #输入的X是单词的序号,序号X对应的词向量是词向量矩阵C的第X行,取出此词向量 X = X.view(-1, n_step * m) #reshape矩阵X tanh = torch.tanh(self.d + torch.mm(X, self.H)) #torch.mm是矩阵相乘 output = self.b + torch.mm(X, self.W) + torch.mm(tanh, self.U) #根据上述公式计算输出 return output model = NNLM() criterion = nn.CrossEntropyLoss() #损失函数为交叉熵损失函数 optimizer = optim.Adam(model.parameters(), lr=0.001) #使用Adam算法进行优化 input_batch, target_batch = make_batch(sentences) #使用make_batch从训练集中获得输入和对应的标记 input_batch = Variable(torch.LongTensor(input_batch)) #这两行是将输入变成variable,但现在的torch版本直接用tensor即可 target_batch = Variable(torch.LongTensor(target_batch)) # Training for epoch in range(5000): #训练5000次 optimizer.zero_grad() #每次训练前清除梯度缓存 output = model(input_batch) #对模型输入input_batch,获得输出output # output : [batch_size, n_class], target_batch : [batch_size] (LongTensor, not one-hot) loss = criterion(output, target_batch) #使用损失函数计算loss,损失函数的输入为模型的输出output和标记target if (epoch + 1)%1000 == 0: #每优化1000次打印一次查看cost的变化 print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss)) loss.backward() #反向传播、自动求导 optimizer.step() #优化、更新参数 # Predict predict = model(input_batch).data.max(1, keepdim=True)[1] #用.max[1]挑出输出中最大的那一个 # Test print([sen.split()[:2] for sen in sentences], '->', [number_dict[n.item()] for n in predict.squeeze()])
作者:yqy2001



n-gram tutorial 模型 nlp

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章