神经网络优化实战:代码助手性能对比分析

本文详细记录了作者使用Gemini和Claude两种代码助手优化神经网络可视化性能的过程。通过将激活模式哈希计算从CPU迁移到GPU,实现了显著的加速效果,并对比分析了两种助手的代码实现差异和优化策略。

理解神经网络系列第5篇:代码助手对比评测

在之前的系列博客文章[1, 2, 3, 4]中,我进行了一些实验,绘制全连接Leaky ReLU网络在训练过程中生成的多面体边界。当我尝试将实验扩展到更大的网络时,发现代码运行速度显著下降,原因是激活模式哈希计算在CPU上进行——每个训练步骤很快,但可视化时一切都会陷入停滞,每个像素都需要前向评估神经网络(总共1024*1024次),每当预测计算完成后,都需要将激活模式传输到CPU执行哈希计算。这个过程非常缓慢,且无法并行化。

我曾考虑编写自定义CUDA代码来加速——没有理由存储激活模式或传输它,正确的解决方案是在前向传播过程中实时计算哈希,理想情况下使用具有可交换更新函数的哈希,这样不同ReLU神经元更新哈希的顺序就不重要了。

但这是个业余项目,我暂时没有时间做太复杂的事情。因此我决定,在实施任何复杂方案之前,先看看我常用的两个代码助手能否帮我解决问题。

我创建了两个不同的目录,将相同的基础代码库检出到两者中,分别创建分支,然后使用以下提示词查询Gemini CLI和Claude Code:

该目录中的Python脚本训练一个全连接Leaky ReLU网络来重现输入图像,并绘制说明ReLU在输入空间中创建的折痕生成的多面体边界的图片。不幸的是,生成多面体可视化的代码很慢,因为它涉及1024*1024次神经网络前向评估,然后需要将激活模式哈希以唯一标识像素所在的多面体。

我希望通过将哈希计算嵌入到GPU上的前向传播过程中来加速计算,而不是在最后计算激活模式的哈希。这可能通过PyTorch钩子实现,但我不确定具体方法。

我知道如果运行python3 ./draw-poly-while-training.py --input ./centered_ring.png --shape [100]*20 --epochs 30 --seed 12345678 --points 5050 --save-interval 10,输出大致如下:

(显示训练日志,其中可视化步骤耗时超过一分钟)

分析表明大部分时间花费在CPU哈希计算上,而不是GPU。我希望你找到一种在GPU上前向传播过程中计算哈希的方法,理想情况下不将激活向量存储在内存中,而是使用可交换更新函数的哈希,使每个ReLU单元在计算前向传播时可以更新最终哈希。

我希望你:

  1. 制定改进和加速代码的合理计划
  2. 实施该计划
  3. 使用指定命令行重新运行脚本,观察是否确实实现了加速——例如检查(a)可视化是否加速(b)10个训练步骤和可视化的总时间是否加速

很容易加速可视化步骤但却大幅减慢训练步骤,导致10个训练步骤和1个可视化步骤的总时间变慢。

还请验证更改前后版本的图像输出是否相同,以确保更改不会破坏任何功能。

然后我让两个模型运行了一段时间。两个模型都提供了更改,但Gemini未能实际验证结果是否相同。Claude一次性解决了问题;Gemini需要以下额外提示:

我运行了你的示例代码并检查了输出。更改前后版本的输出图像不一致,甚至训练损失也发生了变化。值得注意的是,你的版本中看不到任何多面体。你能重新检查你的工作,这次确保检查输出是否相同吗?

有了这个额外的推动/提示,模型提供的解决方案完美运行,甚至比Claude版本稍微快一点。

让我们看看两个模型生成的代码:Gemini分支和Claude分支。阅读更改后,一些情况变得清晰:

  • Gemini在RNG上自找麻烦,生成了一堆随机哈希系数,这打乱了RNG的状态,因此训练运行在更改前后不再具有可比性
  • Gemini使用torch.matmul进行哈希计算,而Claude计算哈希为torch.sum(A * B)
  • Claude将代码分解为更多小函数,而Gemini没有。Claude的代码可读性稍好,Gemini的更改更精简

有趣的是,两个解决方案都不是我最初设想的那样,但它们目前已经足够好,并且比我最初编写的代码提供了相当显著的加速。这是我第一次遇到编码助手以非平凡的方式帮助我优化代码,这确实值得关注。

无论如何,通过这些优化,我现在可以在稍大的神经网络上运行我的数据可视化电影生成,参数达到数百万,为后续研究铺平道路。我现在需要弄清楚如何以编程方式上传YouTube视频,但在此期间,这里有一个视频展示了在"圆圈绘制"任务上训练100神经元、10层深度网络的过程。随机编码改变了我的线条颜色,但这没关系。

像往常一样,这个视频中的问题比答案多。最让我困惑的是后期训练中相对的"不稳定性"。这在"闪烁"中可见,似乎随机地SGD步骤会碰到明显更高的损失,部分屏幕变黑,损失飙升,然后训练需要恢复。有趣的是,在这些情况下多面体的几何形状变化不大,但许多多面体上的线性函数同时发生变化,对整体性能非常不利。

一旦程序化上传工作正常,我将上传更多视频,因为我有一个有趣的观察:当训练发散时(对于更大更深的网络),发散首先从搞乱线性函数开始,只有在它们完全混乱后,多面体的几何形状也开始变得混乱。

到此为止!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计