基于Excel的神经网络工具箱(之一)——DNN神经网络数据结构的算法实现

近三个月一直在研究ANN,为了真正深入地理解ANN的细节,我摈弃了那种直接调用算法库构建ANN的做法,而是选择了一条布满荆棘的小路,从数据结构和算法出发,从0开始一步步构建ANN的结构大厦。因为最熟悉的“开发工具”就是VBA(才疏学浅,其他的语言除了C以外都学艺不精),因此就从三个月前的国庆长假开始正式启动了开发一个Excel的神经网络工具箱的工作,现在已经完成了DNN的部分,可以在Excel中建立一个DNN,并提供了三种不同的训练算法进行训练,最后可以将DNN保存并发布。未来还将增加CNN、RNN的内容。这里记录的东西都是一些学习心得,附上部分源码,以跟同好共同学习

我们一般所谓的DNN其实名字应该叫做全连接多层感知器,每个感知器模拟大脑里神经元的工作方式,通过“树突”上的突触端接受化学电信号,经过“轴突”的突触端将信号传递到下一个神经元。神经元传递信号与否取决于神经元本身是否被输入信号激活,而神经元是否被激活又取决于它本身的特性与输入信号的模式。我个人的理解,神经元就像一个选择性导通的电阻,当接收到“合适”的输入信号时就导通(激活),然后传递信号到下个神经元,否则就不导通。
人工神经网络就是由许多模拟上述神经元工作模式的人工模拟神经元组成的,每一个神经元被称为“感知器”,它其实是一类包含大量输入参数的函数,这个函数其实就是将输入向量的每一个分量乘以一个权值后加总,再加上一个偏置(bias)后经一次非线性变换后再输出。下面这张图大家肯定都看熟了:

将许多感知器单元分层互联起来,使第一层的每个单元的输入来自系统的输入,而后续各层的输入分别来自它的前一层的输出信号,这样就构建了一个完整的MLP网络(Multi-Level Perceptron多层感知器,不过貌似现在大家比较喜欢使用DNN这个名字,当然,广义的DNN还可以有其他更多的结构)。

言归正传,我需要在Excel的VBA里建立这样一个MLP的数据结构,并且实现这个数据结构上的前向传播和反向传播训练算法,其实这个数据结构还是很简单的,首先定义每个单元,每个单元内部仅包含两部分内容,一个是双精度浮点型数据偏置bias,另一个就是权值,由于权值数量较多所以可以用数组表示,因此最简单的NNUnit就可以如下定义

Public Type NNUnit          
    b As Double             ' bias value of unit
    w(1 to w_size) As Double    ' weights of unit
End Type

其实还有更简单的定义方法,那就是将b定义为w(0),那样连b都省掉了,这样做有个好处就是更加方便后续进行反向传播时进行矩阵计算
定义好Unit之后可以直接定义Layer(层)以及整个网络的数据结构:

Public Type layer
    units() As NNUnit       ' all units in the layer
    ActiF As Byte             ' unit activation function type, identified with UTF constants
End Type
Public Type NN
    inSize As Long          ' size of input vector
    hLayer() As layer       ' array of hidden layers
    hLayerCount As Long     ' count of hidden layers, in order to facilitate hidden layer location
    OutLayer As layer       ' the output layer of the net
End Type

去掉不关键的部分后,网络的结构和层的结构也很简单,ActiF定义了单元的激活函数,因为可能在定义网络的时候需要用到不同的激活函数。整个数据结构是一个级联的数组结构,即一个网络包含若干层,而每一层都包含若干单元。大家可以看到我在NN的数据结构中显式地声明了一个输出层以及一个包含若干隐藏层的Layer型数组,实际上更简单的做法是直接声明一个Layer型数组包含所有层,这样还能够简化前向和后向传播算法的代码。
现在数据结构已经搭好了,为了让这个数据结构有内容,我们可以创建并初始化任意一个网络了,我创建了两个基本函数,NewNN()和NNAddLayer(),分别用来创建一个单层网络以及用来向一个网络中添加感知器层。
下面是新建网络函数,接受两个参数

Public Function newNN(name As String, inSize As Long, outSize As Long, outF As Byte) As NN
Dim net As NN, l As layer
Dim i As Long

    With l
        ReDim .units(1 To outSize)
        For i = 1 To outSize
            .units(i) = newUnit(inSize)   ' insert empty units to the output layer
            .utf = outF
        Next
    End With
    With net
        .inSize = inSize
        .OutLayer = l
        .hLayerCount = 0
    End With
    newNN = net
End Function

而NNAddLayer函数在任意网络的输出层前插入一个新的层,接受的参数是新层的单元数量,这时候需要注意,新插入的层可能会打乱输出层的单元连接,因此需要将输出层的所有单元重置一下,比如,原来输出层的前一层有10个单元,输出层有3个单元,每个单元有10个权值与前一层连接,现在插入一个新层,该层有7个单元,这时候应该将新插入的层的每个单元置10个权值与前一个单元连接,而输出层的每个单元的权值应该改成7个,以便与新插入的层全连接。这个过程通过下面函数实现,其中调用了一些基本单元操作函数,就不详细展开了:

Public Function NNAddLayer(net As NN, ByVal s As Long, f As Byte) As NN
' this function adds/inserts one layer before the output layer, returns the new net
' size of the layer should be provided as argument
' net is the neuron net to be modified
' s is the size (number of NNUnit units) of the layer, number of weights are defined
' f is used to define the unit transfering function of the added layer
Dim curUnit As NNUnit
Dim outUnitSize As Long
Dim outUnitCount As Long
Dim curLayerInput As Long ' size of current layer input
Dim i As Long, j As Long

    With net
    ' insert new layer after the last hidden layer

        If .hLayerCount = 0 Then                        ' insert a new layer before the out layer
            curLayerInput = .inSize
            .hLayerCount = 1
            ReDim .hLayer(1 To 1)
            ReDim .hLayer(1).units(1 To s)
        Else                                            ' insert a new layer before the outlayer and after tha last hidden layer
            curLayerInput = UBound(.hLayer(.hLayerCount).units)
            .hLayerCount = .hLayerCount + 1
            ReDim Preserve .hLayer(1 To .hLayerCount)
            ReDim .hLayer(.hLayerCount).units(1 To s)
        End If
        For i = 1 To s
            curUnit = newUnit(curLayerInput)
            .hLayer(.hLayerCount).units(i) = curUnit
        Next
            .hLayer(.hLayerCount).utf = f
    ' re-define the output layer if new layer size differs from previous last layer
        outUnitSize = .OutLayer.units(1).w_size
        outUnitCount = UBound(.OutLayer.units)
        If outUnitSize <> s Then
            For i = 1 To outUnitCount
                .OutLayer.units(i) = resizeUnit(.OutLayer.units(i), s)
            Next
        End If
    End With
    NNAddLayer = net
End Function

通过调用上面两个函数,一个完整的DNN结构就可以在Excel的内存中创建出来。下一篇,我将分享这个DNN的前向传播和反向传播算法的实现,同时,由于Excel本身就是一个非常不错的数据处理工具,我也将分享工具箱中的一个数据处理模块,可以很容易地从Excel的单元格中创建可以用于DNN的训练的数据集,这样有数据分析需要的同学就不可以直接将数据导入Excel处理后便很方便地用于神经网络的训练了

发布了15 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Shepherdppz/article/details/79135006