当前位置: 首页 > news >正文

从零开始:用PyTorch实现线性回归模型

从零开始:用PyTorch实现线性回归模型

引言:为什么线性回归是机器学习的基石

线性回归作为统计学和机器学习中最基础的算法之一,不仅是理解更复杂模型的跳板,也是解决实际问题的强大工具。在数据科学领域,线性回归被广泛应用于房价预测、销售趋势分析、医学研究等多个领域。尽管它看似简单,但其背后蕴含的优化思想和数学原理构成了现代机器学习的基石。

作为机器学习的"Hello World",线性回归模型是理解监督学习概念的完美起点。通过实现线性回归,我们可以掌握:

  • 模型定义与参数初始化
  • 损失函数的概念与实现
  • 梯度下降优化算法
  • 模型训练与评估流程

本文将带你从零开始,使用PyTorch框架实现一个完整的线性回归模型。PyTorch作为当前最流行的深度学习框架之一,其动态计算图特性和Pythonic的设计风格使其成为学习和实现机器学习算法的理想选择。

线性回归的数学原理

线性回归模型假设目标变量yyy与特征变量xxx之间存在线性关系:

y=wx+b+ϵy = wx + b + \epsilony=wx+b+ϵ

其中:

  • www是权重(斜率)
  • bbb是偏置(截距)
  • ϵ\epsilonϵ是误差项

对于多变量情况,公式扩展为:

y=w1x1+w2x2+...+wnxn+by = w_1x_1 + w_2x_2 + ... + w_nx_n + by=w1x1+w2x2+...+wnxn+b

损失函数

为了评估模型预测值y^\hat{y}y^与真实值yyy之间的差距,我们使用**均方误差(MSE)**作为损失函数:

L(w,b)=1N∑i=1N(yi−y^i)2L(w,b) = \frac{1}{N}\sum_{i=1}^{N}(y_i - \hat{y}_i)^2L(w,b)=N1i=1N(yiy^i)2

其中NNN是样本数量,y^i=wxi+b\hat{y}_i = wx_i + by^i=wxi+b是模型预测值。

优化过程

我们的目标是最小化损失函数,这通过梯度下降算法实现:

w:=w−α∂L∂ww := w - \alpha \frac{\partial L}{\partial w}w:=wαwL
b:=b−α∂L∂bb := b - \alpha \frac{\partial L}{\partial b}b:=bαbL

其中α\alphaα是学习率,控制参数更新的步长。

计算梯度:
∂L∂w=2N∑i=1N(wxi+b−yi)xi\frac{\partial L}{\partial w} = \frac{2}{N}\sum_{i=1}^{N}(wx_i + b - y_i)x_iwL=N2i=1N(wxi+byi)xi
∂L∂b=2N∑i=1N(wxi+b−yi)\frac{\partial L}{\partial b} = \frac{2}{N}\sum_{i=1}^{N}(wx_i + b - y_i)bL=N2i=1N(wxi+byi)

为什么选择PyTorch实现

虽然线性回归可以用NumPy等基础库实现,但使用PyTorch有以下优势:

  1. 自动微分:PyTorch的autograd系统自动计算梯度,无需手动推导
  2. GPU加速:轻松切换CPU/GPU,为处理大规模数据做准备
  3. 模块化设计nn.Module提供了清晰的模型组织方式
  4. 生态系统:与PyTorch的其他组件(如数据加载器、优化器)无缝集成
  5. 学习曲线:掌握PyTorch基础后,更容易过渡到复杂模型

即使对于简单的线性回归,使用PyTorch也能让我们熟悉深度学习框架的工作流程,为后续实现神经网络等更复杂模型打下坚实基础。

实现思路概述

我们的线性回归实现将遵循以下步骤:

  1. 数据准备:自己生成和加载训练数据
  2. 模型定义:创建线性回归模型类
  3. 损失函数:定义均方误差损失
  4. 优化器:选择优化算法,本次使用小批量随机梯度下降算法实现
  5. 训练循环:迭代更新模型参数

这种结构化的方法不仅适用于线性回归,也是实现大多数机器学习模型的标准流程。

代码结构解析

以下是我们的实现将包含的关键组件:

1. 数据准备

我们将生成符合线性关系的模拟数据,并添加一些噪声以模拟真实情况。数据将转换为PyTorch张量,以便与框架无缝集成。

2. 模型定义

我们将创建一个简单的线性回归模型,当做我们的模型.

3. 损失函数与优化器

均方误差作为损失函数和小批量随机梯度下降作为优化算法

4. 训练过程

包含典型的训练循环:

  • 前向传播:计算预测值
  • 计算损失
  • 反向传播:计算梯度
  • 优化器步骤:更新参数
  • 清零梯度

通过理解这个简单的线性回归实现,你将掌握PyTorch的基本工作流程,为学习更复杂的深度学习模型铺平道路。

接下来,我们将深入代码实现部分,一步步构建我们的线性回归模型。无论你是机器学习新手还是想巩固基础的开发者,这个实现都将帮助你理解模型训练的核心原理。


d2l版本实现(附带线性注释讲解)

#!/user/bin/env python3
# -*- coding: utf-8 -*-
import random
import torch
from d2l import torch as d2l
import matplotlib.pyplot as plt
def synthetic_data(w, b, num_examples):  #@save"""生成y=Xw+b+噪声该函数用于生成线性回归的合成数据集,通过给定的权重和偏置参数,生成符合线性关系的数据点,并添加高斯噪声以模拟真实场景。参数:w (torch.Tensor): 权重向量,形状为[n],其中n为特征数量b (float或torch.Tensor): 偏置项,标量值num_examples (int): 要生成的样本数量返回:tuple: 包含两个元素的元组- X (torch.Tensor): 输入特征矩阵,形状为[num_examples, n]- y (torch.Tensor): 目标值向量,形状为[num_examples, 1]"""# 生成随机特征矩阵,均值为1, 方差为1,形状为[num_examples, n]X = torch.normal(0, 1, (num_examples, len(w)))# 计算线性变换结果 y = Xw + b也可写成X*w+b,y的形状为[num_examples]的张量y = torch.matmul(X, w) + b# 添加高斯噪声,噪声强度为0.01y += torch.normal(0, 0.01, y.shape)# 将y重塑为列向量并返回return X, y.reshape((-1, 1))true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:', features[0],'\nlabel:', labels[0])
d2l.set_figsize()
d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1)
d2l.plt.show()
# 下面是一个生成器函数,对生成批量数据内存友好.
def data_iter(batch_size, features, labels):"""创建一个数据迭代器,用于批量获取训练数据参数:batch_size (int): 每个批次的样本数量features (torch.Tensor): 特征数据张量labels (torch.Tensor): 标签数据张量返回:generator: 返回一个生成器,每次产生一个元组(features_batch, labels_batch)"""num_examples = len(features)indices = list(range(num_examples)) # [0, 1, 2, ..., num_examples-1]# 随机打乱索引顺序random.shuffle(indices)# 按批次大小循环遍历所有样本索引,# 从索引0开始,每次随机取batch_size个样本,可能是1,3,4,5,62,3,4等等反正大小是batch_size个for i in range(0, num_examples, batch_size):batch_indices = torch.tensor(indices[i: min(i + batch_size, num_examples)]) # 最后一个批次可能不足batch_size个样本,所以q取较小值# 张量支持张量的索引切片,返回一个索引张量yield features[batch_indices], labels[batch_indices]# 指定批量大小,一次取原来的10个样本
batch_size = 10
# 隐式调用data_iter()函数,返回一个生成器对象
for X, y in data_iter(batch_size, features, labels):print(X, '\n', y)break
# 初始化权重参数和偏置参数,随机初始化,因为要对这两个参数求导,所以设置requires_grad=True
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)
print(f"随机初始化参数w和b的值为:\nw:{w}\nb:{b}")
# 定义线性回归模型,其中X是特征矩阵(已知),w是权重向量(预测),b是偏置项(预测)
def linreg(X, w, b):  #@save"""线性回归模型"""return torch.matmul(X, w) + b
def squared_loss(y_hat, y):  #@save'''均方损失:损失函数:param y_hat: 数据的预测值:param y: 数据的真实值:return: 损失函数的值'''return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
def sgd(params, lr, batch_size):  #@save"""小批量随机梯度下降使用小批量随机梯度下降算法更新模型参数,通过计算梯度的平均值来调整参数值,并在更新后将梯度清零。参数:params (iterable): 待更新的模型参数列表,每个参数应包含grad属性lr (float): 学习率,控制参数更新的步长大小batch_size (int): 批量大小,用于计算梯度的平均值返回值:None: 该函数直接修改传入的参数,不返回任何值"""with torch.no_grad():# 更新参数并清零梯度for param in params:# 参数 -= 学习率 * 梯度,这里之所以/batch_size是因为loss函数定义的时候没有除,在这里补上,# 也可以在loss函数定义的时候除,这里不写一样的param -= lr * param.grad / batch_size# 梯度清零param.grad.zero_()lr = 0.03 # 学习率
num_epochs = 3 # 迭代次数
net = linreg # 线性回归模型
loss = squared_loss # 损失函数
for epoch in range(num_epochs):# 每个迭代周期,都会迭代一次完整的数据集,只不过使用的小批量的优化算法,# 没有使用全批量的梯度下降优化而是使用小批量的梯度下降优化for X, y in data_iter(batch_size, features, labels):l = loss(net(X, w, b), y)  # X和y的小批量损失# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,# 并以此计算关于[w,b]的梯度l.sum().backward()# 使用参数的梯度更新参数sgd([w, b], lr, batch_size)with torch.no_grad():train_l = loss(net(features, w, b), labels)print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')
print(f'b的估计误差: {true_b - b}')

上面是d2l官网和加上我的讲解的代码,下面是我自己手写实现的代码供参考:

#!/user/bin/env python3
# -*- coding: utf-8 -*-
import torch
import random
from d2l import torch as d2l# 定义真的w和b
true_w = torch.tensor([2, -3.4, 4.2])
true_b = torch.tensor([4.2])# 生成数据集:
def gen_data(w, b, num_examples):X = torch.normal(0, 1, (num_examples, len(w)))y = X @ w + by+=torch.normal(0,0.01,y.shape)return X, y.reshape((-1, 1))# 定义每一次迭代获得小批次数据:
def data_iter(batch_size, features, labels):n_samples = len(labels)list1 = [i for i in range(n_samples)]random.shuffle(list1)for i in range(0, n_samples, batch_size):index = torch.tensor(list1[i:min(i + batch_size, n_samples)])yield features[index], labels[index].reshape((-1, 1))# 定义损失函数,小批量数据的损失函数
def loss(y_head, y):return (y_head - y.reshape(y_head.shape)) ** 2 / (2 * len(y))# 定义梯度下降更新参数
def sdg(params, alpha):with torch.no_grad():for param in params:param -= alpha * param.gradparam.grad.zero_()# 定义模型,线性预测模型:
def model(w, b, X):return X @ w + b# 随机初始化参数:
w = torch.normal(0, 1, (3, 1), requires_grad=True)
b = torch.randn(1, requires_grad=True)
print(f"随机初始化的w:{w}\nb:{b}")
# 定义一些超参数:
alpha = 0.02
batch_size = 100
epoch_size = 30
num_samples=1000
X, y = gen_data(true_w, true_b, num_samples)
print(y.shape,X.shape)
for epoch in range(epoch_size):for X_batch, y_batch in data_iter(batch_size,X, y):l = loss(model(w, b, X_batch), y_batch)l.sum().backward()sdg([w, b], alpha)print(f"epoch{epoch + 1}: loss:{l.sum()}")
print(f"w的误差{true_w - w.reshape(true_w.shape)},\nb的误差:{true_b - b.reshape(true_b.shape)}")'''
成功完成我的手写线性回归模型的梯度下降优化算法
学到的:
1.首先使用torch.normal(mean,std,shape)获得正态分布的数据集- 之后再加深一下矩阵乘法,*,dot,mv,mm,matmul.特别注意一下[num]张量的特殊性
2.定义模型
3.定义损失函数
4.定义梯度下降优化参数,使用torch.no_grad()上下文更新参数,之后梯度清零.
5.初始化模型参数,记得requires_grad=True.可以使得损失函数l.sum().backward()的时候自动存取梯度信息
6.开始迭代-->调用模型得到y_head-->调用损失函数获得损失-->损失函数.backward()-->梯度下降更新参数继续迭代
'''

如果觉得对你有帮助留下一个关注和赞把❤️❤️❤️❤️

http://www.lryc.cn/news/619271.html

相关文章:

  • 比特币与区块链:去中心化的技术革命
  • VUE2连接USB打印机
  • Pytorch FSDP权重分片保存与合并
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day3
  • 【Qt开发】常用控件(三) -> geometry
  • 疏老师-python训练营-Day44预训练模型
  • php7 太空船运算符
  • Linux 软件编程:文件IO、目录IO、时间函数
  • 适配安卓15(对应的sdk是35)
  • RxJava 在 Android 中的深入解析:使用、原理与最佳实践
  • 大牌点餐接口api对接全流程
  • 《吃透 C++ 类和对象(中):构造函数与析构函数的核心逻辑》
  • Ubuntu22.04轻松安装Qt与OpenCV库
  • 药房智能盘库系统的Python编程分析与实现—基于计算机视觉与时间序列预测的智能库存管理方案
  • 基于大数据spark的医用消耗选品采集数据可视化分析系统【Hadoop、spark、python】
  • 分段锁和限流的间接实现
  • 通信中间件 Fast DDS(一) :编译、安装和测试
  • 机器学习—— TF-IDF文本特征提取评估权重 + Jieba 库进行分词(以《红楼梦》为例)
  • CMake进阶: 使用FetchContent方法基于gTest的C++单元测试
  • LINUX812 shell脚本:if else,for 判断素数,创建用户
  • 【GESP】C++一级知识点之【集成开发环境】
  • TF-IDF:信息检索与文本挖掘的统计权重基石
  • [SC]如何使用sc_semaphore实现对共享资源的访问控制
  • 初识神经网络04——构建神经网络2
  • 【从零开始java学习|第四篇】IntelliJ IDEA 入门指南
  • Redis序列化配置类
  • uni-app实战教程 从0到1开发 画图软件 (学会画图)
  • 基于STC8单片机的RTC时钟实现:从原理到实践
  • 聚合搜索中的设计模式
  • 数据结构:中缀到后缀的转换(Infix to Postfix Conversion)