AD自动求导算法

news2025/5/26 5:37:53

Jacobian matrix 雅可比矩阵

在后面的前向反向积累会用到。AD需要用到中间变量的导数,所以Jacobian matrix就是来计算这些的。
在这里插入图片描述

Hessian矩阵

在这里插入图片描述
在牛顿法的优化中有应用。

牛顿法 Newton-type methods

自动求导 Automatic Differentiation, AD

数值微分

import numpy as np

# 定义目标函数
def target_function(x):
    return x**2

# 数值方法计算一阶导数
def numerical_derivative_first_order(f, x, h=1e-5):
    return (f(x + h) - f(x - h)) / (2 * h)

# 数值方法计算二阶导数
def numerical_derivative_second_order(f, x, h=1e-5):
    return (f(x + h) - 2 * f(x) + f(x - h)) / (h * h)

# 在 x = 1 处计算函数值、一阶导数和二阶导数
x = 1.0
y = target_function(x)
dy_dx = numerical_derivative_first_order(target_function, x)
d2y_dx2 = numerical_derivative_second_order(target_function, x)

# 打印结果
print("Function value at x = 1:", y)
print("First derivative at x = 1:", dy_dx)
print("Second derivative at x = 1:", d2y_dx2)

可以用导数的定义计算,但是有误差。

符号微分

SymPy

博客 SymPy 符号计算基本教程
SymPy可以像人一样对符号求导,太强了。
貌似用了很多的匹配规则,不晓得怎么实现的,有点吊的。

符号微分的问题

即便SymPy这样的库已经可以对符号进行微分,但是一个函数的高次导会很复杂,表达式中会包含很多的重复计算,而这些重复的部分是可以简化的。如下图所示:
在这里插入图片描述
为了实现交替进行微分和简化的操作,AD应运而生。

自动微分

链式法则 Chain rule

在这里插入图片描述

前向积累 forward accumulation

前向积累是比较直观的:
在这里插入图片描述
在这里插入图片描述
上面的seed就是用来求偏导的,求x1的偏导,seed={1,0},x2就是seed={0,1}
在这里插入图片描述
符合常规的求导过程。

反向积累 reverse accumulation

神经网络里的反向传播BP是反向积累的一个特例。
在这里插入图片描述

在这里插入图片描述

反向积累只需要令最后的f的seed={1}即可。理解起来不如前向积累直观,但是编程实现起来却更简洁速度也更快。

关键区别:
前向模式 AD: 需要对每个变量计算一次梯度,因此计算成本与变量数目成正比。适用于输入变量较少而输出变量较多的情况。
反向模式 AD: 仅需要对一个输出节点计算一次梯度,因此计算成本与输出节点数目成正比。适用于输入变量较多而输出变量较少的情况。——gpt

我觉得gpt说的有道理。

Automatic differentiation using dual numbers

在这里插入图片描述
太吊了,把 x x x x + x ′ ϵ x+x^{'}\epsilon x+xϵ表示,居然就可以计算导数了,太吊了!

class Dual:
    def __init__(self, realPart, infinitesimalPart=0):
        self.realPart = realPart
        self.infinitesimalPart = infinitesimalPart

    def __add__(self, other):
        return Dual(
            self.realPart + other.realPart,
            self.infinitesimalPart + other.infinitesimalPart
        )

    def __sub__(self, other):
        return Dual(
            self.realPart - other.realPart,
            self.infinitesimalPart - other.infinitesimalPart
        )

    def __mul__(self, other):
        return Dual(
            self.realPart * other.realPart,
            other.realPart * self.infinitesimalPart + self.realPart * other.infinitesimalPart
        )

    def __truediv__(self, other):
        realPart = self.realPart / other.realPart
        infinitesimalPart = (other.realPart * self.infinitesimalPart - self.realPart * other.infinitesimalPart) / (other.realPart ** 2)
        return Dual(realPart, infinitesimalPart)

# Example: Finding the partials of z = x * (x + y) + y * y at (x, y) = (2, 3)
def f(x, y):
    return y/x -x +y
    # return x * (x + y) + y * y
x = Dual(2)
y = Dual(3)
epsilon = Dual(0, 1)
a = f(x + epsilon, y)
b = f(x, y + epsilon)
print("∂z/∂x =", a.infinitesimalPart)  # Output: ∂z/∂x = 28
print("∂z/∂y =", b.infinitesimalPart)  # Output: ∂z/∂y = 81

这里的Dual类实现了加减乘除的求导,其余sin cos In exp的运算也类似。
在这里插入图片描述
当然这里的求导都是一次导,当导数变高阶的时候,可能会有所变化。

具体可能需要参考tensorflow和pytorch的源码
留个坑在这。

tensorflow和pytorch中的AD

源代码我没有看,框架比较复杂。
不过找到一个简易版的tinynn,对于学习应该是够用了。github
关于AD的部分就是ops.py文件,贴在下面:

"""Tensor operations (with autograd context)"""

import numpy as np


def as_tensor(obj):
    # avoid looping import
    from core.tensor import as_tensor
    return as_tensor(obj)


def build_binary_ops_tensor(ts1, ts2, grad_fn_ts1, grad_fn_ts2, values):
    requires_grad = ts1.requires_grad or ts2.requires_grad
    dependency = []
    if ts1.requires_grad:
        dependency.append(dict(tensor=ts1, grad_fn=grad_fn_ts1))
    if ts2.requires_grad:
        dependency.append(dict(tensor=ts2, grad_fn=grad_fn_ts2))
    tensor_cls = ts1.__class__
    return tensor_cls(values, requires_grad, dependency)


def build_unary_ops_tensor(ts, grad_fn, values):
    requires_grad = ts.requires_grad
    dependency = []
    if ts.requires_grad:
        dependency.append(dict(tensor=ts, grad_fn=grad_fn))
    tensor_cls = ts.__class__
    return tensor_cls(values, requires_grad, dependency)


def add_(ts1, ts2):
    values = ts1.values + ts2.values

    # c = a + b
    # D_c / D_a = 1.0
    # D_c / D_b = 1.0
    # also need to handle broadcasting
    def grad_fn_ts1(grad):
        # handle broadcasting (5, 3) + (3,) -> (5, 3)
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)
        # handle broadcasting (5, 3) + (1, 3) -> (5, 3)
        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def sub_(ts1, ts2):
    return ts1 + (-ts2)


def mul_(ts1, ts2):
    values = ts1.values * ts2.values

    # c = a * b
    # D_c / D_a = b
    # D_c / D_b = a
    def grad_fn_ts1(grad):
        grad = grad * ts2.values
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        grad = grad * ts1.values
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def div_(ts1, ts2):
    values = ts1.values / ts2.values

    # c = a / b
    # D_c / D_a = 1 / b
    # D_c / D_b = -a / b**2
    def grad_fn_ts1(grad):
        grad = grad / ts2.values
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        grad = -grad * ts1.values / ts2.values ** 2
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def pow_(ts1, ts2):
    values = ts1.values ** ts2.values

    # c = a ** b
    # D_c / D_a = b * a ** (b-1)
    # D_c / D_b = ln(a) * a ** b
    def grad_fn_ts1(grad):
        grad = grad * ts2.values * ts1.values ** (ts2.values - 1)
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)

        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        grad = grad * (np.log(ts1.values) * values)
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def dot_(ts1, ts2):
    values = ts1.values @ ts2.values

    # c = a @ b
    # D_c / D_a = grad @ b.T
    # D_c / D_b = a.T @ grad
    def grad_fn_ts1(grad):
        return grad @ ts2.values.T

    def grad_fn_ts2(grad):
        return ts1.values.T @ grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def maximum_(ts1, ts2):
    values = np.maximum(ts1.values, ts2.values)

    def grad_fn_ts1(grad):
        grad = grad * (ts1.values >= ts2.values)
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        grad = grad * (ts2.values > ts1.values)
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def minimum_(ts1, ts2):
    values = np.minimum(ts1.values, ts2.values)

    def grad_fn_ts1(grad):
        grad = grad * (ts1.values <= ts2.values)
        for _ in range(grad.ndim - ts1.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts1.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    def grad_fn_ts2(grad):
        grad = grad * (ts2.values < ts1.values)
        for _ in range(grad.ndim - ts2.values.ndim):
            grad = grad.sum(axis=0)
        for i, dim in enumerate(ts2.shape):
            if dim == 1:
                grad = grad.sum(axis=i, keepdims=True)
        return grad

    return build_binary_ops_tensor(
        ts1, ts2, grad_fn_ts1, grad_fn_ts2, values)


def exp_(ts):
    values = np.exp(ts.values)

    def grad_fn(grad):
        return values * grad

    return build_unary_ops_tensor(ts, grad_fn, values)


def max_(ts, axis=None):
    values = np.max(ts.values, axis=axis)

    def grad_fn(grad):
        return grad * (ts.values.max(axis=axis, keepdims=1) == ts.values)

    return build_unary_ops_tensor(ts, grad_fn, values)


def min_(ts, axis=None):
    values = np.min(ts.values, axis=axis)

    def grad_fn(grad):
        return grad * (ts.values.min(axis=axis, keepdims=1) == ts.values)

    return build_unary_ops_tensor(ts, grad_fn, values)


def log_(ts):
    values = np.log(ts.values)

    def grad_fn(grad):
        return grad / ts.values

    return build_unary_ops_tensor(ts, grad_fn, values)


def sum_(ts, axis):
    values = ts.values.sum(axis=axis)
    if axis is not None:
        repeat = ts.values.shape[axis]

    def grad_fn(grad):
        if axis is None:
            grad = grad * np.ones_like(ts.values)
        else:
            grad = np.expand_dims(grad, axis)
            grad = np.repeat(grad, repeat, axis)
        return grad

    return build_unary_ops_tensor(ts, grad_fn, values)


def transpose_(ts, axes=None):
    values = ts.values.transpose(axes)

    if axes is None:
        axes = reversed(range(ts.values.ndim))
    axes = list(axes)

    # recover to original shape
    def grad_fn(grad):
        return grad.transpose(np.argsort(axes))

    return build_unary_ops_tensor(ts, grad_fn, values)


def getitem_(ts, key):
    values = ts.values[key]

    def grad_fn(grad):
        recover_grad = np.zeros_like(ts.values)
        recover_grad[key] = grad
        return recover_grad

    return build_unary_ops_tensor(ts, grad_fn, values)


def neg_(ts):
    values = -ts.values

    def grad_fn(grad):
        return -grad

    return build_unary_ops_tensor(ts, grad_fn, values)


def reshape_(ts, newshape):
    shape = ts.values.shape
    values = ts.values.reshape(newshape)

    def grad_fn(grad):
        return grad.reshape(shape)

    return build_unary_ops_tensor(ts, grad_fn, values)


def pad_(ts, pad_width, mode):
    values = np.pad(ts.values, pad_width=pad_width, mode=mode)
    slices = list()
    for size, (before, after) in zip(values.shape, pad_width):
        slices.append(slice(before, size-after))

    def grad_fn(grad):
        return grad[tuple(slices)]

    return build_unary_ops_tensor(ts, grad_fn, values)


def flatten_(ts):
    shape = ts.shape
    values = ts.values.ravel()

    def grad_fn(grad):
        return grad.reshape(shape)
    return build_unary_ops_tensor(ts, grad_fn, values)


def clip_(ts, min, max):
    values = ts.values.clip(min, max)

    mask = np.ones(ts.shape, dtype=bool)
    if min is not None:
        mask &= ts.values >= min
    if max is not None:
        mask &= ts.values <= max

    def grad_fn(grad):
        return grad * mask
    return build_unary_ops_tensor(ts, grad_fn, values)


def max(obj, axis=None):
    return max_(as_tensor(obj), axis=axis)


def maximum(obj1, obj2):
    return maximum_(as_tensor(obj1), as_tensor(obj2))


def minimum(obj1, obj2):
    return minimum_(as_tensor(obj1), as_tensor(obj2))


def exp(obj):
    return exp_(as_tensor(obj))


def sum(obj, axis=None):
    return sum_(as_tensor(obj), axis=axis)


def log(obj):
    return log_(as_tensor(obj))


def reshape(obj, newshape):
    return reshape_(as_tensor(obj), newshape)


def pad(obj, pad_width, mode="constant"):
    return pad_(as_tensor(obj), pad_width, mode=mode)


def flatten(obj):
    return flatten_(as_tensor(obj))


def clip(obj, min=None, max=None):
    return clip_(as_tensor(obj), min, max)

所以似乎这些深度学习框架似乎并没有按照交替进行符号求导和简化的步骤去进行AD。而是把每一个算子的梯度计算方法都写了出来。这样当我们去训练神经网络的时候,下,下,显存除了要存本来的参数,还要存梯度信息。这就会增大开销,所以为什么有
zero_grad 这样的操作。

class Tensor:
    # ...
    def zero_grad(self):
        self.grad = np.zeros(self.shape)

参考:PyTorch中在反向传播前为什么要手动将梯度清零?
一文详解pytorch的“动态图”与“自动微分”技术

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

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

相关文章

构建企业级AI中台,实现业务场景价值闭环

从数据资产到模型资产&#xff0c;大模型的发展&#xff0c;正在加速企业构建AI中台&#xff0c;旨在让更多 AI 的能力能够被构建出来、能够被管理起来、能够面向业务开放起来&#xff0c;打通从数据到模型到最后决策的全链路。 本文将从为什么建AI中台、如何建AI中台、企业AI…

盲盒、一番赏小程序搭建,打开线上盲盒市场

近几年&#xff0c;我国潮玩市场发展非常迅速&#xff0c;在互联网的影响下&#xff0c;盲盒更是迅速走红网络&#xff0c;深受年轻人的喜欢&#xff0c;各大社交平台上关于盲盒的讨论度也是层出不穷。 一番赏与盲盒的机制都是差不多的&#xff0c;盲盒是在包装一样的盒子中放…

ubuntu系统如何安装man命令的中文文档

ubuntu系统如何安装man命令的中文文档 背景 在Linux系统上需要使用一些命令的时候&#xff0c;往往会通过man命令去查询命令的使用方法和参数的说明&#xff0c;但是这些文档说明都是英文的&#xff0c;怎么样才能变成中文的文档&#xff0c;看上去更加清晰呢&#xff1f; 解…

到底谁还在用企业公示系统,聪明的人已经...

随着互联网和数字经济的发展&#xff0c;企业背调变得越来越重要了。毕竟现在企业数量不断增加&#xff0c;据调查数据显示&#xff1a;截止到23年8月11日全国市场总量已经达到3.4亿户&#xff0c;今年新增2049万户。 其中&#xff0c;企业总量9922万户&#xff0c;今年新增621…

线性代数-第五课,第六课,第七课,第八课

第五课 判断某向量是否可由某向量组线性表示 把向量组组成一个行列式&#xff0c;计算行列式的秩 把所有向量放在一起构成一个行列式&#xff0c;计算行列式的秩 如果两个行列式的秩相等&#xff0c;表示可以线性表示&#xff0c;写答案的格式如下 线性表示&#xff1a;bk…

深入浅出XTTS:Oracle数据库迁移升级利器

演讲大纲&#xff1a; 1. 什么是XTTS 2. 适用场景 3. XTTS的基本操作步骤 4. XTTS案例分享 今天主要跟大家分享一下XTTS,在网上曾看过相关讨论,但发现按网上讲的那些去实际操作的话,还是会遇到一些坑,并不能实际落下来,所以今天想跟大家分享一些实战干货. 一、什么是XTTS …

Linux第5步_测试虚拟机网络连接

安装好VMwareTools后&#xff0c;就可以测试虚拟机网络连接了&#xff0c;目的是实现虚拟机上网。 1、打开“控制面板”&#xff0c;得到下图&#xff1a; 2、双击“网络和 Internet” &#xff0c;得到下图&#xff1a; 3、双击“网络和共享中心” 4、点击“更改适配器设置”…

海康威视摄像头+服务器+录像机配置校园围墙安全侦测区域入侵侦测+越界侦测.docx

一、适用场景 1、校园内&#xff0c;防止课外时间翻越围墙到校外、从校外翻越围墙到校内&#xff1b; 2、通过服务器摄像头的侦测功能及时抓图保存&#xff0c;为不安全因素提供数字化依据&#xff1b; 3、网络录像机保存监控视频&#xff0c;服务器保存抓拍到的入侵与越界&am…

大数据时代的WEB运维高级架构师,Web系统运维工程师的实战成长之路

一、教程描述 本套WEB架构师教程&#xff0c;大小30.61G&#xff0c;共有183个文件。 二、教程目录 01-Web架构之单机时代&#xff08;共7课时&#xff09; 02-Web架构之集群时代&#xff08;共9课时&#xff09; 03-Web架构之DNS&#xff08;共6课时&#xff09; 04-Web…

2397. 被列覆盖的最多行数

给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix &#xff1b;另给你一个整数 numSelect&#xff0c;表示你必须从 matrix 中选择的 不同 列的数量。 如果一行中所有的 1 都被你选中的列所覆盖&#xff0c;则认为这一行被 覆盖 了。 形式上&#xff0c;假设 s {c1…

肿瘤til细胞类型

TISCH (comp-genomics.org)

抖店申请流程是什么?

我是电商珠珠 想要入驻抖店的人很多&#xff0c;但是知道流程的新手却没有几个。 从开店资料到入驻流程&#xff0c;我来具体的跟大家讲一讲。 第一个&#xff0c;新手开店资质 1、营业执照 营业执照是入驻门槛之一&#xff0c;营业执照类型分为两类&#xff0c;一类为企业…

阿里云服务器Valheim端口2456、2457和2458放行设置

使用阿里云服务器搭建Valheim英灵神殿需要开启2456-2458端口&#xff0c;阿里云服务器默认只开放了22核3389端口&#xff0c;开通2456端口是在安全组中配置的&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说下阿里云服务器安全组开通端口流程&#xff1a; 阿里云服务器安…

UE5.1保存资源报错

UE5.1保存资源报错 错误&#xff1a; The asset /Game/XXX(XXX.uasset) failed to save. Cancel: Stop saving all assets and return to the editor. Retry: Attempt to save the asset again. Continue: Skip saving this asset only. 解决: 1. 可能是进程中有多开的项目&…

洛谷P1024[NOIP2001 提高组] 一元三次方程求解(cpp)(二分查找)

目录 1.题目 2.思路 3.AC 1.题目 # [NOIP2001 提高组] 一元三次方程求解 ## 题目描述 有形如&#xff1a; 这样的一个一元三次方程。给出该方程中各项的系数&#xff08;a,b,c,d 均为实数&#xff09;&#xff0c;并约定该方程存在三个不同实根&#xff08;根的范围在 -…

stm32实战之su-03t语音模块固件的制作与烧录

目录 su-03t简介 管脚定义 ​​智能公元语音固件制作​​ 账号注册 创建产品 产品配置 唤醒词自定义 命令词自定义 发音人配置 其他配置 生成和下载语音固件 固件烧录 下载SDK固件烧录工具 SU-03T驱动分享 su-03t简介 SU-03T 是一款低成本、低功耗、小体积的离线…

巨杉数据库荣登2023胡润全球猎豹企业榜

胡润研究院与广州南沙联合发布《2023胡润全球猎豹企业榜》&#xff0c;这是胡润研究院首次发布“全球猎豹企业”。榜单列出了全球成立于2000年后&#xff0c;五年内最有可能达到独角兽级十亿美金估值的高成长性企业。巨杉数据库凭借在分布式文档型数据库领域的创新突破&#xf…

本地监控jar包可视化性能数据

一、机器申请 二、maven项目jar打包 三、机器性能监控 1.jdk版本配置 本地下载的机器虽自带jdk&#xff0c;但是jdk版本过低&#xff0c;需重新安装jdk 参考&#xff1a; Linux系统安装JDK1.8 详细流程_linux安装jdk1.8-CSDN博客 2.jvm参数修改 需修改jvm堆内存 栈内存信…

【Java期末】学生成绩管理系统

诚接计算机专业编程任务(C语言、C、Python、Java、HTML、JavaScript、Vue等)10/15R&#xff0c;如有需要请私信我&#xff0c;或者加我的企鹅号&#xff1a;1404293476 本文资源下载地址&#xff1a;https://download.csdn.net/download/weixin_47040861/88697244 —————…

环境监测LoRa网关解决方案应用空气质量监控

随着全球工业化和城镇化的快速发展&#xff0c;空气质量问题越来越受到关注。环境监测技术的发展&#xff0c;可以有效地帮助我们监测和改善空气质量。而LoRa网关则是一种可以帮助我们实现远距离、低功耗通信的无线通信技术&#xff0c;它的应用可以为空气质量监测提供解决方案…