[NCNN学习笔记]-0

1、前言

经过前面对NEON基础的学习,对NEON有了一定的了解, 现在正准备逐步开始学习NCNN,关于NCNN的入门介绍可以参考如下链接。

2 、学习NCNN

下面会逐步学习NCNN中src/layer/arm 文件夹中关于NEON的代码,只提取部分关于NEON的代码进行学习。

2.1 absval_arm.cpp

先从一个简单的实现学习吧,这个文件主要是用来对数据进行取绝对值的操作。在学习之前先看一个简单的在NEON中取绝对值的例子

#include <arm_neon.h>
#include <iostream>
#include <vector>
using namespace std;
#include <arm_neon.h>
int main(){
    vector<float> data = {-1,-2,1,3};
    float32x4_t p0 = vld1q_f32(data.data());
    p0 = vabsq_f32(p0); 
    vst1q_f32(data.data(), p0);
    for(auto d : data){
        cout << d << endl;   // 1,2,1,3
    }
    return 0;
}

下面是NCNN中的实现,整体也十分简单,主要是对剩余向量进行处理方式。

int AbsVal_arm::forward_inplace(Mat& bottom_top_blob, const Option& opt) const
{
    int w = bottom_top_blob.w;
    int h = bottom_top_blob.h;
    int d = bottom_top_blob.d;
    int channels = bottom_top_blob.c;
    int elempack = bottom_top_blob.elempack;
    int size = w * h * d * elempack; // 数据类型占用的寄存器个数,参考 https://github.com/Tencent/ncnn/wiki/element-packing

    #pragma omp parallel for num_threads(opt.num_threads)
    for (int q = 0; q < channels; q++)
    {
        float* ptr = bottom_top_blob.channel(q);

        int i = 0;
#if __ARM_NEON
        // 将长度为16的整数倍的数据进行向量化加速
        for (; i + 15 < size; i += 16)
        {
            float32x4_t _p0 = vld1q_f32(ptr); // 提取4个float32的值到q寄存器
            float32x4_t _p1 = vld1q_f32(ptr + 4);
            float32x4_t _p2 = vld1q_f32(ptr + 8);
            float32x4_t _p3 = vld1q_f32(ptr + 12);
            _p0 = vabsq_f32(_p0);        // 计算绝对值
            _p1 = vabsq_f32(_p1);
            _p2 = vabsq_f32(_p2);
            _p3 = vabsq_f32(_p3);
            vst1q_f32(ptr, _p0);      // 结果写回
            vst1q_f32(ptr + 4, _p1);
            vst1q_f32(ptr + 8, _p2);
            vst1q_f32(ptr + 12, _p3);
            ptr += 16;
        }
        // 处理剩余数据
        // 处理8的倍数长度的数据
        for (; i + 7 < size; i += 8)
        {
            float32x4_t _p0 = vld1q_f32(ptr);
            float32x4_t _p1 = vld1q_f32(ptr + 4);
            _p0 = vabsq_f32(_p0);
            _p1 = vabsq_f32(_p1);
            vst1q_f32(ptr, _p0);
            vst1q_f32(ptr + 4, _p1);
            ptr += 8;
        }
        // 处理4的倍数长度的数据
        for (; i + 3 < size; i += 4)
        {
            float32x4_t _p = vld1q_f32(ptr);
            _p = vabsq_f32(_p);
            vst1q_f32(ptr, _p);
            ptr += 4;
        }
#endif // __ARM_NEON
        // c语言实现方式
        for (; i < size; i++)
        {
            *ptr = *ptr > 0 ? *ptr : -*ptr;

            ptr++;
        }
    }
    return 0;
}
2.2 arm_activation.h

这个文件中完成了部分激活函数在NEON上的实现,在学习这些激活函数之前,先了解一些NEON中基本的api用法吧。

  • vcleq_f32(v1,v2)逐元素比较,将v1小于v2的位置记为4294967295,反之记为0
float32x4_t _v = {-1, 2, -3, 4};
float32x4_t _zero = vdupq_n_f32(0.f);
uint32x4_t _lemask = vcleq_f32(_v, _zero);  {4294967295,0,4294967295,0}
  • vbslq_f32(mask,v1,v2) 根据mask中的元素,从v1和v2中挑选值,为4294967295选取v1中的元素,反之选取V2中的元素
float32x4_t a = {1,2,3,4};
float32x4_t b = {11,12,13,14};
uint32x4_t _lemask  = {0,0,4294967295,4294967295};
auto res = vbslq_f32(_lemask, a, b); // {11,12,3,4}
  • vnegq_f32, 取相反数
float32x4_t v1 = {1,2,4,8};
auto res = vnegq_f32(v1); // {-1,-2,-4,-8}
  • vrecpeq_f32,取倒数,损失较大,需要额外操作增加精度
float32x4_t v1 = {1,2,4,8};
auto res = vrecpeq_f32(v1);  // 0.98047 0.499023 0.249512 0.124756

// vrecpsq_f32(a,b) =  2 - a * b
res = vmulq_f32(vrecpsq_f32(v1, res), res) // 0.999996 0.499998 0.249999 0.125
res = vmulq_f32(vrecpsq_f32(v1, res), res)  // 1 0.5 0.25 0.125

下面开始对arm_activation.h 的几个激活函数开始学习

static inline float32x4_t activation_ps(float32x4_t _v, int activation_type, const ncnn::Mat& activation_params){
    if (activation_type == 1)  // relu
    {	
        const float32x4_t _zero = vdupq_n_f32(0.f);
        _v = vmaxq_f32(_v, _zero);  // 将小于0的元素变成0  
    }
    else if (activation_type == 2)
    {	
  
        const float32x4_t _zero = vdupq_n_f32(0.f);
        const float32x4_t _slope = vdupq_n_f32(activation_params[0]);   // 斜率
        const uint32x4_t _lemask = vcleq_f32(_v, _zero)
        float32x4_t _ps = vmulq_f32(_v, _slope);
        _v = vbslq_f32(_lemask, _ps, _v);  // 通过_lemask从 _ps和_v中挑选数据
    }    
    else if (activation_type == 3)
    {
        const float32x4_t _min = vdupq_n_f32(activation_params[0]);
        const float32x4_t _max = vdupq_n_f32(activation_params[1]);
        _v = vmaxq_f32(_v, _min);
        _v = vminq_f32(_v, _max);      // 直接限制最大最小值
    }    
    else if (activation_type == 4)
    {
        _v = sigmoid_ps(_v);           // sigmoid
    }
    else if (activation_type == 5)  // mish
    {
        _v = vmulq_f32(_v, tanh_ps(log_ps(vaddq_f32(exp_ps(_v), vdupq_n_f32(1.f)))));    // mish
    }
    else if (activation_type == 6)   // hardswish
    {
        const float alpha = activation_params[0];
        const float beta = activation_params[1];
        const float32x4_t _zero = vdupq_n_f32(0.f);
        const float32x4_t _one = vdupq_n_f32(1.f);
        float32x4_t _ans = vdupq_n_f32(beta); 
        _ans = vmlaq_n_f32(_ans, _v, alpha);  // beta + (_v * alpht)
        _ans = vmaxq_f32(_ans, _zero);  
        _ans = vminq_f32(_ans, _one); 
        _v = vmulq_f32(_ans, _v);      //  (beta + (_v * alpht)) * _v
    }

}
3、总结

本次学习了NCNN中求绝对值以及部分激活函数在NEON上的实现,但仍存在部分问题,例如tanh_ps中的许多数值计算的部分,后面有机会再进行详细的学习。

相关推荐

  1. [NCNN学习笔记]-0

    2024-03-19 19:28:04       44 阅读
  2. [NCNN学习笔记]-1

    2024-03-19 19:28:04       46 阅读
  3. [NCNN学习笔记]-4

    2024-03-19 19:28:04       41 阅读
  4. NCNN源码学习(1):Mat详解

    2024-03-19 19:28:04       22 阅读
  5. jQuery学习笔记(1.0

    2024-03-19 19:28:04       42 阅读
  6. jQuery学习笔记(3.0

    2024-03-19 19:28:04       35 阅读
  7. jQuery学习笔记(2.0

    2024-03-19 19:28:04       42 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-19 19:28:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-19 19:28:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-19 19:28:04       87 阅读
  4. Python语言-面向对象

    2024-03-19 19:28:04       96 阅读

热门阅读

  1. wsl安装ubuntu

    2024-03-19 19:28:04       41 阅读
  2. 111111111111

    2024-03-19 19:28:04       36 阅读
  3. 进击的PostgreSQL

    2024-03-19 19:28:04       38 阅读
  4. ES代替品:轻量级搜索引擎MeiliSearch

    2024-03-19 19:28:04       38 阅读
  5. tcp服务

    2024-03-19 19:28:04       46 阅读
  6. TikTok视频播放量为零的原因及解决方法

    2024-03-19 19:28:04       147 阅读
  7. element-plus el-table表格默认选中某一行

    2024-03-19 19:28:04       43 阅读