NNLM——预测下一个单词

一、原理篇 

      NNLM(Neural Network Language Model,神经网络语言模型)是一种通过神经网络进行语言建模的技术,通常用于预测序列中的下一个词。

        NNLM的核心思想是使用词嵌入(word embedding)将词转换为低维向量,并通过神经网络学习语言中的词序关系。

        NNLM的基本结构包括以下几个部分:

  • 输入层:输入一个固定长度的词窗口,例如 n 个词的上下文(前  n - 1 个词)作为输入。
  • 嵌入层:将每个输入词映射到一个低维空间,得到词向量。这一层的权重矩阵通常表示为词嵌入矩阵。
  • 隐藏层:一个或多个隐藏层可以捕获词之间的关系,一般是全连接层。
  • 输出层:用于预测下一个词的概率分布,通常使用softmax函数。

        假设一个句子为 w_1, w_2, \dots, w_T,我们希望通过上下文词 w_{t-n+1}, \dots, w_{t-1} 来预测下一个词 w_t。其目标是最大化预测正确的概率,即:

P(w_t | w_{t-1}, w_{t-2}, \dots, w_{t-n+1})

1. 词嵌入查找

        每个词 w都有一个唯一的索引。我们用嵌入矩阵 C 来将这些词映射到低维向量空间:

x_i = C(w_{t-i})

        其中x_i 表示第 i 个词的嵌入向量。对于所有 n−1 个词的上下文,我们得到词嵌入向量的集合:

\mathbf{x} = \left[ C(w_{t-n+1}), C(w_{t-n+2}), \dots, C(w_{t-1}) \right]

2. 嵌入向量拼接

        将所有词嵌入向量拼接成一个大的向量:

x = \left[ x_{t-n+1}, x_{t-n+2}, \dots, x_{t-1} \right]

        这个拼接向量 x 包含了上下文中的信息。

3. 隐藏层计算

        将拼接后的向量 x 输入到隐藏层,隐藏层的权重矩阵为 W,偏置向量为 b,激活函数为 f(例如tanh)。隐藏层的输出表示为:

h = f(W \cdot \mathbf{x} + b)

        在图中,“most computation here”指的就是这个计算过程。

4. 输出层与Softmax

        隐藏层的输出 h 通过输出层进行预测。输出层使用softmax函数来计算词汇表中每个词的概率分布:

P(w_t = i | w_{t-1}, w_{t-2}, \dots, w_{t-n+1}) = \frac{\exp(U_i \cdot h + c_i)}{\sum_{j=1}^{V} \exp(U_j \cdot h + c_j)}

其中:

  • U_i 是输出层对应词 i 的权重向量,
  • c_i​ 是词 i 的偏置,
  • V 是词汇表的大小。

        最终输出层会给出一个词汇表大小的概率分布,用于预测下一个词的概率。

5. 损失函数

        模型通过最大化训练数据的似然来进行优化。通常使用交叉熵损失来最小化预测的词分布和真实标签之间的差距:

L = - \sum_{t=1}^{T} \log P(w_t | w_{t-1}, w_{t-2}, \dots, w_{t-n+1})

        通过最小化该损失函数,可以优化模型参数,使得模型能够更好地预测下一个词。

二、代码篇

# 1.导入必要的库
import torch
import torch.nn as nn
import torch.optim as optimizer
import torch.utils.data as Data# 2.数据准备
sentences = ["I like milk","I love hot-pot","I hate coffee","I want sing","I am sleep","I go home","Love you forever"]word_list = " ".join(sentences).split()     # 获取个句子单词
word_list = list(set(word_list))            # 获取单词列表word_dict = {w: i for i, w in enumerate(word_list)}     # 单词-位置索引字典
number_dict = {i: w for i, w in enumerate(word_list)}   # 位置-单词索引字典vocab_size = len(word_list)         # 词汇表大小# 3.X-生成输入和输出数据
def make_data():input_data = []output_data = []for sen in sentences:word = sen.split()input_temp = [word_dict[n] for n in word[:-1]]output_temp = word_dict[word[-1]]input_data.append(input_temp)output_data.append(output_temp)return input_data, output_datainput_data, output_data = make_data()
input_data, output_data = torch.LongTensor(input_data), torch.LongTensor(output_data)   # 数据转换:将 input_data 和 output_data 转换为 LongTensor,以便用于模型训练。
dataset = Data.TensorDataset(input_data, output_data)       # 建数据集:Data.TensorDataset 将输入和输出配对为数据集
loader = Data.DataLoader(dataset, 4, True)                  # 数据加载器:DataLoader,使用批量大小为 2,随机打乱样本# 4.初始化参数
m = 2
n_step = 2
n_hidden = 10# 5.模型定义
class NNLM(nn.Module):def __init__(self):super(NNLM, self).__init__()self.C = nn.Embedding(vocab_size, m)self.H = nn.Linear(n_step * m, n_hidden, bias=False)self.d = nn.Parameter(torch.ones(n_hidden))self.U = nn.Linear(n_hidden, vocab_size, bias=False)self.W = nn.Linear(n_step * m, vocab_size, bias=False)self.b = nn.Parameter(torch.ones(vocab_size))def forward(self, X):X = self.C(X)               # X = [batch_size, n_step, m]X = X.view(-1, n_step * m)  # 展平 X = [batch_size, n_step * m]hidden_output = torch.tanh(self.d + self.H(X))output = self.b + self.W(X) + self.U(hidden_output)return output# 6.定义训练过程
model = NNLM()
optim = optimizer.Adam(model.parameters(), lr=1e-3)
criterion = nn.CrossEntropyLoss()# 7.模型训练
for epoch in range(5000):for batch_x, batch_y in loader:pred = model(batch_x)loss = criterion(pred, batch_y)if (epoch + 1) % 1000 ==0:print(epoch+1, loss.item())optim.zero_grad()loss.backward()optim.step()# 8.模型测试
pred = model(input_data).max(1, keepdim=True)[1]
print([number_dict[idx.item()] for idx in pred.squeeze()])

代码简单解释:

        1.最开始导入一些必要的库

       

        2.首先需要准备一些数据,用于模型训练并测试

word_list = " ".join(sentences).split()

        ①对于文本数据,肯定要进行一个分词操作,先使用" ".join(sentences).split()来切分每一个句子的每个单词,这时候获得的word_list列表就是:

['I', 'like', 'milk', 'I', 'love', 'hot-pot', 'I', 'hate', 'coffee', 'I', 'want', 'sing', 'I', 'am', 'sleep', 'I', 'go', 'home', 'Love', 'you', 'forever']

        ②但是这里得到的单词可能会有重复的情况,我们需要得到不重复的单词列表,为后面的创建词典提供方便。

word_list = list(set(word_list))  

  set (word_list) :将 word_list 转换为集合(set),自动去除列表中的重复元素。

  list(set (word_list) ):再将集合转换回列表,这样可以保持原数据类型一致(即 word_list 仍然是一个列表)。

['milk', 'coffee', 'sing', 'hot-pot', 'home', 'you', 'am', 'I', 'sleep', 'love', 'want', 'hate', 'go', 'forever', 'like', 'Love']

        ③然后就是构造词典:单词到位置的索引字典、位置到单词的索引字典。

word_dict = {w: i for i, w in enumerate(word_list)}     # 单词-位置索引字典
number_dict = {i: w for i, w in enumerate(word_list)}   # 位置-单词索引字典

word_dict: {'sleep': 0, 'go': 1, 'home': 2, 'milk': 3, 'hate': 4, 'Love': 5, 'love': 6, 'am': 7, 'want': 8, 'sing': 9, 'forever': 10, 'hot-pot': 11, 'I': 12, 'like': 13, 'you': 14, 'coffee': 15}

number_dict:{0: 'sleep', 1: 'go', 2: 'home', 3: 'milk', 4: 'hate', 5: 'Love', 6: 'love', 7: 'am', 8: 'want', 9: 'sing', 10: 'forever', 11: 'hot-pot', 12: 'I', 13: 'like', 14: 'you', 15: 'coffee'}

        这里使用enumerate是因为 enumerate 函数可以方便地同时获取列表元素的索引对应的值。也就是我们想要的字典。

        ④然后就是获得词汇表大小,在模型搭建中需要用到。

        3.有了初始数据,我们需要构建出数据X,也就是输入数据和目标输出数据。

def make_data():input_data = []output_data = []for sen in sentences:word = sen.split()input_temp = [word_dict[n] for n in word[:-1]]output_temp = word_dict[word[-1]]input_data.append(input_temp)output_data.append(output_temp)return input_data, output_data

        ①先构建空的输入数据input_data和输出数据output_data。

        ②将每个句子的前 n-1个单词的位置添加到输入数据input_data中,第 n 个单词的位置添加到输入数据output_data中,得到每个输入 x 和输出 y 在词汇表中的顺序:

input_data:[[12, 13], [12, 6], [12, 4], [12, 8], [12, 7], [12, 1], [5, 14]]

output_data:[3, 11, 15, 9, 0, 2, 10]

        这是个二维的矩阵,行元素代表一个句子中用于训练输入/测试输出的单词在词汇表中的位置索引;列元素是不同的句子

        每个句子都是3个单词,前2个作为前文信息作为输入,第3个作为预测输出,我们前面给的一共是7个句子。

        4.初始化参数

        这里的m指的是维度,也就是一个单词要嵌入到多少维度,由于这里的数据量比较小,每个句子也只有3个单词,所以这给出的维度选个很低的2。

        n_step=2,指的是用两个单词来预测下一个目标单词。

        n_hidden=10,指的是隐藏层的数量。

        5.前面的数据已经初步定义好了,这里就要搭建NNLM模型了。

class NNLM(nn.Module):def __init__(self):super(NNLM, self).__init__()self.C = nn.Embedding(vocab_size, m)self.H = nn.Linear(n_step * m, n_hidden, bias=False)self.d = nn.Parameter(torch.ones(n_hidden))self.U = nn.Linear(n_hidden, vocab_size, bias=False)self.W = nn.Linear(n_step * m, vocab_size, bias=False)self.b = nn.Parameter(torch.ones(vocab_size))def forward(self, X):X = self.C(X)               # X = [batch_size, n_step, m]X = X.view(-1, n_step * m)  # 展平 X = [batch_size, n_step * m]hidden_output = torch.tanh(self.d + self.H(X))output = self.b + self.W(X) + self.U(hidden_output)return output

         ①def _init_(self):定义各层和参数

  • self.C:词嵌入层,将输入词转换为词向量。

  vocab_size 定义词汇表的大小,m 是词嵌入的维度,表示每个词将被嵌入成 m 维的向量。

  • self.H:线性层,将展平后的输入映射到隐藏层。

       n_step * m 是展平后的输入大小,n_hidden 是隐藏层的维度,用来控制隐藏层输出的特征数量。

  • self.d:偏置向量,用于隐藏层的输出。

  n_hidden 是偏置项的维度,与隐藏层输出匹配,用于提升隐藏层的表达能力。

  • self.U:线性层,将隐藏层输出映射到词汇表空间。

  n_hidden 是隐藏层输出的大小,vocab_size 是词汇表大小,用于将隐藏层的特征映射到每个词的预测空间。

  • self.W:线性层,从输入直接映射到词汇表空间。

  n_step * m 是展平后的输入大小,vocab_size 是词汇表大小,用于将输入直接映射到词汇表的预测空间。

  • self.b:偏置向量,用于最终输出层的分数调整。

  vocab_size 是词汇表的大小,用作最终输出层的偏置。

        这里分别用了Embedding、Linear和Parameter:

  • Embedding:嵌入层,用于将离散的词汇索引(如单词的整数表示)映射到连续的稠密向量空间。
  • Linear:全连接层(线性层),用于将输入的特征通过线性变换映射到输出空间。
  • Parameter:可学习的参数。

      ②def forward(self,X):定义神经网络在前向传播过程中的计算步骤

  • X = self.C(X)
    首先通过嵌入层将输入的词索引(X)转换为词向量表示,这个时候得到是三维度的:[batch_size, n_step, m]
  • X = X.view(-1, n_step * m)
    然后将 X 从三维张量展平为二维张量 [batch_size, n_step * m],方便输入到全连接层 self.H。
  • hidden_output = torch.tanh(self.d + self.H(X))
    接着,利用公式h=tanh(W_hX+d)计算得到隐藏层输出。
  • output = self.b + self.W(X) + self.U(hidden_output)
    最后,利用公式output=b+WX+Uh得到最终的输出。

        6.定义训练过程

        这里初始化model,并且设置优化器为Adam,并且使用了交叉熵损失。

        7.模型训练

for epoch in range(5000):for batch_x, batch_y in loader:pred = model(batch_x)loss = criterion(pred, batch_y)if (epoch + 1) % 1000 ==0:print(epoch+1, loss.item())optim.zero_grad()loss.backward()optim.step()

        这里从数据加载器中加载数据,将当前批次的输入数据batch_x传入模型中得到预测结果,同时计算预测值与真实值的损失loss,每1000个epoch打印损失。然后梯度清零、反向传播计算梯度、更新模型参数。

        8.模型测试

pred = model(input_data).max(1, keepdim=True)[1]
  • model(input_data):将输入数据传递给模型,获取每个类别的得分(logits)。
  • max(1, keepdim=True)[1]:

          max(1):对每个样本找出最大得分的类别索引。
          keepdim=True:保持输出维度不变。
          [1]:提取每个样本的最大值索引(预测类别)。

print([number_dict[idx.item()] for idx in pred.squeeze()])
  • pred.squeeze():移除维度为 1 的维度,得到一维张量。
  • [idx.item() for idx in pred.squeeze()]:将每个索引转换为整数。
  • number_dict[idx.item()]:通过索引查找可读标签。
  • print([...]):打印出模型预测的类别标签。

输出:

1000 0.05966342240571976
1000 0.034198883920907974
2000 0.005526650696992874
2000 0.009151813574135303
3000 0.0021409429609775543
3000 0.0015856553800404072
4000 0.0006656644982285798
4000 0.0005017295479774475
5000 0.00018937562708742917
5000 0.00020660058362409472
['milk', 'hot-pot', 'coffee', 'sing', 'sleep', 'home', 'forever']

参考

Neural Network Language Model PyTorch实现_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/57910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】类和对象(十二):实现日期类

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解C的实现日期类&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1 /!/>/</>/<运算符重载2 /-//-运算符重载(A) 先写&#xff0c;再通过写(B…

KTHREAD--InitialStack和KernelStack和TSS的esp0

InitialStack和TSS.esp0的关系,在这里可以看到 mov ecx, [esi_KTHREAD.InitialStack] ; esi: newthread lea eax, [ecx-210h] ; 越过FPXSAVE指令存储地址 test byte ptr [eax-1Ah], 2 ; 判断efalgs寄存器的VIF位是否为1 jnz short loc_458743 sub eax, 10h…

xlrd.biffh.XLRDError: Excel xlsx file; not supported

文章目录 一、问题报错二、报错原因三、解决思路四、解决方法 一、问题报错 在处理Excel文件时&#xff0c;特别是当我们使用Python的xlrd库来读取.xlsx格式的文件&#xff0c;偶尔会遇到这样一个错误&#xff1a;“xlrd.biffh.XLRDError: Excel xlsx file; not supported”。…

二叉树进阶-二叉搜索树

目录 1.二叉树的概念 2.二叉搜索树的操作 2.1二叉搜索树的结构 2.2实现节点的查找&#xff08;find&#xff09; 2.3实现增加节点&#xff08;insert&#xff09; 2.4实现删除节点&#xff08;erase&#xff09; 2.5析构函数 2.6二叉搜索树的完整实现 3.二叉搜索树的应…

「Mac畅玩鸿蒙与硬件24」UI互动应用篇1 - 灯光控制小项目

本篇将带领你实现一个互动性十足的灯光控制小项目&#xff0c;用户可以通过点击按钮来控制灯光的开关。该项目将涉及状态管理、动态图片加载以及按钮交互&#xff0c;是学习鸿蒙应用开发的重要基础。 关键词 UI互动应用状态管理动态图片加载用户交互 一、功能说明 在这个灯光…

自制inscode项目推荐:色块小游戏

小编的inscode部署项目&#xff1a;割绳子游戏。 更多精彩内容见InsCode - 让你的灵感立刻落地~ 介绍一下项目及玩法。 游戏概述 颜色匹配小游戏是一款基于HTML、CSS和JavaScript开发的简单而有趣的网页游戏。游戏的目标是通过点击颜色块&#xff0c;将整个游戏板上的所有方块…

DevOps赋能:优化业务价值流的实战策略与路径(下)

下篇&#xff1a;加速工作项流动与持续改进优化 —— 跨越差距&#xff0c;迈向卓越交付 在上篇中&#xff0c;我们已经深入探讨了看板方法的四大核心实践&#xff0c;它们共同致力于实现“顺畅且高质量地交付价值”的终极目标。然而&#xff0c;理想与现实之间往往存在一定的…

使用带有令牌认证的 Jupyter Notebook 服务器

当你不想在默认浏览器打开Jupyter Notebook,但是在其他浏览器打开http://localhost:8890/lab或者http://localhost:8889/tree&#xff0c;却显示 Token authentication is enabled&#xff0c;如下图 可以按以下步骤操作&#xff1a; 获取令牌&#xff1a;在启动 Jupyter Note…

FRIENDLYARM Tiny6410 superboot烧写进sd卡教程

友善之臂真的垃圾&#xff0c;pdf乱不说&#xff0c;资料在哪也不说&#xff0c;没有视频教程&#xff0c;就染你自己去一堆资料里找&#xff0c;** superboot在资料B盘tooles-image里 注意有些朋友scan不到sd卡是因为没有给这个软件开启管理员模式&#xff0c;他没权限去扫描…

宝藏虚拟化学习资料大全

最近发现了关于虚拟化的宝藏资料&#xff0c;瑞斯拜&#xff01;原文链接如下&#xff1a; 500篇关于虚拟化的经典资料&#xff0c;含CPU虚拟化&#xff0c;磁盘虚拟化&#xff0c;内存虚拟化&#xff0c;IO虚拟化。 目录 &#x1fa90; 虚拟化基础 &#x1f343; 虚拟化分类&…

C++模拟真人动态生成鼠标滑动路径

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

【Redis实践】使用zset实现实时排行榜以及一些优化思考

文章目录 1.概述2.zset的基本概念说明2.1.数据结构说明2.2.zset做排行榜的指令 3. 项目中的实践3.1.RedisTemplate实现排行榜3.2.可能存在的问题及解决方案3.2.1. 限制成员的数量3.2.2.保留当前分数与最高分数3.2.3.批量操作成员分数&#xff0c;减少并发 4.总结 1.概述 我们在…

C++_STL_xx_番外01_关于STL的总结(常见容器的总结;关联式容器分类及特点;二叉树、二叉搜索树、AVL树(平衡二叉搜索树)、B树、红黑树)

文章目录 1. 常用容器总结2. 关联式容器分类3. 二叉树、二叉搜索树、AVL树、B树、红黑树 1. 常用容器总结 针对常用容器的一些总结&#xff1a; 2. 关联式容器分类 关联式容器分为两大类&#xff1a; 基于红黑树的set和map&#xff1b;基于hash表的unorder_set和unorder_ma…

【LwIP源码学习4】主线程tcpip_thread

前言 本文对lwip的主要线程tcpip_thread进行分析。 正文 tcpip_thread是lwip最主要的线程&#xff0c;其创建在tcpip_init函数中 sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);tcpip_init函数被TCPIP_Init函数调用。…

光圈,感光度,感光器件

光圈&#xff08;通光孔&#xff09;&#xff0c;是一个用来控制光线透过镜头进入机身内感光面光量的装置&#xff0c;通常设置在镜头内。通常&#xff0c;我们用f值来表达光圈大小。通俗来说&#xff0c;摄像机镜头拍照时&#xff0c;不可能随意改变镜头直径&#xff0c;但可以…

Llama 3.2 Vision Molmo:多模态开源生态系统基础

编者按&#xff1a; 视觉功能的融入对模型能力和推理方式的影响如何&#xff1f;当我们需要一个既能看懂图像、又能生成文本的 AI 助手时&#xff0c;是否只能依赖于 GPT-4V 这样的闭源解决方案&#xff1f; 我们今天为大家分享的这篇文章&#xff0c;作者的核心观点是&#xf…

高效视频制作大提速,视频剪辑软件的高级自定义命令功能批量调整视频的色调、饱和度和亮度,轻松驾驭视频编辑技巧

在浩瀚的数字海洋中&#xff0c;视频如同璀璨的星辰&#xff0c;而每一颗星辰都渴望被精心雕琢&#xff0c;闪耀出最独特的光芒。想象一下&#xff0c;你手握一把神奇的钥匙&#xff0c;能够轻松解锁批量视频剪辑的奥秘&#xff0c;让每一帧画面都跃动着你的创意与激情。这把钥…

[RootersCTF2019]ImgXweb

审题 看到robots.txt,看到里面的文件&#xff0c;打开看到 you-will-never-guess这个字符串 进行注册登录 可以看到典型的jwt加密的Cookie 想到之前的字符串可能是密匙&#xff0c;更改为admin&#xff0c;进行登录。 成功后可以看到flag.png。 发现图形打不开 使用curl进…

32单片机HAL库的引脚初始化

在使用HAL库时&#xff0c;GPIO初始化函数定义在stm32f4xx_hal_gpio.c文件中&#xff0c;如下&#xff1a; void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); 由这个函数可以看出&#xff0c;在初始化GPIO时&#xff0c;需要向函数传入2个结构体&…

ubuntu【桌面】 配置NAT模式固定IP

DHCP分配导致虚拟机IP老变&#xff0c;SSH老要重新配置&#xff0c;设成静态方便些 一、设NAT模式 1、设为NAT模式 2、看模式对应的虚拟网卡 - VMnet8 3、共享主机网卡网络到虚拟网卡 - VMnet8 二、为虚拟网卡设置静态IP 记住这个IP 三、设置ubuntu固定IP 1、关闭DHCP并…