day34 python深度学习训练优化实践:CPU vs GPU

目录

[深度学习训练优化实践:CPU vs GPU](#深度学习训练优化实践:CPU vs GPU)

一、实验背景

[二、CPU 训练](#二、CPU 训练)

(一)实验设置

(二)实验结果

[三、GPU 训练](#三、GPU 训练)

(一)实验设置

(二)实验结果

[四、优化 GPU 训练](#四、优化 GPU 训练)

(一)减少打印频率

(二)记录间隔实验

[五、call 方法的探索](#五、call 方法的探索)

[(一)call 方法的作用](#(一)call 方法的作用)

[(二)为什么推荐使用 model(x)](#(二)为什么推荐使用 model(x))

六、总结


在深度学习的探索之旅中,训练模型是至关重要的环节,而选择合适的硬件设备(CPU 或 GPU)对训练效率有着显著的影响。今天,我将通过实践对比 CPU 和 GPU 在训练多层感知机(MLP)模型时的性能差异,并探索优化 GPU 训练的方法。

一、实验背景

我选择了经典的鸢尾花数据集作为实验对象。这是一个包含 4 个特征、3 个分类的小型数据集,非常适合用于初步探索和模型验证。模型架构是一个简单的多层感知机(MLP),包含一个输入层、一个隐藏层(10 个神经元)和一个输出层。训练过程中,我设置了 20000 个训练轮数(epoch),并使用随机梯度下降(SGD)优化器和交叉熵损失函数。

在实验中,我详细记录了训练时间以及每个 epoch 的损失值,以便分析 CPU 和 GPU 的性能差异。此外,我还尝试了不同的优化方法,以探索如何提高 GPU 训练的效率。

二、CPU 训练

首先,我在 CPU 上运行了训练过程。以下是详细的实验设置和结果:

(一)实验设置

  • 数据集:鸢尾花数据集(4 个特征,3 个分类)

  • 模型架构:MLP(输入层 -> 隐藏层 [10 个神经元] -> 输出层)

  • 优化器:随机梯度下降(SGD),学习率 = 0.01

  • 损失函数:交叉熵损失

  • 训练轮数:20000 个 epoch

  • 硬件设备:12th Gen Intel Core i9-12900KF(16 核心,24 线程)

(二)实验结果

  • 训练时间:2.93 秒

  • 损失值变化:随着训练的进行,损失值逐渐降低,最终趋于稳定。

通过可视化损失曲线,可以看到损失值随着训练轮数的增加而逐渐减小,这表明模型在不断学习并逐渐收敛。

从图中可以看到,损失值在初始阶段下降较快,随着训练的深入,下降速度逐渐减缓,最终趋于平稳。

三、GPU 训练

为了利用 GPU 的强大计算能力,我将模型和数据迁移到 GPU 上,并重新运行训练。然而,结果让我有些意外:

(一)实验设置

  • 硬件设备:NVIDIA GeForce RTX 3080 Ti

  • CUDA 版本:11.1

  • cuDNN 版本:8005

(二)实验结果

  • 训练时间:11.29 秒

  • 损失值变化:与 CPU 训练类似,损失值逐渐降低并趋于稳定。

为什么 GPU 的训练时间比 CPU 长?经过分析,我发现这主要是由于以下几个原因:

  1. 数据传输开销 :在 GPU 训练时,数据需要从 CPU 内存传输到 GPU 显存,每次获取损失值(loss.item())时,又需要将数据从 GPU 传回 CPU,这增加了大量的数据传输时间。

  2. 核心启动开销:GPU 的每个操作都需要启动一个核心,而核心启动本身存在固定开销。对于简单的模型和小数据集,这些开销占据了相当大的比例。

  3. 性能浪费:由于数据量较小,GPU 的很多计算单元没有被充分利用,导致其并行计算能力无法发挥出来。

四、优化 GPU 训练

为了减少数据传输开销,我尝试了两种优化方法:

(一)减少打印频率

我减少了打印训练信息的频率,只在每 200 个 epoch 打印一次损失值,而不是每个 epoch 都打印。这样可以减少数据从 GPU 到 CPU 的传输次数,从而节省时间。优化后的训练时间缩短到了 10.38 秒,虽然仍有差距,但已经比之前有所改善。

(二)记录间隔实验

我进一步进行了实验,观察记录间隔(即每隔多少个 epoch 记录一次损失值)对训练时间的影响。实验结果如下:

记录间隔(轮) 记录次数(次) 剩余时长(秒)
100 200 10.43
200 100 10.02
1000 20 10.12
2000 10 9.74

从实验结果可以看出,记录次数和剩余时长之间并没有明显的线性关系。这可能是因为 loss.item() 是一个同步操作,GPU 需要等待 CPU 完成才能继续运算,但这种等待时间并不是简单的线性累加。

五、__call__ 方法的探索

在 PyTorch 中,模型的前向传播可以通过调用 model.forward(x)model(x) 来实现。实际上,model(x) 是通过 __call__ 方法实现的,它会调用模型的 forward 方法来完成前向计算。这种设计使得模型可以像函数一样被调用,同时保留了对象的内部状态。例如,nn.Linearnn.ReLU 等组件都继承自 nn.Module,它们都定义了 __call__ 方法,从而可以像函数一样被调用。

(一)__call__ 方法的作用n

__call__ 是 Python 中的一个特殊方法,它允许类的实例像函数一样被调用。这种特性使得对象可以表现得像函数,同时保留对象的内部状态。例如:

python 复制代码
class Adder:
    def __call__(self, a, b):
        print("执行加法操作")
        return a + b

adder = Adder()
print(adder(3, 5))  # 输出: 8

在 PyTorch 中,nn.Module 类定义了 __call__ 方法,当调用 model(x) 时,实际上会调用 model.__call__(x),而 __call__ 方法会进一步调用 model.forward(x)。这种设计不仅保持了代码的一致性,还允许在调用过程中插入额外的逻辑(如钩子函数)。

(二)为什么推荐使用 model(x)

PyTorch 官方强烈建议使用 model(x) 而不是直接调用 model.forward(x),因为 model(x) 会触发完整的前向传播流程,包括钩子函数的执行。钩子函数是一种强大的工具,可以在前向传播和反向传播过程中插入自定义逻辑,用于调试、监控或修改梯度等操作。

六、总结

通过今天的实验,我深刻体会到了 CPU 和 GPU 在不同场景下的性能差异。对于小型数据集和简单模型,CPU 的单核性能和低开销使其更具优势;而对于大型数据集和复杂模型,GPU 的并行计算能力则能够显著提升训练效率。同时,我也学会了如何通过减少数据传输和优化打印频率来优化 GPU 训练过程。此外,对 __call__ 方法的理解让我对 PyTorch 的设计有了更深入的认识。在未来的学习中,我将继续探索更多优化技巧,以充分发挥 GPU 的潜力,提高深度学习模型的训练效率。同时,我也会更加关注模型架构和数据处理方法,以进一步提升模型的性能。

@浙大疏锦行

相关推荐
老唐7778 分钟前
图解深度学习 - 人工智能、机器学习和深度学习
人工智能·深度学习·机器学习
jay神10 分钟前
基于Python+YOLO模型的手势识别系统
开发语言·python·深度学习·yolo·手势识别系统
视觉AI18 分钟前
Jetson系统烧录与环境配置全流程详解(含驱动、GCC、.Net设置)
linux·人工智能·ubuntu·计算机视觉·.net
Mr.看海25 分钟前
看海回测系统回测过程
人工智能
张彦峰ZYF33 分钟前
解锁内心的冲突:神经症冲突的理解与解决之道
学习
晓晓不觉早37 分钟前
解码AI教育革命的核心价值链:算法、神经界面与数字基建
人工智能
小石(努力版)39 分钟前
嵌入式STM32学习——ESP8266 01S的基础介绍
stm32·嵌入式硬件·学习
点云兔子41 分钟前
使用 OpenCV 实现 ArUco 码识别与坐标轴绘制
人工智能·python·opencv
数数科技的数据干货1 小时前
如何避免你的高付费用户活跃度下降?
大数据·人工智能·游戏
IT古董1 小时前
【漫话机器学习系列】274.基尼指数(Gini Index)
人工智能·机器学习