RNN实现退位减法

news2025/3/9 10:49:01

文章目录

  • 前言
  • RNN
    • RNN架构图
    • 前向传播公式
    • 反向传播算法
  • 用RNN实现退位减法
  • 代码
    • 变量的对应关系
  • 总结

前言

最近深入学习了一下RNN,即循环神经网络。RNN是一类比较基础的神经网络,本文使用的是最基础、最简单的循环神经网络的形式。LSTM也是一种常见的循环神经网络,但本文为了降低理解难度,将不会涉及。

RNN

RNN是循环神经网络,是神经网络的一种。它跟普通神经网络有什么区别呢?下图表示了一个普通神经网络。 x x x表示输入层, h h h表示隐藏层, O O O表示输出层, L L L表示损失函数, y y y表示真实输出。
普通神经网络
下图表示了一个循环神经网络。在隐藏层 h h h这里,多了一个指向自己的箭头。
RNN
这里的 x x x表示一个向量,一串带有顺序的信息。在普通的神经网络中,没有办法体现出前一个输入信息对下一个输出的影响,而这样一个循环的方式就可以体现前一个输入信息对下一个输出的影响。

RNN架构图

这里给出更为详细的RNN架构图。由于本人还不太会使用画图软件,这些参数没法以数学公式的形式展示,大家将就着看吧。
RNN架构图
x t x_t xt代表输入层。
h t h_t ht代表隐藏层。
y t y_t yt代表输出层。
W h x W_{hx} Whx是从输入层到隐藏层的参数。
W h h W_{hh} Whh是从隐藏层到隐藏层的参数,正因为它的存在,才使得RNN具有记忆,能够保存上一次输入的信息。
W y h W_{yh} Wyh是隐藏层到输出层的参数。

前向传播公式

{ h t = f ( x t ⋅ W h x + h t − 1 ⋅ W h h ) y t = g ( h t ⋅ W y h ) = g ( f ( x t ⋅ W h x + h t − 1 ⋅ W h h ) ⋅ W y h ) \begin{cases} h_t=f(x_t\cdot W_{hx}+h_{t-1}\cdot W_{hh})\\ y_t=g(h_t\cdot W_{yh})=g(f(x_t\cdot W_{hx}+h_{t-1}\cdot W_{hh})\cdot W_{yh}) \end{cases} {ht=f(xtWhx+ht1Whh)yt=g(htWyh)=g(f(xtWhx+ht1Whh)Wyh)

反向传播算法

E E E表示损失函数,我们要推导误差与参数改变量之间的关系,即 Δ W h x \Delta W_{hx} ΔWhx Δ W h h \Delta W_{hh} ΔWhh Δ W y h \Delta W_{yh} ΔWyh E E E之间的关系。

v t = h t ⋅ W y h , u t = x t ⋅ W h x + h t − 1 ⋅ W h h v_t=h_t\cdot W_{yh},u_t=x_t\cdot W_{hx}+h_{t-1}\cdot W_{hh} vt=htWyh,ut=xtWhx+ht1Whh

δ t y = ∂ E ∂ v t , δ t h = ∂ E ∂ u t \delta_t^y=\frac{\partial E}{\partial v_t},\delta_t^h=\frac{\partial E}{\partial u_t} δty=vtE,δth=utE

为什么这么设呢?其实 δ t y \delta_t^y δty代表了从输出层到隐藏层的反向传播, δ t h \delta_t^h δth代表了从隐藏层到输入层的反向传播,这么设会让后面的公式变得更为简洁。

在本文中,取 E = 1 2 ∑ t = 1 n ( y t − y ^ t ) 2 E=\frac{1}{2}\sum_{t=1}^n(y_t-\widehat{y}_t)^2 E=21t=1n(yty t)2,其中 y ^ t \widehat{y}_t y t代表第 t t t位的真实值。

δ t y = ∂ E ∂ y t ⋅ ∂ y t ∂ v t = ( y t − y ^ t ) ⋅ g ′ ( v t ) \delta_t^y=\frac{\partial E}{\partial y_t}\cdot\frac{\partial y_t}{\partial v_t}=(y_t-\widehat{y}_t)\cdot g'(v_t) δty=ytEvtyt=(yty t)g(vt)

δ t h = ∂ E t ∂ u t + ∂ E t + 1 ∂ u t = ∂ E t ∂ v t ⋅ ∂ v t ∂ h t ⋅ ∂ h t ∂ u t + ∂ E t + 1 ∂ u t + 1 ⋅ ∂ u t + 1 ∂ h t ⋅ ∂ h t ∂ u t = ( δ t y ⋅ W y h T + δ t + 1 h ⋅ W h h T ) ⋅ f ′ ( h t ) \delta_t^h=\frac{\partial E_t}{\partial u_t}+\frac{\partial E_{t+1}}{\partial u_t}=\frac{\partial E_t}{\partial v_t}\cdot \frac{\partial v_t}{\partial h_t}\cdot \frac{\partial h_t}{\partial u_t}+\frac{\partial E_{t+1}}{\partial u_{t+1}}\cdot \frac{\partial u_{t+1}}{\partial h_t}\cdot \frac{\partial h_t}{\partial u_t}=(\delta_t^y\cdot W_{yh}^T+\delta_{t+1}^h\cdot W_{hh}^T)\cdot f'(h_t) δth=utEt+utEt+1=vtEthtvtutht+ut+1Et+1htut+1utht=(δtyWyhT+δt+1hWhhT)f(ht)

∂ E ∂ W h x = ∑ t = 1 n ∂ E ∂ u t ⋅ ∂ u t ∂ W h x = ∑ t = 1 n x t T ⋅ δ t h \frac{\partial E}{\partial W_{hx}}=\sum_{t=1}^n \frac{\partial E}{\partial u_t}\cdot \frac{\partial u_t}{\partial W_{hx}}=\sum_{t=1}^n x_t^T\cdot \delta_t^h WhxE=t=1nutEWhxut=t=1nxtTδth

∂ E ∂ W h h = ∑ t = 1 n ∂ E ∂ u t ⋅ ∂ u t ∂ W h h = ∑ t = 1 n h t − 1 T ⋅ δ t h \frac{\partial E}{\partial W_{hh}}=\sum_{t=1}^n \frac{\partial E}{\partial u_t}\cdot \frac{\partial u_t}{\partial W_{hh}}=\sum_{t=1}^n h_{t-1}^T\cdot \delta_t^h WhhE=t=1nutEWhhut=t=1nht1Tδth

∂ E ∂ W y x = ∑ t = 1 n ∂ E ∂ v t ⋅ ∂ v t ∂ W y h = ∑ t = 1 n h t T ⋅ δ t y \frac{\partial E}{\partial W_{yx}}=\sum_{t=1}^n \frac{\partial E}{\partial v_t}\cdot \frac{\partial v_t}{\partial W_{yh}}=\sum_{t=1}^n h_t^T\cdot \delta_t^y WyxE=t=1nvtEWyhvt=t=1nhtTδty

Δ W h x = η ∑ t = 1 n δ t h ⋅ x t \Delta W_{hx}=\eta\sum_{t=1}^n\delta_t^h\cdot x_t ΔWhx=ηt=1nδthxt

Δ W h h = η ∑ t = 1 n δ t h ⋅ h t − 1 \Delta W_{hh}=\eta\sum_{t=1}^n\delta_t^h\cdot h_{t-1} ΔWhh=ηt=1nδthht1

Δ W y h = η ∑ t = 1 n δ t y ⋅ h t \Delta W_{yh}=\eta\sum_{t=1}^n\delta_t^y\cdot h_t ΔWyh=ηt=1nδtyht

至此,反向传播公式推导完毕。

用RNN实现退位减法

RNN最适合用来处理输入中包含顺序的这种问题。像减法,两个数相减之后,会产生一个退位,这个退位的信息就可以包含在循环中,传递给下一个值。

在计算退位减法中,取 f ( ⋅ ) = g ( ⋅ ) = s i g m o i d ( ⋅ ) f(\cdot)=g(\cdot)=sigmoid(\cdot) f()=g()=sigmoid()

x t x_t xt代表输入层,维度为2,每次输入为减数和被减数的第 t t t位。
h t h_t ht代表隐藏层,设定维度为16维,通过各种参数,学习到减法的含义。
y t y_t yt代表输出层,维度为1,代表预测减法的第 t t t位结果。

代码

接下来直接给出代码。代码中有非常详细的解释,相信也能帮助大家更好地理解RNN。

该代码来自《PyTorch从深度学习到图神经网络》配套代码。其中有我个人的小范围修改。

import copy, numpy as np

## np.random.seed(0)  # 随机数生成器的种子,可以每次得到一样的值


# compute sigmoid nonlinearity
def sigmoid(x):  # 激活函数,这里的x可以是一个数,也可以是向量或者矩阵
    output = 1 / (1 + np.exp(-x))
    return output


def sigmoid_output_to_derivative(output):  # 激活函数的导数
    return output * (1 - output)


int2binary = {}  # 整数到其二进制表示的映射
binary_dim = 8  # 暂时只做256(2^8)以内的减法
## 计算0-256的二进制表示
largest_number = pow(2, binary_dim)
binary = np.unpackbits(
    np.array([range(largest_number)], dtype=np.uint8).T, axis=1)  # uint8表示无符号8位整数,T表示转置,axis=1表示按列解包
## binary是一个256行8列的矩阵,表示了0~255的二进制表示
for i in range(largest_number):
    int2binary[i] = binary[i]

# input variables
alpha = 0.9  # 学习速率
input_dim = 2  # 输入的维度是2
hidden_dim = 16
output_dim = 1  # 输出维度为1

# initialize neural network weights
synapse_0 = (2 * np.random.random((input_dim, hidden_dim)) - 1) * 0.05  # 维度为2*16, 2是输入维度,16是隐藏层维度
synapse_1 = (2 * np.random.random((hidden_dim, output_dim)) - 1) * 0.05
synapse_h = (2 * np.random.random((hidden_dim, hidden_dim)) - 1) * 0.05
# => [-0.05, 0.05),

# 用于存放反向传播的权重更新值
synapse_0_update = np.zeros_like(synapse_0)  ##zeros_like函数的功能是生成一个同样大小的全零矩阵
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)

# training
for j in range(10000):  # 训练10000轮

    # 生成一个数字a
    a_int = np.random.randint(largest_number)
    # 生成一个数字b,b的最大值取的是largest_number/2,作为被减数,让它小一点。
    b_int = np.random.randint(largest_number / 2)
    # 如果生成的b大了,那么交换一下
    if a_int < b_int:
        tt = a_int
        a_int = b_int
        b_int = tt

    a = int2binary[a_int]  # binary encoding
    b = int2binary[b_int]  # binary encoding
    # true answer
    c_int = a_int - b_int
    c = int2binary[c_int]

    # 存储神经网络的预测值
    d = np.zeros_like(c)
    overallError = 0  # 每次把总误差清零

    layer_2_deltas = list()  # 存储每个时间点输出层的误差
    layer_1_values = list()  # 存储每个时间点隐藏层的值

    layer_1_values.append(np.ones(hidden_dim) * 0.1)  # 一开始没有隐藏层,所以初始化一下原始值为0.1

    # moving along the positions in the binary encoding
    for position in range(binary_dim):  # 循环遍历每一个二进制位,从低位到高位

        # generate input and output
        X = np.array([[a[binary_dim - position - 1], b[binary_dim - position - 1]]])  # 从右到左,每次将两个数的当前位作为输入
        y = np.array([[c[binary_dim - position - 1]]]).T  # 正确答案
        # hidden layer (input ~+ prev_hidden)
        layer_1 = sigmoid(np.dot(X, synapse_0) + np.dot(layer_1_values[-1],
                                                        synapse_h))  # (输入层 + 之前的隐藏层) -> 新的隐藏层,这是体现循环神经网络的最核心的地方!!!
        # output layer (new binary representation)
        layer_2 = sigmoid(np.dot(layer_1, synapse_1))  # 隐藏层 * 隐藏层到输出层的转化矩阵synapse_1 -> 输出层

        layer_2_error = y - layer_2  # 预测误差
        layer_2_deltas.append((layer_2_error) * sigmoid_output_to_derivative(layer_2))  # 把每一个时间点的误差导数都记录下来
        overallError += np.abs(layer_2_error[0])  # 总误差

        d[binary_dim - position - 1] = np.round(layer_2[0][0])  # 记录下每一个预测bit位

        # store hidden layer so we can use it in the next timestep
        layer_1_values.append(copy.deepcopy(layer_1))  # 记录下隐藏层的值,在下一个时间点用

    future_layer_1_delta = np.zeros(hidden_dim)

    # 反向传播,从最后一个时间点到第一个时间点
    for position in range(binary_dim):
        X = np.array([[a[position], b[position]]])  # 最后一次的两个输入
        layer_1 = layer_1_values[-position - 1]  # 当前时间点的隐藏层
        prev_layer_1 = layer_1_values[-position - 2]  # 前一个时间点的隐藏层

        # error at output layer
        layer_2_delta = layer_2_deltas[-position - 1]  # 当前时间点输出层导数
        # error at hidden layer
        # 通过后一个时间点(因为是反向传播)的隐藏层误差和当前时间点的输出层误差,计算当前时间点的隐藏层误差
        layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(
            synapse_1.T)) * sigmoid_output_to_derivative(layer_1)

        # 等到完成了所有反向传播误差计算, 才会更新权重矩阵,先暂时把更新矩阵存起来。
        synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
        synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
        synapse_0_update += X.T.dot(layer_1_delta)

        future_layer_1_delta = layer_1_delta

    # 完成所有反向传播之后,更新权重矩阵。并把矩阵变量清零
    synapse_0 += synapse_0_update * alpha
    synapse_1 += synapse_1_update * alpha
    synapse_h += synapse_h_update * alpha
    synapse_0_update *= 0  # 这是一个好的清零数组的方式,值得学习
    synapse_1_update *= 0
    synapse_h_update *= 0

    # print out progress
    if (j % 800 == 0):  # 每训练800次,输出一次结果,让人能够看到训练的情况
        # print(synapse_0,synapse_h,synapse_1)
        print("总误差:" + str(overallError))
        print("Pred:" + str(d))
        print("True:" + str(c))
        out = 0
        for index, x in enumerate(reversed(d)):
            out += x * pow(2, index)
        print(str(a_int) + " - " + str(b_int) + " = " + str(out))
        print("------------")

变量的对应关系

代码中有很多变量,名称跟我们的RNN架构图并不完全对应。在这里给出一些关键变量与RNN架构图的对应关系。

代码中的变量架构图中的变量
layer_1 h t h_t ht
layer_2 y t y_t yt
layer_2_error y ^ t − y t \widehat{y}_t-y_t y tyt
layer_2_deltas δ y \delta^y δy
layer_1_values h h h
prev_layer_1 h t − 1 h_{t-1} ht1
layer_2_delta δ t y \delta_t^y δty
layer_1_delta δ t h \delta_t^h δth
future_layer_1_delta δ t + 1 h \delta_{t+1}^h δt+1h

总结

本文先介绍了RNN,并指出了其相比普通神经网络的优势。然后给出了RNN的前向传播公式,并推导了反向传播算法。最后给出了用RNN实现退位减法的代码。

RNN也是从普通神经网络迈向人工智能的一大步。通过RNN,我们可以让代码“理解”减法,或者至少看上去对减法有了深刻的理解。通过越来越多的训练,它可以“理解”更多东西,从而让人感觉它产生了智能。

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

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

相关文章

中大型工厂人员定位系统源码,实现人、车、物的实时位置监控

UWB高精度定位系统源码&#xff0c;中大型工厂人员定位系统&#xff0c;实现人、车、物的实时位置监控 UWB高精度定位系统源码&#xff0c;智慧工厂人员定位系统源码&#xff0c;基于VueSpring boot前后端分离架构开发的一套UWB高精度定位系统源码。有演示。 随着经济的高速发展…

Flink实时数仓同步:切片表实战详解

一、背景 在大数据领域&#xff0c;初始阶段业务数据通常被存储于关系型数据库&#xff0c;如MySQL。然而&#xff0c;为满足日常分析和报表等需求&#xff0c;大数据平台采用多种同步方式&#xff0c;以适应这些业务数据的不同存储需求。 一项常见需求是&#xff0c;业务使用…

潜水耳机哪个牌子好?认准这几个游泳耳机品牌就对了!

在科技日益发达的今天&#xff0c;人们对于运动设备的需求也在不断提升。作为一项独特的水上运动&#xff0c;潜水爱好者们对耳机的要求也越来越高。一款优秀的潜水耳机不仅能够提供卓越的防水性能和舒适度&#xff0c;还必须具备出色的音质。那么&#xff0c;在众多品牌中&…

C语言进阶—表达式求值

隐式类型转换&#xff1a; C 的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。 为了获得这个精度&#xff0c;表达式中的字符和短整型操作数在使用之前被转换为普通整型&#xff0c;这种转换称为整型提升。 #include <stdio.h>int main() {char c 1;printf(…

鸿蒙Harmony应用开发—ArkTS声明式开发(自定义手势判定)

为组件提供自定义手势判定能力。开发者可根据需要&#xff0c;在手势识别期间&#xff0c;决定是否响应手势。 说明&#xff1a; 从API Version 11开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 onGestureJudgeBegin onGestureJudgeBegi…

C#与python交互(flask发送Get/Post请求)

先运行python&#xff0c;再运行C# **ps: 注意修改端口号**python发送Get/Post请求 # -*- coding: utf-8 -*- # Time : 2024/1/25 15:52 # Author : YY # File : post_test.py # Content&#xff1a;提交数据给客户端 from flask import Flask, request, jsonify, redirect…

Vue系列-环境快速搭建

vue环境快速搭建 演示视频 快速搭建Vue开发环境pnpm和yarn 1. 基本信息 作者: GMCY系列: Vue仓库: GitHub | Gitee话题(GitHub): tools \ vue创建时间: 2024/03/02 2. 介绍 功能 批处理文件vue 环境的快速搭建nodejs, npm, pnpm, yarn 自动 下载安装npm, pnpm, yarn 自动 …

计网:HTTPS协议详解

1、HTTP 与 HTTPS 有哪些区别&#xff1f;​​​ HTTP以明文方式传输数据&#xff0c;不提供任何加密。如果攻击者截取了传输报文&#xff0c;就可以直接读取其中的信息。HTTPS利用SSL/TLS加密数据包&#xff0c;报文以密文方式传输。 HTTP 连接建立相对简单&#xff0c; TCP …

【办公类-22-08】周计划系列(3-3)“信息窗+主题知识(上传+打印)” (2024年调整版本)

作品展示 背景需求&#xff1a; 前文将信息窗主题知识的内容提取并优化结构 【办公类-22-07】周计划系列&#xff08;3-1&#xff09;“信息窗主题知识&#xff08;提取&#xff09;” &#xff08;2024年调整版本&#xff09;-CSDN博客文章浏览阅读803次&#xff0c;点赞7次…

新一代WLAN解决方案与WLAN的配置实现

案例背景为二层旁挂式组网&#xff0c;转发方式为直接转发&#xff0c;管理Vlan为100&#xff0c;业务Vlan为101。 基本配置&#xff1a; SW1&#xff1a; [SW1]VLAN batch 100 101 [SW1-GigabitEthernet0/0/1]port link-type trunk [SW1-GigabitEthernet0/0/1]port trunk a…

SQL 初级

SQL 初级 SQL 简介 SQL (Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统&#xff08;RDBMS&#xff09;。 SQL 的范围包括数据插入、查询、更新和删除&#xff0c;数据库模式创建和修改&#xff0c;以及数据访问控制。 SQL 是什么&#xff1f; SQL…

【Linux C | 网络编程】多播的概念、多播地址、UDP实现多播的C语言例子

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

提高生产效率!虹科MSR165快速检测机器故障,实现精准优化

如今&#xff0c;各种生产机器、机床和运输机都采用了复杂的驱动技术&#xff0c;以便在三个轴上准确生成线性运动或者高效旋转运动。所有机械运动中都会出现特征性的振动或震动模式&#xff0c;可以在该背景下利用这些模式来监测和优化整个驱动技术的机电参数。在这个过程中&a…

太阳能光伏电池的simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 光伏电池的基本结构 4.2 光伏电池的工作原理 5.完整工程文件 1.课题概述 太阳能光伏电池的simulink建模与仿真.分析不同光照温度&#xff0c;光照强度下的光伏电池的U-I特性曲线以及P-V特性曲线。 …

备战蓝桥(模板篇)

扩展欧德里几算法 质数筛 分解质因数 LCA BFS floyd Dijkstra prime 日期是否合法 Tire异或 模拟散列表 字符哈希 Tire字符串统计

官方教程 | 在 OpenBayes 平台进行组织协作

想和好 homie 共享账户余额、存储、数据集、模型、容器等资源&#xff0c;又不想共享自己的账户密码&#xff1f; 想跟团队成员分工协作、高效 Coding、加速炼丹&#xff0c;又想隔离权限、差异化管理&#xff1f; 经过为期半年的内测和完善&#xff0c;OpenBayes贝式计算的组织…

平面纯弯梁单元Matlab有限元编程 |欧拉梁单元| 简支梁|悬臂梁|弯矩图 |变形图| Matlab源码 | 视频教程

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

Ajax、Axios、Vue、Element与其案例

目录 一.Ajax 二.Axios 三.Vue 四.Element 五.增删改查案例 一.依赖&#xff1a;数据库&#xff0c;mybatis&#xff0c;servlet&#xff0c;json-对象转换器 二.资源&#xff1a;elementvueaxios 三.pojo 四.mapper.xml与mapper接口 五.service 六.servlet 七.html页…

目前研一,是选 FPGA 还是 Linux 嵌入式?

目前研一&#xff0c;是选 FPGA 还是 Linux 嵌入式? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux 的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&a…

Sora:AI视频生成的新机遇与挑战

随着科技的飞速进步&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;技术已经深入渗透到社会的各个领域。其中&#xff0c;Sora这类基于AI的视频生成工具因其高度逼真的生成能力而备受瞩目。然而&#xff0c;正如一枚硬币有两面&#xff0…