一.导入库
import torch
导入pytorch库,这是一个用于深度学习和张量运算的强大工具。
import matplotlib.pyplot as plt
从 matplotlib 库中导入 pyplot 模块,并将其简称为 plt
二.定义生成数据的函数
def creat_data(w,b,data_num):
定义了一个函数名为creat_data的函数,其中
w:通常为权重(weigh)用于线性模型中的系数
b:表示偏置(bias)用于线性模型中的截距。(截距:在一个线性模型中,截距通常指模型中的常数项,以最简单的线性模型y=kx+b来说,截距即为b,在模型中可以要求x=0,不必一定要使x不等于0,例如当y=kx+b时,x=0,仍可以得到y的估计值;截距在模型中起到矫正的作用,确保所有自变量为0时,模型仍然能给出一个合理的预测值。)
data_num:表示要生成的数据样本数量。
1. 生成输入数据
x = torch.normal(0, 1, (data_num, len(w)))
使用torch.normal从正态分布中生成一个数据
0:均值(mean)为0
1:标准差(std)为1
(data_num,lem(w)):生成的张量形状为(data_num,lem(w));即生成data_num行,每行有lem(w)个数值
w为1行4列的张量,意味着存在4个特征,这也对应了x的列数,也就是说w的列数与X的行数相等。 x = torch.normal(0, 1, (data_num, len(w))),这行代码使用 torch.normal 函数从均值为 0、标准差为 1 的正态分布中随机采样来生成输入数据 x。生成的张量 x 的形状为 (data_num, len(w)),也就是 (500, 4)(因为data_num为 500,len(w) 为 4,对应 4 个特征)。

2.计算线性组合
y = torch.matmul(x, w) + b
torch.matmul(x,w):执行矩阵乘法,将输入数据x与权重w相乘
+b:加上偏置值b,与线性模型的y=kx+b相同
3. 生成噪声数据
noise = torch.normal(0,0.01,y.shape)
作用:生成一个噪声张量,用于模拟数据中的随机噪声。
0:均值为0
0.01:标准差为0.01
y.shape:噪声的形状与y相同,可以确保对每个输出值添加噪声
4. 将噪声添加到目标值
y += noise
作用:将生成的噪声加到原本计算好的y上
意义:这样生成的y不是严格的线性结果,而是有一定的随机性,更符合数据中常见的噪声数据
return x, y
三.生成模型数据
1.
num = 500
true_w = torch.tensor([8.1, 2, 2, 4])
用torch.tensor函数创建一个张量,内容为【8.1,2,2,4】用于数据生成或模型训练中作为参考值
2.
true_b = torch.tensor(1.1)
用torch.tensor函数创建一个标准张量数值为1.1,用于线性表示的截距,即当所有输入特征值为0时,输出截距
3.
x, y = creat_data(true_w, true_b, num)
调用之前定义的creat_data函数,传入数据true_w, true_b, num
利用creat_data函数给定的权重和截距生成一组数据
x:生成输入的数据张量生成其形状为 (500, 4)(500 行,每行 4 个特征)。
y:根据线性关系y = x*ture_w+ture_b计算得到目标值,并加入少量噪声,使得数据更符合真实情况。
4.
plt.scatter(x[:, 3], y, 1)
使用 Matplotlib 的 scatter 函数绘制散点图。
x[:,3]:从输入数据x中取出所有样本的第四列(因为样本的列是从0开始的,所有x[:,3]表示所有样本的第四个特征
y:作为每个样本的特征值,作为图的纵坐标
1:第三个参数代表散点的大小
plt.show()
调用 Matplotlib 的 show 方法,显示绘制好的图形。
四.定义数据提供器函数
1.定义函数
def data_provider(data, lable, batchsize):
函数名为data_provider;
data:表示输入的数据(例如特征矩阵)。
lable:对应的数据标签或目标值(例如回归或分类问题中的真实值)。
batchsize:每个批次的样本数量,也可表示为步长
2. 获取数据总数
length = len(lable)
计算lable的长度得到样本总数
3.生成样本索引列表
indices = list(range(length))
random.shuffle(indices)
通过length的长度生成一个0到length-1长度的线性表,并通过使用 random.shuffle 对索引列表 indices 进行原地随机打乱。
4.按批次遍历数据索引
for each in range(0, length, batchsize):
从0到length遍历数据索引,以batchsize为步长
5.选取当前批次的索引
get_indices = indices[each:each+batchsize]
根据当前起始位置each,从打乱的indices列表中切片出当前批次的索引,范围为[each:each+batchsize]
6.根据索引选取当前批次的数据和标签
get_data = data[get_indices]
get_label = lable[get_indices]
get_data = data[get_indices]:利用前面的获取的索引,从数据data中选取对应的样本
get_label = lable[get_indices]:从标签lable中选取对应的标签
7.使用生成器返回当前批次的数据
yield get_data,get_label
batchsize = 16#步长为16
yield为有存档点的return,意味着函数在执行到 yield 时会保存当前状态,下一次迭代时从此处继续执行。
五.
for batch_x,batch_y in data_provider(x,y,batchsize):#batch_x,_y为新的数据0
print(batch_x,batch_y)
break
利用定义的data_provider函数,传入的参数是x,y,batchsize,该函数会打乱数据顺序,并依次按批次返回数据。每次迭代返回一个元组(get_data,get_label)
在每一次循环中,将生成器的第一个值返回给batch_x,第二个值返回给batch_y
六.定义模型函数
def fun(x, w, b): # 定义模型
pred_y = torch.matmul(x, w) + b # 预测值:计算 x 与 w 的矩阵乘法,再加上偏置 b
return pred_y
定义一个函数名为fun的函数,传入数据张量x,以及权重w,再加上偏置值b
torch.matmul(x, w)表示对数据和权重进行矩阵乘法运算,再加上偏置值b,即为标准的y=x*w+b的线性运算
七.定义 MAE(平均绝对误差)损失函数
def maeLoss(pre_y, y):
return torch.sum(abs(pre_y – y) / len(y))
定义一个函数名为maeLoss的函数,用于计算模型损失
pre_y:为模型预测的输出
y:实际的标签和预测值
abs(pre_y – y):计算真实值与模拟值之间的绝对误差,绝对误差表示两个值之间的距离,没有正负区别
abs(pre_y – y) / len(y):将绝对误差除以样本数量,目的是为了对误差求平均,得到平均绝对误差(MAE)。
torch.sum(…):对所有经过除法处理后的误差求和,从而计算出总的平均绝对误差。
八.定义随机梯度下降(SGD)优化器函数
def sgd(paras, lr): # 随机梯度下降
with torch.no_grad(): # 在该代码块内不记录梯度变化(不会构建梯度追踪图)
for para in paras:
para -= para.grad * lr #操作更新参数
para.grad.zero_() # 清除参数的梯度,为下一次迭代做准备
参数paras:一个可迭代参数,包含了所有需要更新的参数
参数lr:学习率,用于控制更新步长

with torch.no_grad():开启一个上下文管理器告诉PyTorch代码块内不需要记录梯度信息,避免在更新参数时,影响自动求导机制,从而节省内存和计算开销。回传时,比如只需要有loss这个偏差产生的梯度,而不需要para-=para.grad*lr产生的梯度,张量网上只要运算就会产生梯度
para -= para.grad * lr:对当前参数进行原地更新
para.grad 是参数对应的梯度。用梯度乘以学习率得到参数更新后的步长
注:不能写成para = para – para.grad * lr因为这样创建一个新的参数para,导致原来的参数引用丢失,从而影响模型参数的更新。
para -= para.grad * lr para.grad,求梯度, para.grad.zero_() #使用过的梯度归0
九.开始训练
训练流程

1.定义训练次数
epochs = 50
2.外层循环:遍历每个训练轮次
for epoch in range(epochs):
每次循环作为一次完整的训练轮次(epoch),在每个轮次中会遍历所有数据
data_loss = 0
在每个轮次(epoch)开始前会先清零累计损失,以便后续将每个批次的损失累加起来。
3.内层循环:按批次遍历数据
for batch_x, batch_y in data_provider(x, y, batchsize):
调用data_provider(x, y, batchsize):生成器,每次迭代返回一批数据。每次循环batch_x为当前批次输入数据,batch_y为当前真实标签
4. 计算当前批次的预测值
pred_y = fun(batch_x, w_0, b_0)
调用函数fun来计算当前批次的预测值,fun实现了线性模型:计算公式为
pred_y= batch_x*w_0+b_0
5.计算当前批次的损失函数
loss = maeLoss(pred_y, batch_y)
使用定义的函数maeLoss计算平均绝对误差
6.反向传播计算梯度
loss.backward()
Pytorch会通过计算图计算w_0,b_0对loss的梯度,并将梯度存储在对应的参数.grad()中
7.使用随机梯度下降更新参数并累加当前批次的损失
sgd([w_0,b_0],lr)
data_loss += loss
参数 [w_0, b_0] 是一个列表,包含需要更新的参数。lr为学习率,用于控制每次参数更新的步长。
在sgd函数中会在不记录梯度的上下文中,利用当前梯度对参数进行 更新,并将梯度清
零,为下次迭代作准备
8.累加当前批次的损失
data_loss += loss
可以统计所有批次中累计的损失,便于观察训练中的损失情况
9. 绘制拟合结果图
idx = 1
plt.plot(x[:,idx].detach().numpy(),x[:,idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(x[:,idx],y,1)
plt.show()
十.项目所有原码
import torch
import matplotlib.pyplot as plt #画图
import random #进行随机操作
def creat_data(w,b,data_num):#生成数据
x=torch.normal(0,1,(data_num,len(w)))
y=torch.matmul(x,w)+b#matmul表示矩阵相乘
noise = torch.normal(0,0.01,y.shape)#噪声加到y上
y+=noise
return x,y
num = 500
true_w = torch.tensor([8.1,2,2,4])
true_b = torch.tensor(1.1)
x,y = creat_data(true_w,true_b,num)
plt.scatter(x[:,3],y,1)#画一个散点图
plt.show()
def data_provider(data,lable,batchsize):#每次访问这个函数,就能提供一批数据 batchsize为步长 data为数据,lable为Y
length = len(lable)#求出数据有多长,样本的数量
indices = list(range(length))#将500的长度转变成一个范围,再将这个范围转变成一个线性表
random.shuffle(indices)#不按顺序取数,把数据打乱
for each in range(0,length,batchsize):
get_indices = indices[each:each+batchsize]
get_data = data[get_indices]
get_label = lable[get_indices]
yield get_data,get_label #有存档点的return
batchsize = 16#步长为16
for batch_x,batch_y in data_provider(x,y,batchsize):#batch_x,_y为新的数据0
print(batch_x,batch_y)
break
def fun(x,w,b):#定义模型
pred_y = torch.matmul(x,w) + b#预测值
return pred_y
def maeLoss(pre_y,y):
return torch.sum(abs(pre_y-y)/len(y))
def sgd(paras,lr): #随机梯度下降
with torch.no_grad():# 属于这句代码的部分,不计算梯度
for para in paras:
para -= para.grad * lr#不能写出para = para – para.grad*lr,因为这样会认为重新生成了一个para
para.grad.zero_()
#初始数据 />
lr = 0.3
w_0 = torch.normal(0,0.01,true_w.shape,requires_grad=True)
b_0 = torch.tensor(0.01,requires_grad=True)#0.01为起始值
print(w_0,b_0)
#开始训练
epochs = 50
for epoch in range(epochs):
data_loss = 0
for batch_x,batch_y in data_provider(x,y,batchsize):
pred_y = fun(batch_x,w_0,b_0)
loss = maeLoss(pred_y,batch_y)
loss.backward()
sgd([w_0,b_0],lr)
data_loss += loss
print("epoch %03d:loss:%.6f"%(epoch,data_loss))#保留三位证书 6位小数
print("真实的函数数值是",true_w,true_b)
print("训练得到的数据是",w_0,b_0)
#画图
idx = 1
plt.plot(x[:,idx].detach().numpy(),x[:,idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(x[:,idx],y,1)
plt.show()

