autograde
autograd自动求导
torch.Tensor与torch.Function为autograd的核心类,其相互连接生成DAG,PyTorch采用动态计算图,每次前向传播时会重新构建计算图
Tensor部分属性说明:
-
requires_grad属性
- 新建Tensor时,用
requires_grad
参数指定是否记录对叶子节点的操作,便于backward()
求解梯度
requires_grad
缺省值为False
,当设置为True
时,则与之有依赖的节点会自动变为True
可用require_grad_()
方法修改Tensor的requires_grad
属性值 - 调用
.detach()
或with torch.no_grad():
将不再计算张量梯度和跟踪张量历史记录,在评估模型,测试模型时经常用到
- 新建Tensor时,用
-
grad_fn属性
通过运算创建的Tensor(非叶子节点)会自动赋予grad_fn
属性,用来记录梯度函数,叶子节点的grad_fn
为None
-
grad属性
中间缓存,存储变量的梯度值,backward()
结束后,非叶子节点梯度自动释放 -
backward()函数
- 接收的参数应与调用
backward()
函数的Tensor变量同维度,或可broadcast,若求导的是标量则参数可省略 - 利用
loss.backward()
进行张量梯度的求解,此时计算各变量梯度,并将累加结果保存到grad
属性
计算完成后非叶子节点的梯度自动释放,若要进行多次反向传播,梯度累加,需指定retain_graph=True
- 接收的参数应与调用
标量反向传播
若x,w,b都为标量,z=wx+b 对标量z调用backward()
,则无需给backward()传入参数
import torch
# 定义输入张量
x = torch.Tensor([2])
# 初始化权重参数 w 和 b 设置requires_grad为 True 自动求导
w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
# 前向传播
y = torch.mul(w, x)
z = torch.add(y, b)
"""------------------------------- 叶子节点的requires_grad属性值 ----------------------"""
# 查看w b x y 叶子节点的 requires_grad属性值
print("x,w,b的requires_grad属性值:{}, {}, {}".format(x.requires_grad, w.requires_grad, b.requires_grad))
# y,z 与 w,b 有依赖关系,故其 requires_grad 属性为 True
"""------------------------------- 非叶子节点的requires_grad 属性 ----------------------"""
print("y,z的requires_grad属性:{},{}".format(y.requires_grad, z.requires_grad))
# 是否为叶子节点
print("x,w,b,y,z是否为叶子节点:{},{},{},{},{}".format(x.is_leaf, w.is_leaf, b.is_leaf, y.is_leaf, z.is_leaf))
"""------------------------------- 叶子节点的 grad_fn 属性 -----------------------------"""
# x,w,b的grad_fn属性:None,None,None
print("x,w,b的grad_fn属性:{},{},{}".format(x.grad_fn, w.grad_fn, b.grad_fn))
"""------------------------------- 非叶子节点的 grad_fn 属性 ----------------------------"""
# y,z的grad_fn属性:<MulBackward0 object at 0x7f05702d6c50>,<AddBackward0 object at 0x7f05701a30f0>
print("y,z的grad_fn属性:{},{}".format(y.grad_fn, z.grad_fn))
# 反向传播,自动求导
# 执行 backward 后计算图自动清空
z.backward()
# 若要多次使用反向传播,需将参数 retain_graph 设置为 True 此时梯度累加
# z.backward(retain_graph=True)
"""------------------------------- 查看叶子节点的梯度grad -------------------------------"""
# w,b,x梯度分别为:tensor([2.]),tensor([1.]),None
print("w,b,x梯度分别为:{},{},{}".format(w.grad, b.grad, x.grad))
"""---------------------- 非叶子节点的梯度grad 执行 backward 后,会自动清空 --------------"""
# y,z梯度分别为:None,None
print("y,z梯度分别为:{},{}".format(y.grad, z.grad))
非标量反向传播
经常使用的损失值loss一般都是标量,也有非标量的情况,如Deep Dream的目标值就是一个含有多个元素的张量,pytorch不让张量对张量进行求导,只允许标量对张量进行求导
若目标张量对一个非标量调用backward()
则需要传入一个gradient参数,该参数也是一个张量,且需要与调用backward()
的张量形状相同,这个参数就是为了把张量对张量的求导转换成标量对张量求导
假设目标值loss=(y1,y2,...,ym),传入参数为v=(v1,v2,...,vm),那么就可以把对loss的求导转换成对loss*vT
标量的求导,再将dloss/dx得到的雅可比矩阵Jacobian乘以张量vT便可得到梯度矩阵
import torch
# 定义叶子节点张量x 形状为1x2
x= torch.tensor([[2,3]]),dtype=torch.float,requires_grad=True)
# 初始化Jacobian矩阵
J= torch.zeros(2,2)
#初始化目标张量
y= torch.zeros(1,2)
#定义x与y的映射关系
#y1=x1**2+3*x2
#y2=x2**2+2*x1
y[0,0]= x[0,0]**2+3*x[0,1]
y[0,1]= x[0,1]**2+2*x[0,0]
"""
y.backward(torch.Tensor([[1,1]]))
print(x.grad)
# print: tensor([[6.,9.]])
# 结果有误
"""
# 因这里重复使用backward(),需使用参数retain_graph=True
y.backward(torch.Tensor([[1,0]]),retain_graph=True)
J[0]=x.grad
# 梯度累加,需要对x的梯度清零
x.grad=torch.zeros_like(x.grad)
# 生成y2对x的梯度
y.backward(torch.Tensor([[0,1]]))
J[1]=x.grad
# 打印Jacobian矩阵
print(J)
使用Tensor与autograd实现机器学习
import torch
import matplotlib.pyplot as plt
# %matplotlib inline
"""--------------生成数据,可视化分布----------------"""
torch.manual_seed(100)
dtype= torch.float
# 生成 x坐标, x为 tensor,并将 x形状转换为100*1
# torch.linspace(-1,1,100) # torch.Size([100])
x= torch.unsqueeze(torch.linspace(-1,1,100), dim=1) # torch.Size([100,1])
# 生成 y坐标, y为 tensor,形状为100*1, 并加上噪声
y= 3* x.pow(2)+ 2+ 0.3*torch.rand(x.size())
# 画图,将 Tensor转换成 ndarray数据
# plt.scatter(x.numpy(), y.numpy())
# plt.show()
"""-------------初始化权重参数----------------------"""
# 随机初始化参数 w,b 需要学习其值,将其 requires_grad属性设置为 True
w= torch.randn(1,1, dtype=dtype, requires_grad=True)
b= torch.randn(1,1, dtype=dtype, requires_grad=True)
"""
plt.scatter(x.numpy(), y.numpy(), color="blue", marker='o', label="true") # true data
plt.plot(x.numpy(), (x.pow(2).mm(w)+b).detach().numpy(), 'r-', label="predict") # predict
plt.show()
"""
"""--------------训练模型----------------------"""
lr= 0.001
y_pred= torch.Tensor()
epoches= 800
for ii in range(epoches):
# 前向传播
y_pred= x.pow(2).mm(w)+ b
loss= 0.5* (y_pred-y)** 2
loss= loss.sum()
# 自动计算梯度,存放在 grad属性中
loss.backward()
# 手动更新参数,用 torch.no_grad(),使上下文切断自动求导计算
with torch.no_grad():
w-= lr* w.grad
b-= lr* b.grad
# 梯度清零
w.grad.zero_()
b.grad.zero_()
"""--------------可视化训练结果----------------"""
# detach()
plt.plot(x.numpy(), y_pred.detach().numpy(), 'r-', label="predict") # predict
plt.scatter(x.numpy(), y.numpy(), color="blue", marker='o', label="true") # true data
plt.xlim(-1, 1)
plt.ylim(2, 6)
plt.legend()
plt.show()
print(w, b)