3.4-GRU

1网络结构

1.1与LSTM相比

  1. LSTM里面有三个门,还有一个增加信息的tanh单元,参数量相较于RNN显著增加;

  2. 因此GRU在参数上比LSTM要少;

  3. 另外,LSTM 将必要信息记录在记忆单元中,并基于记忆单元的信息计算隐藏状态。与此相对,GRU 中不需要记忆单元这样的额外存储;如下图所示:

    在这里插入图片描述

1.2 GRU具体的网络结构

  1. GRU的结构如下图;右边是LSTM的结构图,作为对比:

    1. LSTM能够控制梯度的流动就是因为记忆单元那条梯度流动的路径上只有加法和对应元素相乘的乘法;
    2. GRU这里虽然去掉了额外的记忆单元,但是隐藏信息是也是从加法和对应元素相乘的乘法路径上流过的,因此梯度消失问题也可以得到缓解;

    在这里插入图片描述

  2. GRU的计算涉及以下公式:
    z = σ ( x t W x ( z ) + h t − 1 W h ( z ) + b ( z ) ) (1) \boldsymbol{z}=\sigma(\boldsymbol{x}_{t}W_{x}^{{(\mathrm{z})}}+\boldsymbol{h}_{t-1}W_{h}^{{(\mathrm{z})}}+\boldsymbol{b}^{{(\mathrm{z})}}) \tag{1} z=σ(xtWx(z)+ht1Wh(z)+b(z))(1)

    r = σ ( x t W x ( r ) + h t − 1 W h ( r ) + b ( r ) ) (2) \boldsymbol{r}=\sigma(\boldsymbol{x}_{t}W_{x}^{{(\mathrm{r})}}+\boldsymbol{h}_{t-1}\boldsymbol{W}_{h}^{{(\mathrm{r})}}+\boldsymbol{b}^{{(\mathrm{r})}}) \tag{2} r=σ(xtWx(r)+ht1Wh(r)+b(r))(2)

    h ~ = tanh ⁡ ( x t W x + ( r ⊙ h t − 1 ) W h + b ) (3) \tilde{\boldsymbol{h}}=\tanh(\boldsymbol{x}_t\boldsymbol{W}_x+(\boldsymbol{r}\odot\boldsymbol{h}_{t-1})\boldsymbol{W}_h+\boldsymbol{b}) \tag{3} h~=tanh(xtWx+(rht1)Wh+b)(3)

    h t = ( 1 − z ) ⊙ h t − 1 + z ⊙ h ~ (4) \boldsymbol{h}_t=(1-\boldsymbol{z})\odot\boldsymbol{h}_{t-1}+\boldsymbol{z}\odot\tilde{\boldsymbol{h}} \tag{4} ht=(1z)ht1+zh~(4)

  3. 由于GRU的输出只有 h t \boldsymbol{h}_t ht,因此需要结合两个部分的信息:旧的信息 ( 1 − z ) ⊙ h t − 1 (1-\boldsymbol{z})\odot\boldsymbol{h}_{t-1} (1z)ht1和新的信息 z ⊙ h ~ \boldsymbol{z}\odot\tilde{\boldsymbol{h}} zh~;不像LSTM专门有一个记忆单元,旧的和新的记忆都被先存到了记忆单元里面,所以LSTM的隐藏状态直接基于当前时刻的记忆单元;所以类比LSTM的门结构,有以下说明:

    1. z ⊙ h ~ \boldsymbol{z}\odot\tilde{\boldsymbol{h}} zh~是新的信息,所以 z \boldsymbol{z} z相当于输入门,对新增信息进行加权;
    2. ( 1 − z ) ⊙ h t − 1 (1-\boldsymbol{z})\odot\boldsymbol{h}_{t-1} (1z)ht1是旧的信息,所以 ( 1 − z ) (1-\boldsymbol{z}) (1z)充当了遗忘门,给上一个隐藏状态加权,决定哪些信息的权重可以降低;
    3. 因此, z \boldsymbol{z} z这里的门结构称为更新门,同时充当了输入门和遗忘门的作用;
  4. 还有一个重置(reset)门 r \boldsymbol{r} r,决定在多大程度上“忽略”过去的隐藏状态;

    1. 如果 r \boldsymbol{r} r是0,则根据(3)式和(4)式,新的隐藏状态 h t \boldsymbol{h}_t ht仅取决于当前时刻的输入 x t \boldsymbol{x}_t xt

1.3 LSTM和GRU选哪个

  1. 不是绝对的;根据不同的任务和超参数设置,结论可能不同;
  2. 因为 GRU 的超参数少、计算量小,所以特别适合用于数据集较小、设计模型需要反复实验的场景。

2 GRU的代码实现

所有代码位于:GY/basicNLP (gitee.com);

代码位于:GRU_model/GRU.py

2.1初始化

  1. 与LSTM相比,GRU少了一个门,但是参数还是整合在一起;因此维度从4H变成3H;代码如下:

    class GRU:def __init__(self, Wx, Wh, b):'''输入参数都是整合了四个仿射变换的参数@param Wx: (D, 3H)@param Wh: (H, 3H)@param b: (1, 3H)'''self.params = [Wx, Wh, b]if GPU:self.grads = [to_gpu(np.zeros_like(Wx)), to_gpu(np.zeros_like(Wh)), to_gpu(np.zeros_like(b))]else:self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]self.cache = None
    

2.2前向计算

  1. LSTM的几个门的计算,都是使用 x t \boldsymbol{x}_{t} xt h t − 1 \boldsymbol{h}_{t-1} ht1;因此可以先把四个参数整合到一起先统一算矩阵乘法;而GRU这里只有式子(1)(2)是可以整合的;所以这里在实现的时候单纯按照公式计算了;没有整合;

  2. 代码如下:

        def forward(self, x, h_prev):'''@param x: (N, D)@param h_prev: (N, H)@return h_next: (N, H)'''Wx, Wh, b = self.paramsH = Wh.shape[0]# 取出门以及tanh节点的权重以及偏置Wxz, Wxr, Wxh = Wx[:, :H], Wx[:, H:2 * H], Wx[:, 2 * H:]Whz, Whr, Whh = Wh[:, :H], Wh[:, H:2 * H], Wh[:, 2 * H:]bz, br, bh = b[:H], b[H:2 * H], b[2 * H:]# 直接按照公式计算z = sigmoid(np.dot(x, Wxz) + np.dot(h_prev, Whz) + bz) # z:(N,H);+bz时会进行广播r = sigmoid(np.dot(x, Wxr) + np.dot(h_prev, Whr) + br) # r:(N,H);h_hat = np.tanh(np.dot(x, Wxh) + np.dot(r * h_prev, Whh) + bh) #h_hat:(N,H)h_next = (1 - z) * h_prev + z * h_hat # *对应元素相乘self.cache = (x, h_prev, z, r, h_hat)return h_next
    

2.3反向传播

  1. 首先计算下图中红色字处的梯度:

    在这里插入图片描述

    1. 在代码上就体现为:

      dh_hat =dh_next * z
      dh_prev = dh_next * (1-z)
      
  2. 然后计算tanh节点的各个参数的梯度;根据下式;但此时是把 r ⊙ h t − 1 \boldsymbol{r}\odot\boldsymbol{h}_{t-1} rht1看成数据;从结构图中也可以看出 r ⊙ h t − 1 \boldsymbol{r}\odot\boldsymbol{h}_{t-1} rht1是与tanh节点分开的;
    h ~ = tanh ⁡ ( x t W x + ( r ⊙ h t − 1 ) W h + b ) \tilde{\boldsymbol{h}}=\tanh(\boldsymbol{x}_t\boldsymbol{W}_x+(\boldsymbol{r}\odot\boldsymbol{h}_{t-1})\boldsymbol{W}_h+\boldsymbol{b}) h~=tanh(xtWx+(rht1)Wh+b)

    1. 代码如下:

      # tanh
      dt = dh_hat * (1 - h_hat ** 2)
      dbh = np.sum(dt, axis=0) # 广播;所以梯度累加回去
      dWhh = np.dot((r * h_prev).T, dt)
      dhr = np.dot(dt, Whh.T)
      dWxh = np.dot(x.T, dt)
      dx = np.dot(dt, Wxh.T)
      
  3. 然后就可以计算上一时刻隐藏状态的梯度了;它是两个分支梯度的和;

    dh_prev += r * dhr
    
  4. 更新门 z \boldsymbol{z} z处的梯度如下图蓝色笔迹:

    在这里插入图片描述

    1. 代码如下:

      # update gate(z)
      dz = dh_next * h_hat - dh_next * h_prev
      dt = dz * z * (1-z) # 乘上sigmoid函数的局部梯度
      # gate(z)这里仿射变换的梯度计算
      dbz = np.sum(dt, axis=0) # 广播;所以梯度累加回去
      dWhz = np.dot(h_prev.T, dt)
      dh_prev += np.dot(dt, Whz.T)
      dWxz = np.dot(x.T, dt)
      dx += np.dot(dt, Wxz.T) # 与tanh节点处x的梯度累加
      
  5. 然后剩下一个重置门 r \boldsymbol{r} r的梯度计算;

    # reset gate(r)
    dr = dhr * h_prev
    dt = dr * r * (1-r) # 乘上sigmoid函数的局部梯度
    dbr = np.sum(dt, axis=0) # 广播;所以梯度累加回去
    dWhr = np.dot(h_prev.T, dt)
    dh_prev += np.dot(dt, Whr.T)
    dWxr = np.dot(x.T, dt)
    dx += np.dot(dt, Wxr.T)
    
  6. 整个梯度传播过程中,尤其注意输入 x t \boldsymbol{x}_t xt和上一时刻隐藏状态 h t − 1 \boldsymbol{h}_{t-1} ht1梯度的累加;因为它们有好多分支;

  7. 最后对梯度进行整理;比如参数权重是整合在一起的,因此梯度也要整合一下;

3 Time GRU的代码实现

代码位于:GRU_model/GRU.py

  1. 初始化:少了记忆单元;代码如下;

    class TimeGRU:def __init__(self, Wx, Wh, b, stateful=False):self.params = [Wx, Wh, b]if GPU:self.grads = [to_gpu(np.zeros_like(Wx)), to_gpu(np.zeros_like(Wh)), to_gpu(np.zeros_like(b))]else:self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]self.layers = Noneself.h, self.dh = None, None # 没有了记忆单元self.stateful = stateful
    
  2. 前向计算与Time LSTM相比,也是少了记忆单元;

        def forward(self, xs):'''@param xs: (N, T, D);N批数据;T时间步;D特征维度@return hs: (N, T, H)'''Wx, Wh, b = self.paramsN, T, D = xs.shapeH = Wh.shape[0]self.layers = []if GPU:hs = to_gpu(np.empty((N, T, H), dtype='f'))if not self.stateful or self.h is None:self.h = to_gpu(np.zeros((N, H), dtype='f'))else: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 = GRU(*self.params)self.h = layer.forward(xs[:, t, :], self.h)hs[:, t, :] = self.hself.layers.append(layer)return hs
    
  3. 反向传播过程基本一致;

    在这里插入图片描述

4基于PTB数据集对GRU模型进行训练和学习

4.1无改进的GRU训练和学习模型

代码位于:GRU_model/GRULM.pyGRU_model/train_GRULM.py

  1. 代码基本都一样;详见代码了;下图是训练过程中的困惑度曲线;跟LSTM的很相似;

    在这里插入图片描述

4.2有改进的GRU训练和学习模型

代码位于:GRU_model/better_GRULM.pyGRU_model/train_better_GRULM.py

  1. 这里的改进和LSTM那边一样,即权重共享、加入dropout层;然后GRU多层化;

  2. 以下是实验结果:

    在这里插入图片描述

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

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

相关文章

MySQL数据库(基础篇)

🌏个人博客主页:心.c 前言:今天讲解的是MySQL的详细知识点的,希望大家可以收货满满,话不多说,直接开始搞! 🔥🔥🔥文章专题:MySQL 😽感…

1.c#(winform)编程环境安装

目录 安装vs创建应用帮助查看器安装与使用( msdn) 安装vs 安装什么版本看个人心情,或者公司开发需求需要 而本栏全程使用vs2022进行开发c#,着重讲解winform桌面应用开发 使用***.net framework***开发 那先去官网安装企业版的vs…

AI绘画入门实践 | Midjourney:使用 --chaos 给图像风格来点惊喜

在 Midjourney 中,--chaos 影响初始图像网格的多样性,指 MJ 每次出的4张图之间的差异性。 默认值为0,值越高,差异性越大。 使用格式:--chaos 0-100的整数值 使用演示 a lot of flowers --chaos 0 --v 6.0a lot of fl…

项目打包与运行

前端运行时必须有与后端相同的数据库版本,数据库账号密码 右侧maven -> 展开要打包的项目 -> 生命周期 -> 双击package 打包好之后在target目录下 右键打开 在资源目录下输入cmd,执行以下命令即可运行(端口号为yml文件…

Redis实战篇(黑马点评)笔记总结

一、配置前后端项目的初始环境 前端: 对前端项目在cmd中进行start nginx.exe,端口号为8080 后端: 配置mysql数据库的url 和 redis 的url 和 导入数据库数据 二、登录校验 基于Session的实现登录(不推荐) &#xf…

【iOS】—— retain\release实现原理和属性关键字

【iOS】—— retain\release实现原理和属性关键字 1. retain\reelase实现原理1.1 retain实现原理1.2 release实现原理 2. 属性关键字2.1 属性关键字的分类2.2 内存管理关键字2.2.1 weak2.2.2 assgin2.3.3 strong和copy 2.4 线程安全的关键字2.5 修饰变量的关键字2.5.1常量const…

文件上传总结

一、原理 通过界面上的上传功能上传了一个可执行的脚本文件,而WEB端的系统并未对其进行检测或者检测的逻辑做的不够好,使得恶意用户可以通过文件中上传的一句话木马获得操控权 二、绕过方法 1>前端绕过 1.删除前端校验函数 checkFile() 2.禁用js…

大数据平台之HBase

HBase是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,是Apache Hadoop生态系统的重要组成部分。它特别适合大规模结构化和半结构化数据的存储和检索,能够处理实时读写和批处理工作负载。以下是对HBase的详细介绍。 1. 核心概念 1.1 表&#x…

打造一篇完美的【数学建模竞赛论文】:从准备到撰写的全面指南

目录 一、赛前准备 1.1 报名与纪律要求 1.2 MD5码上传 1.3 竞赛准备 1.4 时间分配 二、论文格式规范 2.1 摘要 2.2 参考文献 2.3 排版要求 三、建模过程与方法 3.1 问题分析与模型假设 3.2 模型构建与求解 3.3 结果分析与检验 四、论文撰写技巧 4.1 论文结构 4…

Godot入门 07 世界构建2.0

添加基础节点Node,重命名为Coins,整理场景树,拖动Coin到Coins节点下。 添加基础节点Node,重命名为Platforms,整理场景树,拖动Platform到Platforms节点下。 添加游戏背景 设置当前图层名称为Mid 添加图层元…

飞牛爬虫FlyBullSpider 一款简单方便强大的爬虫,限时免费 特别适合小白!用它爬下Boss的2024年7月底Java岗位,分析一下程序员就业市场行情

一、下载安装FlyBullSpider 暂时支持Window,现在只在Win11上做过测试 1 百度 点击百度网盘 下载 链接:https://pan.baidu.com/s/1gSLKYuezaZgd8iqrXhk8Kg 提取码:Fly6 2 csdn https://download.csdn.net/download/fencer911/89584687 二、体验初…

vue3 vxe-table 点击行,不显示选中状态,加上设置isCurrent: true就可以设置选中行的状态。

1、上个图&#xff0c;要实现这样的&#xff1a; Vxe Table v4.6 官方文档 2、使用 row-config.isCurrent 显示高亮行&#xff0c;当前行是唯一的&#xff1b;用户操作点击选项时会触发事件 current-change <template><div><p><vxe-button click"sel…

C++入门基础(超详细) 需:C语言基础

1.C的发展史 大致了解一下 C的起源可以追溯到1979年&#xff0c;当时BjarneStroustrup(本贾尼斯特劳斯特卢普&#xff0c;这个翻译的名字不 同的地方可能有差异)在贝尔实验室从事计算机科学和软件工程的研究工作。面对项目中复杂的软件开 发任务&#xff0c;特别是模拟和操作系…

Linux权限维持篇

目录 SSH后门 &#xff08;1&#xff09;软链接sshd &#xff08;2&#xff09;SSH Key 生成公私钥 创建个authorized_keys文件来保存公钥 通过修改文件时间来隐藏authorized_keys &#xff08;3&#xff09;SSH Keylogger&#xff08;记录日志&#xff09; Linux的PA…

【Go系列】Go的UI框架Fyne

前言 总有人说Go语言是一门后端编程语言。 Go虽然能够很好地处理后端开发&#xff0c;但是者不代表它没有UI库&#xff0c;不能做GUI&#xff0c;我们一起来看看Go怎么来画UI吧。 正文 Go语言由于其简洁的语法、高效的性能和跨平台的编译能力&#xff0c;非常适合用于开发GUI…

MICA:面向复杂嵌入式系统的混合关键性部署框架

背景 在嵌入式场景中&#xff0c;虽然 Linux 已经得到了广泛应用&#xff0c;但并不能覆盖所有需求&#xff0c;例如高实时、高可靠、高安全的场合。这些场合往往是实时操作系统的用武之地。有些应用场景既需要 Linux 的管理能力、丰富的生态&#xff0c;又需要实时操作系统的高…

vue中scoped详解以及样式穿透>>>、/deep/、::v-deep

1、scoped scoped属性用于限制样式仅应用于当前组件。当一个style标签拥有scoped属性时&#xff0c;它的CSS样式就只能作用于当前的组件&#xff0c;通过该属性&#xff0c;可以使得组件之间的样式不互相污染。 原理&#xff1a;当样式中加了scoped属性时候&#xff0c;编译的…

数据库解析一维和二维简易JSON,

项目还在使用Oracle11&#xff0c;不支持后续官方的json解析方式&#xff0c; 在 前年、去年、今年 接连 遇到json解析问题后&#xff08;其实是公司的轮子效率太慢&#xff0c;太复杂&#xff0c;决定自己造个轮子&#xff0c;看看到底为什么慢&#xff0c;是不是真的很复杂&a…

【最新】cudnn安装教程

最近换了新电脑需要重新安装cuda和cudnn&#xff0c;发现现在cudnn的安装比以前方便多了&#xff0c;直接在官网下载exe安装包一键运行即可。安装的时候注意cuda和cudnn的对应关系即可&#xff1a;【最新】cuda和cudnn和显卡驱动的对应关系-CSDN博客 访问cudnn下载链接cuDNN 9…

Git 基础 GitHub【学习笔记】

一、Git 优势 大部分操作在本地完成&#xff0c;不需要联网完整性保证尽可能添加数据而不是删除或修改数据分支操作非常快捷流畅与 Linux 命令全面兼容 二、Git 程序安装 https://git-scm.com 三、Git 结构 #mermaid-svg-9Go6R1leWXWrDCqn {font-family:"trebuchet ms&quo…