简历项目总结

1.基于opencv的缺陷检测

1.基于opencv DNN模块的UNet道路裂纹检测 

  1. 对输入图像进行预处理,转为模型需要的输入格式数据(根据模型文件.onnx文件可以知道模型对输入数据的要求。)
  2. dnn::readNetFromONNX 读取模型完成推理与预测
  3. 解析输出结果(.onnx文件可以知道模型输出内容 例如name:203,type:float32[1,2,320,480], 这里的2表示背景和裂纹两个通道的预测得分,(二分类)通过比较两个得分哪个大,就知道是背景还是裂隙)
  4. 通过轮廓发现函数提取UNet模型推理之后的裂纹并绘制轮廓。(需要将result转为CV_8U)

 2.基于opencv 差分图像二值分析的刀片缺陷检测 

  1. 获取中间的ROI部分,对ROI部分进行轮廓发现与分析,获得每个刀片的位置。
  2. 对每个刀片根据二位模板计算差分图像。
  3. 对差分图像进行轮廓发现与分析找到华和与缺陷。

2.基于CUDA和openmp的近似最近邻检索

分别实现了基于kmeans++和kmeansⅡ的乘积量化PQ和向量量化VQ

1.VQ (两层VQ) 

  1. 聚类两次
  2. 依次计算与聚类中心的距离,该聚类中心所属的向量的距离。

2.PQ 

  1. M*96维向量可以分为3组32维或6组16维向量。 M*3*32
  2. 对每组做聚类,聚K类,得到K*3。用聚类中心代替原始向量。
  3. 查询向量同样分为3段,每段计算与聚类中心的距离。得到3组M个距离。
  4. 将3组距离相加得到M个距离再排序,得到topK.

3.kmeans++和kmeansⅡ

kmeansⅡ

  1. 初始一个聚类中心,最终生成l*iteration_times个聚类中心(2k*5个)
  2. 计算原始向量集到当前聚类中心集距离总和
  3. 计算所有向量可以加入聚类中心集的概率:2k*向量到聚类中心集的距离(到所有聚类中心距离的最小值)/第二步的距离总和。
  4. 取2k个加入聚类中心集,迭代5次得到2k*5个聚类中心。

k-means++算法选择初始聚类中心的基本原则是:初始的聚类中心之间的相互距离要尽可能的远。它选择初始聚类中心的步骤是:

  • 1)从输入的数据点集合中随机选择一个点作为第一个聚类中心c1​ ;
  • 2)对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x),并根据概率选择新的聚类中心ci​ ;
  • 3)重复过程(2)直到找到k个聚类中心。

关于第二步:

  1. 先得到每个数据点的权重(kmeansⅡ得到聚类中心的所属向量的个数)
  2. 对于每个点计算 概率:  当前点的权重*当前点到已选择的聚类中心集的距离 / 所有数据点到已选择的聚类中心集的距离和 
  3. 根据概率选择最大的,依次选择K个。然后进行普通的kmeans得到最终的K个聚类中心

3.基于毕昇C++的异构算子迁移

将图像处理库ITK中的itkSimilarityIndexImageFilter算子迁移到华为毕晟服务器上并且实现异构并行计算。相较于原算子实现了数十 倍的性能提升。

第一步:

        vec_mul(vc, va, vb);

        vec_cmp<EQ>(va, v_a_0);
        vec_select(dst_a, v_a_0, v_a_1);
        vec_cmp<EQ>(vb, v_b_0);
        vec_select(dst_b, v_b_0, v_b_1);
        vec_cmp<EQ>(vc, v_a_0);
        vec_select(dst_intersection, v_a_0, v_a_1);
……
//主机端
for(int i = 0; i <= M * N; i++ ){

      if(get_A[i] != 0){
        m_CountOfImage1[threadId]++;
      }

      if(get_B[i] != 0){
        m_CountOfImage1[threadId]++;
      }

      if(get_intersection[i] != 0){
        m_CountOfImage1[threadId]++;
      }
      
    }

问题:vec_select,vec_cmpv有数据上限限制,结果要放到128位寄存器里,主机端相加慢

第二步:利用vec_cross_add向量求和。

        vec_cmp<EQ>(va, v_0);
        vec_select(dst_a, v_0, v_1);
        vec_cmp<EQ>(vb, v_0);
        vec_select(dst_b, v_0, v_1);

        vec_mul(vc, va, vb);
        vec_cmp<EQ>(vc, v_0);
        vec_select(dst_intersection, v_0, v_1);

        //向量求和
        vec_cross_add(res1.data(), dst_a);
        vec_cross_add(res2.data(), dst_b);
        vec_cross_add(res_intersection.data(), dst_intersection);

问题:输入device端的数据必须是常量表达式,但是itk返回类型又不是常量。(申请的vector大小必须是已知的)

第三步:划分group成功。

第四步:

  1. 毕昇向量vector.load() 和 store() 完成后,原数组会发生转置。

  2. 之前数据出现乱码原因: memcpy() 是异步执行,必须得wait() 同步。Q.launch<>(){}前后使用Q.wait()同步

第五步:

使用vec_cmpv代替vec_cmp ,并用std::bitset<8>.count()方法代替select

        bisheng::vector<uint8_t, unitLength / 8> cmpMask_a;
        bisheng::vec_cmpv<GT>(cmpMask_a, va, v_0);
        bisheng::vector<uint8_t, unitLength / 8> cmpMask_b;
        bisheng::vec_cmpv<GT>(cmpMask_b, vb, v_0);
        vec_mul(vc, va, vb);
        bisheng::vector<uint8_t, unitLength / 8> cmpMask_c;
        bisheng::vec_cmpv<GT>(cmpMask_c, vc, v_0);

        for(int i = 0; i < unitLength / 8; ++i){

          std::bitset<8> binary_a( cmpMask_a[i] );
          int num_ones_a = binary_a.count();

          std::bitset<8> binary_b( cmpMask_b[i] );
          int num_ones_b = binary_b.count();

          std::bitset<8> binary_c( cmpMask_c[i] );
          int num_ones_c = binary_c.count();

          res1[0] += num_ones_a;
          res2[0] += num_ones_b;
          res_intersection[0] += num_ones_c;

        }

4.基于AscendC的MatmulPrelu融合算子开发 

修改 AscendC的 MatmulLeakyRelu  工程样例,参考 Ascend  社区算子开发文档,利用矢量计算 API  实现 MatmulPrelu  融合算子开发。npu  仿真性能达到1500000tick。

AscendC端到端算子开发工程:

创建算子工程:

  1. 修改add_custom.json内容,报错算子输入输出的名字、数量和类型
  2. 利用msopgen生成创建算子工程 
    msopgen gen -i add_custom.json -c ai_core-<soc_version> -lan cpp -out   $HOME/sample/AddCustom
  3. 编写
    CMakePresets.json // 编译配置项
    op_host                      // host侧实现文件
    │   ├── add_custom_tiling.h    // 算子tiling定义文件
    │   ├── add_custom.cpp         // 算子原型注册、shape推导、信息库、tiling实现等内容文件
    op_kernel                   // kernel侧实现文件 
    │   ├── add_custom.cpp        // 算子核函数实现文件 
  4. op_kernel/add_custom.cpp // 算子核函数实现文件: 编写算子类、进行核函数的定义,并在核函数中调用算子类的Init和Process函数。
  5. op_host/add_custom_tiling.h // 算子tiling定义文件:实现数据切分和分块的过程。
  6. 在“add_custom.cpp”文件中实现算子的shape推导,多算子融合的话,对算子中间的Tensor shape以及dtype进行推导,1.可以提前为所有张量静态分配内存,避免动态内存分配带来的开销。2.可以让我们在图执行之前,就知道各Tensor的数据类型和形状,提前校验其正确性
  7. 进行算子原型注册。修改“add_custom.cpp”文件中的算子原型注册,此函数为入口函数。

算子工程编译部署 

  1. 修改CMakePresets.json中ASCEND_CANN_PACKAGE_PATH为CANN软件包安装路径
  2. ./build.sh 进行算子工程编译。生成一个自定义算子安装包custom_opp_<target os>_<target architecture>.run
  3. 在自定义算子包所在路径下./custom_opp_<target os>_<target architecture>.run安装自定义算子
  4. 命令执行成功后,自定义算子包中的相关文件将部署至当前环境的OPP算子库的vendors/customize目录中,如果用户部署多个自定义算子包,可通过如下命令指定路径安装:./custom_opp_<target os>_<target architecture>.run --install-path=<path>

tiling注册与实现

标量运算,尽量在CPU侧执行。

数据对齐到一个data block(32B):

注册:

1.首先完成算子TilingData结构定义头文件的编写,该文件命名为“算子名称_tiling.h”

设置总计算量和每个核上总计算数据分块的个数

定义结构:

BEGIN_TILING_DATA_DEF(TilingData)               // 注册一个tiling的类,以tiling的名字作为入参
  TILING_DATA_FIELD_DEF(uint32_t, totalLength); // 添加tiling字段,总计算数据量
  TILING_DATA_FIELD_DEF(uint32_t, tileNum);     // 添加tiling字段,每个核上总计算数据分块个数
END_TILING_DATA_DEF;

 注册TilingData结构:

// 注册算子tilingdata类到对应的AddCustom算子
REGISTER_TILING_DATA_CLASS(AddCustom, TilingData)
//第一个参数为op_type(算子类型),第二个参数为TilingData的类名

实现:

完成算子host实现cpp文件中Tiling函数实现,该文件命名为“算子名称.cpp”

tiling.h定义了TilingData的类,此时可以用TilingData定义一个具体的实例。

设置TilingData的totallength、tilenum等字段值。

// 用TilingData定义一个具体的实例
TilingData tiling;
// 设置TilingData
tiling.set_totalLength(totalLength);
tiling.set_tileNum(TILE_NUM);

通过调用TilingData类的SaveToBuffer接口完成TilingData的序列化和保存。

// 序列化并保存
tiling.SaveToBuffer(context->GetRawTilingData()->GetData(), context->GetRawTilingData()->GetCapacity());
context->GetRawTilingData()->SetDataSize(tiling.GetDataSize());

非对齐shape:

数据不对齐,有的核就要多算一个数据块(最小单位ALIGNNUM),

tingling结构就要配置好参数:分配较多数据量的核的总数据块数量

分配较少数据量的核的总数据块数量

分配较多数据量的核的总数据长度

分配较少数据量的核的总数据长度

定义和注册算子需要使用的tiling参数,

#ifndef ADD_CUSTOM_UNALIGN_TILING_H
#define ADD_CUSTOM_UNALIGN_TILING_H
#include "register/tilingdata_base.h"
namespace optiling {
BEGIN_TILING_DATA_DEF(TilingDataUnalign)
  TILING_DATA_FIELD_DEF(uint32_t, formerNum); // 添加tiling字段,分配到较多数据量的核心数,即大块
  TILING_DATA_FIELD_DEF(uint32_t, tailNum);   // 添加tiling字段,分配到较少数据量的核心数,即小块
  TILING_DATA_FIELD_DEF(uint32_t, formerLength);  // 添加tiling字段,大块的长度
  TILING_DATA_FIELD_DEF(uint32_t, tailLength); // 添加tiling字段,小块的长度
  TILING_DATA_FIELD_DEF(uint32_t, alignNum); // 添加tiling字段,需要对齐到的最小数据量
END_TILING_DATA_DEF;
// 注册算子tilingdata类到对应的AddCustom算子
REGISTER_TILING_DATA_CLASS(AddCustomUnalign, TilingDataUnalign)
}
#endif // ADD_CUSTOM_UNALIGN_TILING_H

tiling实现,在“op_host/add_custom.cpp”中注册tiling实现接口。

namespace optiling {
constexpr uint32_t BLOCK_DIM = 8;
constexpr uint32_t SIZE_OF_HALF = 2;
constexpr uint32_t BLOCK_SIZE = 32;
// shape需要对齐到的最小单位
constexpr uint32_t ALIGN_NUM = BLOCK_SIZE / SIZE_OF_HALF;
static ge::graphStatus TilingFunc(gert::TilingContext *context)
{
    TilingDataUnalign tiling;
    uint32_t totalLength = context->GetInputTensor(0)->GetShapeSize();
    context->SetBlockDim(BLOCK_DIM);
    // 如果是非对齐的shape,需要向上对齐到最小单位
    uint32_t totalLengthAligned = ((totalLength + ALIGN_NUM - 1) / ALIGN_NUM) * ALIGN_NUM;
    // 把所有的数据尽可能均匀地分配到每个核上,如果不能均分的话,那么会有部分核多算一个最小单位ALIGN_NUM
    // 通过模的计算,可以得到多算一个最小单位的核的数量,也可以得到少算一个最小单位的核的数量
    // eg:1999 对齐后的总数据量为2000个数,核心数为8,数据块的最小单位是16,那么:
    // 1、最小单位数据块的总数:2000 / 16 = 125
    // 2、有5个核会分到16个最小单位的数据块:125 % 8 =5,可以称之为大块
    // 3、有3个核会分到15个最小单位的数据块:8 - 5 = 3,可以称之为小块
    uint32_t formerNum = (totalLengthAligned / ALIGN_NUM) % BLOCK_DIM;
    uint32_t tailNum = BLOCK_DIM - formerNum;
    // 计算大块和小块的数据量
    uint32_t formerLength = ((totalLengthAligned / BLOCK_DIM + ALIGN_NUM - 1) / ALIGN_NUM) * ALIGN_NUM;//16*16=256
    uint32_t tailLength = (totalLengthAligned / BLOCK_DIM / ALIGN_NUM) * ALIGN_NUM;//15*16=240
    tiling.set_formerNum(formerNum);
    tiling.set_tailNum(tailNum);
    tiling.set_formerLength(formerLength);
    tiling.set_tailLength(tailLength);
    tiling.set_alignNum(ALIGN_NUM);
    tiling.SaveToBuffer(context->GetRawTilingData()->GetData(), context->GetRawTilingData()->GetCapacity());
    context->GetRawTilingData()->SetDataSize(tiling.GetDataSize());
    context->SetTilingKey(1);
    size_t *currentWorkspace = context->GetWorkspaceSizes(1);
    currentWorkspace[0] = 0;
    return ge::GRAPH_SUCCESS;
}
} // namespace optiling

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值