Python(C++)自动微分导图

🎯要点

  1. 反向传播矢量化计算方式
  2. 前向传递和后向传递计算方式
  3. 图节点拓扑排序
  4. 一阶二阶前向和伴随模式计算
  5. 二元分类中生成系数高斯噪声和特征
  6. 二元二次方程有向无环计算图
  7. 超平面搜索前向梯度下降算法
  8. 快速傅里叶变换材料应力和切线算子
  9. GPU CUDA 神经网络算术微分
    在这里插入图片描述

Python自动微分前向反向

自动微分不同于符号微分和数值微分。符号微分面临着将计算机程序转换为单一数学表达式的困难,并且可能导致代码效率低下。数值微分(有限差分法)会在离散化过程和取消过程中引入舍入误差。这两种经典方法在计算更高导数时都存在问题,复杂性和误差会增加。最后,这两种经典方法在计算函数对许多输入的偏导数时都很慢,而这是基于梯度的优化算法所需要的。自动微分解决了所有这些问题。
在这里插入图片描述
符号微分是我们将要解开的梯度计算的下一种方法。这是一个系统的过程,将由算术运算和符号组成的表达式转换为表示其导数的表达式。这是通过将微积分的导数规则(例如求和规则)应用于闭式表达式来实现的。

实际上,符号微分是计算机手动推导表达式导数的方式。例如下面的两个函数 f f f g g g,我们可以使用微积分导出其导数的表达式。
g ( x ) = cos ⁡ ( x ) + 2 x − e x f ( g ) = 4 g 2 \begin{gathered} g(x)=\cos (x)+2 x-e^x \\ f(g)=4 g^2 \end{gathered} g(x)=cos(x)+2xexf(g)=4g2

f ( g ( x ) ) = 4 ( cos ⁡ ( x ) + 2 x − e x ) 2 ( 4 ) \begin{aligned} &f(g(x))=4\left(\cos (x)+2 x-e^x\right)^2\qquad(4) \end{aligned} f(g(x))=4(cos(x)+2xex)2(4)

d f d x = d f d g ⋅ d g d x = 8 ( cos ⁡ ( x ) + 2 x − e x ) ⋅ ( − sin ⁡ ( x ) + 2 − e x ) ( 5 ) \frac{d f}{d x}=\frac{d f}{d g} \cdot \frac{d g}{d x}=8\left(\cos (x)+2 x-e^x\right) \cdot\left(-\sin (x)+2-e^x\right)\qquad(5) dxdf=dgdfdxdg=8(cos(x)+2xex)(sin(x)+2ex)(5)

要找到 f ( g ( x ) ) f(g(x)) f(g(x)) 输入的导数,我们只需将其插入上面的转换表达式中并对其求值即可。在实践中,我们以编程方式实现这个过程,并且所表示的变量将不仅仅是标量(例如向量、矩阵或张量)。下面是我们如何符号微分等式4得到等式 5 。

from sympy import symbols, cos, exp, diffx = symbols("x")
fog = 4 * (cos(x) + 2 * x - exp(x)) ** 2
dfdx = diff(fog, x)
print(dfdx)

输出

4*(2*x - exp(x) + cos(x))*(-2*exp(x) - 2*sin(x) + 4)

这解决了数值微分中出现的数值不准确和不稳定的问题,因为我们有一个可以直接计算函数梯度的表达式。不过,我们仍面临限制其优化神经网络可行性的问题。

我们在符号微分中看到的主要问题是表达式膨胀。表达式膨胀导致导数表达式通过变换呈指数增长,这是系统地将导数规则应用于原始表达式的惩罚。以下面的乘法规则为例。
d d x f ( x ) g ( x ) = f ′ ( x ) g ( x ) + g ′ ( x ) f ( x ) \frac{d}{d x} f(x) g(x)=f^{\prime}(x) g(x)+g^{\prime}(x) f(x) dxdf(x)g(x)=f(x)g(x)+g(x)f(x)
导数表达式不仅在术语上有所增长,而且在计算上也有所增长。这甚至没有考虑到 f f f g g g 本身可以是复杂的函数 - 可能会增加更多的表达式膨胀。

当我们导出 d f d x \frac{d f}{d x} dxdf 时,我们看到了一些表达式膨胀,这是一个相对简单的函数。现在想象一下,尝试对许多可能一遍又一遍地应用导数规则的复合函数执行相同的操作,对于神经网络代表许多复杂的复合函数,是极其不切实际的。
f ( x ) = e w x + b + e − ( w x + b ) e w x + b − e − ( w x + b ) ∂ f ∂ w = ( − x e − b − w x − x e b + w x ) ( e − b − w x + e b + w x ) ( − e − b − w x + e b + w x ) 2 + − x e − b − w x + x e b + w x − e − b − w x + e b + w x \begin{gathered} f(x)=\frac{e^{w x+b}+e^{-(w x+b)}}{e^{w x+b}-e^{-(w x+b)}} \\ \frac{\partial f}{\partial w}=\frac{\left(-x e^{-b-w x}-x e^{b+w x}\right)\left(e^{-b-w x}+e^{b+w x}\right)}{\left(-e^{-b-w x}+e^{b+w x}\right)^2}+\frac{-x e^{-b-w x}+x e^{b+w x}}{-e^{-b-w x}+e^{b+w x}} \end{gathered} f(x)=ewx+be(wx+b)ewx+b+e(wx+b)wf=(ebwx+eb+wx)2(xebwxxeb+wx)(ebwx+eb+wx)+ebwx+eb+wxxebwx+xeb+wx

表达式膨胀

上式显示的是神经网络中看到的线性投影,后面是非线性激活函数 tanh。结果表明,在不进行简化和优化的情况下,寻找梯度来更新权重 w w w 可能会导致大量的表达式膨胀和重复计算。

面临的另一个缺点是符号微分仅限于闭式表达式。编程之所以有用,是因为它能够使用控制流根据程序的状态改变程序的行为方式,同样的原理也经常应用于神经网络。

无控制流:

from sympy import symbols, diffdef f(x):if x > 2:return x * 2 + 5return x / 2 + 5x = symbols("x")
dfdx = diff(f(x))
print(dfdx)
TypeError: cannot determine truth value of Relational

示例中暗示的最后一个缺点是我们可能会导致重复计算。在等式4 和 5 中,我们评估 e x e^x ex 三次:一次是在计算等式4 ,两次计算等式5。这可以在更大的范围内实现更复杂的功能,从而为符号微分创造更多的不切实际性。我们可以通过缓存结果来减少这个问题,但这不一定能解决表达式膨胀问题。

自动微分将复合函数表示为组成它们的变量和基本运算。所有数值计算都以这些运算为中心,由于我们知道它们的导数,我们可以将它们串联起来以得出整个函数的导数。简而言之,自动微分是数值计算的增强版本,它不仅可以评估数学函数,还可以计算它们的导数。

下面,我留下了一个示例,仅显示接受两个输入 x 1 x_1 x1 x 2 x_2 x2 的函数的评估跟踪的原始计算。
y = f ( x 1 , x 2 ) = x 1 x 2 + x 2 − ln ⁡ ( x 1 ) x 1 = 2 , x 2 = 4 ( 6 ) \begin{gathered} y=f\left(x_1, x_2\right)=x_1 x_2+x_2-\ln \left(x_1\right) \\ x_1=2, x_2=4 \end{gathered}\qquad(6) y=f(x1,x2)=x1x2+x2ln(x1)x1=2,x2=4(6)

正向原始追踪  输出  v − 1 = x 1 2 v 0 = x 2 4 v 1 = v − 1 v 0 2 ( 4 ) = 8 v 2 = ln ⁡ ( v − 1 ) ln ⁡ ( 2 ) = 0.693 v 3 = v 1 + v 0 8 + 4 = 12 v 4 = v 3 − v 2 12 − 0.693 = 11.307 y = v 4 11.307 \begin{aligned} &\begin{array}{|c|c|} \hline \text { 正向原始追踪 }& \text { 输出 } \\ \hline v _{-1}= x _1 & 2 \\ \hline v _0= x _2 & 4 \\ \hline v _1= v _{-1} v _0 & 2(4)=8 \\ \hline v _2=\ln \left( v _{-1}\right) & \ln (2)=0.693 \\ \hline v_3=v_1+v_0 & 8+4=12 \\ \hline v _4= v _3- v _2 & 12-0.693=11.307 \\ \hline y=v_4 & 11.307 \\ \hline \end{array}\\ \end{aligned}  正向原始追踪 v1=x1v0=x2v1=v1v0v2=ln(v1)v3=v1+v0v4=v3v2y=v4 输出 242(4)=8ln(2)=0.6938+4=12120.693=11.30711.307

在评估轨迹之上,我们可以使用有向无环图作为数据结构,以算法方式表示评估轨迹。有向无环图中的节点表示输入变量、中间变量和输出变量,而边则描述输入到输出转换的计算层次结构。最后,该图必须是有向且无环的,以确保正确的计算流程。整体而言,这种类型的有向无环图通常称为计算图。
在这里插入图片描述
前向模式:

class Variable:def __init__(self, primal, tangent):self.primal = primalself.tangent = tangentdef __add__(self, other):primal = self.primal + other.primaltangent = self.tangent + other.tangentreturn Variable(primal, tangent)def __sub__(self, other):primal = self.primal - other.primaltangent = self.tangent - other.tangentreturn Variable(primal, tangent)def __mul__(self, other):primal = self.primal * other.primaltangent = self.tangent * other.primal + other.tangent * self.primalreturn Variable(primal, tangent)def __truediv__(self, other):primal = self.primal / other.primaltangent = (self.tangent / other.primal) + (-self.primal / other.primal**2) * other.tangentreturn Variable(primal, tangent)def __repr__(self):return f"primal: {self.primal}, tangent: {self.tangent}"

前向模式下自动微分计算

def mul_add(a, b, c):return a * b + c * adef div_sub(a, b, c):return a / b - ca, b, c = Variable(25.0, 1.0), Variable(4.0, 0.0), Variable(-5.0, 0.0)
print(f"{a = }, {b = }, {c = }")
print(f"{mul_add(a, b, c) = }")
a.tangent, b.tangent, c.tangent = 0.0, 1.0, 0.0
print(f"{div_sub(a, b, c) = }")

反向模式

class Variable:def __init__(self, primal, adjoint=0.0):self.primal = primalself.adjoint = adjointdef backward(self, adjoint):self.adjoint += adjointdef __add__(self, other):variable = Variable(self.primal + other.primal)def backward(adjoint):variable.adjoint += adjointself_adjoint = adjoint * 1.0other_adjoint = adjoint * 1.0self.backward(self_adjoint)other.backward(other_adjoint)variable.backward = backwardreturn variabledef __sub__(self, other):variable = Variable(self.primal - other.primal)def backward(adjoint):variable.adjoint += adjointself_adjoint = adjoint * 1.0other_adjoint = adjoint * -1.0self.backward(self_adjoint)other.backward(other_adjoint)variable.backward = backwardreturn variabledef __mul__(self, other):variable = Variable(self.primal * other.primal)def backward(adjoint):variable.adjoint += adjointself_adjoint = adjoint * other.primalother_adjoint = adjoint * self.primalself.backward(self_adjoint)other.backward(other_adjoint)variable.backward = backwardreturn variabledef __truediv__(self, other):variable = Variable(self.primal / other.primal)def backward(adjoint):variable.adjoint += adjointself_adjoint = adjoint * (1.0 / other.primal)other_adjoint = adjoint * (-1.0 * self.primal / other.primal**2)self.backward(self_adjoint)other.backward(other_adjoint)variable.backward = backwardreturn variabledef __repr__(self) -> str:return f"primal: {self.primal}, adjoint: {self.adjoint}"

反向模式自动微分计算

def mul_add(a, b, c):return a * b + c * adef div_sub(a, b, c):return a / b - ca, b, c = Variable(25.0, 1.0), Variable(4.0, 0.0), Variable(-5.0, 0.0)print(f"{a = }, {b = }, {c = }")
d = mul_add(a, b, c)
d.backward(1.0)
print(f"{d = }")
print(f"{a.adjoint = }, {b.adjoint = }, {c.adjoint = }")a.adjoint, b.adjoint, c.adjoint = 0.0, 0.0, 0.0
e = div_sub(a, b, c)
e.backward(1.0)
print(f"{e = }")
print(f"{a.adjoint = }, {b.adjoint = }, {c.adjoint = }")

👉更新:亚图跨际

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

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

相关文章

理解 decltype() 指定符(C++ 11 及以上版本)

目录 1. 功能 2. 语法格式 3. 理解 3.1 第一点 1.2 第二点 4. 例释 在 C 编程语言中,decltype 是一个用于检查实体的声明类型或表达式的类型和值类别的关键字。该关键字在 C11 中引入,主要用于泛型编程中,因为在泛型编程中&#x…

数据类型 NVARCHAR2 与 VARCHAR2 的对比

数据类型 NVARCHAR2 与 VARCHAR2 的对比 在数据库系统中,字符数据类型是用于存储文本数据的关键部分。在达梦数据库(DM Database)以及许多其他关系数据库管理系统(例如 Oracle),常见的字符数据类型有 NVAR…

C语言阴阳迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <Windows.h> enum WASD {W…

CityHash、FarmHash

CityHash和FarmHash都是由Google开发的非加密哈希函数&#xff0c;专为快速处理大量数据而设计。它们在数据中心和大规模分布式系统中尤其有用&#xff0c;用于任务如数据分区、查找优化、数据校验等。这两种哈希函数都以其高效的性能和良好的分布特性而著称。 CityHash City…

设计模式 -- 外观模式(Facade Pattern)

1 问题引出 组建一个家庭影院 DVD 播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能&#xff0c;其过程为&#xff1a; 直接用遥控器&#xff1a;统筹各设备开关 开爆米花机&#xff0c;放下屏幕 &#xff0c;开投影仪 &#xff0c;开音响&#xf…

【人工智能】AI算法系统设计与算法建模的详细阐述

&#x1f3c6;&#x1f3c6;欢迎大家来到我们的天空&#x1f3c6;&#x1f3c6; &#x1f3c6;&#x1f3c6;如果文章内容对您有所触动&#xff0c;别忘了点赞、关注&#xff0c;收藏&#xff01; &#x1f3c6; 作者简介&#xff1a;我们的天空 &#x1f3c6;《头衔》&#x…

自定义全局变量在uniapp的Vuex应用

本文介绍了uniapp使用自定义全局变量的方法。当同一业务在连续页面操作时&#xff0c;存在部分筛选变量需要始终保持一致&#xff0c;比如时间筛选条件等&#xff0c;来回跳转页面时如果采用变量传递&#xff0c;常较为繁琐&#xff0c;存在遗漏传递或未清除上一次变量值&#…

图像金字塔的作用

1. 概述 图像金字塔是图像多尺度表达的一种&#xff0c;主要应用与图像分割&#xff0c;是一种以多分辨率来解释图像的有效但概念简单的结构。图像金字塔实际上是一张图片在不同尺度下的集合&#xff0c;即原图的上采样和下采样集合。金字塔的底部是高分辨率图像&#xff0c;而…

LuaJit分析(九)LuaJit中的JIT原理分析

Jit in luajit Luajit是一款高性能的lua解释器&#xff0c;与官方的lua解释器相比&#xff0c;luajit的高速除了将解释器直接以汇编代码实现外&#xff0c;还支持jit模式&#xff08;Just in time&#xff09;。Jit模式即将luajit的字节码编译成处理器能够直接执行的机器码&am…

vue3如何监听reactive对象是哪个属性发生的变化

在 Vue 3 中&#xff0c;如果你想监听 reactive 对象中的某个属性发生的变化&#xff0c;你可以使用 watch 函数进行监听。watch 函数允许你观察 reactive 对象的某个属性或者整个对象&#xff0c;并在变化时执行相应的操作。 1. 监听 reactive 对象的某个属性 如果你只想监听…

C++学习/复习补充记录 --- 图论(深搜,广搜)

数据结构与算法 | 深搜&#xff08;DFS&#xff09;与广搜&#xff08;BFS&#xff09;_深搜广搜算法-CSDN博客 深度优先搜索理论基础 深搜和广搜的区别&#xff1a; &#xff08;通俗版&#xff09; dfs是可一个方向去搜&#xff0c;不到黄河不回头&#xff0c;直到遇到绝境了…

在Unity中使用C#进行Xml序列化时保留特定小数位的方法参考

序列化方法代码参考&#xff1a; using System.IO; using System.Xml.Serialization;public class XmlTool {public static string ToXml<T>(T obj){XmlSerializer xmlSerializer new XmlSerializer(typeof(T));using var stringWriter new StringWriter();//让xml文档…

linux驱动 -- 输入子系统

1:输入子系统介绍 一个统一的输入设备的开发框架&#xff0c; 统一生成设备文件&#xff0c; 统一返回固定格式值。 2:输入子系统开发设备 键盘、鼠标、触摸屏等等。 3&#xff1a;输入子系统运行框架 应用层&#xff1a;操作设备文件openclosereadwrite 输入子系统&#xff…

Netty 学习笔记

Java 网络编程 早期的 Java API 只支持由本地系统套接字库提供的所谓的阻塞函数&#xff0c;下面的代码展示了一个使用传统 Java API 的服务器代码的普通示例 // 创建一个 ServerSocket 用以监听指定端口上的连接请求 ServerSocket serverSocket new ServerSocket(5000); //…

OS常规测试方法-PPMU

step 0: 检查工作&#xff1a; 检查每根pin连接到指定的PPMU资源是否正确继电器资源在PRJ文件中是否定义正确 step 1 设计者设计的测试电路继电器重置初始化close应该闭合的继电器 step 2 DPS pin电压置0V&#xff0c;同时考虑电流量程wait闭合测试机DPS通道RELAYwait st…

android13 隐藏状态栏里面的飞行模式 隐藏蓝牙 隐藏网络

总纲 android13 rom 开发总纲说明 目录 1.前言 2.问题分析 3.代码分析 4.代码修改 5.编译运行 6.彩蛋 1.前言 android13 隐藏状态栏里面的飞行模式,或者其他功能,如网络,蓝牙等等功能,隐藏下图中的一些图标。 2.问题分析 这里如果直接找这个布局的话,需要跟的逻…

nefu暑假acm集训1 构造矩阵 个人模板+例题汇总

前言&#xff1a; 以下都是nefu暑假集训的训练题&#xff0c;我在此把我的模板和写的一些练习题汇总一下并分享出来&#xff0c;希望在能满足我复习的情况下能帮助到你。 正文&#xff1a; 模板&#xff1a; #include<bits/stdc.h> using namespace std; typedef long…

算法训练营|图论第5天

题目&#xff1a;107.寻找存在的路径 题目链接&#xff1a; 107. 寻找存在的路径 (kamacoder.com) 代码&#xff1a; #include<bits/stdc.h> #include<unordered_map> #include<unordered_set> using namespace std; vector<int>father; int find(…

Celery 中,广播模式可以通过使用 RabbitMQ 的 fanout 交换机来实现

这里写自定义目录标题 设置 Django 的 settings 模块从 Django 的 settings 文件中加载 Celery 配置自动发现任务使 Celery 实例可用配置 Celery 的任务路由 在 Celery 中&#xff0c;广播模式可以通过使用 RabbitMQ 的 fanout 交换机来实现。fanout 交换机会将消息广播到所有绑…

嵌入式开发者必备资料库 【完全免费】

嵌入式资料网盘分享文案 &#x1f31f; 嵌入式开发者必备资料库 &#x1f31f; 亲爱的嵌入式开发爱好者们&#xff0c; 我们为大家准备了一份丰富的嵌入式资料网盘&#xff0c;涵盖了单片机、Linux系统、硬件设计等多个领域的优质资源&#xff0c;助力你的学习和项目开发&…