NLP_循环神经网络(RNN)

文章目录

  • RNN结构
  • RNN实战
  • RNN小结


RNN结构

NPLM 在处理长序列时会面临一些挑战。首先,由于它仍然是基于词的模型,因此在处理稀有词汇或者词汇表外的词汇时效果不佳。其次,NPLM不能很好地处理长距离依赖关系。而上面这两个局限,恰恰就是RNN的优势。

RNN的核心思想是利用“循环”的机制,将网络的输出反馈到输入,这使得它能够在处理数据时保留前面的信息,从而捕获序列中的长距离依赖关系,在处理序列数据,如文本、语音和时间序列时具有明显的优势。

RNN可以看作一个具有“记忆”的神经网络。RNN的基本原理是通过循环来传递隐藏状态信息,从而实现对序列数据的建模。一个简单的RNN包括输入层、隐藏层和输出层。在每个时间步(可理解为每次循环的过程),RNN会读取当前输入,并结合前一个时间步的隐藏状态来更新当前的隐藏状态。然后,这个隐藏状态会被用于生成输出和更新下一个时间步的隐藏状态。通过这种方式,RNN可以推获序列中的依赖关系。最后,输出层根据隐藏层的信息产生预测。

通过在每个时间步共享权重(即在处理各个token时使用相同的RNN),RNN
能够处理不同长度的输入序列。这种权重共享机制使得RNN 具有很大的灵活性,因为它可以适应各种长度的序列,在处理自然语言和其他可变长度序列数据时更具优势,而不像NPLM 那样受到窗口大小固定的限制。

RNN的基本架构图:
在这里插入图片描述

例如一段文本。我们可以将这段文本分成单词或字符,并将其作为 RNN 的输入。对于每一个时间步,RNN会执行以下操作。
(1)接收前时间步的输入x_t(即上图中的xt)。
(2)结合前一时间步的隐藏层状态h_(t-1),计算当前时间步的隐藏层状态 h_t(即上图中的ht)。这通常通过一个激活函数(如tanh函数)实现。计算公式如下(其中, W_hh 是隐藏层到隐藏层的权重矩阵,W_xh是输入到隐藏层的权重矩阵):
在这里插入图片描述
(3)基于当前时间步的隐藏层状态h_t,计算输出层y_t(RNN在时间步t的输出)。
这通常通过一个线性变换和激活函数(如softmax函数)实现。计算公式如下:
在这里插入图片描述
通过上述操作,RNN可以处理整个序列数据,并在每个时间步生成一个输出。需要注意的是,RNN具有参数共享的特性。这意味着在不同时间步,RNN 使用相同的权重矩阵(W_hh,W_xh和W_hy)和偏置(b_h和b_y)进行计算。

RNN虽然在某些方面具有优势,但它的局限性也不容忽视。在训练过程中,RNN可能会遇到梯度消失和梯度爆炸的问题,这会导致网络很难学习长距离依赖关系。为了解决这些问题,研究人员提出了LSTM 和 GRU等改进型RNN结构。LSTM、GRU广义上属于 RNN,不过这些结构引入了门控机制,使得模型能够更好地捕捉到序列中的长距离依赖关系,从而在许多NLP 任务中表现更优。

RNN实战

我们使用了一个LSTM层替换了NPLM原有的线性层。之后,定义了一个基于RNN的语言模型,它包含一个嵌入层、一个LSTM层和一个线性层。该模型将输入的词序列转换为嵌入向量,将嵌入向量输入LSTM层中,并将LSTM层的输出传递到线性层中,以生成最终的输出。其中LSTM层的输入是词嵌入,输出是在每个时间步的隐藏状态。我们只选择最后一个时间步的隐藏状态作为全连接层的输入,以生成预测结果。

在 RNN 模型中,网络结构主要取决于以下几个参数。
(1)词嵌入大小embedding_size:决定了词嵌入层的输出维度。
(2)隐藏层大小n_hidden:决定了LSTM(或其他RNN变体)层的隐藏状态大小。

这意味着,在RNN模型中,我们可以灵活地处理不同长度的输入序列,而不需要改变网络结构。这是RNN模型与NPLM的一个重要区别。这使得RNN模型在处理自然语言任务时更具优势,因为它可以很好地处理不同长度的文本序列。

循环神经网络中的种种细节,比如隐藏层状态的计算 h_t = tanh(W_hh * h_(t-1) + W_xh *x_t + b_h),又比如基于当前时间步的隐藏层状态 h_t,计算输出层 y_t的线性变换和激活,为什么在代码中都没有体现?

其实,这是因为RNN实现的细节已经被封装在PyTorch的LSTM层了。这里的代码1stm_out._=self.1stm(X)虽然很简洁,但实际上它涵盖了LSTM处理输入序列的复杂计算过程。对于输入序列 X,LSTM会逐时间步处理,每个时间步的输入不仅包括当前时间步的数据,还会接收上一时间步的隐藏层状态。这样,信息就在时间步之间传递,形成一种循环。在每个时间步,通过LSTM内部的门控机制,网络计算并更新当前的状态,并生成对应的输出。

在处理输入序列时,LSTM内部会进行以下操作。
对于每个时间步t,LSTM会接收当前时间步的输入x_t及上一个时间步的隐藏状态h_(t-1) 和细胞状态c_(t-1)。

接着,LSTM会计算输入门、遗忘门和输出门的激活值。这些门控机制使得 LSTM 能够有选择地保留或遗忘之前的信息,从而更好地捕捉长距离依赖关系。
这些门的计算公式如下。
在这里插入图片描述

其中,w_ii是从当前输入x_t到输入门的权重矩阵,而W_hi是从前一时间步的隐藏状态h_(t-1)到输入门的权重矩阵。W_if是从当前输入x_t到遗忘门的权重矩阵,W_hf是从前一时间步的隐藏状态h_(t-1)到遗忘门的权重矩阵。W_io是从当前输入x_t到输出门的权重矩阵,W_ho是从前一时间步的隐藏状态h_(t-1)到输出门的权重矩阵。偏置项b_ii、b_hi、b_if、b_hf、b_io 和 b_ho,即各自门控或单元的偏置。以上所有的权重矩阵和偏置项都是在模型的训练过程中通过反向传播和优化算法学习得到的。

LSTM更新细胞状态c_t。这是通过结合输入门、遗忘门和当前输入的信息来实现的。计算公式如下。
在这里插入图片描述

最后,LSTM 会计算当前时间步的隐藏状态h_t,这通常作为输出。计算公式如下。
在这里插入图片描述

在整个循环过程中,LSTM会逐时间步处理输入序列,并产生对应的输出。这使得LSTM 能够捕捉到输入序列中的长距离依赖关系,并在各种NLP任务中表现出优越的性能。

不过,你要注意到,在我们的循环神经网络中,n_step这个参数将不再出现。理解这个差异有助于你了解原始的NPLM 和 RNN 模型的本质区别。

  • 在NPLM 中,n_step作为一个重要参数,直接影响模型的结构。具体来说,n_step决定了模型中第一个线性层的输入大小(n_step * m)。这是因
    为NPLM 会将词嵌入层的输出展平,然后将其输入第一个线性层。因此,n_step 的值将直接影响线性层的输入大小,从而影响整个模型的结构。

  • 在RNN模型中,因为RNN模型是专门为处理任意长度的序列数据设计的,我们会将词嵌入层的输出直接输入LSTM(或其他RNN变体)层,而不需要将其展平。因此,输入序列的长度不会影响网络结构。

RNN模型在处理序列数据时具有优势,因为它们可以捕捉序列中的长距离依赖关系。在本例中,我们构建了一个基于LSTM 的 RNN模型,能够更好地捕捉长距离依赖。虽然在这个简单的示例中,模型的改进可能不会显著提高效率,但在处理更复杂的自然语言任务时,LSTM的性能通常会比简单的线性模型更好。

RNN并不是NLP任务的完美解决方案,它的局限性主要包括以下几点。

  • 顺序计算:这些网络在处理序列时,需要按照时间步的顺序进行计算。这意味着在某个时间步的计算完成之前,无法进行下一个时间步的计算。这种顺序计算限制了这些网络的并行计算能力,从而降低了计算效率和速度。
  • 长距离依赖问题:尽管LSTM 和GRU等RNN变体拥有了更好的记忆功能,但在处理非常长的序列时,这些网络仍然可能无法完全捕捉到序列中的长距离依赖关系。
  • 有限的可扩展性:RNN及其变体在面对更大规模的数据集和更复杂的任务时,可能会遇到扩展性问题。随着序列长度的增加,它们的计算复杂性也会增加,这可能导致训练时间过长和资源需求过高。

在RNN时代,NLP应用落地整体表现不佳的原因有以下几点。

  • 模型表达能力不足:尽管RNN及其变体在某些任务中取得了不错的成果,但它们的表达能力可能不足以处理复杂的NLP任务。这是因为自然语言中的依赖关系和语义结构可能非常复杂,而这些网络可能无法捕捉到全部信息。
  • 缺乏大规模数据:在RNN时代,大规模的预训练数据集和计算资源相对较少。这使得模型难以从大量的无监督文本数据中学习到丰富的语言知识,从而影响了它们在实际应用中的表现。
  • 优化算法发展不足:在RNN时代,优化算法仍处在相对初级的阶段,可能无法充分利用可用的数据和计算资源。这可能导致模型训练过程中的梯度消失、梯度爆炸等问题,从而影响模型的性能和稳定性。
# 构建一个非常简单的数据集
sentences = ["我 喜欢 玩具", "我 爱 爸爸", "我 讨厌 挨打"] 
# 将所有句子连接在一起,用空格分隔成多个词,再将重复的词去除,构建词汇表
word_list = list(set(" ".join(sentences).split())) 
# 创建一个字典,将每个词映射到一个唯一的索引
word_to_idx = {word: idx for idx, word in enumerate(word_list)} 
# 创建一个字典,将每个索引映射到对应的词
idx_to_word = {idx: word for idx, word in enumerate(word_list)} 
voc_size = len(word_list) # 计算词汇表的大小
print(' 词汇表:', word_to_idx) # 打印词汇到索引的映射字典
print(' 词汇表大小:', voc_size) # 打印词汇表大小

在这里插入图片描述

# 构建批处理数据
import torch # 导入 PyTorch 库
import random # 导入 random 库
batch_size = 2 # 每批数据的大小
def make_batch():
    input_batch = []  # 定义输入批处理列表
    target_batch = []  # 定义目标批处理列表
    selected_sentences = random.sample(sentences, batch_size) # 随机选择句子
    for sen in selected_sentences:  # 遍历每个句子
        word = sen.split()  # 用空格将句子分隔成多个词
        # 将除最后一个词以外的所有词的索引作为输入
        input = [word_to_idx[n] for n in word[:-1]]  # 创建输入数据
        # 将最后一个词的索引作为目标
        target = word_to_idx[word[-1]]  # 创建目标数据
        input_batch.append(input)  # 将输入添加到输入批处理列表
        target_batch.append(target)  # 将目标添加到目标批处理列表
    input_batch = torch.LongTensor(input_batch) # 将输入数据转换为张量
    target_batch = torch.LongTensor(target_batch) # 将目标数据转换为张量
    return input_batch, target_batch  # 返回输入批处理和目标批处理数据
input_batch, target_batch = make_batch() # 生成批处理数据
print(" 输入批处理数据:",input_batch)  # 打印输入批处理数据
# 将输入批处理数据中的每个索引值转换为对应的原始词
input_words = []
for input_idx in input_batch:
    input_words.append([idx_to_word[idx.item()] for idx in input_idx])
print(" 输入批处理数据对应的原始词:",input_words)
print(" 目标批处理数据:",target_batch) # 打印目标批处理数据
# 将目标批处理数据中的每个索引值转换为对应的原始词
target_words = [idx_to_word[idx.item()] for idx in target_batch]
print(" 目标批处理数据对应的原始词:",target_words)

在这里插入图片描述

import torch.nn as nn # 导入神经网络模块
# 定义循环神经网络(RNN)
class RNN(nn.Module):
    def __init__(self):
        super(RNN, self).__init__() # 调用父类的构造函数
        self.C = nn.Embedding(voc_size, embedding_size) # 定义一个词嵌入层
        # 用 LSTM 层替代第一个线性层,其输入大小为 embedding_size,隐藏层大小为 n_hidden
        self.lstm = nn.LSTM(embedding_size, n_hidden, batch_first=True) 
        # 第二个线性层,其输入大小为 n_hidden,输出大小为 voc_size,即词汇表大小
        self.linear = nn.Linear(n_hidden, voc_size) 
    def forward(self, X):  # 定义前向传播过程
        # 输入数据 X 张量的形状为 [batch_size, n_step]
        X = self.C(X)  # 将 X 通过词嵌入层,形状变为 [batch_size, n_step, embedding_size]
        # 通过 LSTM 层
        lstm_out, _ = self.lstm(X) # lstm_out 形状变为 [batch_size, n_step, n_hidden]
        # 只选择最后一个时间步的输出作为全连接层的输入,通过第二个线性层得到输出 
        output = self.linear(lstm_out[:, -1, :]) # output 的形状为 [batch_size, voc_size]
        return output # 返回输出结果
n_step = 2 # 时间步数,表示每个输入序列的长度,也就是上下文长度 
n_hidden = 2 # 隐藏层大小
embedding_size = 2 # 词嵌入大小
model = RNN() # 创建循环神经网络模型实例
print(' RNN 模型结构:', model) # 打印模型的结构

在这里插入图片描述

import torch.optim as optim # 导入优化器模块
criterion = nn.CrossEntropyLoss() # 定义损失函数为交叉熵损失
optimizer = optim.Adam(model.parameters(), lr=0.1) # 定义优化器为 Adam,学习率为 0.1
# 训练模型
for epoch in range(5000): # 设置训练迭代次数
    optimizer.zero_grad() # 清除优化器的梯度
    input_batch, target_batch = make_batch() # 创建输入和目标批处理数据
    output = model(input_batch) # 将输入数据传入模型,得到输出结果
    loss = criterion(output, target_batch) # 计算损失值
    if (epoch + 1) % 1000 == 0: # 每 1000 次迭代,打印损失值
        print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
    loss.backward() # 反向传播计算梯度
    optimizer.step() # 更新模型参数

在这里插入图片描述

# 进行预测
input_strs = [['我', '讨厌'], ['我', '喜欢']]  # 需要预测的输入序列
# 将输入序列转换为对应的索引
input_indices = [[word_to_idx[word] for word in seq] for seq in input_strs]
# 将输入序列的索引转换为张量
input_batch = torch.LongTensor(input_indices) 
# 对输入序列进行预测,取输出中概率最大的类别
predict = model(input_batch).data.max(1)[1]  
# 将预测结果的索引转换为对应的词
predict_strs = [idx_to_word[n.item()] for n in predict.squeeze()]  
for input_seq, pred in zip(input_strs, predict_strs):
    print(input_seq, '->', pred)  # 打印输入序列和预测结果

在这里插入图片描述

RNN小结

RNN
优势:
核心思想是利用“循环”的机制,将网络的输出反馈到输入,这使得它能够在处理数据时保留前面的信息,从而捕获序列中的长距离依赖关系,在处理序列数据,如文本、语音和时间序列时具有明显的优势。

劣势:
并行计算(降低了计算效率和速度)、
长距离依赖问题(但在处理非常长的序列时,这些网络仍然可能无法完全捕捉到序列中的长距离依赖关系)、
有限的可扩展性(随着序列长度的增加,它们的计算复杂性也会增加,这可能导致训练时间过长和资源需求过高)


学习的参考资料:
(1)书籍
利用Python进行数据分析
西瓜书
百面机器学习
机器学习实战
阿里云天池大赛赛题解析(机器学习篇)
白话机器学习中的数学
零基础学机器学习
图解机器学习算法

动手学深度学习(pytorch)

(2)机构
光环大数据
开课吧
极客时间
七月在线
深度之眼
贪心学院
拉勾教育
博学谷
慕课网
海贼宝藏

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【芯片设计- RTL 数字逻辑设计入门 8 -- 四选一多路器】

文章目录 四选一多路输出器verilog case 语句verilog 代码testbench 代码仿真波形 问题小结 四选一多路输出器 制作一个四选一的多路选择器,要求输出定义上为线网类型 状态转换: d0 00 d1 01 d2 10 d3 11verilog case 语句 case(express…

蓝桥杯Web应用开发-CSS 基础语法4(字体属性、链接中的伪类、列表样式)

专栏持续更新中 字体属性 字体属性用于定义字体的类型、字号大小、加粗、斜体等方面样式。常用的字体属性如下表所示: 属 性可 取 值描 述fontfont-style、font-variant、font-weight、font-size(或 line-height)、font-family在一个声明中…

如何使用Docker部署DashDot服务器仪表盘并结合cpolar实现公网访问

📑前言 本文主要是使用Docker部署DashDot服务器仪表盘并结合cpolar实现公网访问的文章,如果有什么需要改进的地方还请大佬指出⛺️** 🎬作者简介:大家好,我是青衿🥇 ☁️博客首页:CSDN主页放风…

go语言每日一练——链表篇(六)

传送门 牛客面试必刷101题—— 判断链表中是否有环 牛客面试必刷101题—— 链表中环的入口结点 题目及解析 题目一 代码 package mainimport . "nc_tools"/** type ListNode struct{* Val int* Next *ListNode* }*//**** param head ListNode类* return bool…

算法笔记刷题日记——3.简单入门模拟 3.2 查找元素

刷题日记 3.2 查找元素 B1041 B1004 B1028 B1032 A1011 A1006 A1036 错题记录 B1028 人口普查 某城镇进行人口普查,得到了全体居民的生日。现请你写个程序,找出镇上最年长和最年轻的人。 这里确保每个输入的日期都是合法的,但不一定是合理的…

网工内推 | 上市公司运维岗,红帽认证优先,带薪年假,节日福利

01 捷科智诚 招聘岗位:配置管理岗-运维 职责描述: 1.负责日常配置管理平台的维护及优化,包括权限管理、基线管理、版本管理、发布管理、变更管理等; 2.负责维护各个管理平台的用户管理、权限管理、项目初始化等工作; …

一些常见的电源方案

开关电源: RM C 板:(24V电压,10A电流) SMBJ30CA:静电和浪涌保护(TVS/ESD) 一般使用NMOS管,因为PMOS管导通电阻与NMOS管比较会较大 模电非基础01——从一种常见的防反接,上电缓启…

【深度学习理论】持续更新

文章目录 1.统计学习理论 1.统计学习理论 统计学习理论,一款适合零成本搞深度学习的大冤种的方向 从人类学习到机器学习的对比(学习的过程分为归纳和演绎 ),引出泛化和过拟合的概念。 如何表示归纳的函数规律呢?以监督…

RP2040 SPI

SPI 的库文件是arduino开源库,在程序中include。 SPI IIC都是通信协议【模块】,需要硬件支持,MCU都会集成相应的模块。arduino都集成在了内核中,直接用API函数调用即可。其他单片机的架构也是相同的。 SPI的库和函数及其相关说明…

NLP_词的向量表示Word2Vec 和 Embedding

文章目录 词向量Word2Vec:CBOW模型和Skip-Gram模型通过nn.Embedding来实现词嵌入Word2Vec小结 词向量 下面这张图就形象地呈现了词向量的内涵:把词转化为向量,从而捕捉词与词之间的语义和句法关系,使得具有相似含义或相关性的词语在向量空间…

2024牛客寒假算法基础集训营1——H

输入 3 4 11 1 8 1 4 1 5 1 1 4 11 5 8 1 4 1 5 1 1 4 0 2 0 0 0 3 0 4 1 输出 3 6 5 思路&#xff1a; 考虑二进制&#xff0c;有点像数位dp 本题考虑集合划分&#xff0c;累加最大值即可 代码如下&#xff1a; #include<bits/stdc.h> using namespace std;void solv…

2024三掌柜赠书活动第八期:Web3与DAO:下一代互联网演进逻辑

目录 前言关于Web3和DAO关于《Web3与DAO&#xff1a;下一代互联网演进逻辑》编辑推荐内容简介作者简介精彩书评图书目录书中前言/序言《Web3与DAO&#xff1a;下一代互联网演进逻辑》全书速览结束语 前言 随着区块链技术的崛起&#xff0c;Web3和DAO成为了当前互联网领域炙手…

【达梦数据库】使用DBeaver管理达梦数据库

使用DBeaver管理达梦数据库 Step1 安装相关程序 达梦8数据库DBeaver社区版 Step2 新建驱动 类型参数驱动名称DM8驱动类型Generic类名dm.jdbc.driver.DmDriverURL模板jdbc:dm://{host}:{port}默认端口5236默认数据库默认用户SYSDBA Step3 连接服务

docker部署笔记系统flatnotes

效果 安装 创建目录 mkdir -p /opt/flatnotes/data && cd /opt/flatnotes/ chmod -R 777 /opt/flatnotes/ 创建并启动容器(可以自己修改账户和密码) docker run -d \ --restart unless-stopped \ --name flatnotes \ -p "10040:8080" \ -v "/dat…

vue3 之 组合式API—生命周期函数

vue3的生命周期API 生命周期函数基本使用 1️⃣导入生命周期函数 2️⃣执行生命周期函数 传入回调 <scirpt setup> import { onMounted } from vue onMounted(()>{// 组件挂载完毕mounted执行了 }) </script>执行多次 生命周期函数是可以执行多次的&#xff…

【pikachu csrf】

cxrf 个人理解getPOST 个人理解 当被攻击用户登陆访问网站时&#xff0c;在保持登陆状态时点击小黑子&#xff08;黑客&#xff09;搭建的恶意链接而导致用户受到攻击。 举个例子 我去攻击网站&#xff0c;但是我找不到漏洞&#xff0c;这个时候我注册一个账号&#xff0c;发现…

C++——stack与queue与容器适配器

1.stack和queue的使用 1.1stack的使用 栈这种数据结构我们应该挺熟了&#xff0c;先入后出&#xff0c;只有一个出口(出口靠栈顶近)嘛 stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类&#xff0c;这些容器类应该支持以操作&#xff1a; empty&#xff1…

【leetcode题解C++】77.组合 and 216.组合总和III and 17.电话号码的字母组合

77. 组合 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ] 示例 2&#xff1a; 输入&#xff1a…

Linux系统调试课:ftrace跟踪器介绍

文章目录 一、什么是frace跟踪器?二、Ftrace 配置三、Ftrace 文件系统四、Ftrace 初体验五、函数跟踪六、Ftrace function_graph七、函数 Profiler沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是frace跟踪器? 操作系统内核对应用开发工程师来说就像一个黑盒,…

页面单跳转换率统计案例分析

需求说明 页面单跳转化率 计算页面单跳转化率&#xff0c;什么是页面单跳转换率&#xff0c;比如一个用户在一次 Session 过程中访问的页面路径 3,5,7,9,10,21&#xff0c;那么页面 3 跳到页面 5 叫一次单跳&#xff0c;7-9 也叫一次单跳&#xff0c; 那么单跳转化率就是要统计…