C++调用Python深度学习模型,包含TensorFlow和PyTorch等构造的模型,然后使用GPU出现问题。包含C++调用Python函数,C++加载模型到GPU,GPU内存占用过大,计算完毕内存无法释放等问题!

本文主要分析,C++调用Python深度学习模型,模型加载到GPU,然后GPU内存中的模型释放,以及调用了GPU,GPU内存不够,内存占用过多,Utilities利用率低,GPU跑不起来等各种疑难杂症!

(注:关于GPU利用率低的问题,如何提高深度学习GPU使用效率的解决方案,请查看我的这一篇文章: 深度学习PyTorch,TensorFlow中GPU利用率较低,CPU利用率很低,且模型训练速度很慢的问题总结与分析

1. C++调Python所构建的深度学习模型

1.1 项目描述

最近在帮忙做一个项目,上层用C++写的一个QT软件,是人脸识别分析的一个软件。C++负责界面,多线程,实时显示等各种应用的业务。Python主要负责深度学习这一方面。Python构建了几个深度学习模型,包含ResNet101, LSTM, VGG16等网络,用于特征提取,表情分类,时序分析等算法方面的逻辑。
C++这一块,有一个选项是进行人脸检测,这个时候,为了加速,需要调用GPU来进行计算。由于算法这边提供的是Python写好的,没有进行TensorFlow或者PyTorch转换成C++的这一种思路。况且,我们的深度学习模型,由于是不同人负责不同的块,包含了TensorFlow,Keras,PyTorch,如果用一个转换包,也不好将三种不同框架转换为1个可用的C++代码。
为了方便,就在Python下写成单独的函数,C++直接通过Python的接口,来调用Python代码。换句话说就是,将你用Python写的深度学习代码,比如,文件名为:DNN_algorithm.py给导入到C++里面,调用相关函数就可以像运行python一样通过C++运行深度学习代码。

1.2 C++和Python代码的构建

Python写好深度学习的代码,就可以用C++将你DNN_algorithm.py 里面的各个函数,类,使用起来了。我的python代码,首先是加载训练好的模型,函数名称是load_model();然后,使用model_predict(image)函数,来进行图像的人脸检测,也叫做预测。

2. Python深度学习模型的构建代码,Python文件名为DNN_algorithm.py

按照常规的方式,在Python下构建模型,训练和保存好模型。当进行预测的时候,直接加载训练好的权重文件就可以。这一部分到处都可以查到相关的指导文件,就不多说这一块。

	class DNN_model():
		def __init__(self):
			self.vgg_model = None
			self.LSTM_model = None
		def load_model(self):
			VGG_net = # construct the model, you can use tensorflow, keras, PyTorch.
			LSTM_net = # construct the LSTM model.
			# load the trained weights to the constructed model architecture.
			self.vgg_model = load_wights(VGG_net, 'vgg_net.h5')
			self.LSTM_model = load_wights(LSTM_net , 'lstm_net.h5')
		def model_predict(self, image):
			class_out = self.vgg_model.predict(image)
			temporal_out = self.LSTM_model.predict(image)
			return class_out, temporal_out

3. C++调用所构建的Python代码的相关函数

C++只讲怎么调用你上面构建的Python的几个函数。先是导入python文件名,然后导入相关函数就可以。具体C++怎么调用Python的,可以搜一下py.h。是Python官方自带的,供C++使用的接口函数。

    //创建代码文件模块:将你用Python写的深度学习代码DNN_algorithm.py给导入到C++里面,方便调用
    m_pModule = PyImport_ImportModule("DNN_algorithm");
    //下面就可以,将你DNN_algorithm.py 里面的各个函数,类,使用起来了。
    //我的python代码,首先是加载训练好的模型,函数名称是load_model();
    //然后,使用model_predict(image)函数,来进行图像的人脸检测。
    PyObject* pResult = NULL;
    //调用python加载模型的函数load_model。
    pResult = PyObject_CallMethod(m_pInstanceME, "load_model", NULL);
    PyObject* pFunc = NULL;
    //调用python模型预测的函数model_predict。
    pFunc = PyObject_GetAttrString(m_pInstanceME, "model_predict");
    //这句话,就是将python函数,与C++这边采集到的图像argList,给模型拿去预测。
    pResult2 = PyEval_CallObject(pFunc, argList); 

4. 加载模型到GPU,然后使用GPU进行计算

4.1 C++加载TensorFlow和Keras模型到GPU

对于这一块,由于我遇到的问题是Tensorflow 和Keras这部分的,PyTorch下加载没问题。所以就说一下TensorFlow和Keras,在C++调用模型的时候,如何加载模型到GPU,如何run起来。

其实只需要在调用Python的文件那边,主动加入这些函数,你的模型,就自动加载到GPU上的。PyTorch不是这个用法,PyTorch需要显示的将模型加到device上 :

model=model.to(device) #这是PyTorch的加载方法

	# 这是Keras的加载方法
	import os
	os.environ['KMP_DUPLICATE_LIB_OK']='TRUE'
	os.environ['CUDA_VISIBLE_DEVICES']='0'
	os.environ["TF_CPP_MIN_LOG_LEVEL"]='3'
	## 如果你的GPU内存不够,不允许TF和Keras开辟很大的内存,下面的也可以来进行限制。
	config = tf.ConfigProto()
	config.gpu_options.per_process_gpu_memory_fraction = 0.5  #程序最多只能占用指定gpu50%的显存
	config.gpu_options.allow_growth = True	#程序按需申请内存
	sess = tf.Session(config = config)
  • 这个时候,查看你的任务管理器的GPU的情况,包括内存和cuda的使用率等。当C++调用了load_model的函数之后,查看你的GPU的内存和使用率是否上去。
  1. 如果内存利用率没有上去,就是检查你的模型是否load上去。这种情况下,先在python下面运行,看你的模型load上去GPU没有,如果没有,那就是python代码的问题。
  2. 如果Python能够load上GPU去,但是C++调用代码之后,没有load上GPU上,那就是C++调用Python的问题。你检查你的C++代码是否正确调用Python。如果不确定,先写一个简单的print函数,然后用C++调用一下,如果行,就按照这个调用方式调用函数。
  • 我的Python代码下面是一个类,你就要在C++这边,先实例化这个类,才能调用下面的类的成员函数。

4.2 C++加载模型到GPU之后,神经网络运行完预测函数,GPU的内存一直被占用,没释放的问题。

在Python下面,我们run完模型的预测函数,也就是model_predict()完成之后,或者代码运行完,GPU的内存直接被释放掉。因此,python下,无需考虑模型占用内存的问题。
当我们使用C++,来调用Python所写的深度学习模型的时候,如上面所述的流程,先构建模型,加载权重文件,然后模型预测,阶段性的处理完了采集的图像。随后,我们的界面,可以做其他的业务,比如,浏览,报表分析,等等。但此时,GPU还被占着,只有你关闭这个exe,或者退掉整个程序,才释放了由于使用GPU进行神经网络预测(推理)所加载的模型及其占用的GPU内存。

在Python下面,可以采用以下的方法,来清理缓存,收集垃圾数据,(PS:只是暂时的清除一些临时变量,作用其实不大,GPU内存占用一样的无法减少。

    def delete_model(self):
        del self.vgg_model #删除模型
        del self.LSTM_model #删除模型
        gc.collect() #回收一些临时变量和垃圾数据
        K.clear_session() #清除session
        tf.reset_default_graph() # 重置 graph。

请注意:如果你只是暂时的没有接收到数据,GPU暂时没有需要处理的图像数据(也许十几秒之后,就采集到新的图像,所以有可能随时要用)。此时,不用释放和删除内存。如果删除和释放了GPU内存,如果新的图像数据来了,你还要重新加载数据到GPU,这个过程是很耗时的。

  • 强制释放GPU所占用内存
    当完成了人脸检测任务,由于当前程序尚在执行,GPU内存一直被占着。
    如果你的业务下面,有其他算法需要GPU来进行处理,或者GPU用于其他的处理线程,此时,可以关闭掉你人脸检测任务,彻底清除掉GPU缓存和内存占用。个人感觉有点kill的意思。执行了下面的这个代码,你的GPU内存瞬间释放,因为前面整个加载的模型,全部被close了。这是强制性的。
	from numba import cuda
    cuda.select_device(0) #选择你的device id。在上面我们指定了那一块GPU用来处理,这里就指定那块。
    cuda.close() # 然后,关闭掉这个cuda线程。

下面是简要的描述一下Numba这个库。

cuda.close()
Explicitly close all contexts in the current thread.
Compiled functions are associated with the CUDA context. This makes it not very useful to close and create new devices, though it is certainly useful for choosing which device to use when the machine has multiple GPUs.

Numba 是一个利用CUDA核在GPU上进行快速计算的Python库,主要用于高性能计算。特点如下:
1. Numba: High Productivity for High-Performance Computing
2. GPU-Accelerated Libraries for Python
3. Massive Parallelism with CUDA Python]

当你的代码,执行上述的close。此时,如果你还想加载模型,然后进行预测,会出问题的。因为你的cuda被强制close掉了。要想重新运行起来,只有关闭程序,重新运行代码。如果想在这个程序里面,再次检测人脸。。。。。这个时候,就报错了。。。。
!!!因此,cuda.close()只适合于强制关闭GPU,留给其他任务。本任务是不可能再次使用的。

4.3 关于上一轮预测完成(也就是深度学习模型使用完成),下一次加载模型到GPU失败的问题的解决方法。

如果你的C++写的应用,比如QT界面,需要执行完本次深度学习预测任务,然后继续去收集图像或者其他数据,再次进行人脸检测。这时候,如果你加载模型到GPU失败了,应该是上一次执行的session未清空,或者这些缓存变量,没有给清除掉。因此,你需要在每次执行完深度学习预测 model_predict()之后,clear某些session。因此,delete_model()就可以再次加载模型到GPU了。如果使用cuda.close()。你不能再次加载到GPU的。

5. GPU一直占着,加载成功,但是GPU的利用率为0,或者很低的情况。

这个时候,点开你的资源管理器,如果GPU内存被占,然后上面的利用率,cuda这个栏目,总是为0。你可以看一下,你的模型代码,是否正在执行预测的前向计算,也就是是否正在进行model_predict。或者是检查模型是否读入图像数据,正在输出结果。这个时候,如果真的是在预测阶段,那么GPU的利用率,一定有50%,或者80%,不可能是0。最大的原因是:你的模型,大部分时间花在了等待数据预处理阶段,包括了图像resize,人脸对齐,convert color space,还有就是特征检测,滤波,(我遇到的问题是,大部分时间在花在光流法处理图像)。因此,感觉非常慢,而且感觉GPU没有利用上。一度怀疑是不是深度学习代码的问题。最后是opencv图像预处理的问题。你的GPU利用率就是有一个小的尖峰脉冲形式的抖动。其实代表你的模型正在预测,GPU正在被使用,只是速度极快,实时利用率这一栏只有一个小的脉冲抖动。
解决方法:采用CUDA来进行图像预处理的加速,opencv-python 4.几及其以上版本,已经完全支持某些特定函数的CUDA开发了,在python上就可以调用CUDA实现的的GPU加速图像处理函数了。

  • TODO:下一个博文,我就讲我如何使用cuda和GPU进行图像预处理的加速。平时大家总是觉得GPU主要用来深度学习的加速,其实某些耗时的图像算法,CUDA也有对应版本。

1. Numba: High-Performance Python with CUDA Acceleration
2. Numba for device management
3. CUDA Device Management
4. C++ call python neural network model, the model was loaded on GPU, but can’t run on the GPU, the CPU run the model.

如果有用,记得点赞👍加收藏哦。!!!!

C++调用Python深度学习模型,包含TensorFlow和pytorch等构造的模型,然后使用GPU出现问题!1. C++调Python深度学习文件1.1 项目描述1.2 C++和Python代码的构建2. Python深度学习模型的构建代码,Python文件名为DNN_algorithm.py3. C++调用所构建的Python代码的相关函数4. 加载模型到GPU,然后使用GPU进行计算4.1 C++加载TensorFlow和Keras模型到GPU4.2 C++加载模型到GPU之后,NN运行完预测函数
在 C 语言中实现深度学习模型可以使用一些开源的库,比如 Caffe、TensorFlow 等。这些库都提供了一系列的函数和工具,帮助你定义、训练和测试深度学习模型。 首先,你需要准备好训练数据和测试数据。训练数据用于训练深度学习模型,测试数据用于评估模型的准确性。 然后,你需要使用 C 语言编写程序,使用这些库中的函数来定义深度学习模型的结构。这通常包括确定模型的输入和输出,以及模型中间的各个层...
加“壳”,能在C++中进行操作,但是不发生数据拷贝,底层的存储方式还是按照python的方式; 数据类型转换,从python中的存储方式,直接转换成C++中的数据类型,发生数据拷贝,效率较低,尤其是在数据量比较大的情况下,这种转换的代价很高。 这一点非常值得关注,当Python中考虑调用C++接口时,大多情况下都是为了提高程序的运行速率(有时候可能是为了能够调用C++的库)。因此,通过以上哪一种方式传递数据需要衡量。 如果你有现成的C
本贴为"深度学习C++实现" github.com/fansmale/cann 的配套教程. 1. 动机 作为一个程序员,必须时刻把show me the code摆在第一位. 使用C++的原因如下: 使用python很容易成为调包侠,不符合我程序员的身份; java虽然是我的最爱, 但它GPU的支持不行, 后期扩展不好办. 2. 特点 为增强可读性和可维护性,我希望自己的程序具备如下特点: 不需要外部包的支持. 已有的深度学习代码, 往往需要各种附加的包, 增加了安装运行的难度. 本程序则从头做起,
博主要使用QT做界面,并调用python脚本使用tensorflow进行深度学习,于是开始寻找通过QT调用python脚本的方法。 网上的几种方法比如添加外部工具,只是方便了在编译器中运行脚本,不能解决在程序中通过代码调用脚本的需求,由于使用python3,添加cpython库则出现slots冲突问题,并且解决不了,出现collect2: error: ld returned 1 exit status几次尝试解决未果,于是决定直接用process来解决 方法也非常简单,只需要添加#inclu
PyTorch作为一款端到端的深度学习框架,在1.0版本之后已具备较好的生产环境部署条件。除了在web端撰写REST API进行部署之外(参考),软件端的部署也有广泛需求。尤其是最近发布的1.5版本,提供了更为稳定的C++前端API。 工业界与学术界最大的区别在于工业界的模型需要落地部署,学界更多的是关心模型的精度要求,而不太在意模型的部署性能。一般来说,我们用深度学习框架训练出一个模型之后,使用Python就足以实现一个简单的推理演示了。但在生产环境下,Python的可移植性和速...
PyTorch加载模型model.load_state_dict()问题,Unexpected key(s) in state_dict: “module.features..,Expected . 51722 用Macbook-苹果系统写代码出现显示问题Text input context does not respond to _valueForTIProperty: weixin_46218833: 给整无语了这 bug