带你一文透彻学习【PyTorch深度学习实践】分篇——线性回归(训练周期:前馈、反馈、权重更新)
创始人
2025-05-31 16:06:07
0

 
“梦想使你迷醉,距离就成了快乐;追求使你充实,失败和成功都是伴奏;当生命以美的形式证明其价值的时候,幸福是享受,痛苦也是享受。”

--------史铁生《好运设计》
 

🎯作者主页追光者♂🔥

        

🌸个人简介计算机专业硕士研究生💖、2022年CSDN博客之星人工智能领域TOP4🌟、阿里云社区特邀专家博主🏅、CSDN-人工智能领域新星创作者🏆、预期2023年10月份 · 准CSDN博客专家📝  
无限进步,一起追光!

        

🍎欢迎大家 点赞👍  收藏⭐   留言📝

        
在阅读本篇前,建议阅读:

  • 带你一文透彻学习【PyTorch深度学习实践】分篇——线性模型 & 梯度下降
  • 带你一文透彻学习【PyTorch深度学习实践】分篇——反向传播(前馈计算,反向传播)

🌿鉴于PyTorch深度学习实践系列文章,篇幅较长,有粉丝朋友反馈说不便阅读。因此这里将会分篇发布,以便于大家阅读。本次发布的是 “基础 模型&算法 回顾”章节中的

目录

    • 💧1.4 线性回归——使用PyTorch实现 y^=ωx+b\widehat y = \omega x +by​=ωx+b
      • 🥝1.4.1 Pytorch深度学习的一般流程
      • 🥝1.4.2 线性单元----Linear Unit
      • 🥝1.4.3 练习(训练周期:前馈、反馈、权重更新)
      • 🥝1.4.4 练习2——测试一下其它优化器效果如何
    • 💧二、其它:仅十字路口路况模拟
      • 🥝2.1 先来看大致效果
      • 🥝2.2 具体源代码
      • 🥝2.3 附:曲线路况文件
      • 🥝2.4 附:红灯判断函数

💧1.4 线性回归——使用PyTorch实现 y^=ωx+b\widehat y = \omega x +by​=ωx+b

🥝1.4.1 Pytorch深度学习的一般流程

  • 前馈(算损失)、反馈(算梯度)、更新(使用 (随机) 梯度下降算法 更新权重)!

  • 前馈计算损失,反馈计算梯度!

用PyTorch进行深度学习模型构建的一般过程:

  • [1] 准备数据集(Prepare dataset)
  • [2] 设计用于计算最终结果的模型(Design model)
  • [3] 构造损失函数优化器(Construct loss and optimizer)
  • [4] 设计循环周期(Training cycle)——前馈、反馈、更新

在pytorch中,若使用mini-batch的 (小批量随机梯度下降) 风格,一次性求出一个批量的y^\widehat yy​,则需要xxx以及y^\widehat yy​作为矩阵参与计算【注:只要知道了 输入xxx 和 输出y^\widehat yy​ 各自的维度,那么www和bbb的维度就能推出来】,此时利用 numpy的广播机制,可以将原标量参数ω\omegaω 扩写为 同维度的矩阵[w][w][w],参与运算而不改变其Tensor的性质。

广播机制/扩充维度,以便可以实现矩阵相加~
在这里插入图片描述
www和bbb 都会 自动扩充!例如 下所示:
在这里插入图片描述

🥝1.4.2 线性单元----Linear Unit

在下述 线性模型的计算图中,红框区域为线性单元,其中的ω\omegaω以及bbb是需要反复训练确定的,在设计时,需要事先设计出二者的维度。

(而由于公式y^=ωx+b\widehat y = \omega x +by​=ωx+b,因此,只要确定了y^\widehat yy​以及xxx的维度,就可以确定上述两个量的维度大小)

例如:已知xxx 和 zzz 或(yyy)的维度,即可求www和bbb的维度.
在这里插入图片描述

线性模型 “计算图”:

在这里插入图片描述
由上述理论,由于前边的计算过程都是针对矩阵的,因此最后的losslossloss也是矩阵,但由于 要进行 反向传播 调整参数,因此losslossloss应当是个标量,因此要对矩阵[loss][loss][loss]内的每个量求和 并求 均值(MSE)。

loss=1NΣ[loss1⋮lossn]loss = \frac{1}{N}\Sigma \begin{bmatrix} {loss_1}\\ {\vdots}\\ {loss_n}\\ \end{bmatrix}loss=N1​Σ​loss1​⋮lossn​​

🥝1.4.3 练习(训练周期:前馈、反馈、权重更新)

使用PyTorch实现线性回归:

  1. 准备数据集

  2. 用类封装设计一个模型。目的是 为了前向传播forward,即计算y^\widehat yy​ (预测值)

  3. 使用PyTorch的API 来定义 loss 和 optimizer。其中,计算loss是为了计算损失 从而反向传播 求解梯度,optimizer是为了根据得到的梯度 更新权重。

  4. 训练过程 :forward+backward+update (前馈 反馈 更新)

首先是几张图,便于理解:
在这里插入图片描述
无论哪种,目的都是把维度拼出来:
在这里插入图片描述
torch.nn.Linear 中的 部分参数 说明:
在这里插入图片描述
此外,为了便于理解下图中 forward()的参数,这里我已经做了说明。可回顾再往下的 Python基础语法知识~

注意:Python中,一个类实例要变成一个可调用对象,只需要实现一个特殊方法__ call __(),Module实现了函数 __ call __(),call()里面有一条语句是要调用forward()。因此新写的类中需要重写forward()覆盖掉父类中的forward()。

call()函数的作用是可以直接在对象后面加(),例如实例化的model对象 model = LinearModel(),和实例化的linear对象 self.linear = torch.nn.Linear(1, 1),y_pred = self.linear(x)。(这在下面的最终代码中 都将会看到)

self.linear(x)也由于函数call的实现 将会调用torch.nn.Linear类中的forward,至此完成封装,也就是说forward最终是在torch.nn.Linear类中实现的,具体怎么实现,可以不用关心,大概就是完成y= wx + b操作。

其实,也可以从源码中可见一斑:
在这里插入图片描述

在这里插入图片描述

Python基础语法知识回顾(函数中 多个参数的传递,*args,**kwargs):当调用函数时,可以传入 数目 正好的值:

def func(a, b, c, x, y):passfunc(1, 2, 3, x=6, y=7)

也可以传入 多个数量的值,这样即可实现:

def func(*args, x, y):  # 定义 *args,那么用户就可以传入多个参数print(args)func(1, 2, 3, 9, x=6, y=7)  # 用户传入 多个参数 1 2 3 9,然后再是x、y的匹配

输出:元组的形式,(进而可以取元素进行遍历)
在这里插入图片描述
上述内容中,x=6,y=7 也可 在函数中其他 形式来表示:

def func(*args, **kwargs):  # 定义 *args,那么用户就可以传入多个参数,以元组的形式print(args)print(kwargs)  # 两个star,那么 就会把参数变成 字典的形式func(1, 2, 3, 9, x=6, y=7)  # 用户传入 多个参数 1 2 3 9,然后再是x、y的匹配

输出:
在这里插入图片描述
以上操作,是函数里面,参数传递 常用到方式~

那么,

class Foobar:def __init__(self):pass# 让对象可调用!!def __call__(self, *args, **kwargs):print("Hello" + str(args[0]))foobar = Foobar()
foobar(1, 2, 3)

在这里插入图片描述
构造 损失函数和优化器: 我想,我已经写的很细了… :

在这里插入图片描述

老师讲到,backward之前,要把梯度清零,这是框架里面的需求,老师特意强调的(在之前 自己 手动实现的 梯度下降---->反向传播 目录 1.3内的code,是 在 反向传播之后 才把梯度清零的…)en…总之,这里记得 在框架里面的话,backward 和 梯度下降 更新权重之前,先梯度清零!

在这里插入图片描述

基础上面所有这些分析,本次关于 【线性回归——PyTorch实现】的练习code如下,注释我写的也比较细:

# 昵 称:XieXu
# 时 间: 2023/2/15/0015 19:10
# 2023.2.16 08:53
import torch# 1、准备数据集
# 数据作为矩阵参与Tensor计算
# x,y都是矩阵,(3行1列),即:共三个数据,每个数据 只有一个特征
x_data = torch.Tensor([[1.0], [2.0], [3.0]])  # 注意 x 和 y的值 都必须是“矩阵”
y_data = torch.Tensor([[2.0], [4.0], [6.0]])# 2、使用类来 设计模型
''' 2023.2.16 08:41
我们的模型类应该继承于 nn.Module,它是所有神经网络模块的基类。
必须实现成员方法 __init__() 和 forward()
nn.linear类包含两个成员Tensors:weight和bias。
nn.linear类已经实现了神奇的方法__call__(),这使得该类的实例可以像函数一样被调用。
通常情况下,forward()将被调用。
'''
# 将我们的模型 定义为 类!! 2023.2.15 20:18
# LinearModel类 继承于 Module类 (Module这个父类 中有许多方法,是未来 模型训练过程中 要用到的!!)
class LinearModel(torch.nn.Module):# 定义“构造函数”# 即初始化 对象的时候 默认调用的函数def __init__(self):# 调用父类的init。【这就像是模板一样,写上就行!必须写!】super(LinearModel, self).__init__()  # 调用父类的initial# Linear 是一个 类,torch.nn.Linear() 加括号 就是在“构建对象”! 2023.2.15 20:28# Linear()这个 对象 包含 权重 weight(w)以及 偏置bias(b) 两个Tensor。。因此可以直接用linear 来完成 权重乘以输入 加上 偏置 的计算# (1,1)是指输入x和输出y的特征维度,这里数据集中的x和y的特征 都是1维的 2023.2.16 08:44# 该线性程 需要学习的参数 是w和b,获取w和b的方式分别是 linear.weight 和 linear.biasself.linear = torch.nn.Linear(1, 1)# 注:Linear 也是继承 自Module的,所以可以自动地进行 反向传播 ~ 2023.2.15 20:31# self.linear 是个对象,这个对象的类型 是torch下 nn这个模块 中的 Linear这个类 ~ 2023.2.15 20:33# nn为缩写,即 Neural Network 神经网络。(神经网络中的一个组件:Linear 它能够完成 权重和x相乘 算出中间值如Z Z再加上偏移量B 作为输出)# 前馈函数forward,对父类函数 overwrite(重写、覆盖)2023.2.16 08:46# 就得叫做 forward,一定要叫 这个名!!这是必须要定义的 2023.2.15 20:19# 前馈的过程中 所要实现的计算~def forward(self, x):# 调用linear中的call(),以利用父类forward()计算wx+by_pred = self.linear(x)return y_pred# 之所以 这里没有 反馈函数backward,是因为 由Module构建的对象 会自动根据“计算图”生成model = LinearModel()  # 实例化模型# 3、定义loss和优化器
# 构造的criterion对象所接受的参数为(y',y)
# criterion = torch.nn.MSELoss(size_average=False)  # UserWarning: size_average and reduce args will be deprecated, please use reduction='sum' instead. warnings.warn(warning.format(ret))
criterion = torch.nn.MSELoss(reduction='sum')  # 由上面警告,改为这样 即可~ 2023.2.15 23:13# model.parameters()用于检查模型中所能进行优化的张量。即 model.parameters() 自动完成参数的初始化操作
# learningrate(lr)表学习率,可以统一 也可以不统一
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)# 4、训练周期:前馈、反馈、更新
for epoch in range(1000):# 前馈计算y_predy_pred = model(x_data)# 前馈计算损失lossloss = criterion(y_pred, y_data)# 打印调用loss时,会自动调用内部__str__()函数,避免产生计算图# print(epoch, loss)print(epoch, loss.item())  # 还是得以item来访问,输出loss的 取值 2023.2.16 09:01# 梯度清零optimizer.zero_grad()  # 也有朋友把这称作“初始化梯度”...# 梯度反向传播,计算图清除loss.backward()  # 自动反向传播,计算梯度值# 根据传播的梯度以及学习率更新参数optimizer.step()  # 更新参数w和b 2023.2.16 08:51# Output  输出最终的w和b
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())# TestModel 测试部分
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)print('y_pred = ', y_test.data)

输出:

在这里插入图片描述
直接输出y_pred的值:

print('y_pred = ', y_test.item())

在这里插入图片描述

附:在翻看评论时发现的,便于理解吧~ (2023.2.17 23:24)

在这里插入图片描述
2023.2.17 23:34.
在这里插入图片描述

🥝1.4.4 练习2——测试一下其它优化器效果如何

另外,可尝试 不同的优化器 在如上 的线性模型中的效果:
在这里插入图片描述
所用的code依然是基于上面小目录1.4.3,只是稍微改动一下,顺便加个可视化。(这里由于在调用LBFGS优化器 有些问题,因此这里就不测试了…故LBFGS相关注释可不用打开!!)

# 昵 称:XieXu
# 时 间: 2023/2/16/0016 9:57# 验证不同优化器的效果
import torch
import matplotlib.pyplot as plt
import matplotlibmatplotlib.rc("font", family='Microsoft YaHei')  # 坐标系中汉字说明x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])epoch_list = []
Loss_list = []class LinearModel(torch.nn.Module):def __init__(self):  # 构造函数super(LinearModel, self).__init__()self.linear = torch.nn.Linear(1, 1)  # 构造对象,并说明输入输出的维数,第三个参数默认为true,表示用到bdef forward(self, x):y_pred = self.linear(x)  # 可调用对象,计算y=wx+breturn y_predmodel = LinearModel()  # 实例化模型criterion = torch.nn.MSELoss(reduction='sum')
# model.parameters()会扫描module中的所有成员,如果成员中有相应权重,那么都会将结果加到要训练的参数集合上optimizer = torch.optim.SGD(model.parameters(), lr=0.01)  # lr为学习率
# optimizer = torch.optim.Adagrad(model.parameters(), lr=0.01)
# optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# optimizer = torch.optim.Adamax(model.parameters(), lr=0.01)
# optimizer = torch.optim.ASGD(model.parameters(), lr=0.01)
# optimizer = torch.optim.LBFGS(model.parameters(), lr=0.01)  # TypeError: step() missing 1 required positional argument: 'closure'
# optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01)
# optimizer = torch.optim.Rprop(model.parameters(), lr=0.01)# def closure():    # 这里写的有问题,所以 暂时不测试 LBFGS优化器了!!
#     y_pred = model(x_data)
#     loss = criterion(y_pred, y_data)
#     loss.backward()
#     return loss
# optimizer.step(closure=closure)for epoch in range(1000):y_pred = model(x_data)loss = criterion(y_pred, y_data)print(epoch, loss.item())optimizer.zero_grad()loss.backward()optimizer.step()  # 使用LBFGS优化器时会报错:TypeError: step() missing 1 required positional argument: 'closure'# optimizer.step(closure=closure)  # LBFGS优化器时报错,暂时不测试该优化器了...# optimizer.zero_grad()  # 梯度清零放到这里也行。。2023.2.16 16:08epoch_list.append(epoch)Loss_list.append(loss.item())print('w=', model.linear.weight.item())
print('b=', model.linear.bias.item())x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
# print('y_pred = ', y_test.item())plt.plot(epoch_list,Loss_list)plt.xlabel("Epoch")
plt.ylabel("Loss")plt.title("所用优化器:SGD")
# plt.title("所用优化器:Adagrad")
# plt.title("所用优化器:Adam")
# plt.title("所用优化器:Adamax")
# plt.title("所用优化器:ASGD")
# plt.title("所用优化器:LBFGS")  # TypeError: step() missing 1 required positional argument: 'closure'...LBFGS优化器时报错,暂时不测试该优化器了..
# plt.title("所用优化器:RMSprop")
# plt.title("所用优化器:Rprop")plt.show()

为了便于查看,我将轮数设置为100轮,上面的1000后面可自行修改~

此外,为了关于观察,我将所有图形都分别截取,然后放到如下一张图里面了。大家在实验时可以 自行打开注释,分别测试几个优化器即可~

可以看出,Adagrad优化器的损失是比较大的,其它几个优化器也各有千秋,有损失收敛比较快的,也由曲线形式收敛的~本例中,老师最初选取的是SGD–小批量随机梯度下降 优化器。

在这里插入图片描述
(训练1000轮,效果是比较好的,近乎准确.)

附:torch.nn.Linear的pytorch官方文档,可以看看官方的描述,进去后 甚至可以按照torch的版本 来选文档来看。

💧二、其它:仅十字路口路况模拟

关于这部分内容,可参阅:2023年春《移动计算技术》:基于Python的【道路交通流仿真】示例:直行道路/环形路口/十字交叉路口(含红绿灯) 交通流仿真。此外,在近期的其它部分Blog中也部分涉及到该仿真模拟,具体请参阅其它Blog。

🥝2.1 先来看大致效果

在这里插入图片描述

🥝2.2 具体源代码

这里仅展示核心部分吧,完整地较长,可以在资源里面下载。

test_2.py

# Run后,也可以大致演示交通流量预测的状况。2023.2.28 19:28from trafficSimulator import *# Create simulation
sim = Simulation()# Add multiple roads
sim.create_roads([((0, 100), (148, 100)),((148, 100), (300, 100)),((150, 0), (150, 98)),((150, 98), (150, 200)),
])sim.create_gen({'vehicle_rate': 20,'vehicles': [[1, {"path": [0, 1]}],[1, {"path": [0, 3]}],[1, {"path": [2, 3]}],[1, {"path": [2, 3]}]]
})sim.create_signal([[0], [2]])# Start simulation
win = Window(sim)
win.offset = (-150, -110)
win.run(steps_per_update=5)

test_2即可执行文件。

🥝2.3 附:曲线路况文件

即若路况为曲线时,执行的生成函数:

def curve_points(start, end, control, resolution=5):# If curve is a straight lineif (start[0] - end[0])*(start[1] - end[1]) == 0:return [start, end]# If not return a curvepath = []for i in range(resolution+1):t = i/resolutionx = (1-t)**2 * start[0] + 2*(1-t)*t * control[0] + t**2 *end[0]y = (1-t)**2 * start[1] + 2*(1-t)*t * control[1] + t**2 *end[1]path.append((x, y))return pathdef curve_road(start, end, control, resolution=15):points = curve_points(start, end, control, resolution=resolution)return [(points[i-1], points[i]) for i in range(1, len(points))]TURN_LEFT = 0
TURN_RIGHT = 1
def turn_road(start, end, turn_direction, resolution=15):# Get control pointx = min(start[0], end[0])y = min(start[1], end[1])if turn_direction == TURN_LEFT:control = (x - y + start[1],y - x + end[0])else:control = (x - y + end[1],y - x + start[0])return curve_road(start, end, control, resolution=resolution)

🥝2.4 附:红灯判断函数

from scipy.spatial import distance
from collections import dequeclass Road:def __init__(self, start, end):self.start = startself.end = endself.vehicles = deque()self.init_properties()def init_properties(self):self.length = distance.euclidean(self.start, self.end)self.angle_sin = (self.end[1]-self.start[1]) / self.lengthself.angle_cos = (self.end[0]-self.start[0]) / self.length# self.angle = np.arctan2(self.end[1]-self.start[1], self.end[0]-self.start[0])self.has_traffic_signal = Falsedef set_traffic_signal(self, signal, group):self.traffic_signal = signalself.traffic_signal_group = groupself.has_traffic_signal = True@propertydef traffic_signal_state(self):if self.has_traffic_signal:i = self.traffic_signal_groupreturn self.traffic_signal.current_cycle[i]return Truedef update(self, dt):n = len(self.vehicles)if n > 0:# Update first vehicleself.vehicles[0].update(None, dt)# Update other vehiclesfor i in range(1, n):lead = self.vehicles[i-1]self.vehicles[i].update(lead, dt)# Check for traffic signalif self.traffic_signal_state:# If traffic signal is green or doesn't exist# Then let vehicles passself.vehicles[0].unstop()for vehicle in self.vehicles:vehicle.unslow()else:# If traffic signal is redif self.vehicles[0].x >= self.length - self.traffic_signal.slow_distance:# Slow vehicles in slowing zoneself.vehicles[0].slow(self.traffic_signal.slow_factor*self.vehicles[0]._v_max)if self.vehicles[0].x >= self.length - self.traffic_signal.stop_distance and\self.vehicles[0].x <= self.length - self.traffic_signal.stop_distance / 2:# Stop vehicles in the stop zoneself.vehicles[0].stop()

 


 

🍒 热门专栏推荐

  • 🥇Python&AI专栏:【Python从入门到人工智能】
  • 🥈前端专栏:【前端之梦~代码之美(H5+CSS3+JS.】
  • 🥉论文阅读&项目专栏:【小小的项目 (实战+案例)】
  • 🍎C语言/C++专栏:【C语言、C++基础代码~】
  • 🌞问题解决专栏:【工具、技巧、解决办法】
  • 📝 加入Community 一起追光:追光者♂社区

 

持续创作优质好文ing…✍✍✍

 

记得一键三连哦!!!

 

求关注!求点赞!求个收藏啦!

在这里插入图片描述

相关内容

热门资讯

A股玻尿酸巨头出手!2700字... 医美龙头巨子生物“成分争议”风波持续发酵。日前,美妆博主大嘴博士(香港大学化学博士郝宇)发文,质疑巨...
计算机组成原理实验1---运算...     本实验为哈尔滨工业大学计算机组成原理实验,实验内容均为个人完成,...
3 ROS1通讯编程提高(1) 3 ROS1通讯编程提高3.1 使用VS Code编译ROS13.1.1 VS Code的安装和配置...
前端-session、jwt 目录:   (1)session (2&#x...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
EL表达式JSTL标签库 EL表达式     EL:Expression Language 表达式语言     ...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
【内网安全】 隧道搭建穿透上线... 文章目录内网穿透-Ngrok-入门-上线1、服务端配置:2、客户端连接服务端ÿ...
【Spring Cloud A... 文章目录前言Metadata元数据ClassMetadataSpring中常见的一些元注解Nacos...
React篇-关于React的... 一.简介1.介绍用于构建用户界面的 JavaScript 库2.创建项目(1)手动创建Documen...
win7 Pro 英文版添加中... win7pro x64英文版添加中文语言包1、下载语言包,并解压成lp.cab,复制到...
Android开发-Andro... 01  Android UI 1.1  UI 用户界面(User Interface,...
基于springboot教师人... 基于springboot教师人事档案管理系统【源码+论文】 开发语言:Jav...
编写软件界面的方式 本文重点解决如下问题:编写软件的界面有哪几种方式?通常情形下࿰...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
GO语言小锤硬磕十三、数组与切... 数组用来保存一组相同类型的数据,go语言数组也分一维数组和多维数组。 直接上代码看一下...
三级数据库备考--数据库应用系... 1.数据库应用系统设计包括概念设计、逻辑设计、物理设计3个步骤,每个步骤的设计活动按照...
prometheus数据持久化... https://segmentfault.com/a/1190000015710814 promet...
孩子用什么样的灯对眼睛没有伤害... 现代社会高速发展,越来越多的人开始重视身体健康,尤其是很多家长ÿ...
微软Bing GPT支持AI绘... 我想要一张图片:大象、珊瑚、火山、云朵我想要一张图片:亚特兰蒂斯...