自然语言处理(13:RNN的实现)

news2025/3/31 7:16:45

系列文章目录

第一章 1:同义词词典和基于计数方法语料库预处理

第一章 2:基于计数方法的分布式表示和假设,共现矩阵,向量相似度

第一章 3:基于计数方法的改进以及总结

第二章 1:word2vec

第二章 2:word2vec和CBOW模型的初步实现

第二章 3:CBOW模型的完整实现

第二章 4:CBOW模型的补充和skip-gram模型的理论

第三章 1:word2vec的高速化(CBOW的改进)

第三章 2:word2vec高速化(CBOW的二次改进)

第三章 3:改进版word2vec的学习以及总结

第四章 1:RNN(RNN的前置知识和引入)

第四章 2:RNN(RNN的正式介绍)

第四章 3:RNN的实现

第四章 4:处理时序数据的层的实现

第四章 5:RNNLM的学习和评价


文章目录

目录

系列文章目录

文章目录

前言

一、RNN的实现

1.RNN层的实现

2.Time RNN层的实现


前言

通过之前的探讨,我们已经看到了RNN的全貌。实际上,我们要实现的是一个在水平方向上延伸的神经网络。另外,考虑到基于Truncated BPTT的学习,只需要创建一个在水平方向上长度固定的网络序列即可,来吧,开始实现(迫不及待!!)


一、RNN的实现

如上图所示,目标神经网络接收长度为T的时序数据(T为任意值), 输出各个时刻的隐藏状态T个。这里,考虑到模块化,将图中在水平方向上延伸的神经网络实现为“一个层”,如下图所示。

如图5-17所示,将垂直方向上的输入和输出分别捆绑在一起,就可以 将水平排列的层视为一个层。换句话说,可以将(x0,x1,···,xT−1)捆绑为 xs 作为输入,将(h0,h1,···,hT−1)捆绑为hs作为输出。这里,我们将进 行Time RNN层中的单步处理的层称为“RNN层”,将一次处理T步的层称为“Time RNN层 ”。

我们接下来进行的实现的流程是:首先,实现进行RNN单步处理的RNN 类;然后,利用这个RNN类,完成一次进行T步处理的TimeRNN类。

1.RNN层的实现

现在,我们来实现进行RNN单步处理的RNN类。首先复习一下RNN 正向传播的数学式,如下式所示:

这里,我们将数据整理为mini-batch进行处理。因此,xt(和ht)在行方向上保存各样本数据。在矩阵计算中,矩阵的形状检查非常重要。这里,假设批大小是N,输入向量的维数是D,隐藏状态向量的维数是H,则矩阵的形状检查可以像下面这样进行:

如上图所示,通过矩阵的形状检查,可以确认它的实现是否正确, 至少可以确认它的计算是否成立。基于以上内容,现在我们给出RNN类的初始化方法和正向传播的forward()方法

class RNN:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        
        self.cache = None
        # 中间数据cache 
    
    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)
    
        self.cache = (x, h_prev, h_next)
        return h_next

RNN 的初始化方法接收两个权重参数和一个偏置参数。这里,将通过函数参数传进来的模型参数设置为列表类型的成员变量params。然后,以各个参数对应的形状初始化梯度,并保存在grads中。最后,使用None对反向传播时要用到的中间数据cache进行初始化。

正向传播的forward(x, h_prev) 方法接收两个参数:从下方输入的x和从左边输入的h_prev。剩下的就是按下式进行实现。

顺便说一下,这里 从前一个RNN层接收的输入是h_prev,当前时刻的RNN层的输出(=下一时刻的RNN层的输入)是h_next。

接下来,我们继续实现RNN的反向传播。在此之前,让我们通过下图的计算图再次确认一下RNN的正向传播:

RNN层的正向传播可由上图的计算图表示。这里进行的计算由矩阵乘积“MatMul”、加法“+”和“tanh”这3种运算构成。此外,因为偏置 b 的加法运算会触发广播操作,所以严格地讲,这里还应该加上Repeat节点。不过简单起见,这里省略了它。

剩下就是基于下图,按正向传播的反方向实现各个运算的反向传播:

下面实现RNN层的backward()

def backward(self, dh_next):
    """
    这里看不懂的,可以看博主前几节的文章,或者问Deepseek

    """

    Wx, Wh, b = self.params

    x, h_prev, h_next = self.cache
    dt = dh_next * (1 - h_next ** 2)
    db = np.sum(dt, axis=0)
    dWh = np.dot(h_prev.T, dt)
    dh_prev = np.dot(x.T,, dt)
    dWx = np.dot(x.T, dt)
    dx = np.dot(dt, Wx.T)
    
    
    self.grads[0][...] = dWx
    self.grads[1][...] = dWh
    self.grads[2][...] = db

    return dx, dh_prev

以上就是RNN层的反向传播的实现。接下来,我们将实现Time RNN层

2.Time RNN层的实现

Time RNN 层由T个RNN层构成,如下所示:

由上图可知,Time RNN层是T个RNN层连接起来的网络。我们将这个网络实现为Time RNN层。这里,RNN层的隐藏状态h保存在成员变量中。如下图所示,在进行隐藏状态的“继承”时会用到它。

如上图所示,我们使用Time RNN层管理RNN层的隐藏状态。这样一来,使用Time RNN的人就不必考虑RNN层的隐藏状态的“继承工作”了。另外,我们可以用stateful这个参数来控制是否继承隐藏状态。

下面,我们来看一下Time RNN层的实现。

class TimeRNN:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx),np.zeros_like(Wh),np.zeros_like(b)]
        self.layers = None

        self.h, self.dh = None, None
        self.stateful = stateful

    def set_state(self, h):
        self.h = h
    def reset_state(self):
        self.h = None

初始化方法的参数有权重、偏置和布尔型(True/False)的stateful。 一个成员变量layers在列表中保存多个RNN层,另一个成员变量,h保存调用forward() 方法时的最后一个RNN层的隐藏状态。另外,在调用 backward() 时,dh保存传给前一个块的隐藏状态的梯度(关于dh,我们会在 反向传播的实现中说明)。

考虑到TimeRNN类的扩展性,将设定Time RNN层的隐藏状态的 方法实现为set_state(h)。另外,将重设隐藏状态的方法实现为 reset_state()。

当 stateful 为 True 时,Time RNN 层“有状态”。这里说的“有状态”是指维 持Time RNN层的隐藏状态。也就是说,无论时序数据多长,Time RNN 层的正向传播都可以不中断地进行。而当stateful为False时,每次调用Time RNN 层的forward() 时,第一个RNN层的隐藏状态都会被初始化为 零矩阵(所有元素均为0的矩阵)。这是没有状态的模式,称为“无状态”。

(True就是这一层受上一层的影响,具有依赖性;

False就是相互独立,各个数据互不影响)

接着,我们来看一下正向传播的实现:

def forward(self, xs):
    Wx, Wh, b = self.params
    N, T, D = xs.shape
    D, H = Wx.shape
    
    self.layers = []
    hs = np.empty((N, T, H), dtype='f')

    if not self.stateful or self.h is None:
        self.h = np.zeros((N, H), dtype='f')
    

    for t in range(T):
        layer = RNN(*self.params)
        self.h = layer.forward(xs[:, t, :], self.h)
        hs[:, t, :] = self.h
        self.layers.append(layer)
    
    return hs

正向传播的forward(xs)方法从下方获取输入xs,xs囊括了T个时序数 据。因此,如果批大小是N,输入向量的维数是D,则xs的形状为(N,T,D)。 在首次调用时(self.h为None时 ), RNN层的隐藏状态h由所有元素 均为0的矩阵初始化。另外,在成员变量stateful为False的情况下,h将 总是被重置为零矩阵。

在主体实现中,首先通过hs=np.empty((N, T, H), dtype='f') 为输出准 备一个“容器”。接着,在T次for循环中,生成RNN层,并将其添加到成员变量layers中。然后,计算RNN层各个时刻的隐藏状态,并存放在hs 的对应索引(时刻)中。

(如果调用Time RNN层的forward()方法,则成员变量h中将存放 最后一个RNN层的隐藏状态。在stateful为True的情况下,在下 一次调用forward()方法时,刚才的成员变量h将被继续使用。而在 stateful为False的情况下,成员变量h将被重置为零向量。)

接下来是Time RNN层的反向传播的实现。用计算图绘制这个反向传播:

在上图中,将从上游(输出侧的层)传来的梯度记为dhs,将流向 下游的梯度记为dxs。因为这里我们进行的是Truncated BPTT,所以不需要流向这个块上一时刻的反向传播。不过,我们将流向上一时刻的隐藏状态的梯度存放在成员变量dh中。这是因为在第7章探讨seq2seq(sequence to-sequence,序列到序列)时会用到它(具体请参考第7章 )。 以上就是Time RNN层的反向传播的全貌图。如果关注第t个RNN层, 则它的反向传播如下图所示。

从上方传来的梯度dht和从将来的层传来的梯度dhnext会传到第t个 RNN层。这里需要注意的是,RNN层的正向传播的输出有两个分叉。在正 向传播存在分叉的情况下,在反向传播时各梯度将被求和。因此,在反向传 播时,流向RNN层的是求和后的梯度。考虑到以上这些,反向传播的实现 如下所示。

def backward(self, dhs):
    Wx, Wh, b = self.params
    N, D, H = dhs.shape
    D, H = Wx.shape
    
    dxs = np.empty((N, T, D), dtype="f")
    dh = 0
    grads = [0, 0, 0]
    
    for t in reversed(range(T)):
        layer = self.layers[t]
        dx, dh = layer.backward(dhs[:, t, :] + dh) # 求和后的梯度
        dxs[:, t, :] = dx

        for i, grad in enumerate(layer.grads):
            grad[i] += grad
    for i, grad in enumerate(grads):
        self.grads[i][...] = grad
    self.dh = dh
    return dxs

这里,首先创建传给下游的梯度的“容器”(dxs)。接着,按与正向传 播相反的方向,调用RNN层的backward()方法,求得各个时刻的梯度dx, 并存放在dxs的对应索引处。另外,关于权重参数,需要求各个RNN层的 权重梯度的和,并通过“...”用最终结果覆盖成员变量self.grads。

以上就是对Time RNN层的实现的说明。

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

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

相关文章

无人机宽带自组网机载电台技术详解,50KM超远图数传输系统实现详解

以下是关于无人机宽带自组网机载电台技术以及50KM超远图数传输系统实现的详解: 无人机宽带自组网机载电台技术详解 无人机宽带自组网机载电台是一种专门为无人机设计的通信设备,它支持宽带数据传输和自组网功能。这种电台的实现技术涉及多个方面&#x…

MySQL 表 t1 建立联合索引 (a, b, c),在 where a < ? and b > ? and c < ? 中哪些索引生效

文章目录 联合索引 abc 均范围扫描时的索引生效情况无回表 表数据量非常少无回表 表数据量多有回表总结 联合索引 abc 均范围扫描时的索引生效情况 场景&#xff1a;表 t1 建立联合索引 (a, b, c)&#xff0c;在 where a < ? and b > ? and c < ? 中哪些索引生效…

BP神经网络+NSGAII算法(保真)

BP神经网络NSGAII算法 非常适合用来当作实验验证自己的结论&#xff0c;构建一个神经网络模型&#xff0c;并使用NSGAII多目标优化算法来实现多领域的毕业论文的设计。仅仅使用简单的matlab代码就可以实现自己的多目标优化任务。 BP神经网络算法 我的任务是预测三个变量的值…

每日一题-力扣-2829. k-avoiding 数组的最小总和 0326

解决"k-avoiding 数组的最小总和"问题 这道题有两种主要解法。 解法一&#xff1a;直接数学计算&#xff08;最优解&#xff09; 通过数学推导直接计算出结果&#xff0c;不需要构建实际的数组。 class Solution:def minimumSum(self, n: int, k: int) -> int…

OSI模型_TCP/IP模型_五层模型

文章目录 OSI模型_TCP/IP模型_五层模型模型对比模型层级对比关键区别对比 OSI模型OSI模型概述举例说明流程图示 TCP/IP 四层模型模型结构举例说明流程图示 TCP/IP 五层模型模型的结构举例说明流程图示 OSI模型_TCP/IP模型_五层模型 学OSI&#xff0c;用TCP/IP&#xff0c;分析选…

SpringCould微服务架构之Docker(2)

Docker和虚拟机的差别&#xff1a; 虚拟机是在操作系统中模拟硬件设备&#xff0c;然后运行另外一个操作系统。

LINUX基础IO [六] - 文件理解与操作

目录 前言 C语言文件操作回顾 文件的打开与关闭 文件的增删改查 文件系统调用 比特位方式的标志位传递原理 访问文件的本质 文件描述符fd 理解文件描述符fd 三个流的理解 文件描述符的分配规则 重定向再理解 输出重定向 输入重定向 如何理解一切皆文件 理解…

拥抱人工智能大模型时代:大模型会改变我们的生活吗?

在这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着我们的生活和工作方式。尤其是随着人工智能大模型&#xff08;如ChatGPT、DeepSeek等&#xff09;的崛起&#xff0c;人们对于AI技术的期待和关注达到了前所未有的高度。那么&…

常见框架漏洞攻略-ThinkPHP篇

漏洞名称&#xff1a;Thinkphp5x远程命令执行及getshell 第一步&#xff1a;开启靶场 第二步&#xff1a;准备工具 第三步&#xff1a;启动工具&#xff0c;进行漏洞检测 #存在漏洞 1.目标存在tp5_invoke_func_code_exec_1漏洞2.目标存在tp5_dbinfo_leak漏洞payload:http://47…

LlamaFactory部署及模型微调【win10环境】

1.Llama-Factory简介 LLaMA-Factory&#xff0c;全称 Large Language Model Factory&#xff0c;旨在简化大模型的微调过程&#xff0c;帮助开发者快速适应特定任务需求&#xff0c;提升模型表现。它支持多种预训练模型和微调算法&#xff0c;适用于智能客服、语音识别、机器翻…

vue3配置代理实现axios请求本地接口返回PG库数据【前后端实操】

前端编写 安装 axios 如果当前未安装axios&#xff0c;可以执行如下指令安装 npm install axios配置代理 当前为基于Vite构建的项目&#xff0c;在 vite.config.ts 中配置代理&#xff0c;在defineConfig中新增server配置&#xff0c;主要关注两个点&#xff1a; 一、需要代…

trae 配置 gradle springboot项目

一 本机安装gradle 1.下载gradle &#xff1a; https://github.com/gradle/gradle-distributions/releases/download/v8.13.0/gradle-8.13-all.zip 2.配置相关环境变量&#xff1a; GRADLE_HOME&#xff1a;本地的gradle路径。 GRADLE_USER_HOME&#xff1a;gradle 本地仓…

uv:Rust 驱动的 Python 包管理新时代

在 Python 包管理工具层出不穷的今天&#xff0c;pip、pip-tools、poetry、conda 等各有千秋。而今天要介绍的 uv&#xff0c;则是一款由 Astral 团队推出、采用 Rust 编写的全新工具&#xff0c;目标直指成为 “Python 的 Cargo”。它不仅在性能上表现优异&#xff0c;而且在功…

sqlserver 阻止保存要求重新创建表的更改

1 选择 “工具” 菜单&#xff0c;然后点击 “选项” 2 进入选项界面后&#xff0c;选择 “设计器”&#xff0c;取消勾选 “阻止保存要求重新创建表的更改” 选项&#xff0c;点击 “确定”

5.Excel:从网上获取数据

一 用 Excel 数据选项卡获取数据的方法 连接。 二 要求获取实时数据 每1分钟自动更新数据。 A股市场_同花顺行情中心_同花顺财经网 用上面方法将数据加载进工作表中。 在表格内任意区域右键&#xff0c;刷新。 自动刷新&#xff1a; 三 缺点 Excel 只能爬取网页上表格类型的…

在word中使用zotero添加参考文献并附带超链接

一、引言 在写大论文时&#xff0c;为了避免文中引用与文末参考文献频繁对照、修改文中引用顺序/引用文献时手动维护参考文献耗易出错&#xff0c;拟在 word 中使用 zotero 插入参考文献&#xff0c;并为每个参考文献附加超链接&#xff0c;实现交互式阅读。 版本&#xff1a…

性能测试、负载测试、压力测试的全面解析

在软件测试领域&#xff0c;性能测试、负载测试和压力测试是评估系统稳定性和可靠性的关键手段。​它们各自关注不同的测试目标和应用场景&#xff0c;理解这些差异对于制定有效的测试策略至关重要。 本文对性能测试、负载测试和压力测试进行深入分析&#xff0c;探讨其定义、…

Redis中的数据类型与适用场景

目录 前言1. 字符串 (String)1.1 特点1.2 适用场景 2. 哈希 (Hash)2.1 特点2.2 适用场景 3. 列表 (List)3.1 特点3.2 适用场景 4. 集合 (Set)4.1 特点4.2 适用场景 5. 有序集合 (Sorted Set)5.1 特点5.2 适用场景 6. Redis 数据类型的选型建议结语 前言 Redis 作为一款高性能的…

gz sim机器人SDF模型 [持续更新]

机器人SDF模型 linklink的一级pose材质 plugin话题信息通信键盘操作plugin Sensor传感器imu 不算教学&#xff0c;个人的记录 sdf的格式跟urdf有所不同&#xff0c;必须是完整的一个包括&#xff0c;比如< pose></ pose>这样前一个后一个&#xff0c;urdf中是有<…

【MySQL | 六、索引特性(进一步理解)】

目录 索引的理解索引的作用MySQL与磁盘的IOPage单个Page的分类多个Page的组织B树的特点 B树和B树的区别聚簇索引 VS 非聚簇索引聚簇索引的优缺点非聚簇索引的优缺点 创建索引常见索引分为&#xff1a;主键索引InnoDB主键索引的生成过程&#xff08;1&#xff09;初始化&#xf…