RNN流程详解

article/2025/10/3 17:39:48

RNN及其代码流程

本文重点关注RNN的 整个流程,而不是BP的推导过程

什么是RNN

  • Recurrent Neural Network

  • 循环神经网络

在这里插入图片描述

为什么需要RNN?

普通的神经网络都只能单独地处理一个个的输入,前一个输入和后一个输入是完全没有关系的。但是,某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的

**比如,当我们在理解一句话意思时,孤立的理解这句话的每个词是不够的,我们需要处理这些词连接起来的整个序列; **当我们处理视频的时候,我们也不能只单独的去分析每一帧,而要分析这些帧连接起来的整个序列。

举个例子:以简单的词性标注为例

  • 输入为 “我 吃 苹果”
  • 输出为 “我/n 吃/v 苹果/n”
  • 显然“吃”后面的“苹果”是名词的概率大于动词
  • 结论:一个位置的信息会受到其前面信息的影响
  • RNN就是一个可以保存前面时间步信息的神经网络

RNN(循环神经网络)基本框架

下图中值得注意的是,左边的自循环模型才是真正的RNN结构。右边的模型只是我们将RNN的结构按时间步展开,便于理解

在这里插入图片描述

  • 上一层的信息通过隐藏层保留,他将向下一个时间步传递
  • 看上图右半部分
    • x t x_t xt 是第 t t t 个时间步的输入 , s t s_t st 是第 t t t 个时间步保留的隐藏信息,也就是保留的前面的时间步的信息,这个信息将向第 t + 1 t+1 t+1 个时间步传递下去。
    • O t O_t Ot 是第 t t t 个时间步的输出
    • W , U , V W , U , V W,U,V 都是权重矩阵
    • O t = g ( V ⋅ S t ) O_{t}=g\left(V \cdot S_{t}\right) Ot=g(VSt)
    • S t = f ( U ⋅ X t + W ⋅ S t − 1 ) S_{t}=f\left(U \cdot X_{t}+W \cdot S_{t-1}\right) St=f(UXt+WSt1)
  • 现在回到左图
    • 时间步其实就是模型工作的第一次,第二次,第 t t t次。
    • 每次模型工作都会有一个输入 x x x , 和前面工作时留下来的信息,也就是隐藏层 s s s , 然后模型通过式子 S = f ( U ⋅ X + W ⋅ S ) S=f\left(U \cdot X+W \cdot S\right) S=f(UX+WS)更新隐藏层信息,也就是在过去的信息中添加本次输入的信息,再向下一层传递
    • 当前层的隐藏层信息综合了前面的信息,和本次输入的信息,因此可以决定输出。我们用 O = g ( V ⋅ S ) O=g\left(V \cdot S\right) O=g(VS)得到当前模型的输出

测试模型效果(利用模型生成句子的采样函数)

  • sample函数

    def sample(h, seed_ix, n): # 创建一段索引序列# h是隐藏层状态,也就是前面的时间步留下的信息#vocab_size是不重复的字母的数量,这里我们将字母向量化。每个字母在 x 中对应一个位置,该位置为1,则代表该字母出现。为0,则不出现x = np.zeros((vocab_size, 1))x[seed_ix] = 1 # seed_ix是一个数print("seed_ix:%s" % seed_ix)ixes = []for t in range(n): # 一共取出 n 个 indexh = np.tanh(np.dot(Wxh, x) + np.dot(Whh, h) + bh)y = np.dot(Why, h) + byp = np.exp(y) / np.sum(np.exp(y))ix = np.random.choice(range(vocab_size), p=p.ravel())x = np.zeros((vocab_size, 1))x[ix] = 1ixes.append(ix)return ixes
    
    • 功能:输入一个字母的索引,利用当前RNN模型,根据该字母创建整个句子,然后返回句子中出现的字母对应的索引列表

    • 输入

      • h是隐藏层状态,也就是前面的时间步留下的信息
      • seed_ix是一个索引,也就是我们要输入的字母对应的索引
      • n:句子长度(要生成的字母索引的个数)
    • 解析1

        x = np.zeros((vocab_size, 1))x[seed_ix] = 1 # 得到出入索引对应字母的编码向量print("seed_ix:%s" % seed_ix)ixes = []
      
      • vocab_size是不重复的字母的数量,这里我们将字母向量化。每个字母在 x 中对应一个位置,该位置为1,则代表该字母出现。为0,则不出现
      • 就是 one-hot 编码
    • 解析2

       for t in range(n): # 一共取出 n 个 indexh = np.tanh(np.dot(Wxh, x) + np.dot(Whh, h) + bh)y = np.dot(Why, h) + byp = np.exp(y) / np.sum(np.exp(y))ix = np.random.choice(range(vocab_size), p=p.ravel())x = np.zeros((vocab_size, 1))x[ix] = 1ixes.append(ix) #放入 ixes 列表中
      
      • h是隐藏层状态

      • 注意:每个 t 对应的 h 不同,这里的 h 在更新

      • y:得分向量,每个分数都是该分数的索引对应的字母的得分

      • p:利用 softmax , 将得分转化成概率

      • ix:按 p 中的概率取出一个索引

      • 重置编码向量 x ,以供下一个时间步利用

      • 就是实现了以下过程

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJlXa9PV-1636702475945)(C:\Users\nishiyu\AppData\Roaming\Typora\typora-user-images\image-20211111231703589.png)]

lossFun(损失函数)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h756El2H-1636702475946)(C:\Users\nishiyu\AppData\Roaming\Typora\typora-user-images\image-20211112000507073.png)]

  • lossFun

    • 详细介绍流程,不详细介绍 BP 过程中的梯度计算
    def lossFun(inputs, targets, hprev):"""inputs,targets are both list of integers.hprev is Hx1 array of initial hidden statereturns the loss, gradients on model parameters, and last hidden state"""xs, hs, ys, ps = {}, {}, {}, {}hs[-1] = np.copy(hprev)loss = 0# forward passfor t in range(len(inputs)): # inputs里都是数组<int>类型# 将元素编码为向量xs[t] = np.zeros((vocab_size,1)) # 初始化为 0 向量# print("input: %s" % inputs[t])# print("type: %s" % type(inputs[t]))xs[t][inputs[t]] = 1hs[t] = np.tanh(np.dot(Wxh, xs[t]) + np.dot(Whh, hs[t-1]) + bh) # hidden stateys[t] = np.dot(Why, hs[t]) + by # unnormalized log probabilities for next charsps[t] = np.exp(ys[t]) / np.sum(np.exp(ys[t])) # probabilities for next charsloss += -np.log(ps[t][targets[t],0]) # softmax (cross-entropy loss)# BP过程,计算梯度dWxh, dWhh, dWhy = np.zeros_like(Wxh), np.zeros_like(Whh), np.zeros_like(Why)dbh, dby = np.zeros_like(bh), np.zeros_like(by)dhnext = np.zeros_like(hs[0])for t in reversed(range(len(inputs))):dy = np.copy(ps[t])dy[targets[t]] -= 1 # backprop into y. see http://cs231n.github.io/neural-networks-case-study/#grad if confused heredWhy += np.dot(dy, hs[t].T)dby += dydh = np.dot(Why.T, dy) + dhnext # backprop into hdhraw = (1 - hs[t] * hs[t]) * dh # backprop through tanh nonlinearitydbh += dhrawdWxh += np.dot(dhraw, xs[t].T)dWhh += np.dot(dhraw, hs[t-1].T)dhnext = np.dot(Whh.T, dhraw)for dparam in [dWxh, dWhh, dWhy, dbh, dby]:np.clip(dparam, -5, 5, out=dparam) # clip to mitigate exploding gradientsreturn loss, dWxh, dWhh, dWhy, dbh, dby, hs[len(inputs)-1]
    
    • 输入一串字符来训练模型,返回 loss 和 各种参数的梯度

    • 输入:

      • inputs:模型训练数据的输入,也就是上图所示的 [a , b , c , d]
      • targets:模型对 inputs 应该产生的正确结果,也就是上图所示的 [a’ , b’ , c’ , d’]
      • hprev:H-by-1的向量。代表当前时间步的各个神经元的隐藏层的初始状态
    • 解析1

        xs, hs, ys, ps = {}, {}, {}, {}hs[-1] = np.copy(hprev)loss = 0
      
      • 对输入的每个字符来说,其训练的都是不同时间步的模型。eg:
        • inputs = [a , b , c , d]
        • a 训练的是 t = 1 时的模型
        • b 训练的是 t = 2 时的模型
        • c 训练的是 t = 3 时的模型
        • d 训练的是 t = 4 时的模型
      • 这段代码只是初始化
    • 解析2

      for t in range(len(inputs)): #inputs里都是数组<int>类型# 将元素编码为向量xs[t] = np.zeros((vocab_size,1)) # 初始化为 0 向量xs[t][inputs[t]] = 1hs[t] = np.tanh(np.dot(Wxh, xs[t]) + np.dot(Whh, hs[t-1]) + bh) # hidden stateys[t] = np.dot(Why, hs[t]) + by # unnormalized log probabilities for next charsps[t] = np.exp(ys[t]) / np.sum(np.exp(ys[t])) # probabilities for next charsloss += -np.log(ps[t][targets[t],0]) # 正确结果的得分,得分越大,loss越小
      
      • 注意:利用在遍历 inputs 的时候,同时利用索引作为时间步。hs[t-1] 是一直在变的,存储的是前面的时间步的信息
      • 遍历每一个输入的字符
      • 对一个字符建立 one-hot 编码
      • 计算当前时间步的隐藏状态
      • 注意:每一个时间步的参数矩阵都是一样的,本质上是对一个模型的训练,只是输入字符和隐藏状态不同
      • 计算不同每个时间步的输出(分数向量),也就是不同字符对应的输出
      • 利用 softmax 将 分数向量转为概率向量
      • 计算每个字符产生的 loss
  • main函数

    • 在一次训练的过程中,不同时间步的模型的参数矩阵是相同的,因为本质上是对一个模型进行时间上有先后的训练
    • 每次利用一个 seq_length 长的数据训练模型
    • 详细过程都在注释中标出
    n, p = 0, 0
    mWxh, mWhh, mWhy = np.zeros_like(Wxh), np.zeros_like(Whh), np.zeros_like(Why)
    mbh, mby = np.zeros_like(bh), np.zeros_like(by) # memory variables for Adagrad
    smooth_loss = -np.log(1.0/vocab_size)*seq_length # loss at iteration 0while True:if p + seq_length + 1 >= len(data) or n == 0: # 刚开始或者结束时都初始化一次hprev = np.zeros((hidden_size,1)) # 重置隐藏层状态p = 0 # 指向输入数据的第一个# targets 是 inputs 往后推一个词inputs = [char_to_ix[ch] for ch in data[p:p+seq_length]] # 一个存储 index 的 listtargets = [char_to_ix[ch] for ch in data[p+1:p+seq_length+1]] # 一个存储 index 的 list# 这个采样只是为了试试模型的效果, 100 次一采样if n % 100 == 0:sample_ix = sample(hprev, inputs[0], 200) # 采样,得到采样的词的 index 序列txt = ''.join(ix_to_char[ix] for ix in sample_ix) # 通过得到的 index 序列 , 输出这些词print ('----\n %s \n----' % (txt, ))# 利用 seq_length 长的数据,对模型进行一次 loss 和 梯度的计算loss, dWxh, dWhh, dWhy, dbh, dby, hprev = lossFun(inputs, targets, hprev) # 得到 Loss 和 梯度smooth_loss = smooth_loss * 0.999 + loss * 0.001if n % 100 == 0: print ('iter %d, loss: %f' % (n, smooth_loss)) # 100 次一输出# 参数更新for param, dparam, mem in zip([Wxh, Whh, Why, bh, by], [dWxh, dWhh, dWhy, dbh, dby], [mWxh, mWhh, mWhy, mbh, mby]):mem += dparam * dparam # dparam 对应位置点乘 mem是前面梯度的平方和#越到后面学习率越小param += -learning_rate * dparam / np.sqrt(mem + 1e-8) # adagrad updatep += seq_length # p指向下一个 seq_length的开始n += 1 # iteration counter 
    

http://chatgpt.dhexx.cn/article/9AnvtnwR.shtml

相关文章

微信小程序:如何在{{}}中使用函数?WXML+WXS

前言 在项目中涉及到百分比的计算&#xff0c;js浮点数运算会出现精度问题&#xff0c;造成小数位数过多&#xff0c;因此想在模板语法中对值进行相应的处理。 开始 按照常规用法&#xff0c;使用js中的toFixed()进行处理&#xff1a; <view>&#xffe5;{{(cashMone…

wxPython:wx.Font详细解释用法

官方构造解释&#xff1a; 注解用法&#xff1a; wx.Font(pointSize, family, style, weight, underlineFalse, faceName"", encodingFONTENCODING_DEFAULT) pointSize &#xff1a;文字大小&#xff1a;floatfamily &#xff1a;字体系列&#xff1a;FontFamilyst…

小程序 - wxs

wxs&#xff1a; 在小程序里写函数表达式&#xff0c;做数据处理 官方文档 wxs WXS&#xff08;WeiXin Script&#xff09;是小程序的一套脚本语言&#xff0c;结合 WXML&#xff0c;可以构建出页面的结构。 WXS 与 JavaScript 是不同的语言&#xff0c;有自己的语法&#x…

如何在小程序的wxml中书写函数逻辑,wxs的使用

在小程序wxml的页面中我们可以使用{{}}内部来书写简单的js表达式&#xff0c;如三目运算符等&#xff0c;但是对于稍微复杂一点的逻辑我们就需要用函数来解决&#xff0c;如果写在js文件中有些繁琐还需要绑定数据等&#xff0c;此时wxs就配上了用场 wxs只支持ES5&#xff01;&…

【学习】HW5

机器学习/深度学习 一、HW5任务训练数据集评价指标——BLEU工作流程训练技巧Baseline Guidereport 代码数据集迭代器编码器attention解码器Seq2Seq模型初始化优化优化器&#xff1a;Adamlr scheduling验证和推断模型补充 一、HW5 任务 在这个家庭作业中&#xff0c;我们将把英…

WXSS-WXML-WXS语法

目录&#xff1a; 1 WXSS编写程序样式 2 Mustache语法绑定 3 WXML的条件渲染 4 WXML的列表渲染 5 WXS语法基本使用 6 WXS语法案例练习 小程序的自适应单位rpx。在设计稿为iPhone6的时候1px2rpx wxml必须是闭合标签&#xff0c;或者单标签加/&#xff0c;否则会报错&#…

WX:用法

1.条件渲染wx:for 在组件上绑定一个数组&#xff08;默认当前标下变量为index.数组变量为item) wx:for-item可以指定当前元素的变量名 wx:for-index可以指定数组当前下标的变量名 2.条件渲染wx:if wx:if是一个控制属性&#xff0c;需要添加到标签中使用 使用多个标签时可…

【微信小程序】WXML WXSS JS

目录 &#x1f353;小程序代码的构成 - WXML 模板 1. 什么是 WXML 2. WXML 和 HTML 的区别 &#x1f347;小程序代码的构成 - WXSS 样式 1. 什么是 WXSS 2. WXSS 和 CSS 的区别 &#x1f352;小程序代码的构成 - JS 逻辑交互 1. 小程序中的 .js 文件 2. 小程序中 .js 文…

【HW4】

HW4 一、Unsupervised LearningSharing ParametersFormulationIn PracticeVarious ArchitecturesApplication机器翻译 总结 二、HW41、Task2、数据集分析mapping.json文件metadata.json文件testdata.json文件 3、Dataset 4、Dataloader5、Model6、Learning rate scheduleLambda…

【微信小程序】--WXML WXSS JS 逻辑交互介绍(四)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

微信小程序 | 小程序WXSS-WXML-WXS

&#x1f5a5;️ 微信小程序 专栏&#xff1a;小程序WXSS-WXML-WXS &#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; ✨ 个人主页&#xff1a;CoderHing的个人主页 &#x1f340; 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️ &#x1…

【wx】

.js: //message.js const app getApp() Page({data: {background: #D1EEEE,activeKey: 0,motto: Hello World,userInfo: {},hasUserInfo: false,canIUse: wx.canIUse(button.open-type.getUserInfo),navData: [{text: 首页},{text: 健康},{text: 情感},{text: 职场},{text: 育…

wx-wx-wx-wx-wx-wx-wx-wx-wx-wx-wx

一.⼩程序和H5的区别是什么呢&#xff1f; 运⾏环境&#xff1a;⼩程序基于微信&#xff0c;⽽h5的运行环境是浏览器&#xff0c;所以小程序中没有DOM和BOM的相关API&#xff0c;jquey和一些NPM(2.2.1版本前)包都不能在小程序中使用。小程序可以调用宿主环境/微信提供的微信客…

TensorFlow学习笔记——(11)循环神经网络

文章目录 一、循环核二、循环核时间步展开三、循环计算层四、TF描述循环计算层五、循环计算过程1、RNN实现单个字母预测&#xff08;1&#xff09;过程&#xff08;2&#xff09;完整代码 2、RNN实现输入多个字母&#xff0c;预测一个字母&#xff08;1&#xff09;过程&#x…

【mysql组合查询】

mysql组合查询&#xff1a;将查询出来的值作为表头&#xff0c;并统计类别数量 SELECTa.enterprise_name AS 事业部,sum( IF ( a.alarm_reason 3, a.countt, 0 ) ) AS 正常操作,sum( IF ( a.alarm_reason 4, a.countt, 0 ) ) AS 人员操作,sum(IF( a.alarm_reason NULL, a.c…

mysql 组合查询_mysql组合查询

使用UNION 多数SQL查询都只包含一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句)&#xff0c;并将结果作为单个查询结果集返回。这些组合查询通常称为并(union) 有两种情况需要使用组合查询&#xff1a; 在单个表查询中从不同的表返回类似结构…

SQL学习十一、组合查询

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。 请注意&#xff0c; 1、UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时&#xff0c;每个 SELECT 语句中的列的顺序必须相同。 即多个 SELECT 语句查询出来的结果集要能合并到一起…

【MySQL】联合查询

目录 1、前言 2、联合查询 3、内连接和外连接 4、案例演示 4.1 查询篮球哥每科的成绩 4.2 查询所有同学的总成绩及邮箱 5、自连接 5.1 显示所有计算机原理成绩比java成绩高的同学 6、子查询 6.1 查询出篮球哥的同班同学 6.2 多行子查询 7、合并查询 1、前言 在实际…

数据库组合查询

在使用数据库的过程中&#xff0c;数据的查询是使用最多的&#xff0c;所以&#xff0c;数据的精确查询是一个很重要的问题。以前的数据查询是最简单的数据查询&#xff0c;也从来没想过组合查询的问题&#xff0c;可是在做机房收费系统的时候&#xff0c;遇到了一个很大的问题…

SQL组合查询知识

大多数SQL查询只从一个或者多表中返回数据都是单个select语句。但是SQL server允许多个select语句执行&#xff0c;它返回的结果是一个结果集&#xff0c;需要使用 union 组合 &#xff0c;这些组合一般称为并&#xff08;union&#xff09;和复合查询&#xff08;compound que…