Python实现神经网络语言模型(代码详解)

目录 

一、原理图

二、代码

三、结果


引言

神经网络语言模型(Neural Network Language Model, NNLM)是利用神经网络计算词向量的方法,根据(w{t-n+1}...w{t-1})来预测(w{t})是什么词,即用前(n-1)个单词来预测第(n)个单词。


一、原理图


二、代码

参考TensorDataset和DataLoader解释

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from tqdm import tqdm
import numpy as np
import re

sentences = ["我爱你", "喜羊羊", "灰太狼"]  # sentences = ['我爱你', '喜羊羊', '灰太狼']

# 将输入的句子进行中文分字处理。它首先使用正则表达式找到句子中的中文字符,并将句子按照中文字符进行分割。然后,去除分割后的结果中的空白字符,最后返回分割后的中文字符列表。
def seg_char(sent):
    pattern = re.compile(r'([\u4e00-\u9fa5])')
    chars = pattern.split(sent)
    chars = [w for w in chars if len(w.strip()) > 0]
    return chars

# 对给定的句子列表进行中文分字处理,得到一个包含所有句子中汉字的二维数组。
chars = np.array([seg_char(i) for i in sentences])  # chars = [['我' '爱' '你'], ['喜' '羊' '羊'], ['灰' '太' '狼']]

# 将二维数组展平为一个一维数组。
chars = chars.reshape(1, -1)  # chars = [['我' '爱' '你' '喜' '羊' '羊' '灰' '太' '狼']]

# 通过去除数组中的空白字符和重复项,得到汉字的列表。
word_list = np.squeeze(chars)  # word_list = ['我' '爱' '你' '喜' '羊' '羊' '灰' '太' '狼']
word_list = list(set(word_list))  # word_list = ['灰', '太', '狼', '喜', '羊', '爱', '你', '我']

# 建立汉字与索引的映射关系,生成词典。 i=0—>灰;i=1—>太;i=2—>狼...i=7—>我
word_dict = {w: i for i, w in enumerate(word_list)}  # word_dict = {'灰': 0, '太': 1, '狼': 2, '喜': 3, '羊': 4, '爱': 5, '你': 6, '我': 7}

# 创建索引与汉字的反向映射关系,生成反向词典。
number_dict = {i: w for i, w in enumerate(word_list)}  # number_dict = {0: '灰', 1: '太', 2: '狼', 3: '喜', 4: '羊', 5: '爱', 6: '你', 7: '我'}

# 确定词汇表的大小。
n_class = len(word_dict)  # 词汇表的大小 n_class = 8

# NNLM 参数
n_step = 2  # 步数

# 将句子列表转换为神经网络模型训练所需的输入批次和目标批次。输入输出 one-hot 编码
def make_batch(sentences):  # sentences = ['我爱你', '喜羊羊', '灰太狼']
    input_batch = []
    target_batch = []

    # 遍历句子列表,对每个句子进行中文分字处理,得到汉字列表。
    for sen in sentences:  # sen = '灰太狼'

        # 对于每个句子,将汉字列表中的前n-1个字符作为输入,最后一个字符作为目标。
        word = seg_char(sen)  # word = ['灰', '太', '狼']
        input = [word_dict[n] for n in word[:-1]]  # 使用词典将汉字转换为对应的索引。input = [0, 1]
        target = word_dict[word[-1]]  # target = 2

        # 对输入和目标进行 one-hot 编码,生成输入批次和目标批次。
        # [tensor([[0., 0., 0., 0., 0., 0., 0., 1.], [0., 0., 0., 0., 0., 1., 0., 0.]]),
        #  tensor([[0., 0., 0., 1., 0., 0., 0., 0.], [0., 0., 0., 0., 1., 0., 0., 0.]]),
        #  tensor([[1., 0., 0., 0., 0., 0., 0., 0.], [0., 1., 0., 0., 0., 0., 0., 0.]])]
        input_batch.append(torch.eye(n_class)[input])

        # [tensor([0., 0., 0., 0., 0., 0., 1., 0.]), tensor([0., 0., 0., 0., 1., 0., 0., 0.]), tensor([0., 0., 1., 0., 0., 0., 0., 0.])]
        target_batch.append(torch.eye(n_class)[target])

    return input_batch, target_batch

# 将所有输入批次和目标批次合并为一个张量,并整理成模型需要的形状。
input_batch, target_batch = make_batch(sentences)

# tensor:(3, 16)
# tensor([[0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0., 0.],
#         [0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
#         [1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]])
input_batch = torch.cat(input_batch).view(-1, n_step * n_class)

# tensor:(3, 8)
# tensor([[0., 0., 0., 0., 0., 0., 1., 0.],
#         [0., 0., 0., 0., 1., 0., 0., 0.],
#         [0., 0., 1., 0., 0., 0., 0., 0.]])
target_batch = torch.cat(target_batch).view(-1, n_class)

# 定义模型
class NNLM(nn.Module):

    # 初始化函数: 定义了模型的各个层和激活函数
    def __init__(self):
        super(NNLM, self).__init__()
        self.linear1 = nn.Linear(n_step * n_class, 2)  # 全连接层, 输入大小为 n_step * n_class,输出大小为 2
        self.tanh = nn.Tanh()  # tanh 激活函数
        self.linear2 = nn.Linear(2, n_class)  # 另一个全连接层,输入大小为 2,输出大小为 n_class
        self.softmax = nn.Softmax(dim=1)  # softmax 激活函数,用于输出层

    # 前向传播函数: 定义了数据流的传递方式
    def forward(self, x):
        x = self.linear1(x)  # 输入 x 经过第一个全连接层 self.linear1 得到中间表示
        x = self.tanh(x)  # 中间表示经过 tanh 激活函数
        x = self.linear2(x)  # 经过第二个全连接层 self.linear2 得到输出
        x = self.softmax(x)  # 输出经过 softmax 激活函数,得到最终的预测结果
        return x

# 准备训练数据
train_dataset = TensorDataset(input_batch, target_batch)
train_loader = DataLoader(train_dataset, batch_size=1)

# NNLM(
#   (linear1): Linear(in_features=16, out_features=2, bias=True)
#   (tanh): Tanh()
#   (linear2): Linear(in_features=2, out_features=8, bias=True)
#   (softmax): Softmax(dim=1)
# )
model = NNLM()

# 定义损失函数和优化器
# 损失函数使用交叉熵损失函数 nn.CrossEntropyLoss(),用于计算模型预测结果与真实标签之间的差异
criterion = nn.CrossEntropyLoss()  # criterion = CrossEntropyLoss()

# 优化器使用 Adam 优化器 optim.Adam,用于更新模型的参数以最小化损失函数
# Adam (
#     Parameter Group 0
#     amsgrad: False
#     betas: (0.9, 0.999)
#     capturable: False
#     differentiable: False
#     eps: 1e-08
#     foreach: None
#     fused: None
#     lr: 0.001
#     maximize: False
#     weight_decay: 0
# )
optimizer = optim.Adam(model.parameters())

# 训练模型: 使用了一个循环来迭代执行多个 epoch(训练轮数)
epochs = 5000
for epoch in tqdm(range(epochs), desc='训练进度'):  # epoch = 0,..., epoch = 4
    total_loss = 0.0
    correct = 0
    total_samples = 0

    for inputs, targets in train_loader:
        optimizer.zero_grad()  # 首先,使用优化器的 zero_grad() 方法将模型参数的梯度归零,以准备计算新一轮的梯度。

        # 然后,通过模型前向传播计算得到模型的输出 outputs。
    # epoch = 0时:
        # outputs = tensor([[0.1091, 0.0567, 0.0587, 0.0967, 0.1380, 0.1759, 0.1786, 0.1863],
        #                   [0.1626, 0.0645, 0.0532, 0.1406, 0.0927, 0.1947, 0.1429, 0.1487],
        #                   [0.1081, 0.0481, 0.0837, 0.1257, 0.1359, 0.1466, 0.1929, 0.1590]],
        #                   grad_fn=<SoftmaxBackward0>)

    # epoch = 4时:
        # outputs = tensor([[0.1272, 0.1309, 0.1559, 0.0674, 0.1287, 0.0707, 0.1293, 0.1898],
        #                   [0.1423, 0.1347, 0.1282, 0.0570, 0.1251, 0.0887, 0.1523, 0.1716],
        #                   [0.1320, 0.1184, 0.1521, 0.0699, 0.1312, 0.0632, 0.1283, 0.2050]],
        #                  rad_fn=<SoftmaxBackward0>)
        outputs = model(input_batch)

        # 接着,计算模型的预测结果与真实标签之间的交叉熵损失,即模型在当前轮次的损失值 loss。
    # epoch = 0时:
        # loss = tensor(2.0923, grad_fn=<NllLossBackward0>)

    # epoch = 4时:
        # loss = tensor(2.0697, grad_fn=<NllLossBackward0>)
        loss = criterion(outputs, torch.max(target_batch, 1)[1])

        # 使用损失函数的 backward() 方法计算损失关于模型参数的梯度。
        loss.backward()

        # 最后,使用优化器的 step() 方法更新模型的参数,以最小化损失函数。
        optimizer.step()

        total_loss += loss.item() * inputs.size(0)
        _, predicted = torch.max(outputs, 1)
        correct += (predicted == torch.max(targets, 1)[1]).sum().item()
        total_samples += inputs.size(0)

    accuracy = correct / total_samples
    avg_loss = total_loss / total_samples

    # 每隔 100 个 epoch 打印一次当前轮次的损失值。
    if (epoch+1) % 1000 == 0:
        print(f'Epoch {epoch + 1}/{epochs}')
        print(f'Loss: {avg_loss:.4f} - Accuracy: {accuracy:.4f}\n')
        # print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch+1, epochs, loss.item()))

# 预测测试
predict = model(input_batch)  # 使用训练好的模型对测试数据进行预测; 将测试数据输入模型,得到模型的预测结果
_, predict = torch.max(predict, 1)  # 通过 torch.max() 函数找到每个预测结果中概率最大的类别索引

# 将预测结果转换为汉字,并打印出原始输入数据和模型预测得到的结果。
print('输入的是:', [seg_char(sen)[:2] for sen in sentences])
print('预测得到:', [number_dict[n.item()] for n in predict])

三、结果

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑞雪兆我心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值