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.Linear 和 nn.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 的潜力,提高深度学习模型的训练效率。同时,我也会更加关注模型架构和数据处理方法,以进一步提升模型的性能。

@浙大疏锦行

相关推荐
学编程就要猛4 小时前
RAG(Retrieval Augmented Generation)
人工智能·spring ai
冬奇Lab4 小时前
一天一个开源项目(第259篇):Modern Web Guidance —— Chrome 团队给 AI 编码 Agent 的「现代 Web 最佳实践」外挂
人工智能·开源
§ꦿCFོ༉6 小时前
嵌入式八股文—C语言入门学习(四)
c语言·开发语言·学习
飞哥数智坊6 小时前
打造自己的 Personal Agent,从抄大肥鱼作业开始
人工智能·deepseek
投图匠6 小时前
十种不同风格的 CTP接口 框架
开发语言·python
对你无可奈何6 小时前
Magpie 实战:把 WorkBuddy、Qoder 的订阅模型接进一个本地池子
人工智能
Csvn6 小时前
每请求到底多少钱?把 Token 成本算成可观测指标(C01)
人工智能·aigc·agent
吴名氏.6 小时前
Java转AI Agent教程(第十三周)混合检索与重排
java·数据库·人工智能·ai·ai agent
2601_967980117 小时前
深入理解词向量算法及其在Python中的实践
python·word2vec·glove·fasttext·词向量