linear代码解释

📅 2026/7/15 19:35:34 👁️ 阅读次数 📝 编程学习
linear代码解释

假设真实情如下:

外貌,性格,财富,内涵的权重依次为:8.1,2,2,4;偏置值为1.1,根据一个人的外貌,性格,财富,内涵可以计算出其恋爱次数

神经网络的目标便是根据所提供的数据最终找出近似的w,b

显然的是,该模型仅仅是一个线性回归模型.虽然简单,但我们的深度学习旅程也由此启程.

一.导入库

import torch import matplotlib.pyplot as plt #画图的 import random #生成随机数

现来逐步解析:

import torch

PyTorch 库的所有功能导入到当前代码环境中,让我能调用torch命名空间下的所有类、函数和方法

import matplotlib.pyplot as plt #画图的

是 Python绘制可视化图表的核心导入语句,matplotlib是 Python 最常用的绘图库pyplot是其中封装了各类绘图接口的子模块,简写为plt是行业通用规范(能大幅简化代码)。

import random #生成随机数

是 Python生成随机数 / 随机操作的核心导入语句,random是 Python 内置标准库(无需额外安装),封装了各类伪随机数生成函数,能满足从「简单随机数」到「随机抽样 / 打乱序列」的绝大多数需求。

二.产生数据

def create_data(w,b,data_num): #创建一个产生数据方法 x=torch.normal(0,1,(data_num,len(w))) #生成一个服从正态分布的张量 x,作为机器学习模型的输入特征。张量x的形状为:行数=样本数,列数=权重数 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=create_data(true_w,true_b,num)

现逐步分析如下:

def create_data(w,b,data_num):

定义数据生成函数:

根据给定的权重 true_w(张量:4*1)、偏置 true_b(张量:1*1),生成data_num(即样本数量)个样本,每个样本包含:

  • 输入特征x(随机生成);
  • 标签y = w*x + b + 噪声(加入噪声模拟真实数据)。

在 Python 中,len()函数作用于 PyTorch 张量时,只会返回张量「第一维度的长度」(等价于tensor.shape[0])。

x=torch.normal(0,1,(data_num,len(w)))

生成一个服从正态分布的张量x,作为机器学习模型的输入特征。张量x的形状为:行数=样本数,列数=权重数

y=torch.matmul(x,w)+b #matmul表示矩阵相乘

基于多维特征x和权重w,按照线性回归公式y = X·w + b计算标签y

noise=torch.normal(0,0.01,y.shape)#噪声要加到y上,因此noise张量的形状要与y.shape一致

在机器学习生成线性回归数据时,噪声是模拟真实世界数据的关键

  • 真实数据不会严格遵循y = X·w + b的理想线性关系,总会有测量误差、环境干扰等随机因素;
  • 加入小幅度噪声后,数据更贴近真实场景,训练出的模型也更具泛化能力(不会过拟合理想数据)。
y+=noise

将噪声noise加到y上

return x,y

返回张量x(data_num*len(w)) , y(data_num*1)

num=500 true_w=torch.tensor([8.1,2,2,4]) true_b=torch.tensor(1.1)

确定样本数量为500,真实的张量true_w和true_b

X,Y=create_data(true_w,true_b,num)

调用数据生成函数并传入实参true_w,true_b最终生成所需要的数据张量X(500*4)和Y(500*1)

三.数据可视化

plt.scatter(X[:,3],Y,1) #plt.scatter是画一个散点图。点大小为1,X取第3列 plt.show()

现逐步分析如下:

plt.scatter(X[:,3],Y,1) #plt.scatter是画一个散点图。点大小为1,X取第3列

plt.scatter

matplotlib 的散点图绘制函数,核心作用是把「x 轴数据」和「y 轴数据」对应成坐标点画在图上.

X[:,3]

取所有行(所有样本),取第 4 列(索引从 0 开始),即取张量 X 中所有样本的第 4 列数据作为散点图的x 轴坐标

Y:

取Y所有的行和列

注意:所取的x轴数据形状必须和y轴数据形状一致

1:

点大小为1

plt.show()

画图,结果如下:

四.批次数据提供程序

def data_provider(data,label,batchsize): #每次访问这个函数,就能提供一批数据 length=len(label) #获取样本总数 indices=list(range(length)) #给样本贴标签:0,1,2,3,...,length-1 random.shuffle(indices) #将标签打乱 for each in range(0,length,batchsize): get_indices=indices[each:each+batchsize] get_data=data[get_indices] get_label=label[get_indices] yield get_data,get_label #yield表示有存档点的return

现逐步解析如下:

def data_provider(data,label,batchsize):#每次访问这个函数,就能提供一批数据

这是机器学习中批量数据加载的核心骨架,作用是每次调用返回一批(batchsize个)数据和对应的标签,适配模型分批训练的需求.需要传入数据X,Y和批次样本数量batchiseze

即:每次计算时不使用全部的500个样本的数据,而是从这500个样本中随机挑取一批(batchsize个)数据来计算

length=len(label)

获取标签张量 / 数组的第一维度长度(即总样本数),并将其赋值给length

indices=list(range(length)) #indices 索引,是index的复数形式

range(length)

生成一个整数序列:0,1,2,...,length-1(比如length=500,就是 0 到 499);

list(range(length))

把整数序列转为列表,得到列表[0,1,2,...,499],每个元素对应一个样本的位置;

indices=list(range(length))

最终得到「所有样本的列表」,后续可打乱这个列表实现随机取批次。

random.shuffle(indices) #shuffle 乱序

不能按顺序取数据,要把序列打乱

for each in range(0,length,batchsize): get_indices=indices[each:each+batchsize] get_data=data[get_indices] get_label=label[get_indices]

1.for each in range(0, length, batchsize):

  • range(0, 500, 64)生成批次起始位置:0, 64, 128, 192, 256, 320, 384, 448
  • each依次取这些值,代表「每一批数据的起始索引位置」

2.get_indices = indices[each:each+batchsize]

  • 作用:从打乱的索引列表中,取出当前批次的索引,并赋值给get_indices;
  • 示例:
    • each=0时,get_indices = indices[0:64]→ 取前 64 个随机索引;
    • each=64时,get_indices = indices[64:128]→ 取下 64 个随机索引;
    • 最后一批each=448时,get_indices = indices[448:512]→ 自动取到末尾(共 52 个索引,适配不足 batchsize 的情况)。

3.get_data = data[get_indices],get_label = label[get_indices]

  • 作用:根据当前的批次索引get_indices,从总数据和标签中提取对应样本;
  • 维度说明:
    • data[500,4](500 个样本,4 维特征),则get_data[64,4](64 个样本,4 维特征);

    • label[500,1](500 个标签),则get_label[64,1](64 个标签)。

注意:data 是二维张量,但是直接用data[get_indices]也完全合适,且是批量取样本的标准写法。

对于形状为[总样本数, 特征维度]的二维张量data(比如[500,4]):

  • get_indices是「样本索引列表」(比如[12, 45, 89]);
  • data[get_indices]按索引取出对应行(样本),返回形状为[批次大小, 特征维度]的二维张量(比如取 16 个索引,返回[16,4]);
  • 这是 PyTorch/NumPy 中「批量选取样本」的原生支持,完全合理且高效。
yield get_data,get_label #yield表示有存档点的return

返回所需要的批次数据get_data,get_label

注意:因为每次取一批数据时,要求从上一批数据存档点处开始取,因此就要求使用yield返回而非return返回。

五.前向传播

batchsize=16 def qianxiang(x,w,b): pred_y=torch.matmul(x,w)+b return pred_y
batchsize=16

每批次取16个样本

def qianxiang(x,w,b): pred_y=torch.matmul(x,w)+b return pred_y

传入估计张量参数w_0,b_0,和数据X,计算预测值函数并返回张量

六.定义损失函数

def maeLoss(pred_y,y): return torch.sum(abs(y-pred_y))/len(y)

传入标签Y和,计算样本loss的平均值

七.梯度下降算法更新参数

def sgd(paras,lr): with torch.no_grad(): for para in paras: para-=para.grad*lr #不能写成para=para-para.grad()*lr para.grad.zero_()

梯度下降算法:传入估计参数paras和学习率lr

with torch.no_grad():

是 PyTorch 中临时关闭梯度计算的核心上下文管理器,会创建一个上下文环境,在这个环境内执行的所有操作:

  1. 不会构建计算图(PyTorch 记录梯度的核心);
  2. 不会计算张量的grad梯度;
  3. 不会消耗额外的内存 / 算力。

for para in paras:
para-=para.grad*lr
para.grad.zero_()

是 PyTorch 中参数更新 + 梯度清零的核心写法。

原地更新参数

1.para.grad是反向传播计算出的梯度(张量,非方法,所以不加());

2.-=是「原地赋值」,直接修改para本身的数值,保留para的梯度追踪属性(requires_grad=True);

3. 结合torch.no_grad(),仅更新数值,不构建计算图。

梯度清零

1.zero_()是「原地清零」方法,把para.grad重置为 0;

八.训练模型

lr=0.01 w_0=torch.normal(0,0.01,true_w.shape,requires_grad=True) b_0=torch.tensor(0.01,requires_grad=True) 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))

设置学习率lr,并随机生成估计参数w_0,b_0(注意,需要允许对其求梯度)

epochs=50 #训练轮数为50轮

设置训练轮数epochs

for epoch in range(epochs):

进行50轮循环训练

data_loss=0

是机器学习训练中累加批次损失的初始化操作,核心作用是「从零开始累计每一批次的损失值,最终计算整轮训练的总损失 / 平均损失」。

在批量训练循环中,我们需要知道「整轮训练的总损失」(判断模型是否收敛),但模型是按批次计算损失的 ——data_loss=0就是用来承接这些批次损失的「容器」,初始化后逐批累加。

data_loss=0必须写在外层 epoch 循环内内层 batch 循环外

for batch_x,batch_y in data_provider(X,Y,batchsize):

逐个取出data_provider生成的批次数据(特征 + 标签)

  • 每次循环从数据提供器取一批数据;
  • batch_x接收批次特征(形状 16×4),batch_y接收批次标签(形状 16×1);
  • 循环会自动遍历完所有批次(500 个样本→32 批:前 31 批 16 个,最后 1 批 4 个)。
pred_y=fun(batch_x,w_0,b_0) loss=maeLoss(pred_y,batch_y)

计算

loss.backward()

对loss的参量求导

sgd([w_0,b_0],lr)

调用梯度下降算法更新参数w_0,b_0

data_loss+=loss

累加批次损失

print('epoch %03d: loss: %.6f'%(epoch,data_loss))

打印出每轮的批次损失

print('真实的参数值是',true_w,true_b) print('经过训练的参数值是',w_0,b_0)

最终训练结果如下: