linear代码解释

📅 2026/7/15 19:27:58
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): #创建一个产生数据方法 xtorch.normal(0,1,(data_num,len(w))) #生成一个服从正态分布的张量 x作为机器学习模型的输入特征。张量x的形状为:行数样本数,列数权重数 ytorch.matmul(x,w)b#matmul表示矩阵相乘 noisetorch.normal(0,0.01,y.shape)#噪声要加到y上 ynoise return x,y num500 true_wtorch.tensor([8.1,2,2,4]) true_btorch.tensor(1.1) X,Ycreate_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]。xtorch.normal(0,1,(data_num,len(w)))生成一个服从正态分布的张量x作为机器学习模型的输入特征。张量x的形状为:行数样本数,列数权重数ytorch.matmul(x,w)b #matmul表示矩阵相乘基于多维特征x和权重w按照线性回归公式y X·w b计算标签ynoisetorch.normal(0,0.01,y.shape)#噪声要加到y上,因此noise张量的形状要与y.shape一致在机器学习生成线性回归数据时噪声是模拟真实世界数据的关键真实数据不会严格遵循y X·w b的理想线性关系总会有测量误差、环境干扰等随机因素加入小幅度噪声后数据更贴近真实场景训练出的模型也更具泛化能力不会过拟合理想数据。ynoise将噪声noise加到y上return x,y返回张量x(data_num*len(w)) , y(data_num*1)num500 true_wtorch.tensor([8.1,2,2,4]) true_btorch.tensor(1.1)确定样本数量为500,真实的张量true_w和true_bX,Ycreate_data(true_w,true_b,num)调用数据生成函数并传入实参true_wtrue_b最终生成所需要的数据张量X(500*4)和Y(500*1)三.数据可视化plt.scatter(X[:,3],Y,1) #plt.scatter是画一个散点图。点大小为1X取第3列 plt.show()现逐步分析如下:plt.scatter(X[:,3],Y,1) #plt.scatter是画一个散点图。点大小为1X取第3列plt.scatter是matplotlib 的散点图绘制函数核心作用是把「x 轴数据」和「y 轴数据」对应成坐标点画在图上.X[:,3]取所有行所有样本,取第 4 列索引从 0 开始),即取张量 X 中所有样本的第 4 列数据作为散点图的x 轴坐标Y:取Y所有的行和列注意:所取的x轴数据形状必须和y轴数据形状一致1:点大小为1plt.show()画图,结果如下:四.批次数据提供程序def data_provider(data,label,batchsize): #每次访问这个函数就能提供一批数据 lengthlen(label) #获取样本总数 indiceslist(range(length)) #给样本贴标签0123...length-1 random.shuffle(indices) #将标签打乱 for each in range(0,length,batchsize): get_indicesindices[each:eachbatchsize] get_datadata[get_indices] get_labellabel[get_indices] yield get_data,get_label #yield表示有存档点的return现逐步解析如下def data_provider(data,label,batchsize):#每次访问这个函数就能提供一批数据这是机器学习中批量数据加载的核心骨架,作用是每次调用返回一批batchsize个数据和对应的标签适配模型分批训练的需求.需要传入数据XY和批次样本数量batchiseze即:每次计算时不使用全部的500个样本的数据,而是从这500个样本中随机挑取一批(batchsize个)数据来计算lengthlen(label)获取标签张量 / 数组的第一维度长度即总样本数,并将其赋值给lengthindiceslist(range(length)) #indices 索引是index的复数形式range(length)生成一个整数序列0,1,2,...,length-1比如length500就是 0 到 499list(range(length))把整数序列转为列表得到列表[0,1,2,...,499]每个元素对应一个样本的位置indiceslist(range(length))最终得到「所有样本的列表」后续可打乱这个列表实现随机取批次。random.shuffle(indices) #shuffle 乱序不能按顺序取数据,要把序列打乱for each in range(0,length,batchsize): get_indicesindices[each:eachbatchsize] get_datadata[get_indices] get_labellabel[get_indices]1.for each in range(0, length, batchsize):range(0, 500, 64)生成批次起始位置0, 64, 128, 192, 256, 320, 384, 448each依次取这些值代表「每一批数据的起始索引位置」2.get_indices indices[each:eachbatchsize]作用从打乱的索引列表中取出当前批次的索引并赋值给get_indices示例当each0时get_indices indices[0:64]→ 取前 64 个随机索引当each64时get_indices indices[64:128]→ 取下 64 个随机索引最后一批each448时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_dataget_label注意因为每次取一批数据时要求从上一批数据存档点处开始取因此就要求使用yield返回而非return返回。五.前向传播batchsize16 def qianxiang(x,w,b): pred_ytorch.matmul(x,w)b return pred_ybatchsize16每批次取16个样本def qianxiang(x,w,b): pred_ytorch.matmul(x,w)b return pred_y传入估计张量参数w_0b_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 #不能写成parapara-para.grad()*lr para.grad.zero_()梯度下降算法传入估计参数paras和学习率lrwith torch.no_grad():是 PyTorch 中临时关闭梯度计算的核心上下文管理器会创建一个上下文环境在这个环境内执行的所有操作不会构建计算图PyTorch 记录梯度的核心不会计算张量的grad梯度不会消耗额外的内存 / 算力。for para in paras:para-para.grad*lrpara.grad.zero_()是 PyTorch 中参数更新 梯度清零的核心写法。原地更新参数1.para.grad是反向传播计算出的梯度张量非方法所以不加()2.-是「原地赋值」直接修改para本身的数值保留para的梯度追踪属性requires_gradTrue3. 结合torch.no_grad()仅更新数值不构建计算图。梯度清零1.zero_()是「原地清零」方法把para.grad重置为 0八.训练模型lr0.01 w_0torch.normal(0,0.01,true_w.shape,requires_gradTrue) b_0torch.tensor(0.01,requires_gradTrue) print(w_0,b_0) epochs50 for epoch in range(epochs): data_loss0 for batch_x,batch_y in data_provider(X,Y,batchsize): pred_yfun(batch_x,w_0,b_0) lossmaeLoss(pred_y,batch_y) loss.backward() sgd([w_0,b_0],lr) data_lossloss print(epoch %03d: loss: %.6f%(epoch,data_loss))设置学习率lr并随机生成估计参数w_0b_0注意需要允许对其求梯度epochs50 #训练轮数为50轮设置训练轮数epochsfor epoch in range(epochs):进行50轮循环训练data_loss0是机器学习训练中累加批次损失的初始化操作核心作用是「从零开始累计每一批次的损失值最终计算整轮训练的总损失 / 平均损失」。在批量训练循环中我们需要知道「整轮训练的总损失」判断模型是否收敛但模型是按批次计算损失的 ——data_loss0就是用来承接这些批次损失的「容器」初始化后逐批累加。data_loss0必须写在外层 epoch 循环内、内层 batch 循环外for batch_x,batch_y in data_provider(X,Y,batchsize):逐个取出data_provider生成的批次数据特征 标签每次循环从数据提供器取一批数据batch_x接收批次特征形状 16×4batch_y接收批次标签形状 16×1循环会自动遍历完所有批次500 个样本→32 批前 31 批 16 个最后 1 批 4 个。pred_yfun(batch_x,w_0,b_0) lossmaeLoss(pred_y,batch_y)计算和loss.backward()对loss的参量求导sgd([w_0,b_0],lr)调用梯度下降算法更新参数w_0b_0data_lossloss累加批次损失print(epoch %03d: loss: %.6f%(epoch,data_loss))打印出每轮的批次损失print(真实的参数值是,true_w,true_b) print(经过训练的参数值是,w_0,b_0)最终训练结果如下