智能计算系统---实验一BANGC算子实现与TensorFlow集成

孤街醉人 提交于 2020-10-26 23:58:03

实验简介与说明

该实验是寒武纪陈云霁老师所著的《智能计算系统》自带的实验部分

该实验通过使用智能编程语言(BANGC)进行算子开发,对高性能库(CNML)算子进行扩展,并最终集成到编程框架(TensorFlow)中,掌握对高性能库及编程框架进行扩展的能力,使读者可以在 DLP 硬件上自由设计并优化满足特定应用场景的新算子,满足日新月异智能算法的实际需求。

而本文章并不是对该实验的直接解答,而是介绍在实验部分一些无所谓关键的坑。在本人的理解,这个实验的主要目的就是让大家熟悉使用BANGC语言进行MLU的算子开发,让大家认识到智能硬件对于目前一些特定任务,比如各种神经网络,卓越的加速作用。但是限于篇幅或是精力,该书提供的实验文档和寒武纪官网文档对于部分接口的使用没有明确和说明,造成本人在进行实验的部分时踩了不少问题点。故此写下这篇博客向各位读者介绍文档中没有介绍或说明,但对整个实验难度上没有影响的部分。

同时具体的实验步骤请参考寒武纪官方实验介绍

实验内容

前置的实验环境和实验服务器申请部分直接按照官方教程进行即可

算子实现

这个部分需要使用智能编程语言 BCL 实现 PowerDifference 算子,即需要补全/opt/AICSE-demo-student/demo/style_transfer_bcl/src/bangc/PluginPowerDifferenceOp/plugin_power_difference_kernel.h 和 plugin_power_difference_kernel.mlu文件

这一部分没有特别需要注意的接口,但是对于买了《智能计算系统》这本书的同学来说需要注意,书里介绍的是教学用非正式的DLP语言,并不是BANGC,所以里面仅需要参考开发逻辑即可,接口的具体使用请看BANGC语言的官方文档
举个例子,书里对于向量乘法的代码介绍使用的是

__vec_mul(output, input_1, input_2, LEN)

而在BANGC语言中对应的代码是

__bang_mul(output, input_1, input_2, LEN)

同时需要注意算子的参数顺序和类型,因为这个会影响到后续使用时导入参数的顺序问题。

__mlu_entry__ void PowerDifferenceKernel(A, B, C, D, E)

这里可以看同文件路径下的powerDiff.cpp中,这个是后续需要使用的单算子测试程序,里面对于参数的导入顺序分别是输入mlu_input1,输入mlu_input2,次方数pow,输出结果mlu_output以及向量长度dims_a。这里推荐使用给出的参数顺序和数据类型,减少后续修改的代码数量。

 cnrtKernelParamsBufferAddParam(params, &mlu_input1, sizeof(half*));
 cnrtKernelParamsBufferAddParam(params, &mlu_input2, sizeof(half*));
 cnrtKernelParamsBufferAddParam(params, &pow, sizeof(int));
 cnrtKernelParamsBufferAddParam(params, &mlu_output, sizeof(half*));
 cnrtKernelParamsBufferAddParam(params, &dims_a, sizeof(int));

算子测试

这个部分实现对 PowerDifference 算子本身进行测试,保证其功能正确。

需要进行补全 powerDiff.cpp 文件并执行./make.sh

该部分的接口均可以在CNRT官方手册中找到使用方法和示例,无需赘述。

cnplugin集成

这个部分通过高性能库 PluginOp 的接口对 PowerDifference 算子进行封装,主要内容是补全plugin_power_difference_op.cc 和 cnplugin.h 并编译新的 Cambricon-CNPlugin。

这个部分主要需要补全三个部分cnmlCreatePluginPowerDifferenceOpParam、 cnmlCreatePluginPowerDifferenceOp、cnmlComputePluginPowerDifferenceOpForward

cnmlCreatePluginPowerDifferenceOpParam

这个部分主要是确定算子的输入参数,可以参考/opt/AICSE-demo-student/env/Cambricon-CNPlugin-MLU270/pluginops/路径下的其他算子中相应部分进行填写

cnmlCreatePluginPowerDifferenceOp

这个部分创建了该算子的op,同样可以参考/opt/AICSE-demo-student/env/Cambricon-CNPlugin-MLU270/pluginops/路径下的其他算子中相应部分进行填写

同时在/opt/AICSE-demo-student/demo/style_transfer_bcl/src/tf-implementation/tf-add-power-diff/mlu_lib_ops.cc文件中有以下代码使用需要填写的cnmlCreatePluginPowerDifferenceOp功能,可以根据这里使用的方法确认这里填写的参数

tensorflow::Status CreatePowerDifferenceOp(MLUBaseOp** op, MLUTensor* input1,
                                             MLUTensor* input2,
                                             int input3,
                                             MLUTensor* output, int len) {
  MLUTensor* inputs_ptr[2] = {input1, input2};
  MLUTensor* outputs_ptr[1] = {output};


  CNML_RETURN_STATUS(cnmlCreatePluginPowerDifferenceOp(op, inputs_ptr, input3, outputs_ptr, len));
}

而对于内部需要实现的方法cnmlCreatePluginOp,经过试错之后可以获得内部参数说明

cnmlCreatePluginOp(cnmlBaseOp**, const char*, void*, cnrtKernelParamsBuffer_t, cnmlTensor**, int, cnmlTensor**, int, cnmlTensor**, int)

第一个就是op,第二个是op名字,第三个是kernel参数,第四个是输入的tensor数组头指针,第五个是输入tensor的数量,所以是int,第六个是输出的tensor数组头指针,第七个是输出tensor的数量,最后两个写空指针和0即可

cnmlComputePluginPowerDifferenceOpForward

这个部分创建了该算子的前向计算,同样可以参考/opt/AICSE-demo-student/env/Cambricon-CNPlugin-MLU270/pluginops/路径下的其他算子中相应部分进行填写

同时在/opt/AICSE-demo-student/demo/style_transfer_bcl/src/tf-implementation/tf-add-power-diff/mlu_lib_ops.cc文件中有以下代码使用需要填写的cnmlComputePluginPowerDifferenceOpForward功能,可以根据这里使用的方法确认这里填写的参数

tensorflow::Status ComputePowerDifferenceOp(MLUBaseOp* op,
                                              MLUCnrtQueue* queue, void* input1,
                                              void* input2, void* output) {
  void* inputs_ptr[2] = {input1, input2};
  void* outputs_ptr[1] = {output};
  CNML_RETURN_STATUS(cnmlComputePluginPowerDifferenceOpForward(
                                         op, inputs_ptr, outputs_ptr, queue));
}

而对于内部需要实现的方法cnmlCreatePluginOp,经过试错之后可以获得内部参数说明

cnmlComputePluginOpForward_V3(cnmlBaseOp_t, cnmlTensor**, int, cnmlTensor**,  int, void*,cnrtQueue_t)

第一个是op,第二个是输入的tensor数组头指针,第三个是输入tensor的数量,第四个是输出的tensor数组头指针,第五个是输出tensor的数量,第六个还是空指针,第七个是任务队列

TensorFlow算子集成

这个实验的内容是将封装后的算子集成到TensorFlow 编程框架中,将该路径下(/opt/AICSE-demo-student/demo/style_transfer_bcl/src/tf-implementation/tf-add-power-diff/)中的文件依次添加到 TensorFlow源码中(源码路径:/opt/AICSE-demo-student/env/tensorflow-v1.10/)

各文件的具体添加规则见/opt/AICSE-demo-student/demo/style_transfer_bcl/src/tf-implementation/tf-add-power-diff/readme.txt中。

添加完成之后需要重新编译源码,编译之前需要修改编译命令/opt/AICSE-demo-student/env/tensorflow-v1.10/build_tensorflow-v1.10_mlu.sh将jobs_num改为16,不然容易编译报错。

框架算子测试

该部分使用框架 API 测试上一步集成在 TensorFlow 中的算子,保证其功能正确。

主要内容是补全 …/src/online_mlu/power_difference_test_bcl.py、和…/src/online_cpu/power_difference_test_cpu.py 文件,执行:

python power_difference_test_xxx.py

其中该部分需要补全的最关键的代码tf.power_difference的使用上在/opt/AICSE-demo-student/env/tensorflow-v1.10/virtualenv_mlu/lib/python2.7/site-packages/tensorflow/python/ops/gen_math_ops.py中有说明

def power_difference(x, y, pow, name=None):
  r"""TODO: add doc.

  Args:
    x: A `Tensor`. Must be one of the following types: `bfloat16`, `float32`, `half`, `float64`, `int32`, `int64`, `complex64`, `complex128`.
    y: A `Tensor`. Must have the same type as `x`.
    pow: A `Tensor`. Must have the same type as `x`.
    name: A name for the operation (optional).

  Returns:
    A `Tensor`. Has the same type as `x`.
  """
  _ctx = _context._context or _context.context()
  if _ctx is not None and _ctx._thread_local_data.is_eager:
    try:
      _result = _pywrap_tensorflow.TFE_Py_FastPathExecute(
        _ctx._context_handle, _ctx._thread_local_data.device_name,
        "PowerDifference", name, _ctx._post_execution_callbacks, x, y, pow)
      return _result
    except _core._FallbackException:
      try:
        return power_difference_eager_fallback(
            x, y, pow, name=name, ctx=_ctx)
      except _core._SymbolicException:
        pass  # Add nodes to the TensorFlow graph.
      except (TypeError, ValueError):
        result = _dispatch.dispatch(
              power_difference, x=x, y=y, pow=pow, name=name)
        if result is not _dispatch.OpDispatcher.NOT_SUPPORTED:
          return result
        raise
    except _core._NotOkStatusException as e:
      if name is not None:
        message = e.message + " name: " + name
      else:
        message = e.message
      _six.raise_from(_core._status_to_exception(e.code, message), None)
  # Add nodes to the TensorFlow graph.
  try:
    _, _, _op = _op_def_lib._apply_op_helper(
        "PowerDifference", x=x, y=y, pow=pow, name=name)
  except (TypeError, ValueError):
    result = _dispatch.dispatch(
          power_difference, x=x, y=y, pow=pow, name=name)
    if result is not _dispatch.OpDispatcher.NOT_SUPPORTED:
      return result
    raise
  _result = _op.outputs[:]
  _inputs_flat = _op.inputs
  _attrs = ("T", _op.get_attr("T"))
  _execute.record_gradient(
      "PowerDifference", _inputs_flat, _attrs, _result, name)
  _result, = _result
  return _result

这个func的作用便是调用之前我们设计好并集成进TF里的算子,如果一切没有什么问题,最后应该获得如下的结果

comput BCL op cost 294.717073441ms
comput op cost 225.753068924ms
err rate= 5.8923983536261504e-06

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!