使用反向传播法训练神经网络时,模型的参数依据损失函数与对应参数的梯度来调整,即:
model.parameters = model.parameters + learning_rate * gradients
自动微分是机器学习工具包必备的工具,它可以自动计算整个计算图的微分。
PyTorch内建了一个叫做torch.autograd的自动微分引擎,该引擎支持的数据类型为:浮点数Tensor类型 ( half, float, double and bfloat16) 和复数Tensor 类型(cfloat, cdouble)
PyTorch中与自动微分相关的常用的Tensor属性和函数:
- 属性requires_grad:默认值为False,表明该Tensor不会被自动微分引擎计算微分。设置为True,表明让自动微分引擎计算该Tensor的微分
- 属性grad:存储自动微分的计算结果,即调用backward()方法后的计算结果
- 方法backward: 计算微分
范例:
import torch
# 模型:z = x@w + b;激活函数:Softmax
x = torch.ones(5) # 输入张量,shape=(5,)
labels = torch.zeros(3) # 标签值,shape=(3,)
w = torch.randn(5,3,requires_grad=True) # 模型参数,需要计算微分, shape=(5,3)
b = torch.randn(3, requires_grad=True) # 模型参数,需要计算微分, shape=(3,)
z = x@w + b # 模型前向计算
outputs = torch.nn.functional.softmax(z) # 激活函数
print(z)
print(outputs)
loss = torch.nn.functional.binary_cross_entropy(outputs, labels)
# 查看loss函数的微分计算函数
print('Gradient function for loss =', loss.grad_fn)
# 调用loss函数的backward()方法计算模型参数的微分
loss.backward()
# 查看模型参数的微分值
print(w.grad)
print(b.grad)
tensor([ 2.6084, -0.4021, -0.7369], grad_fn=
)
tensor([0.9221, 0.0454, 0.0325], grad_fn=)
Gradient function for loss =
tensor([[ 0.2824, -0.1645, -0.1179],
[ 0.2824, -0.1645, -0.1179],
[ 0.2824, -0.1645, -0.1179],
[ 0.2824, -0.1645, -0.1179],
[ 0.2824, -0.1645, -0.1179]])
tensor([ 0.2824, -0.1645, -0.1179])
-
上下文管理禁用自动微分:no_grad(), 一般用于模型评估或推理计算这些不需要执行自动微分计算的地方,以减少内存和算力的消耗。另外禁止在模型参数上自动计算微分,即不允许更新该参数,即所谓的冻结参数(frozen parameters)。
范例:
- PyTorch的微分是自动积累的,需要用zero_grad()方法手动清零
- backward()方法,一般不带参数,等效于:backward(torch.tensor(1.0))。若backward()方法在DAG的root上调用,它会依据链式法则自动计算DAG所有枝叶上的微分。
TensorFlow通过tf.GradientTape
API来自动追踪和计算微分,GradientTape,翻译为微分带,Tape有点儿历史上磁带机的味道,即在Tape上记录下所有的计算和计算结果。
tf.GradientTape在tf.Variable而非tf.Tensor上计算,因为在TensorFlow中,tf.Tensor为不可变对象,tf.Variable为可变对象;通常用tf.Variable来存储模型参数。
tf.Variable有一个trainable属性,该属性tf.Tensor没有,类似PyTorch Tensor的requires_grad, 即告知自动微分引擎是否追踪该tf.Variable,并自动计算该tf.Variable的微分。
范例:
import tensorflow as tf
# 模型:z = x@w + b;激活函数:Softmax
x = tf.ones([1,5]) # 输入张量,shape=(5,)
labels = tf.zeros(3) # 标签值,shape=(3,)
# 创建模型参数
w = tf.Variable(tf.random.normal([5,3]), trainable=True) # 模型参数,需要计算微分, shape=(5,3)
b = tf.Variable(tf.random.normal([3]), trainable=True) # 模型参数,需要计算微分, shape=(3,)
# 追踪需要自动计算微分的操作
with tf.GradientTape() as tape:
z = x@w + b # 模型前向计算
outputs = tf.nn.softmax(z) # 激活函数
loss = tf.keras.metrics.binary_crossentropy(labels, outputs, from_logits=False)
print(z)
print(outputs)
# 调用tape.gradient()方法计算模型参数的微分
[dl_dw, dl_db] = tape.gradient(loss, [w,b])
# 查看模型参数的微分值和形状
print(dl_dw, dl_dw.shape)
print(dl_db, dl_db.shape)
tf.Tensor([[5.82673 5.987774 3.7632523]], shape=(1, 3), dtype=float32)
tf.Tensor([[0.4344524 0.5103672 0.05518046]], shape=(1, 3), dtype=float32)
tf.Tensor(
[[-0.01459036 0.02949907 -0.01490874]
[-0.01459036 0.02949907 -0.01490874]
[-0.01459036 0.02949907 -0.01490874]
[-0.01459036 0.02949907 -0.01490874]
[-0.01459036 0.02949907 -0.01490874]], shape=(5, 3), dtype=float32) (5, 3)
tf.Tensor([-0.01459036 0.02949907 -0.01490874], shape=(3,), dtype=float32) (3,)
从上述可以看到,TensorFlow的自动微分实现方式与PyTorch大不相同,而且没有把参数和参数的微信封装成一个对象,这点非常不User-Friendly,或者说封装的不好!
为了方便实现模型,模型的参数,与模型参数的微分,TensorFlow又提供了另外一套机制:模型的微分(Gradients with respect to a model), 意思是:TensorFlow开发团队也知道了用tf.Variable实现模型参数,然后用tape.gradient()方法计算微分,tf.Variable和它对应的微分是分离的,是没有封装好的,这种方式对开发者不友好,所以,TensorFlow开发者团队对于构建模型的基础类: tf.Module
或者它的子类 (layers.Layer
, keras.Model
),提供了一个 Module.trainable_variables
的属性,该属性把模型参数都封装好了,使用起来比较方便。不过对应微分还是没封装,坚持自己的个性...对于我们开发者,还是选择遵循...
范例:
import tensorflow as tf
# 模型:z = x@w + b;激活函数:Softmax
x = tf.ones([1,5]) # 输入张量,shape=(1,5)
labels = tf.zeros([1,3]) # 标签值,shape=(1,3)
layer = tf.keras.layers.Dense(3, activation='relu')
with tf.GradientTape() as tape:
# 前向计算
logits = layer(x)
# 计算损失
loss = tf.keras.metrics.binary_crossentropy(labels, logits, from_logits=True)
# 计算梯度
grad = tape.gradient(loss, layer.trainable_variables)
for var, g in zip(layer.trainable_variables, grad):
print(f"{var.name}, shape:{g.shape}")
dense/kernel:0, shape:(5, 3)
dense/bias:0, shape:(3,)
参考资料:
- 《Introduction to gradients and automatic differentiation》
- 《AUTOMATIC DIFFERENTIATION WITH TORCH.AUTOGRAD》