使用 Java Tensorflow API 调用 pb 模型执行推理逻辑,之前一直使用 CPU,近期尝试 GPU 推理,下面记录一下踩坑的过程。
登录后复制
// GPU 使用配置
val gpuConfig = GPUOptions.newBuilder()
.setVisibleDeviceList("0")
.setForceGpuCompatible(true)
.setAllowGrowth(true)
.build()
// Tensorflow 模型加载配置
val configproto = ConfigProto.newBuilder()
.setGpuOptions(gpuConfig)
.setInterOpParallelismThreads(op)
.setIntraOpParallelismThreads(op)
.build()
使用 GPU 还是 CPU 在代码配置阶段主要差别于是否在 configproto 中增加 GpuOptions,GPUOptions 需要单独使用 builder 初始化:
AllowGrouwth: 是否预先分配整个指定的 GPU 内存区域,如果为 True 则按需增长
ForceGpuCompatible: 启用此选项会强制所有 CPU 张量分配有 Cuda 固定内存,对于较大模型不推荐
VisibleDeviceList: 指定 GPU 设备,一般从0开始,例如 /device:GPU:0,也可以逗号分隔指定多台,例如 "0,1,2,3"
tensorflow 基础依赖
登录后复制
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>tensorflow</artifactId>
<version>1.15.0</version>
<scope>provided</scope>
</dependency>
这里正常情况下 CPU 推理使用下面的版本依赖,如果使用 GPU 需使用 jni_gpu 版本:
登录后复制
// GPU
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>libtensorflow_jni_gpu</artifactId>
<version>1.15.0</version>
<scope>provided</scope>
</dependency>
// CPU
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>libtensorflow_jni</artifactId>
<version>1.15.0</version>
<scope>provided</scope>
</dependency>
在使用 Java x Tensorflow x GPU 推理时,首先需要确保自己的显卡配置成功,可以在对应环境下执行下述 python 代码,确保 GPU 配置成功:
登录后复制
from tensorflow.python.client import device_lib
device_lib.list_local_devices()
执行后如果出现下述样例日志,代表 GPU 环境配置无误,可以看到这里是 Device-0,这里 0 和上面的 VisibleDeviceList 对应,可以制定该参数为 "0",使用该显卡。
确定环境无误后,下面开始踩坑 GPU 推理过程。
可以使用 nvidia-smi 命令检测显卡显存与利用率:
如果运行任务,可以使用 watch 命令,固定间隔实时更新显卡使用率,-n 代表频率,1代表1s,执行命令后,bash 上会每s更新一次上图的显卡指标:
登录后复制
watch -n 1 nvidia-smi
pom 修改为 GPU 对应依赖,代码也增加 GPUOptions 后,开始了第一次测试。
任务可执行,但执行期间显卡监控只有显存的增加而 GPU-Util 利用率 始终为 0,怀疑是模型加载到显存中,但是由于某些原因模型无法使用 GPU 推理所以出现如下情况:
查看模型加载的日志才发现有如下异常,无法加载 cuda10.0.so xxxx 类似的报错:
登录后复制
2022-09-27 10:58:21.077390: I tensorflow/cc/saved_model/reader.cc:31] Reading SavedModel from: /root/xudong/3fe686d2-59e1-4821-b5a3-baa79d0d53b8
2022-09-27 10:58:21.110117: I tensorflow/cc/saved_model/reader.cc:54] Reading meta graph with tags { serve }
2022-09-27 10:58:21.136320: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 AVX512F FMA
2022-09-27 10:58:21.144301: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2600000000 Hz
2022-09-27 10:58:21.145011: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x7f3c00495c00 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2022-09-27 10:58:21.145065: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version
2022-09-27 10:58:21.146331: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1
2022-09-27 10:58:21.158831: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1618] Found device 0 with properties:
name: Tesla V100-PCIE-32GB major: 7 minor: 0 memoryClockRate(GHz): 1.38
pciBusID: 0000:21:02.0
2022-09-27 10:58:21.158971: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcudart.so.10.0'; dlerror: libcudart.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.159037: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcublas.so.10.0'; dlerror: libcublas.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.159092: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcufft.so.10.0'; dlerror: libcufft.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.159150: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcurand.so.10.0'; dlerror: libcurand.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.159203: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcusolver.so.10.0'; dlerror: libcusolver.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.159259: W tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libcusparse.so.10.0'; dlerror: libcusparse.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/cuda-11.2/lib64/:/usr/local/cuda/targets/x86_64-linux/lib/:/usr/local/hadoop-2.7.3/lib/native/:/usr/local/jdk1.8.0_131/jre/lib/amd64/server/
2022-09-27 10:58:21.162605: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2022-09-27 10:58:21.162628: W tensorflow/core/common_runtime/gpu/gpu_device.cc:1641] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly if you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu for how to download and setup the required libraries for your platform.
Skipping registering GPU devices...
2022-09-27 10:58:21.283803: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1159] Device interconnect StreamExecutor with strength 1 edge matrix:
2022-09-27 10:58:21.283842: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1165] 0
2022-09-27 10:58:21.283850: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1178] 0: N
2022-09-27 10:58:21.366089: I tensorflow/cc/saved_model/loader.cc:202] Restoring SavedModel bundle.
2022-09-27 10:58:21.572327: I tensorflow/cc/saved_model/loader.cc:151] Running initialization op on SavedModel bundle at path: /root/xudong/3fe686d2-59e1-4821-b5a3-baa79d0d53b8
2022-09-27 10:58:21.641867: I tensorflow/cc/saved_model/loader.cc:311] SavedModel load for tags { serve }; Status: success. Took 564483 microseconds.
上面报错比较多,主要的报错是这句 Could not load dynamic library 'libcudart.so.10.0' :
登录后复制
Could not load dynamic library 'libcudart.so.10.0'; dlerror: libcudart.so.10.0: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: /usr/local/jdk1.8.0_131/jre/lib/amd64/server:/usr/local/jdk1.8.0_131/jre/lib/amd64:/usr/local/jdk1.8.0_131/jre/../lib/amd64:/usr/local/
即无法加载对应 cuda 的 so 文件,so 文件可以看做是 c++ 封装好的 java API,支持 JAVA 调用。回看上面显卡的监控情况,CUDA Version 为 11.4,这里加载 so.10.0,肯定异常,所以需要安装 cuda-10.0,且支持双环境。
打开 CUDA 官网,由于上面报错无法加载 so.10.0,所有这里下载 10.0 对应版本,博主这里使用 linux 环境,下面选项分别为 Linux + x86_64 + CentOS + 7 + runFile(local):
待下载完成后,会获得一个 cuda_10.0.130_410.48_linux.run 文件
博主 cuda 安装在 /usr/local 目录下,所以可以直接将 cuda_10.0.130_410.48_linux.run 文件移动至 /usr/lcoal 下:
执行下述命令:
登录后复制
sudo sh cuda_10.0.130_410.48_linux.run --override
执行安装后几s内会弹出 preface 相关的序言,这里按 'q' 继续即可:
第一个选项必须 accept,否则安装会直接退出
第二个选项安装显卡驱动,我们前边检验已经通过,所以 n 否
第三个选项安装 cuda 工具包 ,必须的 y
第四个安装地址,默认 /usr/local/cuda-10.0 enter 回车即可,也可以自定义位置
第五个安装 cuda 环境,由于我们默认环境为 cuda 11.x 所以这里选择 n 否,否则会覆盖原有环境,如果你没有 cuda 环境,y 即可
第六个是否安装 cuda 样例,y、n 均可
最后一个选项即样例的位置,可以 enter 回车默认,也可以自定义
选择完毕后,会出现 Installing the CUDA .... 提示,代表开始安装。
出现如上日志即代表安装成功,可以看到 /local/usr 目录下也出现 cuda-10,0 的文件夹:
安装期间可能出现报错并退出,提示用户到 /var/log/nvidia-installer.log 目录下查看安装异常日志:
通过查看日志分析原因,安装期间所在机器 GPU 不能处于使用期间,如果 GPU 使用期间安装会导致安装失败,因为 Dirver 需要 upgrade,所以安装期间需要保证 GPU 未使用,可以使用 top 命令查看相关任务的 pid,并 kill 掉对应 pid 保证 GPU 不再使用。
CUDA 是 NVIDIA 推出的运算平台,cuDNN 是用于深度网络的 GPU 加速库,可以支持 GPU 实现高性能并行计算。
进入 cuDNN 官网 可以选择对应版本的 cuDNN,博主上述采用 cuda 10.0 + linux:
这里下载需要注册邮箱,比较麻烦,大家也可以在这里选中版本在网上搜索直接下载即可。 将得到的下载文件后缀修改为 tgz 得到:
登录后复制
cudnn-10.0-linux-x64-v7.4.1.5.tgz
登录后复制
tar -xvf cudnn-10.0-linux-x64-v7.4.1.5.tgz
解压 cudnn 文件得到 cuda 文件夹:
继续执行 cp 命令,将 cuda 中的文件拷贝至刚才安装的 cuda-11.0 的文件夹中:
登录后复制
cp cuda/lib64/* /usr/local/cuda-10.0/lib64/
cp cuda/include/* /usr/local/cuda-10.0/include/
chmod a+r /usr/local/cuda-10.0/lib64/*
chmod a+r /usr/local/cuda-10.0/include/*
登录后复制
cat /usr/local/cuda-10.0/include/cudnn.h | grep CUDNN_MAJOR -A2
出现下述日志即安装成功:
由于我并未未将 cuda-10.0 设置为环境变量,因此如果 shell 脚本中使用 cuda-10.0 需指定对应目录:
登录后复制
export CUDA_DIR="/usr/local/cuda-10.0/"
在 shell 中 export CUDA_DIR 即可指定至所需环境,使用其他 CUDA 环境同理,如果不添加则默认使用环境变量下的 CUDA-11.x。此时再次执行推理任务不再出现无法加载 so.10.0 相关日志,而是 Successfully opened so.10.0 xxx,代表前面安装成功,并且成功加载了 device-0。
再次查看显卡监控:
可以看到在显存占用的情况下,GPU 利用率也不再是 0%,大功告成!
Java x Tensorflow x GPU 踩坑大致就这么多,一般来说推理还是用 python 或者 c++ 更方便一些,如果使用 java API ,可以参考如下表格选择 cuda 版本:
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删