搞深度学习,是买一个RTX3090还是两个RTX3080?
28 个回答
看你要用来学什么。
英伟达旗舰-泰坦级别显卡大部分情况下遵循这么几个原则:
- 两张旗舰差不多等于一张泰坦的价格。
- 纯粹的浮点运算速度两个旗舰高于单泰坦。
- 两张旗舰的显存和单泰坦差不多持平。
这一代3080和3090也基本适用。如果是可并行模型,比如大部分传统机器学习、DNN、CNN和部分RNN,那么多卡算力高不少,而且价格便宜,用起来更加合适。如果是不可并行的模型,比如部分RNN,GCN,那就得用单3090来换取更大的单卡显存。
你升级或者加多几块GPU无非就是两个目的:1提升算力,2增加显存。但是多卡运算不是说你显卡插上就啥都不用管了,直接算力翻倍显存翻倍。而是你要在代码里把不同数据手动分配给不同GPU去算,这一步有的框架自动干了,有的需要你自己去干。
数据在GPU上的分配策略可以分为两种,一种是并行,一种是串行。并行比较好理解,就是把数据分成几个部分,给几个GPU同时计算,同时出结果。而串行则是指,比如一个4层网络,12层给GPU1计算,34层给GPU2计算。在绝大多数情况下,串行是没有意义的,因为同一时间只有一块GPU在运行,达不到增加算力的目的。在反向传播的时候,梯度计算指定给一个GPU,也均摊不了显存,显存依旧会爆,增加显存的目的也没达到。所以我们说多卡一般是多卡并行。
(上面说绝大多数情况下串行没有意义,但是如果框架能提供串行反向传播,每个显卡只计算经由自己前向传播的参数,那多卡串行就是一个不提升算力,但是加倍显存的好方法了。但是就我所知tf和pytorch没有这样的功能。在现有框架下实现这样的功能也有一定难度,这个不是简单的把链式求导法拆开就行的。因为如果用现行框架分段求导,前段求导会多一个隐式求和步骤。)
但是并不是所有模型都能多卡并行的。如果在一次正向传播中,你能把数据分成相互独立的N个部分,这N个部分拿出来任意一个,都可以完成一次完整的正向传播,这就叫可并行的模型。比如CNN做图像分类。这样的模型就可以通过多卡并行来提高运算速度。
而有一些模型不能并行,比如很多GCN,尤其是不规律的异构图。图数据结构都学过吧?假设在图数据上用GCN做深度学习。图中每个节点都是我们的输入X,我们要给每个节点进行分类,输出它们的分类标签Y。现在假设有一幅连通图G(所有节点相互可达),你把图G的节点任意分为不为空的两部分A、B。因G是连通图,则A中必存在一些节点与B中的另一些节点相连。我们管这个叫边界节点。如果你对A的边界节点进行正向传播,势必需要用到B中与其相连的那些节点。这样的模型就不能并行,因为A或B都无法独立的完成一次正向传播。这样你就不能把A和B分开给不同的显卡去算。
当然有一些不可并行模型不是绝对的,比如上面那个例子:
1、可以用一种子图划分的方法,把原图按照某种方法切成一些子图,让这些子图之间尽量不相关。
2、或者如果GCN有n层,则可以把边界节点的n阶邻域作为padding。
3、或者每次计算完A、B后用计算结果构建一张包含所有节点的索引表,在进入下一层前,每个GPU提前从这张表里提取需要的节点。
通过一些方法,不可并行模型转化为了可并行模型,但实际的代码实现上依旧困难重重:
1、子图划分必定损失链接,子图划分的算法设计的不好模型整体的效果就会很差。
2、n阶邻域的规模不是线性的,复杂度是d的n次方,d是节点的度。网络层数一旦增加,需要padding的节点呈指数增加。
3、构建的索引表的行为本身就会占用大量显存,构建索引时要吧所有GPU的结果放在一起,这时候很容易爆显存。索引操作的效率也不高,就算用哈希表索引,效率也远比不上计算框架的矩阵运算。
综上,如果你清楚的知道自己在研究什么,是可并行的,比如传统机器学习、绝大多数CNN和部分RNN,那就去买俩3080,便宜且算力高不少。如果是不可并行的,比如大部分GCN和部分RNN,那你也只能去买一个3090。如果你不能确定下来具体会跑什么样的模型,但是有可能会去跑不可并行模型,那我建议你直接3090。毕竟大显存低算力只是速度慢,而高算力小显存则根本跑不起来。况且以后还有继续升级的空间。还有一点就是多卡并行的代码写起来多少要麻烦一点。
ps:做RNN或者GNN的时候,经常是幻想显卡要是能和cpu和内存一样就好了。显存不够插一块,就算算力不增加,单纯加大显存也好啊。
十一期间抢到块3090,用了一阵,大概说下感受。我的框架主要用tf,模型主要写gcn。
首先就是驱动,先把cuda10复制一份,放到环境变量找不到的地方,或者如果你知道cuda都有哪些环境变量的话,直接去吧环境变量删了,然后装cuda11,显卡驱动会自动更新到最新,cudnn直接用nv官网给的对应cuda版本的cudnn就行,不用管tf给的兼容列表。然后tf也卸载干净,pip装tf-nighty-gpu版,跑一下代码,有可能会报错,说某个dll找不到,直接去刚才备份的cuda10的目录里找缺的的dll就行,复制到cuda11相同目录位置。我这种方法保证兼容,但不保证最省事儿。
在使用体验上,因为我跑的gcn都比较大,是用不了tf的自动求导的,计算梯度都是自己实现,所以优化应该没有官方的好。之前2080ti单精度一个batch3.4s,现在用3090 2.4s左右。不过既然显存大起来了,想必重构一下代码,把部分梯度交给自动求导算,应该还能再提升不少。
目前来看代码兼容性尚可,没见到什么对3090有什么严重的兼容问题,我看tf社区里有提出一些issue,主要和稀疏矩阵运算有关。
看任务或者用机器的人能不能并行
如果有多个使用者,或者一个人同时进行多个模型的训练(parameter search),无脑3080,GPU任务调度会很折损性能,例子:
同时跑两个任务(单个任务不满载,70%),不如等一个跑完再跑另一个
如果是单一模型 ,考虑双卡3080分布式计算
我有两种猜想
- 代码能力超强,随时手动改分布式,注意,并不是DataParallel(DP),而是DistributedDataParallel(DDP)
- 没有写过分布式:)
除了大佬组release的code,基本都是单机单卡,双卡3080可能可以同时做多个parameter search,除此之外,我想不到任何情况3080*2的优势,哪怕是DDP的性能折算之下,可能也只是打平3090。
更新一下,pytorch_lightning 大法好!(本地单机,随手改多机多卡,本地小卡写demo跑通,拉到服务器直接多机多卡跑起来