欢迎光临
我们一直在努力

线性回归代码理解

一.导入库

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()

赞(0)
未经允许不得转载:171主机测评 » 线性回归代码理解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址