深度学习笔记(二)—— 自动求梯度

糊涂涂 2022-8-5 64 8/5

简单例子

首先我们要导入所需要的模块:

from mxnet import nd, autograd

对函数y = 2xTx求关于列向量x的梯度(注:这里我们可以视作给函数y=2x2求导),我们先创建变量x,并赋初始值。

x = nd.arange(4).reshape((4, 1))

为了求有关变量x的梯度,我们需要先调⽤attach_grad函数来申请存储梯度所需要的内存。

x.attach_grad()

下面定义有关变量x的函数。为了减少计算和内存开销,默认条件下MXNet不会记录⽤于求梯度的计算。我们需要调用record函数来要求MXNet记录与求梯度有关的计算。

with autograd.record():
    y = 2 * nd.dot(x.T, x)

注:with语句是一个上下文管理器,可以很好的处理上下文环境产生的异常,这里我们可以简单理解为开辟一个"容器"来"盛放"定义的函数。

autograd.record()是一个记录需要求梯度的函数的程序。

由于x的形状为(4, 1),y是一个标量。接下来我们可以通过调⽤backward函数⾃动求梯度。需要注意的是,如果y不是一个标量,MXNet将默认先对y中元素求和得到新的变量,再求该变量有关x的梯度

y.backward()

函数 y = 2x T x 关于x 的梯度应为4x。现在我们来验证一下求出来的梯度是正确的。

assert (x.grad - 4 * x).norm().asscalar()==0    # 判断梯度是否是4x
print(x.grad)

注:assert 是python中很重要的一个功能,可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,没有实际输出。

输出:

[[ 0.]
 [ 4.]
 [ 8.]
 [12.]]
<NDArray 4x1 @cpu(0)>

这里的输出结果是我们最开始对x初始值设定下每点的梯度值(也可以理解为函数f(x)在各点的导数值)。下面是完整代码:

from mxnet import nd, autograd

# 设定初始值
x = nd.arange(4).reshape((4, 1))
x.attach_grad()
# 定义函数并求导
with autograd.record():
    y = 2 * nd.dot(x.T, x)
y.backward()
# 判定准确性并输出结果
assert (x.grad - 4 * x).norm().asscalar() == 0
print(x.grad)

*训练模式和预测模式

这里不多作介绍,后面再详细说明。

对Python控制流求梯度

在MXNet中,即使函数的计算图包含了Python的控制流(如条件和循环控制),我们也有可能对变量求梯度。如下:

from mxnet import nd, autograd


def f(x):  # 定义函数
    b = x * 2
    while b.norm().asscalar() < 1000:  # 这里x相当于输入的未知数,由于只有一个元素,我们可以把这一步理解为取b的绝对值
        b = b * 2
    if b.sum().asscalar() > 0:
        c = b
    else:
        c = 100 * b
    return c  # 在上述整个过程中,对未知数的转换仅限于对其系数的变换,所以最后的输出结果可以理解为c=k*x


x = nd.random.normal(shape=1)  # 创建一个随机数作为未知数x的值
x.attach_grad()

with autograd.record():
    c = f(x)
c.backward()
assert (x.grad - c / x).norm().asnumpy() == 0  # 验证函数
print(x.grad == c / x)  # 输出判定结果

分析函数可知f (x)= c = kx,求导结果为k,所以最终结果x.grad = k = c / x

输出:

[1.]
<NDArray 1 @cpu(0)>
- THE END -

糊涂涂

8月06日09:26

最后修改:2022年8月6日
0