0x00:前传
去年底又入了一台DELL,这次是因为需要GPU训模型,正好Nvidia出了基于Turing核心的GTX-1660Ti用于笔记本,比2060少了Tensor Core,但显存一样是6G,想想Tensor Core的意义在于FP16加速训练,但半精度训练配置比较繁琐,暂时不折腾也罢,于是6799入了这台Dell-G3-3590游戏本,被我装上Ubuntu训模型,训练速度比CPU快了20~30倍,大概有V100单卡的50%,而价格只及V100的1/10,非常适合用来学习。
第一次折腾CUDA,实现了CUDA9和CUDA10共存,能同时支持下面两个环境,把安装过程记录一下。
tensorflow-gpu 1.9:CUDA9 + cuDNN 7.0
darknet:CUDA10 + cuDNN 7.5
0x01:理论
CUDA分为两个部分,内核态的驱动(.ko)和用户态的动态链接库(.so)。CUDA的内核态驱动是高版本兼容低版本。而一般说的CUDA9或者CUDA10是指用户态动态链接库的版本,跨版本不兼容,因此,理论上只需要安装一个高版本的内核态驱动,然后通过将CUDA的用户态库安装到不同目录中来达到多版本共存目的。
内核态的驱动版本号一般是3xx.mm或4xx.mm,例如,现在最新的是440.64,在Ubuntu下,由nvidia-driver-4xx和它的依赖包提供。
用户态的动态链接库,其实是一系列的功能库组成的集合,例如cudart, cublas, curand, cufft等,由cuda-libraries-10-0和它的依赖包提供。
还有一个部分,是CUDA内核函数编译器(此内核并非操作系统内核,应该是源于filer的kernel概念,这里指运行在GPU中的算子),由cuda-compiler-10-0和它的依赖包提供。如果需要运行自定义的CUDA kernel函数,就需要安装这个包,例如编译darknet就需要它。
0x02:现状
Ubuntu有两个源提供CUDA驱动和库,一个是Ubuntu的官方源,另一个是Nvidia提供的源。Nvidia源比较新,并且提供了CUDA各个版本共存的机制,所以经过一番尝试后,最终选择了Nvidia源。
0x03:安装CUDA内核态的驱动和CUDA10
在新立得包管理器(synaptic)中,打开“设置”->“软件库”->“其他软件”,点击“添加”,分别添加下面两个Nvidia源,然后关闭并自动刷新:
1 2 | deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 / deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 / |
如果是手动编辑/etc/apt/source.list并遇到如下错误:
1 | W: GPG error: https://developer.download.nvidia.cn/compute/machine-learning/repos/ubuntu1804/x86_64 Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY F60F4B3D7FA2AF80 |
可通过命令自动添加证书:
1 | sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F60F4B3D7FA2AF80 |
搜索cuda,找到下面两个包,分别点击右键选“标记安装”,将会自动标记所需依赖。
1 2 3 | cuda-runtime-10-0 cuda-libraries-dev-10-0 cuda-compiler-10-0 |
点击“应用”,然后大概需要下载1G的内容,安装后占据2G空间。安装完后,在终端中运行nvidia-smi检查是否成功。如果提示未找到设备,可以尝试自己载入内核态驱动:
1 | sudo modprobe nvidia-uvm |
如果成功,将会显示如下的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Tue May 5 15:21:43 2020 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 440.82 Driver Version: 440.82 CUDA Version: 10.2 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 166... Off | 00000000:01:00.0 Off | N/A | | N/A 64C P0 0W / N/A | 0MiB / 5944MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| +-----------------------------------------------------------------------------+ |
0x04:安装CUDA9
在新立得包管理器(synaptic)中,打开“设置”->“软件库”->“其他软件”,点击“添加”,分别添加下面两个Nvidia源(注意,这里添加的是16.04的源,只有这个源中才有CUDA9!!!)
1 2 | deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64 / deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64 / |
关闭并自动刷新后,搜索cuda-libraries-9-0,右键选“标记安装”,将会自动标记所需依赖。
好了,现在两个版本的用户态库分别安装到这两个位置了:
1 2 | /usr/local/cuda-9.0 /usr/local/cuda-10.0 |
0x05:安装cuDNN
cuDNN是CUDA的深度学习支持库,但是,不知道为啥,这个跨版本不兼容的库,Nvidia使用了版本兼容的语义进行安装,这会导致只能二选一的问题。
什么是跨版本兼容的语义呢?首先,包名中只有大版本:libcudnn7,第二,程序编译时,指向了只包含大版本号的符号链:/usr/lib/x86_64-linux-gnu/libcudnn.so.7。
遇上这样的情况:TF1.9依赖cuDNN 7.0版,而如果Darknet依赖cuDNN 7.5,就懵了。
解决方案其实很简单:搞清楚具体是哪一个.so,然后在运行程序时,通过环境变量LD_LIBRARY_PATH来控制加载哪一个。
方式一(命令行):
1 2 3 4 5 6 | # 安装cuDNN 7.0 sudo apt install libcudnn7-dev=7.0.5.15-1+cuda9.0 libcudnn7=7.0.5.15-1+cuda9.0 # 备份cuDNN 7.0 sudo mv /usr/lib/x86_64-linux-gnu/libcudnn.so.7* /usr/local/cuda-9.0/lib64/ # 安装cuDNN 7.5 sudo apt install libcudnn7-dev=7.5.1.10-1+cuda10.0 libcudnn7=7.5.1.10-1+cuda10.0 |
方式二(新立得包管理器):
安装cuDNN 7.0
在新立得包管理器(synaptic)中,搜索libcudnn7,选中结果后,点击菜单“软件包”->“强制版本”,选择“7.0.xxx+cuda9.0 (developer.download.nvidia.com)”,确定、应用后,查看libcudnn7右键属性中的“已安装文件”我们发现cuDNN提供的.so库只有这一个:/usr/lib/x86_64-linux-gnu/libcudnn.so.7.0.5,然后有一个/usr/lib/x86_64-linux-gnu/libcudnn.so.7符号链指向了它。
我们在终端中,把这个版本复制到一个目录中,因为后面装7.5版的时候会被覆盖:
1 | sudo mv /usr/lib/x86_64-linux-gnu/libcudnn.so.7* /usr/local/cuda-9.0/lib64/ |
安装cuDNN 7.5
在新立得包管理器(synaptic)中,搜索libcudnn7,对结果中的libcudnn7和libcudnn7-dev分别操作:点击菜单“软件包”->“强制版本”,选择“7.5.xxx+cuda10.0 (developer.download.nvidia.com)”,确定、应用后,系统的cuDNN已经被覆盖为7.5版本了。
这里,我们用7.5版本作为默认安装,也就是说,不改变任何环境变量,默认加载的cuDNN是7.5版本。如果需要7.0版本,那么就通过在命令前增加赋值来启动:
1 | LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64/:$LD_LIBRARY_PATH python3 train.py |
0x06:清理
刚刚,为了安装CUDA9.0,我们添加了Ubuntu 16.04的Nvidia源,这个源中的CUDA10以及更新的包其实是不兼容Ubuntu 18.04的,为了以后不出现误操作,我们把这个源禁用掉:在新立得包管理器(synaptic)中,打开“设置”->“软件库”->“其他软件”,找到两个包含ubuntu1604的源,去掉前面的钩就行啦!
0x07:安装tensorflow, pytorch, darknet ...
后一篇文章《训练一个跑在嵌入式环境的YOLOv4模型检测人,猫,狗》介绍了基于Darknet进行YOLOv4检测模型的训练,至于tensorflow、pytorch介绍的文章很多了,基本都是常规操作,就不多说了~