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():将不再计算张量梯度和跟踪张量历史记录,在评估模型,测试模型时经常用到
  • grad_fn属性
    通过运算创建的Tensor(非叶子节点)会自动赋予grad_fn属性,用来记录梯度函数,叶子节点的grad_fnNone

  • 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)
posted @ 2024-11-01 13:12  sgqmax  阅读(6)  评论(0编辑  收藏  举报