3.1 Split+Sub+Concat合并算子的BCL实现

3.1.1 需求分析

要实现这个算子,首先要知道这个算子具体要做什么。首先尝试读论文《EAST: An Efficient and Accurate Scene Text Detector》和《PVANET: Deep but Lightweight Neural Networks for Real-time Object Detection》,从论文中大致读懂了算法的框架,如下图,但是并没有什么用:


图3-1 算法框架

之后从eval Python脚本中查找相关信息,其中某处引用了model.py中的model(images, weight_decay, is_training)函数,然后逐渐发现mean_image_subtraction(images, means)就是我要找的函数:

代码3-1 Split-Sub-Concat算子的Python实现

def mean_image_subtraction(images, means=[123.68, 116.78, 103.94]):'''image normalization:param images::param means::return:'''num_channels = images.get_shape().as_list()[-1]  # 获取最后一个维度if len(means) != num_channels:raise ValueError('len(means) must match the number of channels')# 在channel维度上拆分channels = tf.split(axis=3, num_or_size_splits=num_channels, value=images)   for i in range(num_channels):channels[i] -= means[i]  # 每个通道减不同的数字return tf.concat(axis=3, values=channels)

从上述代码中可以看出,算子首先需要在通道上拆分,然后3个通道分别减去means的3个值,最后再拼接。

然后要计算means值:对每一张图片求平均,还是一个常数?从Python代码中发现是常数,所以这个means值应该是一个图片数据集所有图片的平均,不能自己计算。

另外还需要知道图像的batch、宽、高和通道分别在哪个维度上。从channels = tf.split(axis=3,...)中可以看出通道在第3个维度,另外按照习惯,第0个维度是batch_size,第1个维度是宽度,第2个维度是高度。从main.cpp中得知,图像的宽度是1280,高度是672。

3.1.2 算子设计

由于图像数据的排列方式,means的3个数字是交错的。而MLU处理连续的数据比较迅速,所以简单的对每个位置求余数,根据余数减去相应的值,耗时一定会比较长。


图3-2 图像数据和平均值的位置关系

所以需要在nram上构造一些数据块,以便进行整体的减法。这样的话需要(1)决定每个块的大小,(2)如何对块赋值。

对于问题(1),根据MLU的要求,块的大小需要是16的倍数;其次,为了让每次迭代,图像都减去相同的数据块,块大小需要是3的倍数;最后,在taskdim为16的情况下,nram块的大小不能超过512。因此我决定设置块大小为384=128×3

对于问题(2),最简单的思路是用for循环,如下所示:

代码3-2 用for循环对减数赋值

    __nram__ minuse[384];for(int i = 0; i < 384; ++i) {switch(i % 3) {case 0: minuse[i] = VALUE1; break;case 1: minuse[i] = VALUE2; break;case 2: minuse[i] = VALUE3; break;}}

但是经过测试,这样的延迟比较高。所以我考虑不用计算的方式,而是直接赋值。所以我用下面的代码来生成代码,粘贴到mlu文件中。

代码3-3 用于生成代码的Python脚本

values = ["(half)123.68", "(half)116.78", "(half)103.94"]
for i in range(384):print("minused_nram[%d] = %s;" % (i, values[i%3]))

代码3-4 生成的部分代码

    minused_nram[0] = (half)123.68;minused_nram[1] = (half)116.78;minused_nram[2] = (half)103.94;minused_nram[3] = (half)123.68;minused_nram[4] = (half)116.78;minused_nram[5] = (half)103.94;......

接下来要对这种方法的速度进行测试,首先在使用这种方法的情况下,查看运行时间,然后把这些赋值的代码全部注释掉,再查看运行时间。经过比较得到结论:这种方法的速度是比较快的。

数据块构造完成之后如何做减法。BANGC提供了__bang_cycle_sub,其功能是,假设有长度 m × n m×n m×n的被减数数组和长度为 n n n的减数数组,则将被减数数组拆分成 m m m份,分别减去减数数组。同时为了减少拷贝次数,我将块大小乘了64倍,但是减数minused_nram保持不变。运行结果见图3-2。

代码3-5 用__bang_cycle_sub做减法

#define ONELINE 384
#define MUCH_DOUBLE 64int quotient = batch_num_ * 1280 * 672 * 3 / ONELINE;__nram__ half input_nram[ONELINE * MUCH_DOUBLE];quotient = quotient / MUCH_DOUBLE;for (int32_t i = taskId; i < quotient; i += taskDim) {offset = i * ONELINE * MUCH_DOUBLE;// 查看是否遍历了所有数据// __bang_printf("loop1 %d\t%d\t%d\n", taskId, i, offset);  __memcpy(input_nram, input_data_ + offset, MUCH_DOUBLE * ONELINE * sizeof(half), GDRAM2NRAM);__bang_cycle_sub(input_nram, input_nram, minused_nram, ONELINE * MUCH_DOUBLE, ONELINE);__memcpy(output_data_ + offset, input_nram, MUCH_DOUBLE * ONELINE * sizeof(half), NRAM2GDRAM);
}


图3-2 算子测试

3.2 算子集成

算子集成分为两步,用BCL将上述算子集成到CNPlugin中,和通过CNPlugin接口集成到TensorFlow框架中。

3.2.1 集成到CNPlugin框架

补全plugin_sbc_op.cc文件:

对于cnmlCreatePluginSBCOp函数,首先获得Kernel的ParamsBuffer,然后根据算子的函数声明,将第一个参数标记为Input,将第二个参数标记为Output,然后传入第三个参数batch_num_。然后用cnmlCreatePluginOp创建算子。

代码3-6 cnmlCreatePluginSBCOp函数

    // 补全cnmlCreatePluginSBCOpcnrtKernelParamsBuffer_t params;cnrtGetKernelParamsBuffer(&params);cnrtKernelParamsBufferMarkInput(params);cnrtKernelParamsBufferMarkOutput(params);cnrtKernelParamsBufferAddParam(params, &batch_num_, sizeof(int));// 由于main.cpp中只添加了3个参数,所以core_num_应该是不用添加的cnmlCreatePluginOp(op, "SBC",reinterpret_cast<void **>(&SBCKernel), params,SBC_input_tensors, 1,SBC_output_tensors, 1,nullptr, 0);cnrtDestroyKernelParamsBuffer(params);

对于cnmlComputePluginSBCOpForward函数,调用cnmlComputePluginOpForward_V4即可。

代码3-7 cnmlComputePluginSBCOpForward函数

    // 补全cnmlComputePluginSBCOpForward cnmlComputePluginOpForward_V4(op,nullptr, inputs, input_num,nullptr, outputs, output_num,queue, nullptr);

补全cnplugin.h文件:

定义cnmlPluginSBCOpParam_t结构体,如下

代码3-8 cnmlPluginSBCOpParam结构体定义

struct cnmlPluginSBCOpParam {int batch_num_;cnmlCoreVersion_t core_version;
};
typedef cnmlPluginSBCOpParam *cnmlPluginSBCOpParam_t;

添加函数声明,如下

代码3-9 cnml函数声明

cnmlStatus_t cnmlCreatPluginSBCOpParam(cnmlPluginSBCOpParam_t *param,int batch_num_);
cnmlStatus_t cnmlDestroyPluginSBCOpParam(cnmlPluginSBCOpParam_t *param);
cnmlStatus_t cnmlCreatePluginSBCOp(cnmlBaseOp_t *op,//cnmlPluginSBCOpParam_t param,cnmlTensor_t *SBC_input_tensors,cnmlTensor_t *SBC_output_tensors,int batch_num_);
cnmlStatus_t cnmlComputePluginSBCOpForward(cnmlBaseOp_t op,void **inputs,int input_num, // == 1void **outputs,int output_num, // == 1cnrtQueue_t queue);

然后将算子的文件拷贝到相关文件夹中,进行算子编译。在编译中出现了错误:tensorflow/stream_executor/mlu/mlu_api/lib_ops/mlu_lib_ops.cc:1926:96: error: 'cnmlCreatePluginPowerDifferenceOp' was not declared in this scope

过思考后发现,这个错误应该是mlu_lib_ops.cc调用了PowerDifference算子,而cnplugin.h中没有PowerDifference算子的信息。所以我将实验一中PowerDifference算子的声明也添加到了cnplugin.h中,顺利解决了此问题。

3.2.2 集成到TensorFlow框架

将CNPlugin的libcnplugin.so文件、cnplugin.h文件以及tf-SBC文件夹中的文件复制到TensorFlow的相关目录中,对TensorFlow进行编译即可。

3.3 PB模型修改

用实验提供的工具,将PB模型转换成TF Events文件,然后用TensorBoard打开。

图3-3 修改前的PB模型 图3-4 修改后的PB模型

从图3-3中可以看出,split节点、3个sub节点和concat节点都要被删除,然后添加SBC节点。由于eval.py的第177行concat = tf.get_default_graph().get_tensor_by_name("concat:0")concat节点进行了引用,所以不得不把新添加的节点命名为concat

然后把PB模型转换为pbtxt文件,从节点(name:"split/split_dim")开始注释,直到节点(name:"concat")结束。然后添加新节点:

代码3-10 PB模型的SBC节点

node {name: "concat"op: "SBC"input: "input_images"attr {key: "T"value {type: DT_FLOAT}}
}

添加完之后再用TensorBoard进行展示,见图3-4。可以看到替换成功了。刚开始我用Sublime编辑器进行修改,运行会报错,后来换成vim,问题解决。

关于上面的节点具体内容,比如op是大写还是小写、attrkey-value分别填什么,我是通过写一个简单模型(代码3-11)导出PB模型、转换成pbtxt格式知道的。

代码3-11 能够输出SBC节点的Python脚本

import tensorflow as tf
from tensorflow.python.framework import graph_utilv1=tf.Variable(tf.constant(1.0,shape=[1]),name='v1')
v2=tf.sbc(v1, name='sbc')init_op=tf.initialize_all_variables()
with tf.Session() as sess:sess.run(init_op)graph_def=tf.get_default_graph().as_graph_def()output_graph_def=graph_util.convert_variables_to_constants(sess, graph_def, ['sbc'])with tf.gfile.GFile('model/model.pb','wb') as f:f.write(output_graph_def.SerializeToString())

3.4 框架推理测试

按照视频的说明,对run.sh中模型路径进行修改,然后对run_aicse.sh的核数进行修改。


图3-5 运行推理测试

上图中添加了grep -E 'fps|recall',所以图3-6中输出被简化了。下表是新旧模型的比较。所有误差都在0.1%以内。

表3-1 模型的比较

原始模型 单核 SBC 单核 SBC 16核
Net FPS 6.067 7.661 12.858
Net 延时 164.8 ms 130.5 ms 77.8 ms
End2End FPS 4.812 5.468 8.444
End2End 延时 207.8 ms 182.9 ms 118.4 ms
召回率 0.767453 0.767453 0.767935
精度 0.836306 0.836306 0.836392
hmean 0.800402 0.800402 0.800703


图3-6 运行结果

智能计算系统实验(3) 综合实验-文本识别OCR-EAST相关推荐

  1. matlab音频信号的采样与重构,信号与系统实验(MATLAB 西电版)实验21 综合实验2-音频信号的采样与重构.ppt...

    [摘要]一.实验目的 在掌握相关基础知识的基础上,学会自己设计实验,学会运用MATLAB语言编程,并具有进行信号分析的能力. 音频信号是一种连续变化的模拟信号,计算机只能处理和记录二进制的数字信号, ...

  2. 无失真传输matlab原理,信号与系统实验(MATLAB版)实验23综合实验4——无失真传输系统.ppt...

    一.实验目的 在掌握相关基础知识的基础上,学会自己设计实验,学会运用MATLAB语言编程,并具有进行信号分析的能力.在本实验中学会利用所学方法,加深了解和掌握无失真的概念和条件. 二.实验内容 1 ...

  3. 数据库实验六综合实验-水果商店进阶一

    文章目录 一.实验目的与要求: 二.实验内容 1. 修正订单详情表orderitems中的水果价格与水果表fruits中的价格一致. 2. 在订单详情表orderitems插入新订单时自动获得水果价格 ...

  4. GIS二次开发:实验五 综合实验

    一.实验目的 1.掌握栅格数据读取与另存: 2.掌握地图制图工具的实现: 3.基于遥感影像的森林资源信息管理与更新系统的实现 二.实验仪器与设备 计算机.visual studio 软件.ArcGIS ...

  5. 【实验】综合实验-咔咔咔还是一顿整

    实验名称:咔咔咔还是一顿整 实验目的: 1.实现各VLAN间彼此通信 2.各VLAN的PC主机通过各自的DHCP服务器获取IP 涉及技术:VLAN间通信/DHCP/DHCP中继/RIP 拓扑图: VL ...

  6. cisco 《连接网络》实验wan综合实验_GNS3实验环境优化与安装

    GNS3概述 GNS3是一款具有图形化界面可以运行在多平台(包括Windows, Linux, and MacOS等)的网络虚拟软件.Cisco网络设备管理员或是想要通过CCNA,CCNP,CCIE等 ...

  7. linux综合性实验,Linux综合实验模板(4页)-原创力文档

    华北科技学院计算机系综合性实验报告 PAGE 第 PAGE 2 页 华北科技学院计算机系综合性实验 实 验 报 告 课程名称 实验学期 至 学年 第 1 学期 学生所在系部 年级 专业班级 学生姓名 ...

  8. 计算机网络思科实验,思科综合实验

    ![](https://s4.51cto.com/images/blog/202012/29/127bf98d34ccea9206a8080589466612.png?x-oss-process=im ...

  9. 文本识别OCR浅析:特征篇

    OCR技术浅探:特征提取(1) 研究背景 关于光学字符识别(Optical Character Recognition, 下面都简称OCR),是指将图像上的文字转化为计算机可编辑的文字内容,众多的研究 ...

最新文章

  1. 正则表达式中^的用法
  2. POJ 3635 Full Tank?
  3. 有哪些非关系型数据库
  4. JavaWeb之国际化
  5. GJM : Unity3D HIAR -【 快速入门 】 三、导入 SDK
  6. 怎么设置linux端口权限,Linux下设置端口权限的系统调用—ioperm和iopl
  7. cocos2d-x 执行在 genymotion上面
  8. error LNK2019: unresolved external symbol “__declspec(dllimport) public: __thiscall 的解决方案
  9. 内容联盟程序_英雄联盟可以偷看你的网页?你的账号密码还安全吗?
  10. (Kinetisnbsp;K60)WDOG看门狗测试
  11. windows 安装apex_《Apex英雄》:如何在你的PC上下载安装玩到它
  12. Spotfire10.10.0 windows10安装与配置
  13. 从市盈率、市净率、增值率看公司盈利经营发展状况
  14. 让Win7系统下的硬盘不在狂闪的诀窍【mfxp】
  15. ecef与enu的转换
  16. linux查看linux版本,内核版本,系统位数,gcc版本,Ubuntu下查看linux版本,内核版本,系统位数,gcc版本...
  17. qls的魔法(区间dp)
  18. 服务器修改和绑定mac地址,MAC地址绑定错误是为什么
  19. 生成 App 专用密码 [How to generate an app-specific password]
  20. 三菱fx3u使用st语言adprw指令通过485-adp-mb模块与台达变频器进行通信

热门文章

  1. 在顺序表中第五个位置插入一个元素9,实现顺序表插入的基本操作,输出顺序表中所有元素
  2. 哪个软件唱歌打分测试,唱歌评分软件哪个好_唱歌评分软件电脑版
  3. 多线程知识总结(VIP典藏版)
  4. AutoCAD 2013【32位/64位】官方简体中文版
  5. movie maker视频剪辑软件的使用
  6. GIS数据常见文件格式
  7. Kivy下添加图片来源表示方法
  8. CPI学院通过DRM-X 4.0加密保护其在线课程(Zoom会议直播和录制的MP4课程)
  9. apache arrow_Apache Arrow和Java:闪电般的大数据传输速度
  10. MySQL的ODBC驱动下载及安装 (免登录注册)