02_语言模型(n-gram,NNLM)学习总结(不对的地方欢迎留言指正)

语言模型 : 就是计算该序列的概率.(通俗讲:就是计算一句话出现的概率,从而判断这是不是一个正常的句子.)
                (解决词与词之间的关系)
算法为模型服务,让模型更高效.
语言模型分为:
1.基于 统计的语言模型 :n-gram
2.基于 神经网络的语言模型
  • 2.1.基于 前馈 神经网络的语言模型: NNLM
    • 上一层的输出只作为下一层的输入
  • 2.2.基于 循环 神经网络的语言模型: RNNLM
    • 又叫反馈神经网络
    • 当前层的输出,除了作为下一层的输入,还返回回来重新作为输入
RNN:2层或3层已经是非常深的神经网络了,因为各个时间步直接相互依赖,求一个而牵扯到全部. 最常用的RNN就是双层双向的
RNN调优:在最后一层每一步的隐状态(h)上,加上一个5层的神经网络.(因为各个神经网络之间没有依赖关系)

RNN:循环神经网络(反馈神经网络):当前层的输出除了作为下一层的输入,还返回回来重新作为输入
CNN:卷积神经网络
NN:前馈神经网络:上一层的输出 只作为 下一层的输入,

统计的语言模型:
缺点:1.越往后概率越小,甚至为0 (第w999这个词出现,依赖于w1 ....w998)
        2.越往后计算量越大,越复杂
    造成这两个缺点的主要原因就是依赖的太多了
解决办法: n-gram
      加一个窗口限制n,n为几那么就依赖几个词
    假设:n=2
        p(w1,w2,w3,w4,w5) = p(w1)*p(w2|w1) * p(w3|w2) * p(w4|w3) * p(w5|w4)
    n为多少最合适?
n大了,那么依赖的前边的词就多了,那么提取的信息就更多更全,但是计算量大,计算出的概率小
n小了,依赖的前边的词就少了,提取到的信息就少,但是计算量小,为0的概率也小.
一般情况下: n取2/3.  n=2叫bigram. n=3叫tregram

基于统计的语言模型:
要判断一段文字是不是一句自然语言,可以通过确定这段文字的概率分布来表示其存在的可能性。 语言模型中的词是有顺序的,给定 m 个词看这句话是不是一句合理的自然语言,关键是看这些词的排列顺序是不是正确的。所以统计语言模型的基本思想是计算条件概率。比如一段文字有 w1,w2,w3...,wm m 个词组成,看看是不是一句话,就可以用下面的公式计算其联合概率:
P(w1,w2,w3...,wm)=P(w1)P(w2|w1)P(w3|w1,w2)...P(wm|w1,w2,...wm-1)P(w1,w2,w3...,wm)=P(w2|w1)P(w3|w1,w2)...P(wm|w1,w2,...wm-1)

n-gram 语言模型解决了用普通的条件概率计算句子概率参数太多难以训练的问题,理论上来说 n 取得越大保留的词序信息就越多,生成的句子越合理,但如果 n 取得比较大,同样会面临数据稀疏的问题, n-gram 模型 解决了参数太多难以训练的问题 ,但 没有解决数据稀疏的问题
之所以存在数据稀疏的问题,是因为我们想把 n 取得大一点来更多的保留词序信息,但 n 太大会导致 w1,w2,w3,...wnw1,w2,w3,...wn 这个序列在语料中很少出现或者根本不出现,(根据经验也知道太长的一句话在语料中出现的次数不是那么多,要远远小于 2 3 个词出现的次数)造成计算出的条件概率接近于 0 ,那算出的句子的概率也是接近于 0 ,这个语言模型就没法用了,这是数据稀疏导致的训练出的语言模型无法用的原因。
下面给出一元模型,二元模型,三元模型的定义:
当 n=1, 一个一元模型(unigram model)即为 :
当 n=2, 一个二元模型(bigram model)即为 :
当 n=3, 一个三元模型(trigram model)即为
然后下面的思路就很简单了,在给定的训练语料中,利用贝叶斯定理,将上述的条件概率值( 因为一个句子出现的概率都转变为右边条件概率值相乘了 )都统计计算出来即可。这里先给出公式:
n-gram存在的问题:
问题1:哪怕是正确的句子,他的句子联合概率也很小
问题2:统计的方式并不是最优的计算方式
优点: 1.采用极大似然估计,参数易训练
        2.完全包含了前n-1个词的全部信息,
        3.可解释性强,直管易理解
缺点: 1.缺乏长期依赖,只能建模到前n-1个词
        2.随着n的增大,参数空间呈指数增长 
        3.数据稀疏
        4.单纯的基于统计频次,泛化能力差

NNLM :基于前馈神经网络的语言模型: 存在的问题:模型复杂
NNLM 是 语言模型(p(w1,w2,w3,w4,w5) = p(w1)*p(w2|w1)*p(w3|w2) ...... )
输入层, 隐藏层, 输出层
常用的信息融合的方式:1.求和 2.求和求平均 3.拼接
残差网络是:将输入输出相加  公式:y = x + f(x)
残差网络的作用:防止梯度消失
y = w3 * x + tanh(x * w1)   w1:是权重不是输入的w1
求偏导范围: sigmoid(0,0.25) , tanh(0,1), relu: 0/1

任何一个算法模型:
1.有数据x
2.随机w和b等参数
3.通过数据x和参数w,b 计算出预测值h
4.通过计算出的h和已知的真实标签y计算代价loss
5.为什么计算loss? 计算代价,是为了求参数的梯度
  • 梯度的方向指的是值上升最快的方向,这也是通常情况下,定义代价的时候,为什么要加 负号 的原因 ,怎么求梯度,拿代价对参数求偏导,则求出了该参数的梯度 
  • 代价越大,梯度越大.代价越小,梯度越小
6. w = w - learning_rate * 梯度

NNLM:编码词向量
通过训练一个语言模型,一旦语言模型训练完成了,那么这个嵌入矩阵C,也就跟着训练完了,那么我们就可以拿着这个C去做任何一个中文字的嵌入.
所有的语言模型都不是为了得到最终输出,而是为了得到中间的某一层,或者说得到 词向量表C

需要人工打标签的是有监督,非人工打标签是无监督

NNLM代码:

# code by Tae Hwan Jung @graykode, modify by wmathor
import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.data as Data

dtype = torch.FloatTensor # torch  float类型的tensor对象 

sentences = [ "i like dog", "i love coffee", "i hate milk"]

word_list = " ".join(sentences).split() # ['i', 'like', 'dog', 'dog', 'i', 'love', 'coffee', 'i', 'hate', 'milk']
# 词汇表 起名:Vocab
word_list = list(set(word_list)) # ['i', 'like', 'dog', 'love', 'coffee', 'hate', 'milk']
word_dict = {w: i for i, w in enumerate(word_list)} # {'i':0, 'like':1, 'dog':2, 'love':3, 'coffee':4, 'hate':5, 'milk':6}
number_dict = {i: w for i, w in enumerate(word_list)} # {0:'i', 1:'like', 2:'dog', 3:'love', 4:'coffee', 5:'hate', 6:'milk'}
# 词汇表的长度
n_class = len(word_dict) # number of Vocabulary, just like |V|, in this task n_class=7

# NNLM(Neural Network Language Model) Parameter
# 前两个词预测第三个词
n_step = len(sentences[0].split())-1 # n-1 in paper, look back n_step words and predict next word. In this task n_step=2
# 隐藏层单元个数
n_hidden = 2 # h in paper
# 每个英文单词用2维表示
m = 2 # m in paper, word embedding dim

def make_batch(sentences):
  input_batch = []
  target_batch = []

  for sen in sentences:
    word = sen.split()
    input = [word_dict[n] for n in word[:-1]] # [0, 1], [0, 3], [0, 5]
    target = word_dict[word[-1]] # 2, 4, 6

    input_batch.append(input) # [[0, 1], [0, 3], [0, 5]]
    target_batch.append(target) # [2, 4, 6]

  return input_batch, target_batch

input_batch, target_batch = make_batch(sentences)
input_batch = torch.LongTensor(input_batch)
target_batch = torch.LongTensor(target_batch)

dataset = Data.TensorDataset(input_batch, target_batch)
loader = Data.DataLoader(dataset=dataset, batch_size=16, shuffle=True)

class NNLM(nn.Module):
  def __init__(self):
    super(NNLM, self).__init__()
    self.C = nn.Embedding(n_class, m)
    self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(dtype)) # 第一个权重W1
    self.W = nn.Parameter(torch.randn(n_step * m, n_class).type(dtype)) # 第三个权重 W3
    self.d = nn.Parameter(torch.randn(n_hidden).type(dtype)) # b1
    self.U = nn.Parameter(torch.randn(n_hidden, n_class).type(dtype)) # 第二个权重W2
    self.b = nn.Parameter(torch.randn(n_class).type(dtype)) # b2

  def forward(self, X):
    '''
    X: [batch_size, n_step]
    '''
    X = self.C(X) # [batch_size, n_step] => [batch_size, n_step, m]
    X = X.view(-1, n_step * m) # [batch_size, n_step * m]
    # torch.mm是矩阵相乘,但是只适合二维,多维会报错
    hidden_out = torch.tanh(self.d + torch.mm(X, self.H)) # [batch_size, n_hidden]
    output = self.b + torch.mm(X, self.W) + torch.mm(hidden_out, self.U) # [batch_size, n_class]
    return output

model = NNLM()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

# Training
for epoch in range(5000):
  for batch_x, batch_y in loader:
    optimizer.zero_grad()
    output = model(batch_x)

    # output : [batch_size, n_class], batch_y : [batch_size] (LongTensor, not one-hot)
    loss = criterion(output, batch_y)
    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()[:n_step] for sen in sentences], '->', [number_dict[n.item()] for n in predict.squeeze()])

文章中的图片是在网上找到的图,如有侵权请私信删除。本文借鉴了很多文章,仅作个人学习总结,如有总结错误,欢迎留言指正

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值