淘先锋技术网

首页 1 2 3 4 5 6 7

Tenser与Autograd

对Tenser求导需要注意以下事项

  1. 创建叶子节点(Leaf Node)的Tenser,使用requires_gard参数指定是否记录对其的操作,以便之后利用backward()方法进行梯度求解。
  2. 利用requires_gard_()方法修改Tenser的requires_gard属性。调用with torch.no_gard_():,将不在计算张量的梯度,跟踪张量的历史记录。
  3. 通过运算创建的(非叶子节点),会自动赋予grad_fn属性,该属性表示梯度函数。
  4. 最后得到的tenser执行backward()函数,此时自动的计算各变量的梯度,并将累加结果保存到gard属性中,计算完成后,非叶子节点的梯度自动释放。
  5. backword()函数接收参数,该参数应和调用backward()函数的Tenser的维度相同。
  6. 反向传播的中间缓存可能会被清空,如果需要多次反向传播,需要指定backward()中的参数retain_graph=True。多次反向传播时梯度是累加的。
  7. 非叶子节点的梯度backward调用后即被清空
  8. 可以通过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()传入参数。

  1. 定义叶子节点及算子节点
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, 
  1. 查看叶子节点。非叶子节点其他属性
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))
  1. 自动求导实现梯度方向的反向传播
# 自动求导实现反向传播
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)