利用PyTorch Autograd自动求导训练线性回归模型

利用PyTorch Autograd自动求导训练线性回归模型

About-说明

第一篇文章中pytorch基础入门 训练原始的线性模型,我们用手动计算微分的方式,
实现了基于Python语言的线性回归模型训练;这虽然非常适合理解相关概念,但是实现方式是非常原始的.

在查看PyTorch相关文档时,很容易就发现它把 Autograd(自动求导) 作为一个突出的优势,所以这篇文章旨在了解 Autograd的概念上,实现上一篇文章的线性回归模型.
归纳而言,这篇会相对简单,主要包含以下几点:

  • PyTorch Autograd 概念介绍
  • 利用 Autograd 替代手动求解导数
  • 实现 Autograd 方式的线性回归模型

PyTorch Autograd 概念介绍

简单介绍就是:
PyTorch中设置 requires_grad 为True, PyTorch就会为你记录表达式转换过程的求导链,
当你执行backward()时,原始参数的grad就包含了对应参数的导数;
对应的官网入门讲解地址

原理虽然简单,但其中有不少细节需要注意:

  • 首先需要这个函数链是可导的
  • 一个 backward 对应一个 grad计算值,所以重复 backward 以及中间环节执行 backward 都会导致 grad 值变化
  • 一个 grad 使用完之后,需要记得 设置为 0 ,再次 backward 才能得到正确的值

利用 Autograd 替代手动求解导数

# 设置 requires_grad=True ,告诉PyTorch需要记录params上所有的操作
params = torch.tensor([1.0, 0.0], requires_grad=True)

# 目前为止 params.grad is None

# 执行 backward 后, params.grad 就保存了微分值
loss = loss_fn(model(t_u, *params), t_c)
loss.backward()

# grad 使用完之后,需要记得 设置为 0
if params.grad is not None:
  params.grad.zero_()

实现 Autograd 方式的线性回归模型

替代之前loop循环

def training_loop(n_epochs, learning_rate, params, t_u, t_c):
  for epoch in range(1, n_epochs + 1):
    if params.grad is not None:
       params.grad.zero_()
    t_p = model(t_u, *params)
    loss = loss_fn(t_p, t_c)
    loss.backward()
    params = (params - learning_rate * params.grad).detach().requires_grad_()
    # print('Epoch %d, Loss %f' % (epoch, float(loss)))
    # print('Params', params)
    if epoch % 500 == 0:
      print('Epoch %d, Loss %f' % (epoch, float(loss)))
  return params

需要着重解释的是 detach().requires_grad_()
这里的作用就是使 params 脱离之前的函数链,PyTorch重新记录新的函数链

完整的代码如下:

"""
PyTorch 基础入门二: PyTorch 自动求导线性模型实现
线性回归参数估计
问题: 华氏温度转换
"""
import torch

# 定义输入数据
t_c = [0.5,  14.0, 15.0, 28.0, 11.0,  8.0,  3.0, -4.0,  6.0, 13.0, 21.0]
t_u = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]

t_c = torch.tensor(t_c)
t_u = torch.tensor(t_u)

# 对应的线性模型为
# t_c = w * t_u + b

def model(t_u, w, b):
    return w*t_u + b

# 定义损失函数
# t_p 为模型估计值
# t_c 为实验数据
def loss_fn(t_p, t_c):
    squared_diffs = (t_p - t_c)**2
    return squared_diffs.mean()
# 以上跟第一篇保持一致
#########################################################################

# 设置 requires_grad=True ,告诉PyTorch需要记录params上所有的操作
params = torch.tensor([1.0, 0.0], requires_grad=True)

# 目前为止 params.grad is None

# 执行 backward 后, params.grad 就保存了微分值
# loss = loss_fn(model(t_u, *params), t_c)
# loss.backward()

if params.grad is not None:
  params.grad.zero_()

def training_loop(n_epochs, learning_rate, params, t_u, t_c):
  for epoch in range(1, n_epochs + 1):
    if params.grad is not None:
       params.grad.zero_()
    t_p = model(t_u, *params)
    loss = loss_fn(t_p, t_c)
    loss.backward()
    # 
    params = (params - learning_rate * params.grad).detach().requires_grad_()
    # print('Epoch %d, Loss %f' % (epoch, float(loss)))
    # print('Params', params)
    if epoch % 500 == 0:
      print('Epoch %d, Loss %f' % (epoch, float(loss)))
  return params

# 特征缩放处理
t_un = 0.1 * t_u
params = training_loop(
    n_epochs = 5000,
    learning_rate = 1e-2,
    params = torch.tensor([1.0, 0.0], requires_grad=True),
    t_u = t_un,
    t_c = t_c)

# 画出图示
import matplotlib.pyplot as plt
t_p = model(t_un, *params)

fig = plt.figure()
plt.title(u"PyTorch linear model")
plt.xlabel("Fahrenheit")
plt.ylabel("Celsius")
plt.plot(t_u.numpy(), t_p.detach().numpy())
plt.plot(t_u.numpy(), t_c.numpy(), 'o')
plt.show()

结果展示

结果其实同第一篇文章一致:
20200511143712

### PyTorch 自动求导机制 #### 计算图构建与自动求导原理 在PyTorch中,`autograd`包提供了对张量上所有操作的自动求导支持[^1]。每当执行涉及可微分的操作时,这些操作会被动态地加入到计算图中,并且每个参与运算的Tensor都会更新其`grad_fn`属性来指向创建它的函数对象[^3]。 对于具体的实现方式,在定义模型参数时可以通过设置`requires_grad=True`使得该变量成为叶子节点并开启梯度追踪功能;当完成一次前向传播过程后,通过调用`.backward()`可以触发整个网络从输出端往回逐层计算各个权重对应的偏导数值即梯度信息[^4]。 需要注意的是,默认情况下只有标量可以直接调用`y.backward()`来进行反向传播以获取损失相对于各参数的变化率。如果是非标量,则需指定关于哪个维度求和作为最终的目标值用于传递给`backward()`方法内的`gradient`参数[^2]。 #### 使用示例 下面展示了一个简单的线性回归案例,其中包含了如何利用PyTorch内置工具完成数据准备、建立简单神经元连接关系以及训练循环内迭代优化的过程: ```python import torch # 定义输入特征x (batch_size=1, feature_num=5),目标标签y(batch_size=1,label_num=3) x = torch.tensor([1., 1., 1., 1., 1.]) y = torch.tensor([0., 0., 0.]) # 初始化权重w(5*3) 和 偏置项b(3), 同时声明它们需要跟踪梯度变化 w = torch.randn((5, 3), dtype=torch.float, requires_grad=True) b = torch.randn((3,), dtype=torch.float, requires_grad=True) # 执行前向传播得到预测结果z=x*w+b z = torch.matmul(x, w) + b # 应用二分类交叉熵损失函数评估当前状态下的误差程度 loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y) # 反向传播计算梯度 loss.backward() print(w.grad) # 输出权重w的梯度 print(b.grad) # 输出偏置b的梯度 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值