多层神经网络,从零开始——(五)、定义数据结构

多层神经网络,从零开始——(五)、定义数据结构_第1张图片

一、 一层的数据结构

多层神经网络结构的示意图如上图所示,首先需要定义每一层的数据。每一层中的权值、阈值是需要保存的信息,因此各自单独定义了一个结构体 Layer_WeightLayer_Threshold

每一层中还包含了其他信息有:

  • S:每一层的输入;
  • R:每一层激活函数的输入 ;
  • Z:每一层的输出 ;
  • d_Matrix_part:上篇博客中公式(26)-(31)中向量 eiT后面的部分;
  • dW:损失函数对权值的导数;
  • avg_dW:所有样本损失函数对权值的导数的平均;
  • dTheta:损失函数对阈值的导数
  • avg_dTheta:所有样本损失函数对阈值的导数的平均;
  • act_fun:每一层对应的激活函数,这里的激活函数 BaseActivationFunction 是一个抽象类,因为激活函数常用的有许多种,比如:sigmoid、tanh、ReLU等等。
!* 该模块定义了神经网络各层用到的数组结构,
!* 具体的参数意义参见PDF文档。
module mod_NNParameter
use mod_Precision
use mod_BaseActivationFunction
implicit none
    !---------------------------------------------------------
    ! 层与层之间的权重
    !---------------------------------------------------------
    type :: Layer_Weight
        !* 注意:W连接两层的结点数目分别为 M,N
        !*       则W为 N×M 的矩阵.
        real(kind=PRECISION), dimension(:,:), allocatable :: W    
    end type
    !=========================================================

    
    
    !---------------------------------------------------------
    ! 层的阈值
    !---------------------------------------------------------
    type :: Layer_Threshold
        !* 数组的大小是该阈值对应层的节点数目
        real(kind=PRECISION), dimension(:), allocatable :: Theta     
    end type
    !=========================================================

    
    
    !---------------------------------------------------------
    ! 层中用到的局部数组:输入、输出等
    !---------------------------------------------------------
    type :: Layer_Local_Array
        !* 数组的大小是该阈值对应层的节点数目
        !* S是输入数组,R=S-theta,Z是输出数组,Z=f(R)=f(S-Theta)
        real(kind=PRECISION), dimension(:), allocatable :: S
        real(kind=PRECISION), dimension(:), allocatable :: R
        real(kind=PRECISION), dimension(:), allocatable :: Z
        
        !* (Gamma^{k+1} W^{k+1})^T ... (Gamma^{n} W^{n}) p_zeta/p_zn
        real(kind=PRECISION), dimension(:), allocatable :: d_Matrix_part
        
        !* 以下zeta表示误差函数。
        !* zeta对W的导数
        !* 数组行、列大小分别是该权重W连接的两层的节点数目
        real(kind=PRECISION), dimension(:,:), allocatable :: dW
        
        !* 所有样本zeta对W的导数的求平均
        real(kind=PRECISION), dimension(:,:), allocatable :: avg_dW
        
        !* zeta对Theta的导数
        !* 数组的大小是该阈值对应层的节点数目
        real(kind=PRECISION), dimension(:), allocatable :: dTheta   
        
        !* 所有样本zeta对Theta的导数的求平均
        real(kind=PRECISION), dimension(:), allocatable :: avg_dTheta  
        
        !* 激活函数
        !* 注:BaseActivationFunction 是抽象类,不能使用动态数组.
        class(BaseActivationFunction), pointer :: act_fun
    end type 
    !=========================================================
    
    
    !* W、Theta 数组也可以统一放到 Layer_Local_Array 结构体中,
    !* 这里单独放置是为了应对未来代码可能的修改。
    
end module

二、多层神经网络结构

使用指针数组即可构造出整个神经网络的结构,需要增加的一些数据为:

  • X:为神经网络的输入;
  • t:为神经网络的目标输出;
  • loss_function:为损失函数,这里的 BaseLossFunction 是一个抽象类,因为损失函数需要根据具体的需求定义,常用的损失函数有MSE、交叉熵等。

另外最重要的是需要将神经网络的前向计算,以及反向误差计算等方法实现。

!* 该模块定义了神经网络结构,以及神经网络结构相应的运算,
!* 如:前向传播计算各层值、反向传播计算误差导数等。
!* 具体的参数、函数意义参见PDF文档。
module mod_NNStructure
use mod_Precision
use mod_NNParameter
use mod_Log
use mod_BaseActivationFunction
use mod_BaseLossFunction
implicit none

    !-------------------------
    ! 工作类:网络结构的数据 |
    !-------------------------
    type, public :: NNStructure

        ! 是否初始化完成的标识
        logical, private :: is_init_basic = .false.
        
        ! 是否初始化内存空间
        logical, private :: is_allocate_done = .false.
        
        ! 是否初始化权值矩阵、阈值
        logical, private :: is_init_weight = .false.
        logical, private :: is_init_threshold = .false.
        
        !* 是否初始化损失函数
        logical, private :: is_init_loss_fun = .false.
        
        ! 层的数目,不含输入层
        integer, public :: layers_count
    
        ! 每层节点数目构成的数组: 
        !     数组的大小是所有层的数目(含输入层)
        integer, dimension(:), allocatable, public :: layers_node_count

        ! 指向所有层权重的指针数组: 
        !     数组的大小是所有层的数目(不含输入层)  
        type (Layer_Weight), dimension(:), pointer, public :: pt_W
    
        ! 指向所有层阈值的指针数组: 
        !     数组的大小是所有层的数目(不含输入层)   
        type (Layer_Threshold), dimension(:), pointer, public :: pt_Theta
    
        ! 指向所有层局部数组结构的指针数组: 
        !     数组的大小是所有层的数目(不含输入层)   
        type (Layer_Local_Array), dimension(:), pointer, public :: pt_Layer

        ! 网络的目标输入
        real(PRECISION), dimension(:), allocatable, private :: X
        ! 网络的目标输出
        real(PRECISION), dimension(:), allocatable, private :: t
        
        !* 损失函数
        class(BaseLossFunction), pointer, private :: loss_function
        
    !||||||||||||    
    contains   !|
    !||||||||||||

        !* 初始化:
        !* (1). 给定网络基本结构、申请内存空间;
        !* (2). 随机初始化权值、阈值.
        procedure, public :: init_basic            => m_init_basic
        
        !* 是否初始化
        procedure, public :: get_init_basic_status => c_is_init_basic
        !* 前向计算,根据输入值,计算神经网络各层的值,
        !* 并返回预测值
        !* Tips:需要初始化结构、设置各层激活函数.       
        procedure, public :: forward_propagation  => m_forward_propagation
        !* 反向计算,计算误差函数对神经网络各层的导数
        !* Tips:需要初始化结构、设置各层激活函数、设置损失函数.
        procedure, public :: backward_propagation => m_backward_propagation
        
        !* 计算参数的平均梯度
        !* 完成一次m_backward_propagation计算后调用
        procedure, public :: calc_average_gradient     => m_calc_avg_gradient
        !* 将平均梯度置 0
        procedure, public :: set_average_gradient_zero => m_set_average_gradient_zero
        !* 设置损失函数
        procedure, public :: set_loss_function             => m_set_loss_function
        !* 设置指定层的激活函数
        procedure, public :: set_activation_function_layer => m_set_act_fun_layer   
        
        !* 计算所有求导变量的值 
        procedure, private :: get_all_derivative_variable => m_get_all_derivative_variable                
       
        !* 设置输入层
        procedure, private :: set_input_layer  => m_set_input_layer
        !* 设置输出层
        procedure, private :: set_output_layer => m_set_output_layer
        !* 随机初始化阈值,默认初始化到(-1,1)
        !* 在Train方法中,可以重新设置初始化.
        procedure, private :: init_layer_weight    => m_init_layer_weight
        !* 随机初始化阈值,默认初始化到(-1,1)
        !* 在Train方法中,可以重新设置初始化.
        procedure, private :: init_layer_threshold => m_init_layer_threshold

        !* 计算所有层中的局部变量 S、R、Z:
        !*      激活函数、输入层、W、Theta已随机初始化
        procedure, private :: get_all_layer_local_var => m_get_all_layer_local_var 
        !* 计算所有的d_Matrix_part:
        !*      目标输出层已初始化. 
        procedure, private :: get_all_d_Matrix_part   => m_get_all_d_Matrix_part
        
        !* 计算目标层的dW
        procedure, private :: get_layer_dW => m_get_layer_dW
        !* 计算所有的dW
        procedure, private :: get_all_dW   => m_get_all_dW
        
        !* 计算目标层的 dTheta
        procedure, private :: get_layer_dTheta => m_get_layer_dTheta
        !* 计算所有的 dTheta
        procedure, private :: get_all_dTheta   => m_get_all_dTheta
        !* 输出网络结构信息到日志
        procedure, private :: print_NN_Structrue => m_print_NN_Structrue
        !* 申请NNStructure包含的指针所需空间
        procedure, private :: allocate_pointer   => m_allocate_pointer
        !* 申请每层所需的内存空间
        procedure, private :: allocate_memory    => m_allocate_memory
        !* 销毁指针 
        procedure, private :: deallocate_pointer => m_deallocate_pointer
        !* 销毁内存空间
        procedure, private :: deallocate_memory  => m_deallocate_memory

        !* 析构函数,清理内存空间
        final :: NNStructure_clean_space

    end type NNStructure
    !--------------------------------------------------------

    
    !-------------------------
    !* 略... ...
    !-------------------------

!||||||||||||    
contains   !|
!||||||||||||

    !* 具体实现略...
     
end module

附录

多层神经网络,从零开始——(一)、Fortran读取MNIST数据集
多层神经网络,从零开始——(二)、Fortran随机生成“双月”分类问题数据
多层神经网络,从零开始——(三)、BP神经网络公式的详细推导
多层神经网络,从零开始——(四)、多层BP神经网络的矩阵形式
多层神经网络,从零开始——(五)、定义数据结构
多层神经网络,从零开始——(六)、激活函数
多层神经网络,从零开始——(七)、损失函数
多层神经网络,从零开始——(八)、分类问题中为什么使用交叉熵作为损失函数
多层神经网络,从零开始——(九)、优化函数
多层神经网络,从零开始——(十)、参数初始化
多层神经网络,从零开始——(十一)、实现训练类
多层神经网络,从零开始——(十二)、实现算例类
多层神经网络,从零开始——(十三)、关于并行计算的简单探讨

你可能感兴趣的:(多层神经网络,从零开始——(五)、定义数据结构)