其实Tensorflow在GPU支持模式下的安装并不困难,严格按照其官方文档就可以了。但整个 过程比较长,中间一些步骤注意不到也可能出错。这里列出要点和排错指南。
确保Ecosystem一致性
要确保Tensorflow能真正利用GPU的算力,就需要保证驱动、CUDA库和Tensorflow的版本相兼容。首先要确定你的硬件支持到哪一个版本的cuda和cudnn。比如我们一个比较初级的机器使用的GPU的是GeForce GTX 1070,这个机器不是Pascal架构的,所以安装Cuda的版本到7.5就ok了,最新的8.0则不是必须的。实际在安装中我们还发现了一个安装了8.0以后的问题,这个会在后面提到。
安装完成后的测试
安装完成后,需要运行一小段tensorflow脚本来测试安装是否正确。Tensorflow的官方教程里给出了两个阶段的测试,第一个是hello world性质的:
$ python
...
>>> import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
>>> print(sess.run(hello))
Hello, TensorFlow!
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print(sess.run(a + b))
42
>>>
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:102] Couldn't open CUDA library libcudnn.so. LD_LIBRARY_PATH:
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcufft.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcuda.so.1 locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcurand.so locally
在这一步可能报的错误有libcudart.so.(x.x) cannot open share object file。这意味着CUDA并未正确安装(*1),或者未配置路径(*2),或者cuda的版本不正确(*3)。
第二个阶段的检查是cudnn是否正确安装了,以及tensorflow在运算时,是否真正将计算分配到了GPU上。注意在上面的hello world中,尽管cudnn并未安装正确,程序只会报一个libcudnn并未找到的警告,程序还会继续正常去行。要检查cudnn是否正确安装,需要使用用到cudnn的库,可以用下面的代码来检查:
import tensorflow as tf
input = tf.Variable(tf.random_normal([100, 28, 28, 1]))
filter = tf.Variable(tf.random_normal([5, 5, 1, 6]))
sess = tf.Session()
sess.run(tf.initialize_all_variables())
op = tf.nn.conv2d(input, filter, strides = [1, 1, 1, 1], padding = 'VALID')
out = sess.run(op)
在前面通过了“hello world”测试的环境下运行上面的代码,会导致程序崩溃:
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:102] Couldn't open CUDA library libcudnn.so. LD_LIBRARY_PATH: ... I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties: name: GeForce GTX 1070 major: 6 minor: 1 memoryClockRate (GHz) 1.759 pciBusID 0000:01:00.0 Total memory: 7.92GiB Free memory: 7.84GiB I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0 I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0: Y I tensorflow/core/common_runtime/gpu/gpu_device.cc:838] Creating ... ... F tensorflow/stream_executor/cuda/cuda_dnn.cc:208] could not find cudnnCreate in cudnn DSO; dlerror: /home/yyang/tensorflow/lib/python3.5/site-packages/tensorflow/python/_pywrap_tensorflow.so: undefined symbol: cudnnCreate
在我的环境里是由于LD_LIBRARY_PATH未配置好。
注:由于运行在一个多人共享的环境上,所以我使用了screen jupter notebook来创建一个可以从browser上远程运行桌面命令和ipynb会话的环境。但是,screen并没有一开始完全继承我的环境设置,所以导致libcuda.so可以加载,而libcudnn.so不能加载。而这种情况,刚好可以通过hello world测试,如果你忽略警告的话。
实际上你还应该运行第三个阶段的测试–确保当你的tensorflow运行时,它是真正运行在GPU上,这需要在创建session时就加上配置信息:
import tensorflow as tf
input = tf.Variable(tf.random_normal([100, 28, 28, 1]))
filter = tf.Variable(tf.random_normal([5, 5, 1, 6]))
sess = tf.Session(config=tf.ConfigProto(log_device_placement=True))
sess.run(tf.initialize_all_variables())
op = tf.nn.conv2d(input, filter, strides = [1, 1, 1, 1], padding = 'VALID')
out = sess.run(op)
重点在第四行,我们加上了log_device_placement信息。
现在运行上面的命令,会得到:
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcublas.so locally
I tensorflow/stream_executor/dso_loader.cc:108] successfully opened CUDA library libcudnn.so locally
...
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:925] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties:
name: GeForce GTX 1070
major: 6 minor: 1 memoryClockRate (GHz) 1.759
pciBusID 0000:01:00.0
Total memory: 7.92GiB
Free memory: 7.84GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0: Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:838] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1070, pci bus id: 0000:01:00.0)
Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: GeForce GTX 1070, pci bus id: 0000:01:00.0
I tensorflow/core/common_runtime/direct_session.cc:175] Device mapping:
/job:localhost/replica:0/task:0/gpu:0 -> device: 0, name: GeForce GTX 1070, pci bus id: 0000:01:00.0
Variable_1: /job:localhost/replica:0/task:0/gpu:0
I tensorflow/core/common_runtime/simple_placer.cc:818] Variable_1: /job:localhost/replica:0/task:0/gpu:0
Variable_1/read: /job:localhost/replica:0/task:0/gpu:0
从上面的输出可以看出,执行任务已经和gpu绑定了。
查错
检查显卡设备、驱动是否安装正确
我们使用lspic | grep -i nvidia来检查显卡安装是否正确,以及驱动是否加载:
01:00.0 VGA compatible controller: NVIDIA Corporation Device 1b81 (rev a1) (prog-if 00 [VGA controller])
Subsystem: Device 7377:0000
Flags: bus master, fast devsel, latency 0, IRQ 16
Memory at f6000000 (32-bit, non-prefetchable) [size=16M]
Memory at e0000000 (64-bit, prefetchable) [size=256M]
Memory at f0000000 (64-bit, prefetchable) [size=32M]
I/O ports at e000 [size=128]
[virtual] Expansion ROM at f7000000 [disabled] [size=512K]
Capabilities: [60] Power Management version 3
Capabilities: [68] MSI: Enable- Count=1/1 Maskable- 64bit+
Capabilities: [78] Express Legacy Endpoint, MSI 00
Capabilities: [100] Virtual Channel
Capabilities: [250] Latency Tolerance Reporting
Capabilities: [128] Power Budgeting <?>
Capabilities: [420] Advanced Error Reporting
Capabilities: [600] Vendor Specific Information: ID=0001 Rev=1 Len=024 <?>
Capabilities: [900] #19
Kernel driver in use: nvidia
Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia
从最后两行来看,这块显卡使用的是nvidia的驱动,且驱动已加载到内核。
驱动版本可以通过下面的语句查询到:
cat /proc/driver/nvidia/version
NVRM version: NVIDIA UNIX x86_64 Kernel Module 367.44 Wed Aug 17 22:24:07 PDT 2016
GCC version: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.2)
看上去完全正确,因为显卡的驱动就是367.44。
检查cuda是否安装正确
要检查cuda库是否安装正确,可以使用cuda编译后产生的deviceQuery工具。同时,你还要检查LD_LIBRARY_PATH是否正确设置:
echo $LD_LIBRARY_PATH
输出中应该包括/usr/local/cuda-x-x/lib64。如果没有,你需要编辑/etc/profile或者~/.bashrc,加入export LD_LIBRARY_PATH=xx。
在我的环境里还出现过上述配置都正确,但tensorflow还是报找不到cuda库的错误,即上文中提到的错误*3。最后发现错误是因为cuda安装了8.0的版本,但tensorflow的0.10版本,如果不是自己编译的话,它的发行版是链接的7.5的版本,所以会出错。最后的解决方案是将cuda版本回退到7.5(因为硬件并不支持8.0所需要的pascal架构)。
其它
我使用了一台多人共享的机器,通过远程连接访问。为了访问更方便快捷,我设置了以下环境:
1) virtualenv。以保证不跟其它人冲突。
pip3 install virtualenv
virtualenv --system-site-packages tensorflow_pyenv
#激活virtualenv环境后,再继续tensorflow安装
source tensorflow_pyenv/bin/activate
pip3 install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.10.0-cp35-cp35m-linux_x86_64.whl
pip3 install jupyer notebook
2)配置jupyter,启用远程服务
为了使jupyter启动的服务能够在远程浏览器中打开,需要配置jupter:
jupyter notebook --generate-config
这样会在当前目标下生成.jupyter的目标,里面有jupyter_notebook_config.py文件,打开并编辑:
c.NotebookApp.password=u''
c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
# It is a good idea to set a known, fixed port for server access
c.NotebookApp.port = 9999
其中password可以使用下面的程序生成:
In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:
Verify password:
Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
将out[2]中的字符串完整地输入到c.NotebookApp.password=u”中的引号中去。
3)通过screen来运行jupyter,使之象一个服务。
安装完成jupter和notebook以后,使用screen来运行jupyter。使用screen的好处是它能将程序放在后台运行,这样终端连接退出也不影响程序运行。在我的环境下,需要在用户目标~下创建一个.screenrc,加上这样的内容:
setenv LD_LIBRARY_PATH /usr/local/cuda-7.5/lib64:$LD_LIBRARY_PATH
source /etc/screenrc
现在启动程序:
screen jupyter notebook
现在可以打开浏览器,输入ip:端口来访问notebook了。
注意,你需要有64bit的screen来运行jupyter。如果使用了32bit的screen,在对一些稍大的一点数据集进行训练时,会因内存不足挂掉。另一个办法是将jupyter notebook的运行daemonize化。你可以修改这个脚本,使得jupyter可以以类似服务的方式来运行。