深度学习 4.微分和自动求导


Author:baiyucraft

BLog: baiyucraft’s Home

原文:《动手学深度学习》


一、微分和导数

  概念相信大家懂得都懂,根据书中的画个图:

import numpy as np
from matplotlib import pyplot as plt


def set_figsize(figsize=(3.5, 2.5)):
    """设置matplotlib的图表大小。"""
    plt.rcParams['figure.figsize'] = figsize


def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    """设置matplotlib的轴。"""
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()


def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear',
         fmts=('-', 'm--', 'g-.', 'r:'), axes=None):
    """绘制数据点。"""
    if legend is None:
        legend = []

    # 设置画布大小
    set_figsize((4, 2.5))
    # 移动坐标轴
    axes = axes if axes else plt.gca()

    # 如果 `X` 有一个轴,输出True
    def has_one_axis(X):
        return hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")

    if has_one_axis(X):
        X = [X]
    if not Y:
        X, Y = [[]] * len(X), X
    elif has_one_axis(Y):
        Y = [Y]
    if len(X) != len(Y):
        X = X * len(Y)
    # 清除当前活动轴
    axes.cla()
    for x, y, fmt in zip(X, Y, fmts):
        if len(x):
            axes.plot(x, y, fmt)
        else:
            axes.plot(y, fmt)
    set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
    plt.show()


if __name__ == '__main__':
    def f(x):
        return 3 * x ** 2 - 4 * x
    
    def g(x):
        return 2 * x - 3

    x = np.arange(0, 3, 0.1)
    plot(x, [f(x), g(x)], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])

运行结果:

二、自动求导

  pytorch是可以通过自动求导来计算导数的。实际中,根据我们设计的模型,系统会构建一个计算图,来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。所以求导所运用的反向传播函数

1.标量对向量的求导

我们想对函数 y = 2 x T x y = 2x^{T}x y=2xTx 关于列向量求 x x x 求导:

x = torch.arange(4.0)
# 能存放梯度
x.requires_grad_(True)
print('\n======x======\n', x)
# y = 2x^{T}x
y = 2 * torch.dot(x, x)
print('\n======y======\n', y)
# 调用反向传播函数
# y = 2x1**2 + 2x2**2 + 2x3**2 + 2x4**2 求导后,4*(x1, x2, x3, x4)
y.backward()
print('\n======y关于x的梯度======\n', x.grad)
print('\n======验证======\n', x.grad == 4 * x)

运行结果:

  现在让我们计算 x x x 的另一个函数:

# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
print('\n======x======\n', x)
print('\n======y======\n', y)
y.backward()
print('\n======y关于x的梯度======\n', x.grad)

运行结果:

2.非标量变量的反向传播

  当y不是标量时,向量y关于向量x的导数的最自然解释是一个矩阵。对于高阶和高维的yx,求导的结果可以是一个高阶张量。

  然而,虽然这些更奇特的对象确实出现在高级机器学习中(包括深度学习中),但当我们调用向量的反向计算时,我们通常会试图计算一批训练样本中每个组成部分的损失函数的导数。这里,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。

# 对非标量调用`backward`需要传入一个`gradient`参数,该参数指定微分函数关于`self`的梯度。在我们的例子中,我们只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x
print('\n======x======\n', x)
print('\n======y======\n', y)
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
print('\n======y关于x的梯度======\n', x.grad)

运行结果:

3.分离计算

  有时,我们希望将某些计算移动到记录的计算图之外。由于某种原因,我们希望将 y 视为一个常数,并且只考虑到 xy被计算后发挥的作用,这里可以用detach()函数将y变为相较于x的常数u

# 分离计算
x.grad.zero_()
y = x * x
print('\n======x======\n', x)
print('\n======y======\n', y)
u = y.detach()
print('\n======u======\n', u)
z = u * x
print('\n======z======\n', z)
z.sum().backward()
print('\n======验证x.grad == u======\n', x.grad == u)

运行结果:

4.Python控制流的梯度计算

  使用自动求导的一个好处是,即使构建函数的计算图需要通过 Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度。在下面的代码中,while 循环的迭代次数和 if 语句的结果都取决于输入 a 的值:

# Python控制流的梯度计算
def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
	else:
        c = 100 * b
	return c

a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
print('\n======a======\n', a)
print('\n======f(a)======\n', d)
print('\n======f(a)关于a的梯度======\n', a.grad)
print('\n======验证======\n', a.grad == d / a)

运行结果:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值