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

news2024/9/23 9:30:19

🎯要点

  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, diff

x = 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, diff

def f(x):
    if x > 2:
        return x * 2 + 5
    return x / 2 + 5

x = 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 = primal
        self.tangent = tangent

    def __add__(self, other):
        primal = self.primal + other.primal
        tangent = self.tangent + other.tangent
        return Variable(primal, tangent)

    def __sub__(self, other):
        primal = self.primal - other.primal
        tangent = self.tangent - other.tangent
        return Variable(primal, tangent)

    def __mul__(self, other):
        primal = self.primal * other.primal
        tangent = self.tangent * other.primal + other.tangent * self.primal
        return Variable(primal, tangent)

    def __truediv__(self, other):
        primal = self.primal / other.primal
        tangent = (self.tangent / other.primal) + (
            -self.primal / other.primal**2
        ) * other.tangent
        return Variable(primal, tangent)

    def __repr__(self):
        return f"primal: {self.primal}, tangent: {self.tangent}"

前向模式下自动微分计算

def mul_add(a, b, c):
    return a * b + c * a

def div_sub(a, b, c):
    return a / b - c

a, 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 = primal
        self.adjoint = adjoint

    def backward(self, adjoint):
        self.adjoint += adjoint

    def __add__(self, other):
        variable = Variable(self.primal + other.primal)

        def backward(adjoint):
            variable.adjoint += adjoint
            self_adjoint = adjoint * 1.0
            other_adjoint = adjoint * 1.0
            self.backward(self_adjoint)
            other.backward(other_adjoint)

        variable.backward = backward
        return variable

    def __sub__(self, other):
        variable = Variable(self.primal - other.primal)

        def backward(adjoint):
            variable.adjoint += adjoint
            self_adjoint = adjoint * 1.0
            other_adjoint = adjoint * -1.0
            self.backward(self_adjoint)
            other.backward(other_adjoint)

        variable.backward = backward
        return variable

    def __mul__(self, other):
        variable = Variable(self.primal * other.primal)

        def backward(adjoint):
            variable.adjoint += adjoint
            self_adjoint = adjoint * other.primal
            other_adjoint = adjoint * self.primal
            self.backward(self_adjoint)
            other.backward(other_adjoint)

        variable.backward = backward
        return variable

    def __truediv__(self, other):
        variable = Variable(self.primal / other.primal)

        def backward(adjoint):
            variable.adjoint += adjoint
            self_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 = backward
        return variable

    def __repr__(self) -> str:
        return f"primal: {self.primal}, adjoint: {self.adjoint}"

反向模式自动微分计算

def mul_add(a, b, c):
    return a * b + c * a

def div_sub(a, b, c):
    return a / b - c

a, 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.coloradmin.cn/o/2085920.html

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

相关文章

使用谷歌浏览器查看原型

需求人员给了一个原型文件包,用谷歌浏览器打开提示以下内容: 找到需求人员发的原型文件包 进入到resources-->chrome,找到axure-chrome-extension.crx,复制一份出来命名为axure-chrome-extension.tar,然后在该目录下…

招联金融基于 Apache Doris 数仓升级:单集群 QPS 超 10w,存储成本降低 70%

在竞争激烈的消费金融市场中,有效利用海量数据、提升业务运营效率是赢得市场的关键。早期招联采用典型的 Lambda 架构提供业务报表、数据运营、个性推荐、风险控制等数据服务,而 Lambda 过多的技术栈也引发了数据孤岛、查询效率不足、代码复用性差以及开…

AI算法平台训练站裸土检测算法训练裸土检测算法源码

在全球化进程加快与环境问题日益突出的今天,裸土检测成为了环境监测和土壤管理中不可或缺的一环。裸土指的是没有植被覆盖的土壤区域,这些区域易受侵蚀,并可能导致土壤流失和环境退化。为了有效应对这些问题,裸土检测算法应运而生…

Redis持久化与主从同步

1 淘汰策略 127.0.0.1:6379> help expireEXPIRE key secondssummary: Set a keys time to live in secondssince: 1.0.0group: generic127.0.0.1:6379> help PEXPIREPEXPIRE key millisecondssummary: Set a keys time to live in millisecondssince: 2.6.0group: gener…

【CSP:202112-1】序列查询(Java)

题目链接 202112-1 序列查询 题目描述 求解思路 模拟:a数组可以看作是记录 f ( x ) f(x) f(x) 函数值发生变化出的 x x x 点(每次自增1)。因此将每段相同数值的 f ( x ) f(x) f(x) 用乘法计算出来即可,最后记得要加上最后一…

Java Web —— 第九天(事务)

事务管理 & AOP 事务回顾 概念 事务 是一组操作的集合,它是一个不可分割的工作单位,这些操作 要么同时成功,要么同时失败 操作 开启事务(一组操作开始前,开启事务): start transaction / begin 提交事务(这组操作全部成功…

服务器访问端口命令

服务器访问端口命令是一组用于管理服务器端口的命令行指令。服务器端口是用于与外部设备或应用程序进行通信的逻辑通道,它允许数据在服务器和其他设备之间传输。以下是一些常见的服务器访问端口命令。 netstat:这个命令用于检查服务器上当前的网络连接和…

FPGA第 5 篇,FPGA技术优略势,FPGA学习方向,FPGA学习路线(FPGA专业知识的学习方向,FPGA现场可编程门阵列学习路线和方向)

前言 前几篇讲了一下FPGA的发展和应用,以及未来前景。具体详细,请看 FPGA发展和应用,以及未来前景https://blog.csdn.net/weixin_65793170/category_12665249.html 这里我们来,记录一下,FPGA专业知识的学习路线 一.…

OpenAI remove key access while using AAD authentication

题意:“OpenAI 在使用 AAD 认证时移除了密钥访问权限” 问题背景: I am calling Azure OpenAI API in my python code. To set it up, we need to provide a few parameters, one of which is openai.api_key. There are 2 options to get this value -…

力扣hot100-动态规划

文章目录 概念动态规划基本思想常见步骤常用技巧常见问题类型 动态规划题目题目: 爬楼梯题解 概念 动态规划 动态规划(Dynamic Programming,简称DP)是一种解决问题的算法思想,通常用于优化问题。它的核心思想是将一个…

K8S声明式的管理方式

一、K8S声明式的管理方式: 1、适合对资源的修改操作 2、声明式管理依赖于yaml文件,所有的内容都在yaml文件中声明 3、编辑好的yml文件还是要靠陈述式命令发布到K8S集群中 二、K8S中支持三种声明式的资源管理方式: 1、deployment格式&…

如何用Java SpringBoot Vue搭建创新创业学分管理系统?实战教程

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

GLM大模型 - CogVideoX:5B 开源,2B 转为 Apache 协议

8月6日,我们发布并开源了CogVideoX-2B模型,受到广大开发者的欢迎。 为了促进社区的自主使用和开放式创新,我们现决定将参数规模更大、性能更强的产品级模型 CogVideoX-5B 开源,同时 CogVideoX-2B 的开源协议调整为更加开放的Apac…

阿里云链接远程桌面Ubuntu22.4,出现的各种问题汇总,太艰辛,所以发出来,帮助一下后边的小伙伴

问题一:远程登录桌面计算机名写什么:写ip,公网ip,用户名不要填 问题二 Win10远程连接Ubuntu20.04桌面黑屏的问题 如果你是用浏览器连接上了云服务器,那么请先logout!

算法的学习笔记—从 1 到 n 整数中 1 出现的次数(牛客JZ43)

😀前言 在编程面试中,求解从 1 到 n 的整数中数字 1 出现的次数是一个常见的挑战。该问题的关键在于如何高效地统计数字 1 出现的次数。本文将详细分析该问题的解题思路,并提供一个高效的 Java 实现。 🏠个人主页:尘觉…

java 切面日志打印出参入参

切面Controller出入参日志打印 项目结构 切面日志对controller下所有的方法生效 切面代码 Slf4j Aspect Component public class ControllerLogAspect {// 定义一个切点,拦截所有Controller层的public方法Before("execution(public * com.jzt.market.cont…

Android解析异步消息处理机制

文章目录 Android解析异步消息处理机制MessageHandlerMessageQueueLooper Android解析异步消息处理机制 Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。其中Message和Handler在上一小节中我们已经接触过了,而Mess…

大数据基础:离线与实时数仓区别和建设思路

文章目录 离线与实时数仓区别和建设思路 一、离线数仓与实时数仓区别 ​​​​​​​二、实时数仓建设思路 离线与实时数仓区别和建设思路 ​​​​​​​一、离线数仓与实时数仓区别 离线数据与实时数仓区别如下: 对比方面 离线数仓 实时数仓 架构选择 传…

ComsolMatlab 两级串联扩张式消声器仿真解与解析解

消声器的声学性能通常要求消声器在工作频率范围内有较大的消声量以及较宽的消声频带。常用的消声器声学性能评价指标通常有传递损失、插入损失、减噪量三种。其中插入损失只能反映整个系统在安装消声器前后声学特性的变化,并不能直接反映消声器本身单独具有的属性。…

计算机毕业设计选题推荐-中药材进存销管理系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…