手动用梯度下降法和随机梯度下降法实现一元线性回归

news/2024/11/16 15:42:14/文章来源:https://www.cnblogs.com/qimoxuan/p/18407823

手动用梯度下降法实现一元线性回归

实验目的

本次实验旨在通过手动实现梯度下降法和随机梯度下降法来解决一元线性回归问题。具体目标包括:

  1. 生成训练数据集,并使用matplotlib进行可视化。
  2. 设计一个`LinearModel`类来实现一元线性回归的批量梯度下降法。
  3. 使用matplotlib显示拟合结果。
  4.  修改`LinearModel`类来实现随机梯度下降法,并重复上述实验步骤。

实验环境

Python版本:3.x
库:NumPy, Matplotlib

实验步骤

步骤1:准备数据

  1. 生成100个训练样本,其中自变量\(X\)取值服从均值为0,方差为1的正态分布。
  2. 设定因变量\(Y\)的关系式为\(Y = 4X + 3 + e_r\),其中\(e_r\)为误差项,取值服从均值为0,方差为1的正态分布。
  3. 使用Matplotlib绘制生成的数据点。
import numpy as np
import matplotlib.pyplot as plt# 设置随机种子以保证结果可复现
np.random.seed(0)# 生成100个训练样本
X = np.random.normal(loc=0, scale=1, size=100)  # X服从N(0,1)
e_r = np.random.normal(loc=0, scale=1, size=100)  # 误差项e_r服从N(0,1)
Y = 4 * X + 3 + e_r  # Y = 4*X + 3 + e_r# 使用matplotlib显示生成的数据
plt.figure(figsize=(8, 6))
plt.scatter(X, Y, color='blue', label='Actual Data')
plt.title('Generated Data for Linear Regression')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.grid(True)
plt.show()

步骤2:定义LinearModel类

  1. 定义一个`LinearModel`类,包含模型初始化、前向传播、损失计算、梯度计算和参数更新等方法。
  2. 在类中实现批量梯度下降法的训练逻辑,通过设置最大迭代次数或验证集错误率来停止迭代。
class LinearModel:def __init__(self):self.w = np.random.randn()  # 初始化权重wself.b = np.random.randn()  # 初始化偏置bdef forward(self, X):""" 计算预测值 """return self.w * X + self.bdef loss(self, y_pred, y_true):""" 计算平均平方误差损失 """return np.mean((y_pred - y_true) ** 2)def gradient(self, X, y_pred, y_true):""" 计算梯度 """dw = 2 * np.mean((y_pred - y_true) * X)db = 2 * np.mean(y_pred - y_true)return dw, dbdef update(self, dw, db, learning_rate):""" 更新权重和偏置 """self.w -= learning_rate * dwself.b -= learning_rate * dbdef train(self, X, Y, epochs, learning_rate):""" 训练模型 """losses = []for epoch in range(epochs):# 前向传播y_pred = self.forward(X)# 计算损失loss = self.loss(y_pred, Y)losses.append(loss)# 计算梯度dw, db = self.gradient(X, y_pred, Y)# 更新参数self.update(dw, db, learning_rate)# 打印损失if epoch % 100 == 0:print(f'Epoch {epoch}: Loss {loss:.4f}')return losses# 使用上面的LinearModel类进行训练
model = LinearModel()
losses = model.train(X, Y, epochs=1000, learning_rate=0.01)# 绘制损失函数的变化情况
plt.figure(figsize=(8, 6))
plt.plot(losses, label='Loss over epochs')
plt.title('Training Loss Over Time')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()# 使用matplotlib显示拟合结果
plt.figure(figsize=(8, 6))
plt.scatter(X, Y, color='blue', label='Actual Data')
plt.plot(X, model.forward(X), color='red', label=f'Fitted Line (w={model.w:.2f}, b={model.b:.2f})')
plt.title('Fitted Line on Generated Data')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.grid(True)
plt.show()

步骤3:显示拟合结果

  1. 使用Matplotlib绘制生成的训练数据点。
  2. 使用Matplotlib绘制批量梯度下降法得到的拟合直线。
#绘制两种方法的损失函数曲线
plt.figure(figsize=(10, 6))
plt.plot(losses, label='Batch Gradient Descent Loss')
plt.plot(sgd_losses, label='Stochastic Gradient Descent Loss')
plt.title('Comparison of Training Loss Over Time')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()
#比较拟合直线
plt.figure(figsize=(10, 6))
plt.scatter(X, Y, color='blue', label='Actual Data')
plt.plot(X, model.forward(X), color='green', label=f'Batch GD Fitted Line (w={model.w:.2f}, b={model.b:.2f})')
plt.plot(X, sgd_model.forward(X), color='red', label=f'SGD Fitted Line (w={sgd_model.w:.2f}, b={sgd_model.b:.2f})')
plt.title('Comparison of Fitted Lines from Batch GD and SGD')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.grid(True)
plt.show()

步骤4:实现随机梯度下降法

  1. 修改`LinearModel`类为`SGDLinearModel`类,使得每次迭代仅使用一个样本点来估计梯度并更新参数。
  2. 使用新类重复步骤2和步骤3的操作。
class SGDLinearModel:def __init__(self):self.w = np.random.randn()  # 初始化权重wself.b = np.random.randn()  # 初始化偏置bdef forward(self, x_i):""" 计算单个样本的预测值 """return self.w * x_i + self.bdef loss(self, y_pred, y_true):""" 计算单个样本的平方误差损失 """return (y_pred - y_true) ** 2def gradient(self, x_i, y_pred, y_true):""" 计算单个样本的梯度 """dw = 2 * (y_pred - y_true) * x_idb = 2 * (y_pred - y_true)return dw, dbdef update(self, dw, db, learning_rate):""" 更新权重和偏置 """self.w -= learning_rate * dwself.b -= learning_rate * dbdef train(self, X, Y, epochs, learning_rate):""" 训练模型 """num_samples = len(X)losses = []for epoch in range(epochs):epoch_loss = 0indices = np.arange(num_samples)np.random.shuffle(indices)  # 打乱样本顺序for idx in indices:# 随机选择一个样本x_i = X[idx]y_i = Y[idx]# 前向传播y_pred = self.forward(x_i)# 计算损失loss = self.loss(y_pred, y_i)epoch_loss += loss / num_samples  # 平均损失# 计算梯度dw, db = self.gradient(x_i, y_pred, y_i)# 更新参数self.update(dw, db, learning_rate)losses.append(epoch_loss)# 打印损失if epoch % 100 == 0:print(f'Epoch {epoch}: Loss {epoch_loss:.4f}')return losses# 使用SGDLinearModel类进行训练
sgd_model = SGDLinearModel()
sgd_losses = sgd_model.train(X, Y, epochs=1000, learning_rate=0.01)# 绘制损失函数的变化情况
plt.figure(figsize=(8, 6))
plt.plot(sgd_losses, label='Loss over epochs (SGD)')
plt.title('Training Loss Over Time with SGD')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.show()# 使用matplotlib显示拟合结果
plt.figure(figsize=(8, 6))
plt.scatter(X, Y, color='blue', label='Actual Data')
plt.plot(X, sgd_model.forward(X), color='red', label=f'Fitted Line (w={sgd_model.w:.2f}, b={sgd_model.b:.2f})')
plt.title('Fitted Line on Generated Data with SGD')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.grid(True)
plt.show()

实验结果分析

数据可视化

生成的训练数据点在二维坐标系中展示,可以看到数据点大致呈线性分布,但存在一定的噪声干扰。

批量梯度下降法结果

使用批量梯度下降法训练得到的模型能够较好地拟合训练数据,得到的拟合直线接近真实关系式\(Y = 4X + 3\)。

随机梯度下降法结果

使用随机梯度下降法训练得到的模型同样能够拟合训练数据,但在训练过程中损失函数的变化更为波动。最终拟合直线与批量梯度下降法得到的结果相近。

损失函数对比

绘制两种方法的损失函数随迭代次数变化的曲线,可以看到随机梯度下降法的损失函数曲线更加波动,而批量梯度下降法的曲线较为平滑。

结论

通过本实验,我们成功实现了批量梯度下降法和随机梯度下降法来解决一元线性回归问题。两种方法都能有效地拟合数据,但在训练过程中表现出不同的特性。批量梯度下降法虽然收敛较慢,但训练过程更加稳定;而随机梯度下降法则收敛较快,但由于每次迭代仅使用一个样本点,因此训练过程更加波动。在实际应用中,可以根据具体需求选择合适的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795347.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[JavaScript] 事件委托以及 Vue 列表循环事件绑定的性能优化

前言 事件委托(Event Delegation) 是一种通过将事件监听器绑定到父元素,而不是直接绑定到每个子元素上的技术。这样可以减少事件监听器的数量,提升性能,并使得对动态添加或移除的元素更容易进行事件处理。 事件冒泡和事件捕获 事件冒泡:从里往外 <div id="parent…

GT收发器

1.GT触发器的IP使用 第一页 第二页 第三页 GTP IP 提供了两种解决跨时钟域的方法:(1)RX Elastic Buffer(RX 弹性缓冲器);(2)RX Phase Alignment(RX 相位对齐电路),两种方法的比较:RXElastic Buffer优点在于稳定,易使用, 执行相位校准的速度快,但是需要时钟和通道进…

Qt 中实现异步散列器

在很多工作中,我们需要计算数据或者文件的散列值,例如登录或下载文件。 而在 Qt 中,负责这项工作的类为 `QCryptographicHash`。 虽然 `QCryptographicHash `很优秀,但它最大的问题在于其散列值的计算是同步的( 即阻塞 ),对小数据来说并没什么影响,但对大数据来说则意味明…

新建工程——STM32学习笔记2

目录新建工程 添加启动程序Start 添加内核程序Core相关资料下载 下载链接(来自B站江协科技) 通过百度网盘分享的文件:STM32入门教程资料 链接:https://pan.baidu.com/s/17_sbwMXU-xBVFA2sK0smrg?pwd=cgn6 提取码:cgn6STM32开发方式有3种,分别是基于寄存器开发、基于标准…

Java八股复习指南-集合

Java集合 Map HashMap 实现原理/底层 Java1.8之前:数组加链表 Java1.8之后:当一个链表的长度超过8,且数组大小超过64时,会将链表转换成红黑树存储,查找效率更高,时间复杂度O(log n)。如果长度超过8,但是数组容量不足64,则会选择扩容数组。 定位算法 计算key的哈希值,并…

幂函数与指数函数区别

本文来自博客园,作者:cytlz2730,转载请注明原文链接:https://www.cnblogs.com/cytwjyy/p/18407701

zabbix“专家坐诊”第255期问答

问题一 Q:大家好,问一下,zabbix做聚合图的时候,可以做到两根线在一个图里- 吗?还是说只能,单边计算聚合,然后再最后作图的时候,添加两条线上去A:两个数据吗 Q:是的。一个端口,有进有出 A:如果是两个监控项可以做到聚和一起问题二 Q:请问大佬们,这个加密是干嘛的?…

项目管理方法,方法和框架–初学者指南

项目可以通过各种方式成功结束。但是,最好,最受欢迎的项目管理方法,方法和框架总是在变化。新概念无时无刻不在出现。所有成功的项目都包含一整套方法,工具和技术。实际上,作为项目管理从业人员,您一生中可能会使用其中的一种以上。 但是,项目管理方法论,方法和框架不仅…

Nodejs-child_process模块解读

在 Node.js 应用程序中,child 进程模块非常重要,有了它可以实现并行处理,这在资源密集型在 Node.js 应用程序中,child 进程模块非常重要,有了它可以实现并行处理,这在资源密集型任务里十分重要。 在本文中,我们将看一下 child 进程模块,解释其目的、使用方式以及如何使…

HPC环境下数据下载:安全与效率的完美平衡!

在能源行业,高性能计算(HPC)应用广泛,比如地震数据处理、油气藏模拟和建模、地理空间分析、风模拟和地形映射等。随着数据量的爆发式增长,HPC环境下数据下载面临着严峻的安全挑战,如何在保障数据高效流转的同时,确保数据安全,成为能源企业必须面对的重要课题。 高性能…

【干货分享】Ftrans安全数据交换系统 搭建跨网数据传输通道

安全数据交换系统是一种专门设计用于在不同的网络、系统或组织之间安全地传输数据的软件或硬件解决方案。这种系统通常包含多种安全特性,以确保数据在传输过程中的保密性、完整性和可用性。 安全数据交换系统可以解决哪些问题? 安全数据交换系统主要解决以下问题: 数据泄露风…