为什么GPU很快,在机器学习中至关重要?

为什么GPU很快

模型通常具有许多很多参数。例如,流行的VGG图像分类模型有大约1.4亿个参数,分为16层!在运行推理(预测)时,你需要将输入数据(图像)传递到每个图层,通常将该数据乘以图层参数。在训练期间,你还必须稍微调整每个参数以更好地拟合数据。那是很大的计算量!

CPU很擅长快速完成一些事情。 这通常很好,有足够的分支(如果用户这样做,那样做),以及大规模并行性实际上不可能的其他顺序约束。GPU很好,可以做很多“慢”的事情。由于它们最初用于执行图形要求,因此它们希望一次性完成大量工作(考虑将图像的所有像素转换为灰度)。所以这里有一个权衡,对于ML来说GPU由于可以并行完成这些巨大的算术运算而赢得大量时间。

具体来说,我的macbook有一个运行速度为3.1Ghz且有4个内核的CPU。NVidia K80 GPU拥有近5000个内核,尽管运行速度要慢得多——562Mhz。虽然这样做并不公平,但你可以看到K80的时钟速度大约慢了6倍,但是并行速度提高了1250倍。

如何考虑GPU

PyTorch不是代码行“GPU -> on”,而是“CUDA”张量。CUDA是一个用于在GPU上执行操作的库。基本上,PyTorch要求你声明要在GPU上放置的内容,然后你可以像往常一样执行操作。所以我想,让我们试着在GPU上添加一个图层:


我运行代码,立即得到了一个错误在隐藏层计算中:


为什么?我立即知道它与我添加的.cuda()代码有关,但我不知道为什么。在思考了GPU应该如何加速之后,我意识到,“当然它不工作,一个张量在GPU上,另一个仍然在主内存中!”一切都搞定了。Pytorch允许在GPU内存中分配张量,然后使用GPU对这些张量进行操作。但是这些操作的结果会怎样呢?让我们试试另一个例子:


这是GPU内存中的另一个张量!经过更多的反复试验,我发现我不得不改变自己的思维方式。原来我以为内存、CPU和GPU都混在一起了:


我意识到我需要这样想:


从本质上说,CPU/主内存和GPU/GPU内存都位于各自的小宇宙中。来自软件工程背景的我开始将GPU操作看作是一个REST API。当你使用REST API时,真正的成本是来回发送数据。在本地执行任务的速度与在远程服务器上执行任务的速度一样快。但是你要避免的是大量的数据来回传输,因为这是纯粹的开销。

把这个类比往前推,我们可以看到,当然,PyTorch matmul结果是一个GPU张量是有道理的。这样就很容易在GPU上进行进一步的操作,而不需要将数据传送到主内存,然后再返回到GPU。所以如果我们想要使用GPU,我们真的想要GPU上的所有参数,因为这些参数将会被反复使用在前向传递中产生预测然后在后向传递中更新。每一批特性都必须被传送到GPU内存中。但是中间的和最终的结果(比如隐藏层的输出)只能存在于GPU内存中。我们需要做的就是不断向GPU发送命令,告诉它如何操作参数和权重。

因此,在API类比中,我们只做两个“重”请求(在上图中加星标的),一个用于初始化权重,另一个用于在训练后获得最终权重。但我们可能会在这两个请求之间发出数百万个轻量级请求来训练模型。

GPU性能的提高是真实的,而且是惊人的

那么什么样的加速是可能的呢?PyTorch有一个不错的MNIST小例子我们可以使用。使用CPU运行10个epoch需要153秒,使用GPU需要83秒。我们可以从理论上说,更大的模型可以获得更大的收益。 不错,真不错。

一些实验

这一切都很棒。经过一番思考和一些糟糕的绘图之后,我对GPU的了解要好得多,并且所需的额外编码也不错。但我的想法是否正确?回答这个问题的最好方法是建立一个实验。我想向自己证明运输数据是工作流程的“缓慢部分”。所以我尝试了以下三件事:
1.做一个200x200矩阵乘以numpy,一个高度优化的CPU线性代数库。
2.使用PyTorch cuda张量在GPU上进行200x200矩阵乘法运算。
3.使用PyTorch cuda张量在GPU上进行200x200矩阵乘法运算,每次都来回复制数据。

正如预期的那样,GPU只运行得更快,这一次大约是6倍。有趣的是,1和3几乎花费了相同的时间。GPU操作的效率几乎完全被数据传输的低效所平衡!

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java