最全的深度学习硬件指南

ylxthr 8年前

来自: http://www.almosthuman.cn/2016/02/04/bqrzz/

深度学习计算密集,所以你需要一个快速多核CPU,对吧?还是说买一个快速CPU可能是种浪费?搭建一个深度学习系统时,最糟糕的事情之一就是把钱浪费在并非必需的硬件上。本文中,我将一步步带你了解一个高性能经济系统所需的硬件。研究并行化深度学习过程中,我搭建了一个GPU集群,为此,我需要仔细挑选硬件。尽管经过了仔细的研究和逻辑推理,但是,挑选硬件时,我还是

会犯相当多的错误,当我在实践中应用集群时,错误就会显现出来。下面就是我想分享的所得的东西,希望你们可以不要再掉入同样的陷阱。

GPU

在这篇博文中,我们假设你会利用GPU来进行深度学习。如果你正在构建或升级你的深度学习系统,忽视GPU是不理智的。GPU正是深度学习应用的核心要素——计算性能提升上,收获巨大,不可忽视。

我在以前的博客中详细谈到过GPU的选择,GPU的选择也许是深度学习系统中最关键的选择。作为目前市场上现有GPU的最佳选择:通常来说,如果你缺钱,我推荐到eBay上购买GTX 680,或者GTX Titan X(如果你很有钱,用来做卷积),或者GTX 980(非常有性价比,但对于大型卷积神经网络有点局限),另外如果你需要一个低成本的存储则可以选择GTX Titan。我之前支持过GTX 580,但是由于cnDNN库的新升级大幅增加了卷积的速度,所有不支持cuDNN的GPU都得淘汰了——GTX 580就是这样的一个GPU。如果你完全不使用卷积神经网络,那么GTX 580仍然是一个可靠的选择。

嫌疑人列表 :你能够识别哪个硬件是糟糕表现的罪魁祸首吗?这些GPU之一?或者还是CPU的错?
</figure>

CPU

为了能够明智地选择CPU我们首先需要理解CPU,以及它是如何与深度学习相关联的,CPU能为深度学习做什么呢?当你在GPU上跑深度网络时,CPU进行的计算很少,但是CPU仍然需要处理以下事情:

  • 在代码中写入并读取变量
  • 执行指令,如函数调用
  • 启动在GPU上函数调用
  • 创建小批量数据
  • 启动到GPU的数据传输

CPU核需要的数字

当我用三个不同的库训练深度学习网络时,我经常观察到一个CPU线程显示100%(并且有时候另一个线程会在0-100%之间浮动),你会立刻知道大部分深度学习库——以及实际上大部分通用软件应用——仅仅只利用一个线程。这意味着多核CPU非常无用。但如果你运行多GPU,并使用像MPI一样的并行化框架,那么你就同时运行了多个程序,你也会需要多个线程。每个GPU跑一个线程还不错,但是每个GPU跑两个线程对于大部分深度学习库来说能够带来更好的表现;这些库在单核上运行,但是某些时候调用异步函数则会用到第二个CPU线程。记住许多CPU能够在每一个核上运行多个线程(尤其对于Intel的CPU来说),因此每个GPU对应一个CPU核通常就够了。

CPU与PCI-Express

这里是个陷阱!一些新型号Haswell CPU并不支持全部40个PCIe通道,而旧的CPU则可以——避免这些CPU,如果你希望建立一个多GPU的系统。并且确保你的处理器确定能够支持PCIe3.0,如果你的主板采用PCIe 3.0。

CPU缓存大小

正如我们之后会看到的,CPU缓存大小与CPU-GPU下游管道运算没有关联,但是我会用一个简短的部分来说明,这样我们就可以确定计算管道上每个可能的瓶颈都被考虑在内,从而全面理解整个过程。

人们购买CPU时经常会忽视缓存这个问题,但是通常来说,它在整个性能问题中是非常重要的一部分。CPU缓存是容量非常小的直接位于CPU芯片上的存储,物理位置非常接近CPU,能够用来进行高速计算和操作。CPU通常有缓存分级,从小型高速缓存(L1,L2)到低速大型缓存(L3,L4)。作为一个程序员,你可以将它想成一个 哈希表 ,每条数据都是一个键值对(key-value-pair),可以高速的基于特定键进行查找:如果找到,就可以在缓存得值中进行快速读取和写入操作;如果没有找到(被称为缓存未命中),CPU需要等待RAM赶上,之后再从内存进行读值——一个非常缓慢的过程。重复的缓存未命中会导致性能的大幅下降。有效的CPU缓存方案与架构对于CPU性能来说非常关键。

CPU是如何决定它的缓存方案是一个复杂的话题,但是通常来说,重复使用的变量、指令和内存RAM地址会保存在缓存中,而不经常出现的则不会。

在深度学习中,相同的内存范围会重复进行小批量读取直至送至GPU(同时这块内存范围会被新数据覆盖),但是内存数据是否能够被存储在缓存中则取决于小批量大小。对于128的小批量的大小,我们会有相对应的0.4MB的MNIST数据与1.5MB的CIFAR数据,能够放入大部分CPU缓存。对于ImageNet来说,我们每个小批量有超过85MB的数据,这即使对于最大的缓存(L3缓存仅仅有几个MB)来说也实在太大了。

由于通常数据集对于缓存太过巨大,新的数据需要从RAM中每次读取一小部分新的——因此需要能够持续访问RAM。

RAM记忆地址存储于缓存中(CPU能够在缓存中迅速查找,指出数据在RAM中的确切地址),但是这只在整个数据集都存储于RAM的时候才能如此,否则记忆地址会改变,缓存也不会加速(利用固定记忆则可以避免这种情况,但是就像你之后会看到的,其实没有太大关系)。

深度学习代码的其他部分——例如变量与函数调用——则会从缓存中受益,但是这些通常都数量很少,并且很容易就存储于小型快速L1缓存的几乎所有CPU。

从这些推论中可以总结一下,CPU缓存大小并不是很重要,下一部分的深层分析则会继续阐述这个结论。

CPU时钟频率(频率)

当人们想到快速的CPU时,他们一般最先想到其频率(clock rate)。4GHz比3.5GHz快,是这样吗?在比较具有相同结构的处理器时,这一般是对的,例如「第三代酷睿处理器」(Ivy Bridge)。但在比较不同架构的处理器时,这个想法却不那么正确。而且这也不是对于性能的最好测量。

在深度学习中,只有很少一部分的计算会用CPU来运行:增加几个变量、评估几个布尔表达式、在GPU或在编程里面调用几个函数——所有这些会取决于CPU核的频率。这种推理似乎是合理的,但当我运行深度学习编程的时候,CPU会有100%的使用率,那这是怎么回事儿?为了找出原因,我做了几个CPU核频率降频的实验。

CPU降频后在MNIST及ImageNet的表现:使用不同的CPU核频率,将MNIST数据集运行200遍或遍历1/4的ImageNet数据集运行作为测量时间,我们测量CPU的性能,其中我们将每个CPU的最高频率定位参照线。对于比较:在性能上,GTX Titan比GTX 680提升了15%;GTX 980比GTX Titan提升了20%;GPU超频比任何GPU提升了5%。

问题是:为什么当CPU核频率是无关紧要的的时候,它会用上100%的使用率?答案可能是CPU的高速缓存未命中:一般情况下,CPU会不断忙于访问RAM,与此同时,它需要一直等待频率较慢的RAM跟上步伐,因此这可能导致了即忙碌又等待的矛盾状态。如果真是这样,将CPU核降频将不会导致其性能的大幅降低——正如你在上面看到的结果。

CPU也执行其他一些操作,如将数据抄到微型批次里,并将数据准备好以便复制到GPU,但这些操作其实只取决于内存频率,而不是CPU核频率。所以我们现在来看看内存。

RAM时钟率

CPU-RAM以及与RAM的其他一些相互作用都挺复杂的。我在这儿会给出该处理过程的一个简化版。为了得到一个更为全面的理解,让我们潜入其中并剖析从CPU RAM 到 GPU RAM的这一过程。

CPU的内存频率与RAM是相互交织的。CPU的内存频率决定了RAM的最大频率,并且这两个东西结合起来构成了CPU整体内存的带宽。但通常RAM本身决定了整体可使用的带宽,因为它比CPU的内存频率更慢。你可以如此决定带宽:

其中64是64位的CPU结构。而我的处理器及RAM模块的带宽是51.2GB/s

但是,带宽只在你复制大量数据的时候才有关。通常在RAM的计时——例如8-8-8——对于小量的数据会比较相关,并且决定了你的CPU需要等待多长时间才能让RAM可以赶上。但正如我上文所述,来自深度学习程序的几乎所有数据要么很容易地融入到CPU的缓存里,要么因为太大而难以在缓存中获益。这意味着计时将不太重要,而带宽可能才是重要的。

那么这与深度学习程序有什么关系?我刚刚说带宽有可能是重要的,但在下一个步骤中,它还不是那么重要。RAM的内存带宽决定了小批次被改写并被配置到用于初始化GPU的转移上有多快,但在下一个步骤,从 CPU-RAM到GPU-RAM,就真正是瓶颈了,此步骤利用了直接存储器存取(DMA)。正如上面所引述的,我的RAM模块的内存带宽是51.2GB/s,但DMA带宽却只有12GB/s!

DMA带宽与一般的带宽相关,但细节是不太需要知道的,你可以看看这个 维基百科条目 (http://en.wikipedia.org/wiki/DDR3_SDRAM#JEDEC_standard_modules),你能在此查阅RAM模块的DMA带宽(峰值传输限制)。但先让我们看看DMA是如何工作的。

直接存储存取(DMA)

具有RAM的CPU只能透过DMA与GPU通信。第一步,一个特定的DMA传输缓冲器在CPU RAM 及 GPU RAM里被保留;第二步,CPU将所请求的数据写入CPU那边的DMA缓冲器里;第三步,受保留的缓冲器在没有CPU的帮助下被转移到GPU RAM里。你的PCIe的带宽是8GB/s (PCIe 2.0) 或是 15.75GB/s (PCIe 3.0),所以你需要买一个像上面所说的,具有良好峰值传输限制的RAM吗?

并不需要。软件在这里起着很大的作用。如果你用聪明的方法来做一些传输的话,你将不再需要用更便宜但也更慢的内存。

异步迷你批次分配

一旦你的GPU在当前的迷你批次中完成计算,它希望马上计算下一批迷你批次。现在你当然可以初始化一个DMA传输,然后等待传输完成以使你的GPU可以继续处理数字。但有一个更有效的方法:提前准备好下一批迷你批量以便让你的GPU完全不再需要等待。这可以在不降低GPU性能的情况下轻易并异步地完成。

异步迷你批次分配的CUDA代码:当GPU开始处理当前批次时,头两个调用被执行;当GPU处理完当前批次以后,最后两个调用被执行。数据传输早在数据流在第二步被同步的时候完成了,所以GPU可以无延迟的开始处理下一个批次。

在ImageNet 2012,对于Alex Krishevsky的卷积网络,一个大小为128的迷你批次只用了0.35秒就进行了一次完整的反向传播。我们能在这么短的时间里分配下一个批次吗?

如果我们采用大小为128的批次及维度为244x244x3的数据,大概总量为0.085 GB。采用超慢内存,我们也有6.4GB/s的速度,或换句话说,每秒75迷你批次!所以有了异步迷你批次分配,即使是最慢的RAM对于应付深度学习也是绰绰有余了。只要采用了异步迷你批次分配,买较快的RAM模块将没有任何好处。

这个过程还间接意味着CPU缓存是不相关的。对于DMA传输来说,CPU可以多快改写(在高速缓存中)及准备(将缓存写入RAM)一个迷你批次是无关紧要的,因为整个传输早在GPU要求下一个迷你批次前就完成了——所以大型缓存真的不是那么重要。

因此底线是RAM的频率是不相关的。买那些便宜的吧——故事完结。

但你需要购买多少呢?

内存大小

你应该拥有至少和你的GPU内存大小相同的内存。你能用更小的内存工作,但是,你或许需要一步步转移数据。不过,就我个人经验而言,内存越大,工作起来越舒服。

心理学家告诉我们,专注力这种资源会随着时间的推移而逐渐耗尽。内存就是为数不多的,让你保存注意力资源,以解决更困难编程问题的硬件之一。与其在内存瓶颈上兜转,浪费时间,不如把注意力放在更加紧迫的问题上,如果你有更多的内存。有了这一前提条件,你可以避免那些瓶颈,节约时间,在更紧迫问题上投入更多的生产力。特别是在Kaggle竞赛中,我发现,额外的内存对特征操作非常有用。所以,如果你有钱而且做了很多预处理工作,那么,额外内存或许会是不错的选择。

硬盘驱动器/SSD

在一些深度学习案例中,硬驱会成为明显的瓶颈。如果你的数据组很大,通常会在硬驱上放一些数据,内存中也放一些,GPU内存中也放两mini-batch。为了持续供给GPU,我们需要以GPU能够跑完这些数据的速度提供新的mini-batch。

为此,我们需要采用和异步mini-batch分配一样的思路。用多重mini-batch异步读取文件 ——这真的很重要!如果不异步处理,结果表现会被削弱很多(5-10%),而且让你认真打造的硬件优势荡然无存 ——在GTX680上,好的深度学习软件跑起来会快得多,胜过GTX980上的糟糕的深度学习软件。

出于这种考虑,如果我们将数据保存为32比特浮点数据,我们会遇到Alex参加ImageNet 所使用的卷积网络遇到的数据传输速率问题,大约每0.3秒0.085GB,计算如下

或每秒290MB。

不过,如果我们将它保存为jpeg数据,我们就能将它压缩5-15,将所需读取带宽降低到大约30MB每秒。类似,一个人能够使用mp3或者其他压缩技巧来处理声音文件,但是,对于其他处理原始32比特浮点数据的数据组来说,不可能很好地压缩数据:我们只能压缩32比特的浮点数据的10-15%。因此,如果你有大的32比特数据组,那么,你肯定需要SSD,因为100-150MB/S的硬驱会很慢,不足以跟上你的GPU——因此,如果你的工作需要处理这样的数据,买个SSD吧,否则的话,一个硬驱就够了。

许多人买一个SSD是为了求得安慰:程序开始和响应都快多了,用大文件预处理也快很多,但是,对于深度学习来说,仅当你的输入维数很高,不能充分压缩数据时,这才是必须的。

如果你买了SSD,你应该选择能够hold住和你通常要处理的数据组大小相当的数据组,也额外留出数十GB的空间。让硬驱也保存未使用过的数据组,这个主意也不错。

电源供应设备(PSU)

一般说来,你需要一个足以容纳你未来所有GPU的PSU。随着时间的推移,GPU通常会更节能;因此,尽管其他组件会需要更换,PSU会用很久,因此,一个好的PSU就是一个好的投资。

CPU和GPU所需瓦特,加上其他组件额外所需以及作为电力峰值缓冲的100-300瓦特,你就能计算出所需瓦特。

一个需要注意的重要部分就是,你的PSU的PCIe接头是否支持一个带有连接线的8pin+6pin的接头。我买了一个PSU,有6x PCIe端口,但是,仅能驱动8pin或者6pin的接头,所以,我没法用那个PSU跑4个GPU。

另一个重要的事情就是买一个带高电效额定功率的PSU,——特别是,如果你跑很多GPU而且还要跑比较久。

在全功率(1000-1500瓦特)上跑一个4 GPU系统,训练一个卷积网络两个礼拜,会消耗300-500kWh,在德国——电力成本更高,每kWh多出20美分——会花60到100欧元(66到111美元)。如果这就是百分百高效的价格,那么,用80%的电能供应来训练这样一个网络将额外增加18到26欧元的成本!就单个GPU来说,这算少得多了,但是,要点仍然在于 ——将更多的钱花在高效电力供应上,会很有意义。

散热

散热十分重要,这更有可能成为一个大瓶颈,比糟糕的硬件选择更能降低系统表现。你可能会满足于给CPU配一个标配的散热片,但是,要给你的GPU配些什么东西,需要特别的考量。

当现代的GPU运行一个算法时,它会一直加速至最大极限,同时加速的还有能量消耗,但当到达它的温度壁垒,通常是80摄氏度,它就会降低运行速度来避免打破临界温度。在保护GPU避免过热,情况安全的同时,也让GPU达到最佳状态。

但是,典型的电扇速度设置是被预编程的,完全不是为运行深度学习项目而设计的,所以,在开始深度学习程序后的几秒内就会达到其温度壁垒。结果,GPU的运行效果下降(几个百分比),如果有好几个GPU,由于GPU之间会相互加热,那么,整体效果的下降幅度就会非常明显(10-25%)。

由于NVIDIA GPU是首屈一指的电竞GPU,它们非常适应Windows系统的运作。Windows系统中,你可以通过几个简单的按键改变风扇的设置,但在Linux系统中不行,不过,多数深度学习库都是在Linux环境下运行的,所以,这会导致问题出现。

最容易且性价比最高的变通方案就是,用一个有更合理风扇设定的BIOS程序刷新GPU以提升速度,这样就能保持GPU凉爽的同时,噪声也不至于不可接受(如果你用服务器解决这个问题,那么,为了避免难以忍受的噪声,你很可能会在风扇运行速度上打折)。也可能超速运行GPU的存储器,这会有一些噪声(30-50兆赫),这也是相对可行的方法。刷新BIO的软件是一个在Windows系统下设计的程序,但可以用wine操作系统从Linux/Unix OS中调用这个程序。

另一个选择是为你的Xorg 服务器(Ubuntu操作系统)设置配置(set a configuration),在这个Xorg 服务器中,你可以设置你的破解登陆档「冷却登陆档」选项,在只有一台GPU的时候这个方法非常有效,但如果对多台GPU,也就是说有「无头(headless)」GPU,比如这些「无头」GPU没有连显示器,那么,就必须模拟一台显示器,但模拟显示器难度非常之高。我曾试了很久,无比沮丧的花了数个小时,用实时(live)启动光盘恢复我的图形设置,始终没法让这个程序在「无头」GPU上正常运转。

还有一个方法花费较高也较为复杂,就是用水冷却。应用于单台GPU时,即使是最大功率的情况,也几乎可以将温度减半,所以永远不会达到温度壁垒。就算是面对空气降温无法应对的多台GPU情况,也可以很好的降温。用水降温的另一个好处是,它运行时产生的噪音较少,如果你处在一个与他们共同工作的环境下,这将带来极大的方便。水冷却法花费大约为每台GPU100美元左右,前期大概要额外投入50美元。水冷却法还需要另花一些精力组装电脑,对于这方面可以找到很多详尽实用的指南手册,且花不了几个小时。维护期间就会简单方便许多了。

从我的经验来看,这些是比较对症下药的方法。我为我的深度学习组团买过大型的塔型机箱,因为他们在GPU的区域有另加的风扇,但我发现,这并没有太大的作用,大约只降低了2-5摄氏度,不值得为此花这么多钱还得忍受笨重的机箱。最重要的还是直接作用于GPU的降温措施:刷新BIOS,水冷法或忍受打了一定折扣的运行效果,在某些场合下,这些都是非常有效的应对方式,你可以根据自己需要找到合适的应对方式。

主板和电脑机箱

你的主板要有足够的PCIe接口,来支持你希望同时运行的GPU(通常局限于4台GPU,即使主板上有很多PCIe槽。记住,多数GPU都需要占2个PCIe槽的宽度,例如,你会需要7个槽口来链接4台GPU。PCIe2.0接口也是可以接受的,但PCIe3.0接口更加经济,即使只对单台GPU性价比也很高,如果对多台GPU,通常都会使用PCIe3.0接口,这会对多GPU运作带来很大的好处,因为PCIe的连接口多数情况下是个瓶颈所在。

主板的选择策略十分直接:就选那款可以支持你想要的硬件配置的就行。

当你选择机箱时,要确保它能满足你GPU的长度要求,这样它才能在主板上放稳。多数机箱都能满足要求,但如果你选的是一个小机箱,就需要多加留心。要核查它的面积和规格,你也可以尝试用google的图片搜索引擎,看看能不能找到GPU放在上面的图片。

显示器

我刚开始也觉得写显示器是件很滑稽的事,但他们确实会造成很大的影响,非常重要,不能忽视。

我在我的3’27英寸显示器上付出了可能是我从未有过的大代价,买了几个显示器后,效率提高了很多。如果我只能用一台显示器,那我的效率肯定会大打折扣。别在这上面和自己过不去,如果你不能有效的运转一个快速的深度学习系统,那它又有什么好处?

我进行深度学习系统工作时,显示器的典型布置一般是:Papers、Google搜索、gmail、stackoverflow网在左,中间是编程界面,右边是输出窗口、R、文件夹、系统显示器、GPU显示器、待办事项以及一些其他小的应用。

组装电脑的其他感言

很多人都会被组装电脑这个主意吓到,硬件很贵,谁都不想花冤枉钱。但道理其实很简单,本就不该装在一起的硬件没法凑合。手动主板上的每个组件都有特定的位置,如果你没有经验也可以找到很多指南、手把手的教程视频教你。

关于组装电脑有一个很大的好处就是,只要你组装过一次,你就会知道各个组件的位置,因为电脑都是以非常相似的方法组建起来的——所以组装电脑可以成为一个重复应用相伴一生的技能。还有什么理由不试一下呢!

结论 / TL;DR

GPU

GTX 680 或者GTX 960 (穷); GTX 980 (表现最佳); GTX Titan (如果你需要存储器的话); GTX 970 (no convolutional nets)。

CPU

每个 GPU2个线程; 全套40 PCIe 线路和合适的PCIe 配件(和底板配套); > 2GHz; 快速缓冲储存区不用太在意。

内存

使用异步mini-batch配置; 时钟频率和计时无关紧要;购入和你已有GPU内存大小相同的CPU内存。

RAM

用异步分批处理配给方式;时钟速率和计时器不必太在意;既然已经有了GPU RAM就不需要买CPU RAM了。

硬驱/SSD

使用异步batch-file读取和压缩数据,如果你有图像或声音数据;除非你处理的是带有大输入维度的32比特浮点数据组,一个硬驱就可以了。

PSU

GPU+CPU+(100-300)就是你所需的电源供应量;如果你采用的是大的卷积网络,那么,就买个带有高效能额定功率的;确保有足够PCIe接头(6+8pin),并足以支持你未来可能买入GPU运行的电量。

散热

如果运行单个GPU,在你的设置中选择冷却登陆档;否则,刷BIOS和加快风扇转速就是最便宜最简单的办法;需要降低噪音(与其他共处一室)时,用水来为你的多GPU散热。

主板

选择PCIe 3.0,带有和你以后所需GPU数量一样多的扩展槽(一个GPU需要两个扩展槽;每个系统最多4个GPU)。

显示器

如果你想升级系统,让它更高产,较之升级GPU,再买一台显示器会有意义得多。

(注:文章2015年4月22日更新:去除了对GTX580的推荐。)

本文选自 timdettmers.com , 作者:Tim Dettmers ,机器之心翻译出品:参与,chenxiaoqing, Ben,20e,微胖,柒柒,wei

</div>