概述
本节需要先了解MXNet中NDArray的基本用法,可以参考我的前一篇博客:【深度学习】MXNet基本数据结构NDArray常用操作
如下示例包括:
- MXNet中使用autograd包自动求解梯度;
- MXNet可以python中自定义的函数(一般化的命令式程序)求梯度;
- MXNet的运行模式包含训练模式和预测模式,可以通过特定函数进行判断。
示例
示例代码中包含了python中的with
用法,先做简单介绍。
with语句
简单来说,with
提供了一种资源管理的方式。资源的管理在程序设计中是一个很常见的问题,例如管理开启的文件、网络 socket 和各种锁(locks)等。最主要的问题就在于我们必须确保这些开启的资源在使用完之后,确实被关闭或释放。如果忘记关闭这些资源,可能会造成程序执行上的效能问题,甚至出现错误。而除了关闭之外,有些特殊的资源在使用完毕之后,还必须进行一些后续的清理操作,这些也都是资源管理上需要注意的。
Python 语言提供了 with
这个独特的语法,可让程序设计者更容易管理这些开启的资源,在这样的语法架构之下,Python 程序会自动进行资源的建立、清理与回收动作,让程序设计者在使用各种资源时更为方便。
下面举一个简单的使用文件资源的例子:
当我们需要使用一个文件资源,可能会这样写:
# 开启
f = open(filename)
...
# 关闭
f.close()
这种写法会有一个问题,如果在使用文件的过程中发生了一些意外状况,造成程序提早跳开时,这个开启的文件就没有被关闭。所以比较好的写法是使用 try
与 finally
:
# 开启
f = open(filename)
try:
...
finally:
# 关闭
f.close()
上面这种写法虽然不会有问题,但是缺点就是必须手动加入关闭文件的代码,不是很方便,也很容易忘记。
with
则可以解决上述问题,使用 with
开启文件时,会将开启的文件放在 f
变量中。 f
只有在 with
的范围内使用,离开范围时就会自动被关闭,回收相关的资源。
# 开启
with open(filename) as f:
f.write("hello mxnet!")
求解梯度代码
# coding=utf-8
# author: BebDong
# 2018.12.10
# 梯度相关计算
from mxnet import nd, autograd
print('------- A Simple Example -------')
# 求解函数:y=2(X^T)X关于列向量X的梯度
X = nd.arange(4).reshape(4, 1) # 创建列向量
X.attach_grad() # 调用attach_grad函数申请存储梯度的内存
with autograd.record():
y = 2 * nd.dot(X.T, X) # 定义函数y。record函数要求MXNet记录与求梯度相关的计算,默认条件不计算
y.backward() # backward函数用于自动求梯度。y若不是标量,MXNet将对y所有元素求和得到新变量,再求这个变量关于X的梯度
assert (X.grad - 4 * X).norm().asscalar() == 0 # 验证梯度是否为4X
print(X.grad) # 输出结果也是NDArray格式
print('\n------- Different Mode Detection -------')
# 默认autograd会将运行模式从预测模式转换为训练模式,可通过函数is_training查看
print(autograd.is_training())
with autograd.record():
print(autograd.is_training())
print('\n------- Gradient on Python Block -------')
# 即使函数的计算包含了python的控制流,使用MXNet也可能对变量求梯度
def f(a): # python控制流,示例包含while循环和if条件
b = a * 2
while b.norm().asscalar() < 1000:
b = b * 2
if b.sum().asscalar() > 0:
c = b
else:
c = 100 * b
return c
a = nd.random.normal(shape=1) # 创建一个元素的NDArray
a.attach_grad() # 调用attach_grad函数申请存储梯度的内存
with autograd.record(): # record函数记录梯度
c = f(a)
c.backward() # backward函数求解梯度
print(a.grad == c / a) # f(a)实际上是一个c关于a的线性函数,梯度为:c/a