目录
目录README.md

自动化测试 —— 基于等价融合算子的深度学习框架差分测试技术

姓名 学号
徐润石 201250167
于欣博 201250165
刘云辉 201250166
桂金鑫 201850107

1. 深度学习框架选择 —— PyTorch

1.1 深度学习框架

深度学习框架的发展主要经历以下几个阶段

  • 21世纪初,神经网络的概念出现,出现了部分工具用来描述和搭建神经网络模型,如Torch、OpenNN。这些工具提出的目的并不是特意用来搭建神经网络模型,并且这些工具大量api并不能直接用来搭建神经网络模型,同时也缺乏GPU的支持,使用者需要做出大量额外的工作。
  • 2012年多伦多大学Alex Krizhevsky等人提出一种全新的深度神经网络架构AlexNet,AlexNet在ImageNet数据集上精度达到SOTA标准。同时期Caffe、Theano出现,提高了CNN、RNN、LSTM模型的搭建效率。这些模型支持多GPU训练,极大减少模型的训练时间。
  • 2015-2016年,谷歌开源TensorFlow框架,成为深度学习领域最流行的深度学习框架之一;FackBook发布PyTorch框架,使用的是Python API;微软研究院研发了CNTK框架。何凯明等人提出了ResNet模型。
  • 2019-2020年,深度学习框架经过不断发展,最终划分为两大板块:TensorFlow、pytorch。

1.2 pytorch

pytorch是基于Torch的开源深度学习库,底层实现采用C++编写,支持GPU加速运算,由Facebook人工智能团队开发维护。pytorch支持动态计算图,计算图在运算过程中支持动态改变。

pytorch的主要特点:

  • 可基于Numpy初始化张量进行计算,具有GPU加速特性
  • 能构建具有自动微分功能的深度神经网络
  • pytorch设计遵循最小封装原则,层次清晰,一般不存在跨层调用,例如tensor需要转化成variable才能被module使用
  • 得益于pytorch先进的设计理念和精简的功能,在许多训练场景中pytorch的性能比TensorFlow更好
  • pytorch接口的实现具有一致性,不会因版本的变化而发生大的改变,接口命名方式易于理解
  • pytorch在提供内容齐全的官方文档和指南同时还维护一个活跃的社区

1.3 深度学习算子库

深度神经网络:输入层、隐藏层、输出层

  • 常见的神经网络类型:卷积神经网络、循环神经网络、生成对抗网络、深度强化学习
  • 深度神经网络模型通过大量训练学习被训练数据的特征,训练后模型可用于预测类似数据的行为或输出
  • 深度神经网络训练的过程中,深度神经网络算子库承担基础运算操作,算子内部逻辑不会因训练过程的进行而发生改变,算子在深度神经网络中具有原子性,作为深度神经网络中相对独立的单位
  • 卷积算子(conv1、conv2、conv3)、池化算子(avg_pool、max_pool)、激活函数(relu、tanh、elu)、分类算子(softmax)

1.4 差分测试

  • 差分测试是将相同测试用例输入到两个功能相同实现形式不全一致的程序、函数中,然后对输出进行差分分析,分析本来应该一致的输出是否存在偏差,进而判断程序、函数是否存在输出异常行为
  • 缺点:结果导向,往往只能捕捉到结果异常,难以捕捉到产生异常的细节;缺少过程管理,同一异常被捕捉到多次,异常类型分布不均;差分异常标准难以判定,大部分根据实践经验获得,缺少理论上的说服力

2. 融合算子列表

2.1 基础算子

卷积算子

  • CNN中完整的卷积是对于两个4维张量进行操作。其中输入 X 尺寸大小为 N×C×H×W ,分别代表了批样本量(batch size),输入通道数(channel),输入长和宽。而卷积核尺寸大小为 D×C×K×K,分别代表输出通道数,输入通道数,和卷积核尺寸(kernel size)。 而在每个输出通道上由 C×K×K 的单个卷积核与 C×H×W 输入通道分别进行二维卷积,再累加在一起形成一个二维的输出(利用torch.einsum这个函数操作实现权重与输入的相乘并且相加)

    • def conv2d(x, weight, bias, stride, pad): 
          n, c, h_in, w_in = x.shape
          d, c, k, j = weight.shape
          x_pad = torch.zeros(n, c, h_in+2*pad, w_in+2*pad)   # 对输入进行补零操作
          if pad>0:
              x_pad[:, :, pad:-pad, pad:-pad] = x
          else:
              x_pad = x
      
          x_pad = x_pad.unfold(2, k, stride)
          x_pad = x_pad.unfold(3, j, stride)        # 按照滑动窗展开
          out = torch.einsum(                          # 按照滑动窗相乘,
              'nchwkj,dckj->ndhw',                    # 并将所有输入通道卷积结果累加
              x_pad, weight)
          out = out + bias.view(1, -1, 1, 1)          # 添加偏置值
          return out
  • 分组卷积:在进行卷积运算时,输入通道不全部参与计算,分割成为几组,每组内部进行正常卷积。需要对输入和权重进行一下reshape,都变成多组形式,然后每个组内进行相乘累加求和。

    • def group_conv2d(x, weight, bias, stride, pad, groups): 
          n, c, h_in, w_in = x.shape
          d, c_g, k, j = weight.shape
          assert c // groups == c_g                           # 保证分组之后通道相同
          x_pad = torch.zeros(n, c, h_in+2*pad, w_in+2*pad)   # 对输入进行补零操作
          if pad>0:
              x_pad[:, :, pad:-pad, pad:-pad] = x
          else:
              x_pad = x
      
          x_pad = x_pad.unfold(2, k, stride)
          x_pad = x_pad.unfold(3, j, stride)                  # 按照滑动窗展开
          h_pad, w_pad = x_pad.size(2), x_pad.size(3)
          # 对输入按照通道分组
          x_pad = x_pad.reshape(n, groups, -1, h_pad, w_pad, k, j)      
        
          # 对权重按照输出通道分组
          weight = weight.reshape(groups, -1, c_g, k, j)                      
          out = torch.einsum(                           # 按照滑动窗相乘,
              'ngchwkj,gdckj->ngdhw',                   # 并将所有输入通道卷积结果累加
              x_pad, weight)
              
          # 再重新reshape成完整输出
          out = out.reshape(n, d, out.size(3), out.size(4))        
          # 添加偏置值
          out = out + bias.view(1, -1, 1, 1)                                         
          return out

激活函数

激活函数是向神经网络中引入非线性因素,通过激活函数神经网络就可以拟合各种曲线。

激活函数主要分为饱和激活函数(Saturated Neurons)和非饱和函数(One-sided Saturations)

  • Sigmoid和Tanh是饱和激活函数
  • ReLU以及其变种为非饱和激活函数
    • 解决梯度消失问题
    • 加速收敛

)

Batch Normalization(BN)

解决多层神经网络中间层的协方差偏移问题,在中间层的输入中实现类似于网络输入进行零均值化和方差归一化的操作

)

  • 训练过程

    • def Batchnorm_simple_for_train(x, gamma, beta, bn_param):
      """
      param:x    : 输入数据,设shape(B,L)
      param:gama : 缩放因子  γ
      param:beta : 平移因子  β
      param:bn_param   : batchnorm所需要的一些参数
          eps      : 接近0的数,防止分母出现0
          momentum : 动量参数,一般为0.9, 0.99, 0.999
          running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
          running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
      """
          running_mean = bn_param['running_mean']  #shape = [B]
          running_var = bn_param['running_var']    #shape = [B]
          momentun = bn_param['momentun']    #shape = [B]
          results = 0. # 建立一个新的变量
      
          x_mean=x.mean(axis=0)  # 计算x的均值
          x_var=x.var(axis=0)    # 计算方差
      
          running_mean = momentum * running_mean + (1 - momentum) * x_mean
          running_var = momentum * running_var + (1 - momentum) * x_var
          
          # 归一化
          x_normalized=(x - running_mean)/np.sqrt(running_var + eps)       
          # 缩放平移
          results = gamma * x_normalized + beta            
      
      
          #记录新值
          bn_param['running_mean'] = running_mean
          bn_param['running_var'] = running_var 
      
          return results , bn_param
  • 测试过程

    • def Batchnorm_simple_for_test(x, gamma, beta, bn_param):
      """
      param:x    : 输入数据,设shape(B,L)
      param:gama : 缩放因子  γ
      param:beta : 平移因子  β
      param:bn_param   : batchnorm所需要的一些参数
          eps      : 接近0的数,防止分母出现0
          momentum : 动量参数,一般为0.9, 0.99, 0.999
          running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
          running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
      """
          running_mean = bn_param['running_mean']  #shape = [B]
          running_var = bn_param['running_var']    #shape = [B]
          results = 0. # 建立一个新的变量
          
          # 归一化
          x_normalized=(x-running_mean )/np.sqrt(running_var +eps)       
          # 缩放平移
          results = gamma * x_normalized + beta            
      
          return results , bn_param

2.2 融合算子

在模型推理和训练中,BN层往往与其他层合并,以减少计算量

  • ensium:爱因斯坦求和约定,以简单的方式表示许多常见的多维线性代数数组运算

  • conv,bn:网络提速

  • conv,bn,relu:主流卷积神经网络模型中 Conv+BN+Relu → CBR融合

  • conv,relu

  • linear,relu

  • bn,relu

3. 测试数据

测试数据见代码实现(在线下载测试数据集进行测试)

4. 差分测试

进行框架下算子融合前后的性能(速度、准确率)比较(见测试数据)

5. 总结

  • 算子融合背景
    • 机器学习特别是深度学习在过去十年复兴,深度神经网络(DNN),如卷积神经网络(CNN)和递归神经网络(RNN)已经成为最先进技术的基础和许多应用的核心推动者,其在过去几年中才出现,但在今天所有的计算中已经非常流行。深度学习成功的背后是越来越大的模型规模和复杂的模型结构,需要巨大的计算和内存资源。在DNN的复杂性不断增加(提高精度)和在资源有限的移动设备上部署这些DNN(为扩大范围所需)之间,存在着权衡的困难
    • 算子融合(或内核/层融合)是一种常见的提高DNN执行效率的方法(基本思想:优化编译器所做的传统循环)
      • 消除不必要的中间结果实例化
      • 减少不必要的输入扫描
      • 实现其他优化机会
  • 研究现状
    • 研究目标
      • 内存墙:遍历实现,基于规则实现
      • 并行墙
    • 技术路线
      • 手工融合
      • XLA:切分子图,基于规则融合
      • TVM:支配树,算子分类
  • 实验总结
    • 通过实验实现ensium以及conv层的融合,提升了pytorch框架下的运行效率和准确性,说明融合算子在框架下能够起到降低运算量的作用,可开发前景广大,在进一步的深度学习框架开发中占据着重要的地位
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

©Copyright 2023 CCF 开源发展委员会
Powered by Trustie& IntelliDE 京ICP备13000930号