Tenser与Autograd
对Tenser求导需要注意以下事项
- 创建叶子节点(Leaf Node)的Tenser,使用requires_gard参数指定是否记录对其的操作,以便之后利用backward()方法进行梯度求解。
- 利用requires_gard_()方法修改Tenser的requires_gard属性。调用with torch.no_gard_():,将不在计算张量的梯度,跟踪张量的历史记录。
- 通过运算创建的(非叶子节点),会自动赋予grad_fn属性,该属性表示梯度函数。
- 最后得到的tenser执行backward()函数,此时自动的计算各变量的梯度,并将累加结果保存到gard属性中,计算完成后,非叶子节点的梯度自动释放。
- backword()函数接收参数,该参数应和调用backward()函数的Tenser的维度相同。
- 反向传播的中间缓存可能会被清空,如果需要多次反向传播,需要指定backward()中的参数retain_graph=True。多次反向传播时梯度是累加的。
- 非叶子节点的梯度backward调用后即被清空
- 可以通过torch.no_grad()包裹代码的形式来阻止autograd去跟踪那些标为为requergrad=True的张量的历史记录。(测试阶段常用)
计算图
计算图是一种有向无环图像,用图像的方法来表示算子与变量之间的关系。
如下图所示:圆形表示变量,矩阵表示算子。如表达式z=wx+b,可以写成两个表达式 y=wx z=y+b其中x,w,b为变量,是用户创建的变量,不依赖其他变量,故又称为叶子节点。
PyTorch调用backward()方法将自动的计算个节点的梯度,这是一个反向传播过程。在反向传播过程中,autograd沿着图2-10.从当前根节点z反向溯源,利用导数链式法则,计算所有叶子节点的梯度,其梯度值将累加到grad属性中。对非叶子节点的计算操作(或Function)记录在grad_fn属性中,叶子节点的grad_fn值为None.
标量反向传播
假设x,w,b都是标量,z=wx+b,对标量z调用backward()方法,我们无需对backward()传入参数。
- 定义叶子节点及算子节点
import torch
x = torch.Tensor([2])
print(x)
w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
y = torch.mul(w, x)
z = torch.add(y, b)
# 查看叶子节点的requite_gard
print("x,w,b的requires_grad:{},{},{}".format(x.requires_grad,
- 查看叶子节点。非叶子节点其他属性
w.requires_grad, b.requires_grad))
# 查看非叶子节点的requires_grad
print("y,z的requires_grad:{},{}".format(y.requires_grad, z.requires_grad))
# 查看叶子节点的grad_fn 判断是否为叶子节点x.is_leaf
print("x,w,b的requires_grad:{},{},{}".format(x.grad_fn, w.grad_fn, b.grad_fn))
- 自动求导实现梯度方向的反向传播
# 自动求导实现反向传播
z.backward()
# 如果多次使用backward需要改动参数为retrain_graph为True
# z.backward(retain_graph=True)
print("w,b的梯度为:{},{}".format(w.grad, b.grad))
控制台输出
F:\Anaconda3\python.exe
tensor([2.])
x,w,b的requires_grad:False,True,True
y,z的requires_grad:True,True
x,w,b的requires_grad:None,None,None
w,b的梯度为:tensor([2.]),tensor([1.])
非标量反向传播
定义叶子节点和计算节点
import torch
x = torch.tensor([[2, 3]], dtype=torch.float, requires_grad=True)
j = torch.zeros(2, 2)
y = torch.zeros(1, 2)
# 定义y与x间的映射 y = x1**2+3*x2 y = 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]
首先让v=(1,0)得到y1对x的梯度,然后使得v=(0,1),得到y2对x的梯度。这个需要重复 使用backward(),需要对retain_graph=True
y.backward(torch.Tensor([[1, 0]]), retain_graph=True)
j[0] = x.grad
x.grad = torch.zeros_like(x.grad)
y.backward(torch.tensor([[0, 1]]))
j[1] = x.grad
print(j)
使用Tenser和Antograd实现机器学习
生成训练数据
import torch
from matplotlib import pyplot as plt
torch.manual_seed(100)
dtype = torch.float
# 生成x坐标数据,x为tensor,需要把x形状转化为100*1
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
# 生成y坐标数据,y为tensor,形状为100*1,加上一些噪声
y = 3*x.pow(2)+2+0.2*torch.rand(x.size())
# 把x,y抓为为numpy
plt.scatter(x.numpy(), y.numpy())
plt.show()
初始化权重
# 初始化权重
w = torch.randn(1, 1, dtype=dtype, requires_grad=True)
b = torch.randn(1, 1, dtype=dtype, requires_grad=True)
训练模型
# 训练
lr = 0.01 # 学习率
for i in range(800):
# 前向传播
y_pred= x.pow(2).mm(w)+b
loss = 1/2 * (y_pred - y)**2
loss = loss.sum()
# 自动计算梯度
loss.backward()
# 手动更新参数
with torch.no_grad():
w -= lr*w.grad
b -= lr*b.grad
# 梯度清零
w.grad.zero_()
b.grad.zero_()
运行结果
print(w, b)