原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=116,作者:DaPalm-大数据,怕了么?

一、TensorRT基本概念

  TensorRT(GIE)是一个C++库,适用于Jetson TX1和Pascal架构显卡,支持fp16特性,也就是半精度运算。由于采取“精度换速度”的策略,在精度无明显下降的前提下,其对inference的加速明显,往往有1倍以上的性能提升。

二、TensorRT编程流程

使用TensorRT主要有连个步骤:

 - 构建阶段:构建一个网络定义(network),执行优化,并生产推理引擎(inference engine)

 - 执行阶段:引擎运行推理任务(只需要输入/输出buffers),主要是通过 IExecutionContext::execute和 IExecutionContext::enqueue同步或异步执行推理

目前支持的层(更新到TensorRT 2.1将支持自定义层,在下篇博客中介绍):
 - Convolution: 2D
 - Activation: ReLu, tanh and sigmoid
 - Pooling: max and average
 - ElementWise: sum, product or max of two tensors
 - LRN: cross-channel only
 - Fully-connected: with or without bias
 - SoftMax: cross-channel only
 - Deconvolution
1.主要两个头文件、两个库:
 1.1.头文件目录:/usr/include/x86_64-linux-gnu/
         NvCaffeParser.h NvInfer.h
 1.2.库目录:/usr/lib/x86_64-linux-gnu
       libnvcaffe_parser.so libnvinfer.so
  demo目录:/usr/src/gie_samples
  doc目录:/usr/share/doc/gie
2.软件主要流程(代码只列出主要部分):
由于会用到cuda需要包含cuda_runtime_api.h
以sampleMNIST为例:
首先从main()开始:
caffe model转gie model,创建序列化engine;
读入需要处理的图像;
用caffe parser解析均值文件(.binaryproto),与需要处理图像相减;
反序列化gieModelStream到engine,创建引擎执行的环境(context )
执行推理doInference(*context, data, prob, 1);//参数:环境,输入,输出,batch大小

关键步骤1:Caffe model转成GIE model以便提供给cuda engine进行inference:

void caffeToGIEModel(const std::string& deployFile, const std::string& modelFile, const std::vector& output, unsigned int maxBatchSize, std::ostream& gieModelStream)
//创建builder
IBuilder* builder = createInferBuilder(gLogger);
//解析caffe model到network,然后输出
INetworkDefinition* network = builder->createNetwork();
ICaffeParser* parser = createCaffeParser();
const IBlobNameToTensor* blobNameToTensor = parser->parse(deployFile,modelFile, *network,DataType::kFLOAT);
//指定哪个tensors是输出
for(auto& s : outputs)   //outputs是传入的string引用。包含需要输出的blob名字
network->markOutput(*blobNameToTensor->find(s.c_str()));
//创建engine(需要给builder传入最大batchsize和workspacesize),最后把network传入builder
ICudaEngine* engine = builder->buildCudaEngine(*network)
//最后销毁不需要的network,paser
*->destory() ;
//序列化engine,最后销毁engine、builder
engine->serialize(gieModelStream);

关键步骤2:对得到的Model的流反序列化到Cuda引擎,并创建用于执行推理的上下文环境context

gieModelStream.seekg(0, gieModelStream.beg);
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(gieModelStream);
IExecutionContext* context = engine->createExecutionContext();

关键步骤3:推理void doInference(IExecutionContext& context, float* input, float* output, int batchSize)

//从上下文环境获取引擎
const ICudaEngine& engine = context.getEngine();
//获得绑定索引数量,只有输出/输出,这值返回2
assert(engine.getNbBindings() == 2)
//通过输入输出tensors返回输入输出索引
int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME),
outputIndex = engine.getBindingIdex(OUTPUT_BLOB_NAME);
//创建GPU buffers和stream。过程与malloc类似
CHECK(cudaMalloc(&buffer[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));
CHECK(cudaMalloc(&buffer[inputIndex], batchSize * OUTPUT_H * OUTPUT_W * sizeof(float)));
//创建流进程
cudaStream_t stream;
CHECK(cudaStreamCreate(&stream));
//***DMA方式将input传入GPU,异步执行batch数据的运算,最后DMA回传output到CPU***
CHECK(cudaMemcpyAsync(buffers[inputIndex]), input, batchSize*INPUT_H*INPUT_W*sizeof(float),cudaMemcpyHostToDevice, stream);
context.enqueue(batchSize, buffers, stream, nullptr);
CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize*OUTPUT_SIZE*sizeof(float), cudaMemcpyDeviceToHost, stream));
cudaStreamSynchronize(stream);//同步
//最后释放stream和buffers
cudaStreamDestroy(stream);
CHECK(cudaFree(buffers[inputIndex]));
CHECK(cudaFree(buffers[outputIndex]));

Weighted-Entropy-based Quantization for Deep Neural Networks
原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=88,作者:DaPalm-大数据,怕了么

 

论文: Weighted-Entropy-based Quantization for Deep Neural Networks  (CVPR2017)

链接:http://openaccess.thecvf.com/content_cvpr_2017/papers/Park_Weighted-Entropy-Based_Quantization_for_CVPR_2017_paper.pdf

代码地址:https://github.com/EunhyeokPark/script_for_WQ

1.绪论部分:

量化是优化神经网络模型前向计算耗时的最有效的方法之一,以便它们部署到资源受限的移动或嵌入式系统中。在这类方法中,最重要是提供低精度损失量化。在这篇论文中,作者提出了一种基于加权熵概念的量化权值和激活值的方法。它不像最近的二值化神经网络,作者提出的方法是根据目标精度来选择量化的比特位数。这种方法更加方便的去权衡精度与性能,以便更合理的选择量化级别。虽然,作者提供了这种自动量化策略,但是对于传统训练算法来说也是很轻易使用的。作者进行大量实验,如分类(AlexNet,GoogleNet,ResNet-50/101),检测(R-FCN with ResNet-50)和语言模型(LSTM网络),不用多说,肯定是有效果的。

2.相关工作:

1.无法权衡精度和性能

2.即使有,也是部分有效,需要大量修改网络,而且不量化第一层和最后一层

提到了一些方法:LogQuan、XNOR-Net、DoReFa-Net,有兴趣可以找出来看看。

3.动机和灵感:

首先对权值和激活值的概率分布进行了分析,呈现钟型分布(bell-shaped distribution),由三部分组成:1.接近0的值占主要部分,但对结果影响不大,分配相对较少的量化级;2.两端大的值相对较少,对结果影响大,但是为了充分利用量化级只分配少量的量化级给这部分;3.既不大又不小的值相对较多,对结果影响相对较大,所以分配更多的量化级。如图右下角就是WQ量化分布图。

4.量化与权值熵:

4.1权值量化

权值量化的高层次思想是将权值分成N个聚类,从而有更多聚类代表重要权值,为每个聚类分配具有代表性的值,然后量化所有权值到某个对应聚类的值。这里最主要的是Weight Entropy,下面公式中的S,这里分成N个聚类C0,...,CN-1。Pn(概率)表示有多少比例的权值在Cn这个聚类范围内;In(重要性)表示Cn这个聚类里面所有权值的平均重要性。i(n,m)表示第n个聚类中的第m个权值的重要性,其与那个权值的二次方成比例。因为值大的权重分布比较稀疏,值小的权重的分布比较密集,所以值较大的Cn会有较大的In和较小的Pn。简单说就是高重要性低概率;高频率低重要性。

(权值量化)提供训练数据(mini-batch input)和期望的logN-bit精度(聚类个数),找聚类N中的最大权值熵。聚类代表的值对应权值量化的level。

权值熵理论限制,不能同时处理正负值,因此分成两个组负值和非负值,分别应应用算法到每组(N/2 levels)。

重要算法1

1.输入(聚类数量N,权值w)

2-3.计算每个权值的重要性

4.重要性排序s=[]

5.初始化聚类边界C0,...CN(假如s=[1,2,3,4],N=2,那么就可以得到初始化的分割边界:c0=0,c1=2,c2=4,也就是将s分成了C0={1,2}和C1={3,4}两份)

6-11.找最大权值熵S的过程中更新聚类边界。第7行遍历所有聚类1~N-1,第8行ck在ck-1和ck+1这个范围遍历的时候,找到最大S,并更新边界对应的聚类ck,即新边界聚类

12-15.遍历每个聚类,第13行计算每个聚类的重要性Ik,第14行根据Ik计算出这个聚类代表的值rk(Figure1的纵轴),第15行根据边界重要性s[ck]计算出权值的边界(Figure1的横轴)

16-17.返回r0-rN-1和b0-bN,分别表示每个聚类的值和边界

4.2激活值量化

 

 

 

Weighted-Entropy-based Quantization for Deep Neural Networks

原创作品,转载时请务必以超链接形式标明文章原始出处:http://www.dapalm.com/?p=69,作者:DaPalm-大数据,怕了么?

 

论文:Receptive Field Block Net for Accurate and Fast Object Detection  (CVPR2017)

链接:https://arxiv.org/abs/1711.07767

代码地址:https://github.com/ruinmessi/RFBNet

1 绪论部分:

当前顶级目标检测器依赖于非常深的CNN主干网络,例如ResNet-101和Inception,优点是它们具有强大的特征表现能力,但是耗时严重。相反地,一些基于轻量级模型的检测器满足实时处理,但是精度是诟病。

在RFBNet这篇论文中,主要想利用一些技巧使用轻量级模型达到速度和精度并举的检测器。灵感来自人类视觉的感受野结构Receptive Fields (RFs) ,提出了新奇的RF block(RFB)模块,来验证感受野尺寸和方向性的对提高有鉴别鲁棒特征的关系。RFBNet是以主干网络(backbone)为VGG16的SSD来构建的。下面是讨论其有效性,两项基准测试实验和结果显示。RFBNet具备同非常深的主干网络检测器的精度,但是保持了实时性。

2 相关工作:

这部分就不介绍了,主要是对目前一步法和两步法的一些利弊总结。有个比喻挺形象:YOLO定位精度差,小目标检出率低;SSD是YOLO的多尺度版本,对小目标检出有改善。

3.2 感受野模块:

RFB是一个类似Inception模块的多分支卷积模块,它的内部结构可分为两个组件:多分支卷积层以及随后的膨胀卷积层。如下图

 

3.2.1.多分支卷积层:
根据RF的定义,用多种尺寸的卷积核来实现比固定尺寸更好。具体设计:1.瓶颈结构,1x1-s2卷积减少通道特征,然后加上一个nxn卷积。2.替换5x5卷积为两个3x3卷积去减少参数,然后是更深的非线性层。有些例子,使用1xn和nx1代替nxn卷积;shortcut直连设计来自于ResNet和Inception ResNet V2。3.为了输出,卷积经常有stride=2或者是减少通道,所以直连层用一个不带非线性激活的1x1卷积层。

3.2.2.膨胀卷积层:

最初来自Deeplab,在保持参数量和同样感受野的情况下,用来获取更高分辨率的特征。图4,5展示两种RFB结构,每个分支都是一个正常卷积后面加一个膨胀卷积,主要是尺寸和膨胀因子不同。没有使用膨胀池化,主要是在组织不同RFs尺寸的时候,不够灵活。对于RFB,卷积核尺寸kernel size和膨胀因子dilate对检测器有细微影响,下面会讲。

 

3.3 RFBNet检测结构

RFBNet检测器利用多尺度一步检测框架SSD,在其中嵌入RFB模块,使得轻量级主干SSD网络也能更快更准。RFB结构可以很容易集成到CNNs中,所以RFBNet检测网络保留了大部分SSD结构。主要修改是替换顶层卷积层为RFB,这些改动小但有用。如下图。

3.3.1.轻量级主干

为了便于比较直接使用SSD原始的主干网络,也就是VGG16。它是将fc6和fc7层转换成卷积层用于下采样参数,并且将pool5从2x2-s2改成3x3-s1.膨胀卷积用来填充空缺和所有dropout层,并移除fc8层。

3.3.2.多尺度特征图RFB

原始SSD,基础主干网络后面接着一系列重叠的卷积层,得到一系列空间分辨率减小而感受野增大的特征图。RFBNet保持了这种结构,也做了些改进,用RFB-b模块替换了原来L2归一化,达到从前面卷积层获取包含小目标的底层特征图。早期版本的RFB,使用的是膨胀max pool来模拟感受野方向性的影响。由于特征图太小,最后的卷积层只保留了部分,像5x5卷积核过大,无法使用而去掉。

3.3.3微调RFB参数

主要就是调节RFB模块中卷积核kernel size以及膨胀因子dilate。RFB-a是调整后的结构,然后从conv4_3层连出。再在后面接一些小的滤波器,对性能有一定提升。

 

4.实验

通过实验结果,和自己试验,超过了目前所有检测方法。显存和时间都有保证。