多层神经网络,从零开始——(六)、激活函数
2018-07-12 本文已影响0人
忆霜晨
维基百科上总结了各种激活函数列表如下,详细表格参见维基百科的词条 Activation function。其中,较为常用的激活函数是ReLU。
BaseActivationFunction 是一个抽象类,定义了激活函数的函数值和导数值。由于个别激活函数例如 softmax 需要整层的信息,因此计算函数值的时候需要传入数组,而不是单个变量。
最后的代码给出了激活函数 softmax 的实现,softmax 实现的时候,需要注意的是,要防止指数计算的时候发生溢出,防止溢出只需要计算的时候减去输入数组的最大值即可。(我们知道双精度浮点数上限是 1.7x10308,也就是说 Ex,当 x > 163.44 的时候就会发生溢出。)
module mod_BaseActivationFunction
implicit none
!-------------------
! 抽象类:激活函数 |
!-------------------
type, abstract, public :: BaseActivationFunction
!||||||||||||
contains !|
!||||||||||||
!* 激活函数
procedure(abs_f), deferred, public :: f
!* 接收向量参数的激活函数
procedure(abs_f_vect), deferred, public :: f_vect
!* 激活函数导数
procedure(abs_df), deferred, public :: df
!* 接收向量参数的激活函数导数
procedure(abs_df_vect), deferred, public :: df_vect
end type BaseActivationFunction
!===================
!-------------------
! 抽象类:函数接口 |
!-------------------
abstract interface
!* 注:对于Sigmoid等激活函数,其是单变量函数,
!* 而对于softmax函数来说,它的输入是一个向量,
!* 是多变量函数,因此函数应接收向量 x.
!* 激活函数
subroutine abs_f( this, index, x, y )
use mod_Precision
import :: BaseActivationFunction
implicit none
class(BaseActivationFunction), intent(inout) :: this
!* index 表示返回 f( x(index) ) 的值
integer, intent(in) :: index
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), intent(out) :: y
end subroutine
!====
!* 接收向量参数的激活函数
subroutine abs_f_vect( this, x, y )
use mod_Precision
import :: BaseActivationFunction
implicit none
class(BaseActivationFunction), intent(inout) :: this
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), dimension(:), intent(out) :: y
end subroutine
!====
!* 激活函数一阶导数
subroutine abs_df( this, index, x, dy )
use mod_Precision
import :: BaseActivationFunction
implicit none
class(BaseActivationFunction), intent(inout) :: this
!* index 表示返回 f'( x(index) ) 的值
integer, intent(in) :: index
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), intent(out) :: dy
end subroutine
!====
!* 接收向量参数的激活函数一阶导数
subroutine abs_df_vect( this, x, dy )
use mod_Precision
import :: BaseActivationFunction
implicit none
class(BaseActivationFunction), intent(inout) :: this
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), dimension(:), intent(out) :: dy
end subroutine
!====
end interface
!===================
end module
module mod_Softmax
use mod_Precision
use mod_BaseActivationFunction
implicit none
!-------------------
! 工作类:激活函数 |
!-------------------
type, extends(BaseActivationFunction), public :: Softmax
!* 继承自BaseActivationFunction并实现其接口
!||||||||||||
contains !|
!||||||||||||
procedure, public :: f => m_fun_Softmax
procedure, public :: f_vect => m_fun_Softmax_vect
procedure, public :: df => m_df_Softmax
procedure, public :: df_vect => m_df_Softmax_vect
end type Softmax
!===================
!-------------------------
private :: m_fun_Softmax
private :: m_df_Softmax
private :: m_fun_Softmax_vect
private :: m_df_Softmax_vect
!-------------------------
!||||||||||||
contains !|
!||||||||||||
!* Softmax函数
subroutine m_fun_Softmax( this, index, x, y )
implicit none
class(Softmax), intent(inout) :: this
integer, intent(in) :: index
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), intent(out) :: y
real(PRECISION), dimension(:), allocatable :: x_c
real(PRECISION) :: sum_exp_x, max_x
allocate( x_c, SOURCE=x )
max_x = MAXVAL(x_c)
x_c = x_c - max_x
sum_exp_x = SUM(EXP(x_c))
y = EXP(x_c(index)) / sum_exp_x
deallocate( x_c )
return
end subroutine
!====
!* 接收向量输入的Softmax函数
subroutine m_fun_Softmax_vect( this, x, y )
implicit none
class(Softmax), intent(inout) :: this
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), dimension(:), intent(out) :: y
real(PRECISION), dimension(:), allocatable :: x_c
real(PRECISION) :: sum_exp_x, max_x
allocate( x_c, SOURCE=x )
max_x = MAXVAL(x_c)
x_c = x_c - max_x
sum_exp_x = SUM(EXP(x_c))
y = EXP(x_c) / sum_exp_x
deallocate( x_c )
return
end subroutine
!====
!* Softmax函数的一阶导数
subroutine m_df_Softmax( this, index, x, dy )
implicit none
class(Softmax), intent(inout) :: this
integer, intent(in) :: index
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), intent(out) :: dy
real(PRECISION) :: y, sum_exp_x
sum_exp_x = SUM(EXP(x))
y = EXP(x(index)) / sum_exp_x
dy = y * (1 - y)
return
end subroutine
!====
!* 接收向量输入的Softmax函数的一阶导数
subroutine m_df_Softmax_vect( this, x, dy )
implicit none
class(Softmax), intent(inout) :: this
real(PRECISION), dimension(:), intent(in) :: x
real(PRECISION), dimension(:), intent(out) :: dy
real(PRECISION) :: sum_exp_x
real(PRECISION), dimension(:), allocatable :: y
allocate(y, SOURCE=x)
sum_exp_x = SUM(EXP(x))
y = EXP(x) / sum_exp_x
dy = y * (1 - y)
deallocate(y)
return
end subroutine
!====
end module
附录
多层神经网络,从零开始——(一)、Fortran读取MNIST数据集
多层神经网络,从零开始——(二)、Fortran随机生成“双月”分类问题数据
多层神经网络,从零开始——(三)、BP神经网络公式的详细推导
多层神经网络,从零开始——(四)、多层BP神经网络的矩阵形式
多层神经网络,从零开始——(五)、定义数据结构
多层神经网络,从零开始——(六)、激活函数
多层神经网络,从零开始——(七)、损失函数
多层神经网络,从零开始——(八)、分类问题中为什么使用交叉熵作为损失函数
多层神经网络,从零开始——(九)、优化函数
多层神经网络,从零开始——(十)、参数初始化
多层神经网络,从零开始——(十一)、实现训练类
多层神经网络,从零开始——(十二)、实现算例类
多层神经网络,从零开始——(十三)、关于并行计算的简单探讨