如何启用Halide后端以提高效率
介绍
本教程指导如何使用Halide语言后端在OpenCV深度学习模块中运行模型。Halide是一个开源项目,让我们以易读的格式编写图像处理算法,根据具体设备计划计算,并以相当好的效率进行评估。
Halide项目的官方网站:http : //halide-lang.org/。
最新的效率比较:https://github.com/opencv/opencv/wiki/DNN-效率
要求
LLVM编译器
- 从http://releases.llvm.org/4.0.0/llvm-4.0.0.src.tar.xz下载LLVM源代码。打开包装 让llvm_root是源代码的根目录。
- 创建目录llvm_root / tools / clang
- 下载Clang与LLVM相同的版本。在我们的例子中,它将来自http://releases.llvm.org/4.0.0/cfe-4.0.0.src.tar.xz。将其打包成llvm_root / tools / clang。请注意,它应该是Clang源代码的根目录。
- 在Linux上构建LLVM
cd llvm_root mkdir build && cd build cmake -DLLVM_ENABLE_TERMINFO = OFF -DLLVM_TARGETS_TO_BUILD =“X86”-DLLVM_ENABLE_ASSERTIONS = ON -DCMAKE_BUILD_TYPE = Release .. make -j4
- 在Windows上构建LLVM(开发者命令提示符)
mkdir \\ path-to-llvm-build \\ && cd \\ path-to-llvm-build \\ cmake.exe -DLLVM_ENABLE_TERMINFO = OFF -DLLVM_TARGETS_TO_BUILD = X86 -DLLVM_ENABLE_ASSERTIONS = ON -DCMAKE_BUILD_TYPE = Release -DCMAKE_INSTALL_PREFIX = \\ path-to-llvm-install \\ -G“Visual Studio 14 Win64”\\ path-to-llvm-src \\ MSBuild.exe / m:4 / t:Build / p:Configuration = Release。\\ INSTALL.vcxproj
\\path-to-llvm-build\\
并且\\path-to-llvm-install\\
是不同的目录。Halide language.
- 从GitHub存储库,https://github.com/halide/Halide或使用git 下载源代码。根目录将是一个halo_root。
git clone https://github.com/halide/Halide.git
- 在Linux上构建Halide
cd halo_root
mkdir build && cd build
cmake -DLLVM_DIR = llvm_root / build / lib / cmake / llvm -DCMAKE_BUILD_TYPE = Release -DLLVM_VERSION = 40 -DWITH_TESTS = OFF -DWITH_APPS = OFF -DWITH_TUTORIALS = OFF ..
make -j4
- 在Windows上构建Halide(开发者命令提示符)
cd halo_root
mkdir build && cd build
cmake.exe -DLLVM_DIR = \\ path-to-llvm-install \\ lib \\ cmake \\ llvm -DLLVM_VERSION = 40 -DWITH_TESTS = OFF -DWITH_APPS = OFF -DWITH_TUTORIALS = OFF -DCMAKE_BUILD_TYPE = Release -G“Visual Studio 14 Win64“..
MSBuild.exe / m:4 / t:Build / p:Configuration = Release。\\ ALL_BUILD.vcxproj
用Halide后端构建OpenCV
构建OpenCV时,添加以下配置标志:
- ENABLE_CXX11 - 启用C ++ 11标准
- WITH_HALIDE - 启用卤化物连接
- HALIDE_ROOT_DIR - Halide构建目录的路径
Sample
// Sample of using Halide backend in OpenCV deep learning module.
// Based on caffe_googlenet.cpp.
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace cv::dnn;
#include <fstream>
#include <iostream>
#include <cstdlib>
/* Find best class for the blob (i. e. class with maximal probability) */
static void getMaxClass(const Mat &probBlob, int *classId, double *classProb)
{
Mat probMat = probBlob.reshape(1, 1); //reshape the blob to 1x1000 matrix
Point classNumber;
minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
*classId = classNumber.x;
}
static std::vector<std::string> readClassNames(const char *filename = "synset_words.txt")
{
std::vector<std::string> classNames;
std::ifstream fp(filename);
if (!fp.is_open())
{
std::cerr << "File with classes labels not found: " << filename << std::endl;
exit(-1);
}
std::string name;
while (!fp.eof())
{
std::getline(fp, name);
if (name.length())
classNames.push_back( name.substr(name.find(' ')+1) );
}
fp.close();
return classNames;
}
int main(int argc, char **argv)
{
std::string modelTxt = "train_val.prototxt";
std::string modelBin = "squeezenet_v1.1.caffemodel";
std::string imageFile = (argc > 1) ? argv[1] : "space_shuttle.jpg";
Net net = dnn::readNetFromCaffe(modelTxt, modelBin);
if (net.empty())
{
std::cerr << "Can't load network by using the following files: " << std::endl;
std::cerr << "prototxt: " << modelTxt << std::endl;
std::cerr << "caffemodel: " << modelBin << std::endl;
std::cerr << "SqueezeNet v1.1 can be downloaded from:" << std::endl;
std::cerr << "https://github.com/DeepScale/SqueezeNet/tree/master/SqueezeNet_v1.1" << std::endl;
exit(-1);
}
Mat img = imread(imageFile);
if (img.empty())
{
std::cerr << "Can't read image from the file: " << imageFile << std::endl;
exit(-1);
}
if (img.channels() != 3)
{
std::cerr << "Image " << imageFile << " isn't 3-channel" << std::endl;
exit(-1);
}
resize(img, img, Size(227, 227)); // SqueezeNet v1.1 predict class by 3x227x227 input image.
Mat inputBlob = blobFromImage(img, 1.0, Size(), Scalar(), false); // Convert Mat to 4-dimensional batch.
net.setInput(inputBlob); // Set the network input.
net.setPreferableBackend(DNN_BACKEND_HALIDE); // Tell engine to use Halide where it possible.
Mat prob = net.forward("prob"); // Compute output.
int classId;
double classProb;
getMaxClass(prob, &classId, &classProb); // Find the best class.
std::vector<std::string> classNames = readClassNames();
std::cout << "Best class: #" << classId << " '" << classNames.at(classId) << "'" << std::endl;
std::cout << "Probability: " << classProb * 100 << "%" << std::endl;
return 0;
} //main
说明
从SqueezeNet仓库下载Caffe模型:train_val.prototxt和squeezenet_v1.1.caffemodel。
还需要使用名称为ILSVRC2012类的文件:synset_words.txt。
将这些文件放入此程序示例的工作目录中。
- 使用.prototxt和.caffemodel文件的路径读取并初始化网络
Net net = dnn::readNetFromCaffe(modelTxt, modelBin);
- 检查网络是否已成功读取
if (net.empty()) { std::cerr << "Can't load network by using the following files: " << std::endl; std::cerr << "prototxt: " << modelTxt << std::endl; std::cerr << "caffemodel: " << modelBin << std::endl; std::cerr << "SqueezeNet v1.1 can be downloaded from:" << std::endl; std::cerr << "https://github.com/DeepScale/SqueezeNet/tree/master/SqueezeNet_v1.1" << std::endl; exit(-1); }
- 读取输入图像并转换为四维Blob,SqueezeNet v1.1可以接受
Mat img = imread(imageFile); if (img.empty()) { std::cerr << "Can't read image from the file: " << imageFile << std::endl; exit(-1); } if (img.channels() != 3) { std::cerr << "Image " << imageFile << " isn't 3-channel" << std::endl; exit(-1); } resize(img, img, Size(227, 227)); // SqueezeNet v1.1 predict class by 3x227x227 input image. Mat inputBlob = blobFromImage(img, 1.0, Size(), Scalar(), false); // Convert Mat to 4-dimensional batch.
- 将blob传递到网络
net.setInput(inputBlob); // Set the network input.
- 为其实现的层启用Halide后端
net.setPreferableBackend(DNN_BACKEND_HALIDE); // Tell engine to use Halide where it possible.
- Make forward pass
- 记住,初始化之后的第一个向前传递需要相当多的时间来实现下一个。这是因为第一次调用时Halide管道的运行时编译。
Mat prob = net.forward("prob"); // Compute output.
- 记住,初始化之后的第一个向前传递需要相当多的时间来实现下一个。这是因为第一次调用时Halide管道的运行时编译。
- 确定最好的班级
int classId; double classProb; getMaxClass(prob, &classId, &classProb); // Find the best class.
- 打印结果
std::vector<std::string> classNames = readClassNames(); std::cout << "Best class: #" << classId << " '" << classNames.at(classId) << "'" << std::endl; std::cout << "Probability: " << classProb * 100 << "%" << std::endl;
对于我们的图像我们得到:
Best class: #812 'space shuttle'
Probability: 97.9812%