• darknet c++源码阅读笔记-01-activation_kernels.cu


    目录

    1. lhtan_activate_kernel

    2. lhtan_gradient_kernel

    3. hardtan_activate_kernel

    4. linear_activate_kernel

    5. logistic_activate_kernel

    6. loggy_activate_kernel

    7.relu_activate_kernel

    8. relu6_activate_kernel

    9. elu_activate_kernel

    10. selu_activate_kernel

    11. relie_activate_kernel

    12. ramp_activate_kernel

    13. leaky_activate_kernel

     14. tanh_activate_kernel

    15. gelu_activate_kernel

    16. softplus_kernel

    17. plse_activate_kernel

    18. sech_gpu

    19. activate_kernel

    20. gradient_kernel


    1. lhtan_activate_kernel

    lhtan是light tanh,近似tanh激活函数。

    1. //! __device__ 函数限定符,指定函数在设备端执行。
    2. __device__ float lhtan_activate_kernel(float x)
    3. {
    4. if(x < 0) return .001*x;
    5. if(x > 1) return .001*(x-1) + 1;
    6. return x;
    7. }

    红色的是tanh函数,绿色的是lhtan函数,值的范围是(负无穷小,正无穷大),梯度很小,看不出来是无穷大。 

    2. lhtan_gradient_kernel

    是lhtan_activate_kernel函数的梯度,x<0时,-0.001*x的梯度是0.001,0<=x<=1时,梯度是1.

    1. //! lhtan_activate_kernel的梯度值
    2. __device__ float lhtan_gradient_kernel(float x)
    3. {
    4. if(x > 0 && x < 1) return 1;
    5. return .001;
    6. }

    3. hardtan_activate_kernel

    hard tanh激活函数。

    1. //! hard tanh激活函数
    2. __device__ float hardtan_activate_kernel(float x)
    3. {
    4. if (x < -1) return -1;
    5. if (x > 1) return 1;
    6. return x;
    7. }

    红色是tanh正切函数,黑色是hard tanh函数,绿色是hard tanh的梯度函数。 

    4. linear_activate_kernel

    线性激活函数.

    __device__ float linear_activate_kernel(float x){return x;}

    5. logistic_activate_kernel

    逻辑回归的激活函数,也称sigmoid激活函数,特点是将y值归一化到(0,1)之间。画图工具

    __device__ float logistic_activate_kernel(float x){return 1.f/(1.f + expf(-x));}

    红色的是sigmoid函数,绿色是其倒数f(x) * (1-f(x)),可以看到x=0时,梯度值最大。

    求梯度代码:

    __device__ float logistic_gradient_kernel(float x){return (1-x)*x;}  // 这里的x应该是sigmoid的y值
    

    6. loggy_activate_kernel

    与上面的sigmoid函数形态类似,不同的是归一化后y范围是(-1,1).

    y = 2 / ( 1 + exp(-x) ) - 1.

    参考sigmoid函数求导方法,求导y’ =  2*sigmoid(x)(1-sigmoid(x))

    紫色是loggy激活函数,红色是其倒数。

    __device__ float loggy_activate_kernel(float x){return 2.f/(1.f + expf(-x)) - 1;}  // 范围(-1,1)
    
    1. __device__ float loggy_gradient_kernel(float x) // x = 2 / (1+exp(-a)) - 1
    2. {
    3. float y = (x+1.F)/2.F; // y = (2 / (1+exp(-a)) - 1 + 1) / 2 = 1 / (1+exp(-a)) = y,即sigmoid函数。
    4. return 2*(1-y)*y; //
    5. }

    7.relu_activate_kernel

    常用的relu激活函数。ReLU = max(0, x),小于0的梯度为0.

    推荐一个好用的画图工具

    __device__ float relu_activate_kernel(float x){return x*(x>0);}
    __device__ float relu_gradient_kernel(float x){return (x>0);}

    8. relu6_activate_kernel

    相对于relu,不止抑制了小于0的值,也抑制了较大的值(大于6的值)。ReLU6=min(6, max(0,x))

    __device__ float relu6_activate_kernel(float x) { return min_val_cmp(max_val_cmp(x, 0), 6); }
    __device__ float relu6_gradient_kernel(float x) { return (x > 0 && x < 6); }
    

    9. elu_activate_kernel

    ELU相对于ReLU的特点是多了个"e",即没有直接丢弃左边小于0的部分,而是用e^x激活。

    (1)大于0的部分:(x >= 0)*x,和ReLU一样,不变;

    (2)小于0的部分:(x < 0)*(expf(x)-1),即e^x-1;

    __device__ float elu_activate_kernel(float x){return (x >= 0)*x + (x < 0)*(expf(x)-1);}
    
    1. //! x = e^a-1, x'=-e^a = 1+x
    2. __device__ float elu_gradient_kernel(float x){return (x >= 0) + (x < 0)*(x + 1);} // 大于0时,梯度为1,小于0时,梯度为x+1

    10. selu_activate_kernel

    SELU(the scaled exponential linear units)相对于ELU多了个S,即前面加了大于1的系数。

    大于0的部分系数是1.0507;小于0的部分系数是1.0507*1.6732;

    __device__ float selu_activate_kernel(float x) { return (x >= 0)*1.0507f*x + (x < 0)*1.0507f*1.6732f*(expf(x) - 1); }
    
    1. //! x = 1.0507*1.6732*(e^a-1), x' = x(1+b/x)=x+b
    2. __device__ float selu_gradient_kernel(float x) { return (x >= 0)*1.0507f + (x < 0)*(x + 1.0507f*1.6732f); }

    蓝色的是ELU, 绿色的是SELU。

    11. relie_activate_kernel

     也就是leaky ReLU,相对于ReLU,保留了小于0时的梯度。

    __device__ float relie_activate_kernel(float x){return (x>0) ? x : .01f*x;}  // leaky ReLU
    
    __device__ float relie_gradient_kernel(float x){return (x>0) ? 1 : .01f;}
    

    12. ramp_activate_kernel

    ramp就是在ReLU+0.1*x

    __device__ float ramp_activate_kernel(float x){return x*(x>0)+.1f*x;}  // ReLU + 0.1*x
    
    __device__ float ramp_gradient_kernel(float x){return (x>0)+.1f;}
    

    13. leaky_activate_kernel

    相对于ReLU,保留了小于0时的梯度。

    __device__ float leaky_activate_kernel(float x){return (x>0) ? x : .1f*x;}  // 和relie_activate_kernel一样。
    
    __device__ float leaky_gradient_kernel(float x){return (x>0) ? 1 : .1f;}
    

     14. tanh_activate_kernel

    tanh激活函数。范围 (-1, 1),相对于前面的loggy_activate_kernel:2.f/(1.f + expf(-x)) - 1,x系数变成了-2,梯度更大。

    __device__ float tanh_activate_kernel(float x){return (2/(1 + expf(-2*x)) - 1);}
    
    1. //! x = 2/(1+exp(-2a)) - 1, x'= (x+1)*2*(1-(x+1)/2) = (x+1)(1-x) = (1-x^2)
    2. __device__ float tanh_gradient_kernel(float x){return 1-x*x;}

    红色的是tanh激活函数,绿色的是loggy_activate_kernel,紫色是tanh函数的梯度函数。

    15. gelu_activate_kernel

    GELU相对于ReLU,越大梯度值越接近1,越小越接近0,没有直接置为0. 范围是(0,无穷大],

    __device__ float gelu_activate_kernel(float x){return (0.5*x*(1 + tanhf(0.797885*x + 0.035677*powf(x, 3))));}
    
    1. __device__ float gelu_gradient_kernel(float x) {
    2. const float x3 = powf(x, 3);
    3. return 0.5*tanhf(0.0356774*x3 + 0.797885*x) + (0.0535161*x3 + 0.398942*x) * powf(sech_gpu(0.0356774*x3 + 0.797885*x), 2) + 0.5;
    4. }

    16. softplus_kernel

    softplus(x) = ln(1+exp(x)),范围(0,无穷大)具体实现如下。

    (1)太大的,直接返回;

    (2)太小的,返回e^x;

    (3)中间的,返回ln(1+exp(x)).

    1. __device__ float softplus_kernel(float x, float threshold = 20) {
    2. if (x > threshold) return x; // too large
    3. else if (x < -threshold) return expf(x); // too small
    4. return log1pf(expf(x)); // log1pf(val)返回ln(val+1)
    5. //return logf(expf(x) + 1);
    6. }

    17. plse_activate_kernel

    分段线性激活。

    1. __device__ float plse_activate_kernel(float x)
    2. {
    3. if(x < -4) return .01f * (x + 4);
    4. if(x > 4) return .01f * (x - 4) + 1;
    5. return .125f*x + .5f;
    6. }

    18. sech_gpu

    __device__ float sech_gpu(float x) { return 2 / (expf(x) + expf(-x)); }
    

     19. activate_kernel

    使用上面定义的激活函数。

    1. /***
    2. * 根据传入的枚举类型,返回的激活值
    3. typedef enum {
    4. LOGISTIC, RELU, RELU6, RELIE, LINEAR, RAMP, TANH, PLSE, REVLEAKY, LEAKY, ELU, LOGGY, STAIR, HARDTAN, LHTAN, SELU, GELU, SWISH, MISH, HARD_MISH, NORM_CHAN, NORM_CHAN_SOFTMAX, NORM_CHAN_SOFTMAX_MAXVAL
    5. }ACTIVATION;
    6. ***/
    7. __device__ float activate_kernel(float x, ACTIVATION a)
    8. {
    9. switch(a){
    10. case LINEAR:
    11. return linear_activate_kernel(x);
    12. case LOGISTIC:
    13. return logistic_activate_kernel(x);
    14. case LOGGY:
    15. return loggy_activate_kernel(x);
    16. case RELU:
    17. return relu_activate_kernel(x);
    18. case RELU6:
    19. return relu6_activate_kernel(x);
    20. case ELU:
    21. return elu_activate_kernel(x);
    22. case SELU:
    23. return selu_activate_kernel(x);
    24. case GELU:
    25. return gelu_activate_kernel(x);
    26. case RELIE:
    27. return relie_activate_kernel(x);
    28. case RAMP:
    29. return ramp_activate_kernel(x);
    30. case LEAKY:
    31. return leaky_activate_kernel(x);
    32. case TANH:
    33. return tanh_activate_kernel(x);
    34. case PLSE:
    35. return plse_activate_kernel(x);
    36. case STAIR:
    37. return stair_activate_kernel(x);
    38. case HARDTAN:
    39. return hardtan_activate_kernel(x);
    40. case LHTAN:
    41. return lhtan_activate_kernel(x);
    42. }
    43. return 0;
    44. }

    20. gradient_kernel

    1. /***
    2. 根据不同的激活函数,调用对应的梯度函数
    3. ***/
    4. __device__ float gradient_kernel(float x, ACTIVATION a)
    5. {
    6. switch (a) {
    7. case LINEAR:
    8. return linear_gradient_kernel(x);
    9. case LOGISTIC:
    10. return logistic_gradient_kernel(x);
    11. case LOGGY:
    12. return loggy_gradient_kernel(x);
    13. case RELU:
    14. return relu_gradient_kernel(x);
    15. case RELU6:
    16. return relu6_gradient_kernel(x);
    17. case NORM_CHAN:
    18. return relu_gradient_kernel(x);
    19. case ELU:
    20. return elu_gradient_kernel(x);
    21. case SELU:
    22. return selu_gradient_kernel(x);
    23. case GELU:
    24. return gelu_gradient_kernel(x);
    25. case RELIE:
    26. return relie_gradient_kernel(x);
    27. case RAMP:
    28. return ramp_gradient_kernel(x);
    29. case LEAKY:
    30. return leaky_gradient_kernel(x);
    31. case TANH:
    32. return tanh_gradient_kernel(x);
    33. case PLSE:
    34. return plse_gradient_kernel(x);
    35. case STAIR:
    36. return stair_gradient_kernel(x);
    37. case HARDTAN:
    38. return hardtan_gradient_kernel(x);
    39. case LHTAN:
    40. return lhtan_gradient_kernel(x);
    41. }
    42. return 0;
    43. }

    参考源码:https://github.com/pjreddie/darknet

  • 相关阅读:
    如何快速本地搭建悟空CRM结合内网穿透工具高效远程办公
    paddlespeech 语音识别 web流服务部署(Streaming Speech Recognition)
    基于springboot实现医院信管系统项目【项目源码+论文说明】
    HTTP请求偶尔失败(21秒后超时) - 问题排查
    Bpmn在vue中不能使用require 进行导入图片,而是要使用new URL
    java合成多个pdf为一个pdf
    adb pull 使用
    mysql死锁排查及解决
    支持向量机核技巧:10个常用的核函数总结
    【Web开发】CSS教学(超详细,满满的干货)
  • 原文地址:https://blog.csdn.net/jizhidexiaoming/article/details/125997437