[NCNN学习笔记]-0

本文介绍了如何在NCNN中利用NEON技术,特别是arm文件夹中的代码,进行数据绝对值计算,并探讨了部分激活函数在NEON上的实现,如ReLU、线性激活等。后续还会关注tanh_ps等计算的优化。
摘要由CSDN通过智能技术生成
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中的许多数值计算的部分,后面有机会再进行详细的学习。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
realesrgan-ncnn-vulkan-20211212-windows是一个基于ncnn框架和Vulkan图形API开发的图像超分辨率增强模型。它是由GitHub用户realsrgan开发的最新版本,最新发布日期为2021年12月12日,专为Windows操作系统而设计。 该模型的主要应用是图像超分辨率增强,通过提高图像的分辨率和细节,使图像看起来更加清晰和真实。它采用深度学习和卷积神经网络等先进的技术,能够将低分辨率的图像转换成高分辨率的图像,从而提升图像的质量和视觉效果。 realesrgan-ncnn-vulkan-20211212-windows的开发使用了ncnn框架和Vulkan图形API,这使得它能够在Windows系统上实现快速且高效的图像处理。ncnn是一个轻量级的深度学习框架,专注于在移动平台和嵌入式设备上实现高性能和低延迟的推理。而Vulkan图形API是一种跨平台的图形渲染和计算API,可以充分利用计算设备的性能,提供高效的图像处理和渲染能力。 realesrgan-ncnn-vulkan-20211212-windows的使用可以通过命令行或者图形界面进行,用户可以根据自己的需求和偏好选择适合的方式。该模型提供了训练好的权重参数,用户可以直接加载这些参数并进行图像超分辨率增强。此外,该模型还支持批量处理和视频处理,方便用户对多个图像进行处理。 总之,realesrgan-ncnn-vulkan-20211212-windows是一个高效、快速且易于使用的图像超分辨率增强模型,适用于Windows系统,并利用了ncnn框架和Vulkan图形API的优势,为用户提供了出色的图像处理效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rex久居

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值