统计语言模型:自编码、自回归、Word2Vec(CBOW、Skip-Gram)

统计语言模型:把语言(词的序列)看做一个随机事件,并赋予相应的概率来描述其属于某种语言集合的可能性。

1.自编码(Auto Encoding,AE)

自编码语言模型:使用自编码神经网络进行训练的模型,即该模型的任务是使输出的句子尽量靠近输入的句子。

​ 在BERT模型中,自编码语言模型的用法是:在输入句子随机找一个或几个子词,用掩码标识将该子词替换掉,再将句子输入模型,让模型输出原始输入句子。除BERT外,训练词向量的Word2Vec模型也属于自编码语言模型。

优点:能够很好地编码上下文语义信息(考虑句子的双向信息)。

缺点:在BERT模型的自编码神经网络中,使用带有[MASK]标识的数据进行预训练,在预训练完成之后,还要使用不带[MASK]标识的数据进行微调。这会导致预训练数据与微调阶段数据不一致的问题。这种问题又叫“BERT的独立性假设”,即如果被[MASK]替换的子词只与句子中的上下文语义相关,与其他被[MASK]替换的子词语义无关,则BERT模型的预训练方式是合理的。但实际上,被[MASK]替换的子词之间也会有语义相关。很显然BERT模型的预训练方式没有学习到被[MASK]替换的子词间的语义关系,这会导致模型在微调阶段无法表现出太好的效果。此外,对于生成式问题,自编码模型表现不好。

2.自回归(Auto Regressive,AR)

自回归语言模型:依据前/后出现的子词来预测当前时刻的子词,代表有ELMo模型、GPT模型等。另外,带有RNN特性的一系列模型都可以被归类为自回归语言模型。

优点:善于输出完整的句子或文章,天然适合生成式任务的处理。

缺点:只能利用单向语义,不能同时利用上下文信息。

3.Word2Vec

​ 将单词映射/嵌入(Embedding)到一个新的空间,形成词向量,以此来表示词的语义信息,在这个新的空间中,语义相同的单词距离很近。

​ 一篇文章有3000个单词,就会有3000个1x3000的one-hot向量,每个one-hot向量表示一个单词。我们需要将其降维到300,就将隐藏层的神经元个数设定为300个,最后在链接3000个output,代表全文3000个单词,通过softmax输出该输入的单词是这3000个单词每一个的概率值,加总为1。通过输入和输出向量间的误差值来优化网络参数。经过不断的优化,损失值最小化后,输入层和隐藏层之间3000x300的权重矩阵,对应输入层中3000单词的向量,每个单词被表示为一个1x300的向量。

3.1 CBOW(Continous Bags Of Words,CBOW)

​ CBOW根据某个词前面的n个词、或者前后各n个连续的词,来计算某个词出现的概率,即给定上下文,来预测input word。相比Skip-Gram,CBOW更快一些。

​ 举例:“小明”,“喜欢”,“吃”,“草莓”。

小明:[1,0,0,0]

喜欢:[0,1,0,0]

吃:[0,0,1,0]

草莓:[0,0,0,1]

预测“吃”,则input为[1,1,0,1],上下文的加总。

3.2 Skip-Gram

​ 以某个词为中心,分别计算该中心词前后可能出现其他词的各个概率,即给定input word来预测上下文。

​ 举例:“小明”,“喜欢”,“吃”,“草莓”。

小明:[1,0,0,0]

喜欢:[0,1,0,0]

吃:[0,0,1,0]

草莓:[0,0,0,1]

input为[0,0,1,0],输出为:[1,0,0,0],[0,1,0,0],[0,0,0,1]

学习简单实现:(代码出处在注释中)

# -*- coding: utf-8 -*-
# @Time : 2022/1/2 14:31
# @Author : hp
'''
代码出处:https://wmathor.com/index.php/archives/1443/
        https://github.com/graykode/nlp-tutorial
'''
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.utils.data as Data

dtype = torch.FloatTensor
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

sentences = ["jack like dog", "jack like cat", "jack like animal",
             "dog cat animal", "banana apple cat dog like", "dog fish milk like",
             "dog cat animal like", "jack like apple", "apple like", "jack like banana",
             "apple banana jack movie book music like", "cat dog hate", "cat dog like"]
#预处理
#分词
word_sequence = " ".join(sentences).split()  # ['jack', 'like', 'dog', 'jack', 'like', 'cat', 'animal',...]
#构建词表
vocab = list(set(word_sequence))  # build words vocabulary
#构建单词索引
word2idx = {w: i for i, w in enumerate(vocab)}  # {'jack':0, 'like':1,...}

# Word2Vec Parameters
batch_size = 8
embedding_size = 2  # 一个单词用一个2维向量表示
C = 2  # 滑动窗口大小为2,左右各取两个词
voc_size = len(vocab)

#生成一一配对的中心词和背景词的索引构成的2列n行的矩阵
skip_grams = []
for idx in range(C, len(word_sequence) - C):
    center = word2idx[word_sequence[idx]]  # 中心词
    context_idx = list(range(idx - C, idx)) + list(range(idx + 1, idx + C + 1))  # context word idx
    context = [word2idx[word_sequence[i]] for i in context_idx]
    for w in context:
        skip_grams.append([center, w])

#构建ont-hot表示
def make_data(skip_grams):
    input_data = []
    output_data = []
    for i in range(len(skip_grams)):
        #np.eye()构建对角全为一的矩阵,根据skip_grams每行第一列的值,取出相应的行,追加到input的列表中
        input_data.append(np.eye(voc_size)[skip_grams[i][0]])
        #输出为类别值
        output_data.append(skip_grams[i][1])
    return input_data, output_data


input_data, output_data = make_data(skip_grams)
input_data, output_data = torch.Tensor(input_data), torch.LongTensor(output_data)
dataset = Data.TensorDataset(input_data, output_data)
loader = Data.DataLoader(dataset, batch_size, True)


# Model
class Word2Vec(nn.Module):
    def __init__(self):
        super(Word2Vec, self).__init__()

        # W and V is not Traspose relationship
        self.W = nn.Parameter(torch.randn(voc_size, embedding_size).type(dtype))
        self.V = nn.Parameter(torch.randn(embedding_size, voc_size).type(dtype))

    def forward(self, X):
        # X : [batch_size, voc_size] one-hot
        # torch.mm only for 2 dim matrix, but torch.matmul can use to any dim
        hidden_layer = torch.matmul(X, self.W)  # hidden_layer : [batch_size, embedding_size]
        output_layer = torch.matmul(hidden_layer, self.V)  # output_layer : [batch_size, voc_size]
        return output_layer


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

# Training
for epoch in range(2000):
    for i, (batch_x, batch_y) in enumerate(loader):
        batch_x = batch_x.to(device)
        batch_y = batch_y.to(device)
        pred = model(batch_x)
        loss = criterion(pred, batch_y)
        if (epoch + 1) % 1000 == 0:
            print(epoch + 1, i, loss.item())

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
#词是二维表示,二维坐标轴可视化
for i, label in enumerate(vocab):
    W, WT = model.parameters()
    x, y = float(W[i][0]), float(W[i][1])
    plt.scatter(x, y)
    plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom')
plt.show()

你可能感兴趣的:(NLP学习笔记,语言模型,回归,word2vec,人工智能)