十二、神经网络语言模型

  • Post author:
  • Post category:其他




神经网络语言模型



1.NNLM的原理



1.1 语言模型

  • 假设

    S

    表示某个有意义的句子,由一串特定顺序排列的词



    w

    1

    ,

    w

    2

    ,

    .

    .

    ,

    w

    n

    w_1,w_2,..,w_n







    w










    1


















    ,





    w










    2


















    ,




    .


    .


    ,





    w










    n





















    组成,

    n

    是句子的长度。目的:计算

    S

    在文本中(语料库)出现的可能性

    P

    (

    S

    )。

在这里插入图片描述



1.2 神经网络语言模型

  • 直接从语言模型出发,将模型最优化过程转化为求词向量表示的过程.

    在这里插入图片描述



2. NNLM的网络结构



2.1 NNLM的结构图

  • NNLM网络结构包括输入层、投影层,隐藏层和输出层

    在这里插入图片描述



2.2 NNLM的计算过程

  • 根据前面的n-1个单词,预测第n个单词的概率

    在这里插入图片描述



2.3 环境

python3.7
torch==1.8.0



2.4 步骤


步骤一:读取数据

# 加载数据
def load_data():
    sentences = ['i like dog', 'i love coffee', 'i hate milk']
    word_list = " ".join(sentences).split()  # ['i', 'like', 'dog', 'i', 'love', 'coffee', 'i', 'hate', 'milk']
    word_list = list(set(word_list))  # 去除重复的单词

    # {'hate': 0, 'dog': 1, 'milk': 2, 'love': 3, 'like': 4, 'i': 5, 'coffee': 6}
    word_dict = {w: i for i, w in enumerate(word_list)}

    # {0: 'like', 1: 'dog', 2: 'coffee', 3: 'hate', 4: 'i', 5: 'love', 6: 'milk'}
    number_dict = {i: w for i, w in enumerate(word_list)}
    return word_dict, number_dict,sentences


步骤二:实现mini-batch迭代器

# 实现一个mini-batch迭代器
def make_batch(sentences):
    input_batch = []
    target_batch = []

    for sen in sentences:
        word = sen.split()  # ['i', 'like', 'dog']
        input = [word_dict[n] for n in word[:-1]]  # 列表对应的数字序列,一句话中最后一个词是要用来预测的,不作为输入
        target = word_dict[word[-1]]  # 每句话的最后一个词作为目标值

        input_batch.append(input)
        target_batch.append(target)

    return input_batch, target_batch  #


步骤三:超参数设置和mini-batch组装

# 超参数
dtype = torch.FloatTensor
n_class = len(word_dict)  # 词典|V|的大小,也是最后分类的类别,这里是7
# NNLM(Neural Network Language Model) Parameter,模型的参数
n_step = len(sentences[0].split()) - 1  # 文中用n_step个词预测下一个词,在本程序中其值为2
n_hidden = 2  # 隐藏层神经元的数量
m = 2  # 词向量的维度

# mini-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):
        """
        C: 词向量,大小为|V|*m的矩阵
        H: 隐藏层的weight
        W: 输入层到输出层的weight
        d: 隐藏层的bias
        U: 输出层的weight
        b: 输出层的bias
        1. 首先将输入的 n-1 个单词索引转为词向量,然后将这 n-1 个词向量进行 concat,形成一个 (n-1)*w 的向量,用 X 表示
        2. 将 X 送入隐藏层进行计算,hidden = tanh(d + X * H) [3,4]  * [4 * 2]
        3. 输出层共有|V|个节点,每个节点yi表示预测下一个单词i的概率,y的计算公式为y = b + X * W + hidden * U
        n_step: 文中用n_step个词预测下一个词,在本程序中其值为2
        n_hidden: 隐藏层(中间那一层)神经元的数量
        m: 词向量的维度
        """
        super(NNLM, self).__init__()
        # 7 * 2
        self.C = nn.Embedding(n_class, m)  # 词向量随机赋值,代替了先使用one-hot,然后使用matrix C映射到词向量这一步
        self.H = nn.Parameter(torch.randn(n_step * m, n_hidden).type(dtype)) # 4 * 2
        self.W = nn.Parameter(torch.randn(n_step * m, n_class).type(dtype)) # 4 * 7
        self.d = nn.Parameter(torch.randn(n_hidden).type(dtype)) # 2
        self.U = nn.Parameter(torch.randn(n_hidden, n_class).type(dtype))
        self.b = nn.Parameter(torch.randn(n_class).type(dtype)) # 词典的大小
        print("---")

    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]
        hidden_out = torch.tanh(self.d + torch.mm(X, self.H))
        output = self.b + torch.mm(X, self.W) + torch.mm(hidden_out, self.U)
        return output


步骤五:实例化模型和预测

# 实例化模型,优化器,损失函数
model = NNLM()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)

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

        loss = criterion(output, batch_y)
        if (epoch + 1) % 1000 == 0:
            print('Epoch:', '%04d' % (epoch + 1), 'cost = ', '{:.6f}'.format(loss))
        loss.backward()
        optimizer.step()

# Test
predict = model(input_batch).data.max(1, keepdim=True)[1]
# squeeze():对张量的维度进行减少的操作,原来:tensor([[2],[6],[3]]),squeeze()操作后变成tensor([2, 6, 3])
print([sen.split()[:n_step] for sen in sentences], '->', [number_dict[n.item()] for n in predict.squeeze()])



2.6 运行结果

运行结果:
---
Epoch: 1000 cost =  0.311220
Epoch: 2000 cost =  0.044960
Epoch: 3000 cost =  0.011267
Epoch: 4000 cost =  0.004640
Epoch: 5000 cost =  0.002216
[['i', 'like'], ['i', 'love'], ['i', 'hate']] -> ['dog', 'coffee', 'milk']



版权声明:本文为GUANGZHAN原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。