tensorflow在1.3后加入了官方的windows支持,可以使用cmake在vs2015下编译c++ library。但坑还是很多,简单整理了1.3版本编译及使用过程。并介绍了编译部分kernel的方法。其他版本也可参考进行编译。
编译版本为1.3,只cpu。带gpu的tensorflow可以用1.5以后的版本。
编译前准备
组件下载或安装
1.CMake version 3.5以上
2.git (http://git-scm.com)
3.swig (http://www.swig.org/download.html)
4.Visual Studio 2015
5.Python 3.5 64-bit (选择添加安装lib文件)
6.NumPy 1.11.0 以上
注意事项:CMake 与 git安装时需要将其加入到环境变量中,否则后续编译过程中会出现找不到相关程序的错误
编译过程
1.设置vs使用64位的编译工具(否者有一定概率报out-of-memory的错误)
方法 cmd.exe D:\temp> “C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvarsall.bat” 路径改为自己的vs安装路径
2.拉tensorflow的源码到本地,并新建一个build文件夹
参考方法如下
D:\temp> git clone https://github.com/tensorflow/tensorflow.git
D:\temp> cd tensorflow\tensorflow\contrib\cmake
D:\temp\tensorflow\tensorflow\contrib\cmake> mkdir build
D:\temp\tensorflow\tensorflow\contrib\cmake> cd build
如果要编译特定版本,请checkout到需要的版本
3.使用cmake生成vs project
1)选择代码目录/build目录
2).编译选择vs2015 64bit
3).设置swig路径/选择编译shared lib
4).点击configure 和 cenerate ,在bulild目录中就可以找到tensorflow项目文件
理论上就可以编译了, 打开项目文件。右击ALL_BUILD,生成。但坑来了,报错会一大堆。
坑解决
1.编译过程中会下载若干个依赖库,如果网络通畅,等等就好了。我是在代理环境中使用,而且部分库在重新编时存在重复下载的问题。故修改了依赖库的路径,指向本地httpserver或本地git库。本地httpserver可以安装apache,然后将下载的文件放入apache网站目录中。本地git可以先clone到本地,然后指过去。
响应的camke文件在tensorflow\contrib\cmake\external文件夹中,修改例子如下:
2.部分依赖库使用camke tar解压会报错(cub 和 farmhash)。网上无好方法,也许是我电脑的问题。
解决方法,1)手动解压。 例如解压1.6.4.zip(cub源码)到 build\cub\src\cub文件夹.
2)删除build\cub\src\cub-stamp\extract-cub.cmakez中 “# Prepare a space for extracting:”后的所有类容。(改文件时cmake cenerate 后生成的。故每次cmake重新 cenerate 后都要删一次)
3.库re2编译错误
解决方法:报错的是其test项目,其实用不到。有多个解决方法,简单粗暴的:打开build\re2\src\re2\CMakeLists.txt 删除 if(RE2_BUILD_TESTING) ….. endif()中的内容即可。或修改tensorflow\contrib\cmake\external\re2.cmake 在ExternalProject_Add函数中添加一行 “CMAKE_ARGS -RE2_BUILD_TESTING=OFF”
4.不能添加AVX编译选项
为提高运算速度,需要添加AVX编译选项,但在cmakelist文件中VC++的编译选项貌似错了。
解决方法:修改tensorflow\contrib\cmake\CMakeLists.txt option(tensorflow_WIN_CPU_SIMD_OPTIONS “Enables CPU SIMD instructions”)为set(tensorflow_WIN_CPU_SIMD_OPTIONS “/arch:AVX” CACHE STRING “Enables CPU SIMD instructions”)
5.编译器的堆空间不足
在编译tf_core_kernels时会出现编译器的堆空间不足的错误。多次修改VC的编译参数无果。最后找到解决方法:自己定义需要编译的kernel,而不是编译全部kernel。减少编译时需要的内存空间。
1).修改cmake编译选项如下:
说明:删除一切不需要的项目,只保留cc_example 与 shared_lib(cc_example可以用来参考如何调用tensorflow)
重新生成vs项目文件,编译过程中可能会卡一段时间,只cup动,请耐心等待。。。
由于没有选择BULID_ALL_KERNELS,需要手动添加用到的kernels。如何确定哪些需要添加呢?
如果没有添加相应的kernels,tensorflow会有提示。例如运行cc_example中的tf_label_image_example.exe
文件在build\Debug(使用Release编译就在Release文件夹中)里。使用cmd运行tf_label_image_example.exe -h .发现需要告诉程序pb文件的地址,和图片的地址。查看项目README,bp文件下载地址为 https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz
下载文件解压,运行 tf_label_image_example –image=”D:/tmp/grace_hopper.jpg” –graph=”D:/tmp/inception_v3_2016_08_28_frozen.pb” –labels=”D:/tmp/imagenet_slim_labels.txt”
在错误信息中可以看到,没有Decodejpeg opkernel
说明需要添加Decodejpeg kenel。
kernel都在tensorflow\core\kernels文件夹中,可以通过文件名来确定缺少的kernel在那个文件中。如果名字不明显,也搜索那个文件包含缺少的kernel名称。注意kernel间是有依赖关系的。如果要添加一个kernel,你可能还需要添加另外的kernel才可以。可以通过看文件的头文件来确定依赖关系
例如maxpooling 就需要avgpooling_op pooling_ops_common eigen_pooling eigen_volume_patch的支持。
响应的修改在那个cmake文件中进行呢?
编译选项tensorflow_BUILD_ALL_KERNELS设置为OFF,查看tensorflow\contrib\cmake\tf_core_kernels.cmake发现其作用的地方。如下:我们将需要的kernel文件添加到tf_core_kernels_srcs中就可以了。
添加完成重新用cmake生成项目,而后编译
反复进行,直到没有错误为止。
这个例子正常时 输出以下内容
I tensorflow/examples/label_image/main.cc:206] military uniform (653): 0.834306
I tensorflow/examples/label_image/main.cc:206] mortarboard (668): 0.0218692
I tensorflow/examples/label_image/main.cc:206] academic gown (401): 0.0103579
I tensorflow/examples/label_image/main.cc:206] pickelhaube (716): 0.00800814
I tensorflow/examples/label_image/main.cc:206] bulletproof vest (466): 0.00535088
“military uniform”概率最大
OPENCV调用tensorflow
tensorflow使用Tensor来存储矩阵,而OPENCV使用Mat。可以通过遍历来进行赋值,也可以通过一下代码,先生成Tensor,然后使用它的data()指针,来初始化Mat。这样Mat与Tensor就可以共享数据了
// allocate a Tensor
Tensor inputImg(DT_FLOAT, TensorShape({1,inputHeight,inputWidth,3}));
// get pointer to memory for that Tensor
float *p = inputImg.flat<float>().data();
// create a "fake" cv::Mat from it
cv::Mat cameraImg(inputHeight, inputWidth, CV_32FC3, p);
// use it here as a destination
cv::Mat imagePixels = ...; // get data from your video pipeline
imagePixels.convertTo(cameraImg, CV_32FC3);
计算图的使用
一般windows下的程序只进行计算而不进行训练。故c++只需要导入计算图就可以。核心代码如下:
std::unique_ptr session; input_layer = "cv-input";//输入layer名称 output_layer = "cv-output";//输出layer名称 string graph_path = tensorflow::io::JoinPath("", "ocr.pb");//pb文件路径 Status load_graph_status = LoadGraph(graph_path, &session);//session导入计算图 std::vector outputs; Tensor tensor_t;// //////自行加入导入tensor_t数据的代码 Status run_status = session->Run({{input_layer, tensor_t}},{output_layer}, {}, &outputs);//运行计算图 Tensor temp = outputs[0];//我的输出是一维的 cout<<temp.dim_size(0)<<endl;//输出的Tensor一维大小 cout<<temp.dtype()<<endl;//输出的Tensor数据类型 cout<<temp.tensor<INT64,1>()<<endl;//输出的Tensor值 cout<<temp.vec()(0)<<endl;//输出的Tensor第一个值
头文件
tensorflow编译后没有直接生成include头文件的功能。我是参考通过pip安装的python环境中的tensorflow lib文件夹中的include文件内容。自己从源码中拷贝出来的。也不是特别麻烦。