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

基于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处理后便很方便地用于神经网络的训练了。

实际算法源码

在ANN Toolbox中,完整的DNN定义为了兼容CNN卷积网络,实际上每个单元不仅仅可以以一维数组形式存储,同时还能存储为二维或三维数组,用于卷积网络的卷积层,同时,还实现了不同的激活函数。另外,为了便于计算每一层的梯度,又定义了两个专门的数据结构,MultiArr和TripleArr,MultiArr其实就是“数组的数组”,而TripleArr则是MultiArr的数组。完整的Unit和Layer定义的代码如下:

基本数据结构及Unit和layer的定义:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'' Data Structure for artifitial neural networks
'' 1, Common public structure used for multiple arrays
' a data type that used to store multiple arrays, usful to store delta of units in each layer _
' also useful for store all outputs of each layer of a net
Public Type MultiArr
    d() As Double
End Type

Public Type TripleArr   'triple arr structure
    m() As MultiArr
End Type

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'' 2, Define Structure NN Unit, class properties of perceptron units
Public Type NNUnit          ' the basic element of neural network, to reduce resource consumption, thus type is to be as simple as possible
    b As Double             ' bias value of unit
    w() As Double           ' weights of unit
End Type

'' 2.2, define layer structure
Public Type layer
    layerType As Byte       ' type of layer identifies the connection type of layer
    units() As NNUnit       ' all units in the layer
    unitSize1 As Long       ' count of weights in dimension 1: number of rows
    unitSize2 As Long       ' count of weights in dimension 2: number of columns
    unitSize3 As Long       ' count of weights in dimension 3: number of levels
    unitDim As Byte         ' dimension of weight array
    utf As Byte             ' unit activation function type, identified with UTF constants
    outSize1 As Long        ' dimension 1 size of output array: number of rows
    outSize2 As Long        ' dimension 2 size of output array: number of columns
    outSize3 As Long        ' dimension 3 size of output array: number of levels
End Type

基本的Unit和Layer创建、初始化和激活函数:

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'' functions of neural networks

Private Function newUnit(ByVal ws As Long, Optional ByVal ws_2 As Long = 0, Optional ByVal ws_3 As Long = 0) As NNUnit
' create a new NNUnit unit, returns created NNUnit
' the unit weight array can be created as 1/2/3 dimensional, d as dimension is a passed parameter
Dim NU As NNUnit
    
    With NU
        If ws_2 = 0 Then            ' unit is 1-D
            ReDim .w(1 To ws)
        ElseIf ws_3 = 0 Then        ' unit is 2-D
            ReDim .w(1 To ws, 1 To ws_2)
        Else                        ' unit is 3-D
            ReDim .w(1 To ws, 1 To ws_2, 1 To ws_3)
        End If
        .b = 0
    End With
    newUnit = NU
End Function

Private Function initUnit(unit As NNUnit, Optional ByVal wType As Long = wTypeRand, _
    Optional ByVal lLimit As Double = -0.05, Optional ByVal uLimit As Double = 0.05, Optional ByVal d As Byte = 1) As NNUnit
' initialize the weights of the sigmoid unit according to given weight type
' d as dimension of the unit weights
Dim i As Long, j As Long, k As Long, m As Long, n As Long, o As Long
Dim rndRange As Double, rndCentre As Double

'On Error Resume Next
    rndRange = uLimit - lLimit
    With unit
        .b = 0
        If wType = wTypeRand Then .b = Rnd() * rndRange + lLimit    'randomize bias value
        Select Case d
        Case 1
            For i = 1 To UBound(.w, 1)
                .w(i) = 0
                If wType = wTypeRand Then .w(i) = Rnd() * rndRange + lLimit     'randomize weights
            Next
        Case 2
            For i = 1 To UBound(.w, 1)
                For j = 1 To UBound(.w, 2)
                    .w(i, j) = 0
                    If wType = wTypeRand Then .w(i, j) = Rnd() * rndRange + lLimit    'randomize weights
                Next
            Next
        Case 3
            For i = 1 To UBound(.w, 1)
                For j = 1 To UBound(.w, 2)
                    For k = 1 To UBound(.w, 3)
                        .w(i, j, k) = 0
                        If wType = wTypeRand Then .w(i, j, k) = Rnd() * rndRange + lLimit   'randomize weights
                    Next
                Next
            Next
        End Select
    End With
    initUnit = unit
End Function

单元的激活函数(为了防止溢出错误,对输入的大小做了限制防止exp(x)溢出):

Private Function UTFunction(fType As Byte, X As Double) As Double
' this function calculates the Unit Transfering Function
' to prevent from overflow error, input x should be larger than 700
    If X > -700 And X < 700 Then
        Select Case fType
        Case UTFLOG
            UTFunction = 1 / (1 + Exp(-1 * X))
        Case UTFTanH
            'UTFunction = Tanh(x)
            UTFunction = (1 - Exp(-1 * X)) / (1 + Exp(-1 * X))
        Case UTFReL
            UTFunction = max(0, X)
        Case UTFLkyReL
            UTFunction = max(0, X) + 0.01 * min(0, X)
        Case UTFLin
            UTFunction = X
        Case UTFSoftMax
            ' softmax is calculated later when results of all units are calculated, here only gives an intermediate result
            UTFunction = Exp(X)
        End Select
    ElseIf X < -700 Then 'if x<-700 exp function will return "overflow" error, then value should be calculated manually
        Select Case fType
        Case UTFLOG
            UTFunction = 0
        Case UTFTanH
            'UTFunction = Tanh(x)
            UTFunction = -1
        Case UTFReL
            UTFunction = 0
        Case UTFLkyReL
            UTFunction = 0.01 * X
        Case UTFLin
            UTFunction = X
        Case UTFSoftMax
            UTFunction = 0
        End Select
    ElseIf X > 700 Then
        Select Case fType
        Case UTFLOG
            UTFunction = 1
        Case UTFTanH
            'UTFunction = Tanh(x)
            UTFunction = 1
        Case UTFReL
            UTFunction = X
        Case UTFLkyReL
            UTFunction = X
        Case UTFLin
            UTFunction = X
        Case UTFSoftMax
            UTFunction = 1E+200
        End Select
    End If
End Function

创建新的层,对于普通的DNN来说,只需要创建全连接层即可,使用newFullConLayer()函数,而在卷积网络中,需要用到三种不同的层:全连接层、卷积层或Pooling层,因此需要分别调用newFullConLayer(), newConvLayer()和newPoolingLayer()三个函数:

Private Function newFullConLayer(unitCount As Long, actiFunction As Byte, inSize As Long, Optional inSize2 As Long = 0 _
    , Optional inSize3 As Long = 0, Optional typeOfANN As Byte = NNTypeMLP) As layer
' creates a full connection layer with give parameters
' layerForCNN = 1 if the layer is a cnn layer, then output is 3D, otherwize layerForCNN = 0 then output is 1-D
Dim l As layer, i As Long

    With l
        .layerType = NNFullConLayer
        ReDim .units(1 To unitCount)
        .outSize1 = unitCount
        If typeOfANN = NNTypeCNN Then
            .outSize2 = 1
            .outSize3 = 1
        Else    'type of ann = nnTypeMLP
            .outSize2 = 0
            .outSize3 = 0
        End If
        .utf = actiFunction
        .unitSize1 = inSize
        .unitSize2 = inSize2
        .unitSize3 = inSize3
        ' the input data is either 1-D or 3-D
        If inSize2 <= 0 Then                ' 1-D input
            For i = 1 To unitCount
                .units(i) = newUnit(inSize)   ' insert empty units to the output layer
            Next
            .unitDim = 1
        ElseIf inSize3 <= 0 Then            ' 2-D input
            For i = 1 To unitCount
                .units(i) = newUnit(inSize, inSize2)
            Next
            .unitDim = 2
        Else                                ' 3-D input
            For i = 1 To unitCount
                .units(i) = newUnit(inSize, inSize2, inSize3)
            Next
            .unitDim = 3
        End If
    End With
    newFullConLayer = l
End Function
    
Private Function newConvLayer(kernelCount As Long, kSize As Byte, inSize As Long, Optional kSize2 As Byte = 0 _
    , Optional inSize2 As Long = 0, Optional channel As Long = 0, Optional ActFunc As Byte = UTFReL) As layer
' creates a convolutional layer with give parameters
Dim l As layer, d As Byte ' d as dimension of inputs (in conv layer case channel is not counted as one of the dimensions)
Dim i As Long
    
    d = 1                                   ' set initial d = 1
    If inSize2 >= 0 Then d = 2                ' d = 2 if ksize >=0
    With l
        .layerType = NNConvLayer
        ReDim .units(1 To kernelCount)
        .outSize1 = inSize - kSize + 1      ' convolution on all rows
        If d = 1 Then                       ' input is 1-D
            .outSize2 = 1
        Else                                ' when input is 2-D
            .outSize2 = inSize2 - kSize2 + 1    ' convolution on all columns when input is 2-D
        End If
        .outSize3 = kernelCount
        .utf = UTFReL
        .unitSize1 = kSize
        .unitSize2 = kSize2
        .unitSize3 = channel
        Select Case d                       ' determine the input data dimensions
        Case 1                              ' if input data is 1-D then units will be 2-D with the second dimension for channel
            For i = 1 To kernelCount
                .units(i) = newUnit(kSize, channel) ' insert empty units to the output layer, even if there's only one channel
            Next
            .unitDim = 2
        Case 2                              ' 2-D input
            For i = 1 To kernelCount
                .units(i) = newUnit(kSize, kSize2, channel)
            Next
            .unitDim = 3
        End Select
    End With
    newConvLayer = l
End Function

Private Function newPoolingLayer(ByVal rate As Byte, ByVal inSize As Long, Optional ByVal rate2 As Byte = 1, _
    Optional inSize2 As Long = 1, Optional channel As Long = 1, Optional Func As Byte = UTFMaxP) As layer
' creates a pooling layer with give parameters, the pooling function can be max-pool or avg-pool
' Func is the pooling function with value: 7 = Max pooling, 8 = Average pooling, 7 is the default value
Dim l As layer 'd As Byte  ' d as input data dimensions, the array defined for pooling layer will always be 3-D
    
    With l
        .layerType = NNPoolLayer
        ReDim .units(1 To 1)                ' a pool layer has always one unit with empty weights
        .outSize1 = inSize / rate
        .outSize2 = inSize2 / rate2     '
        .outSize3 = channel                 ' output on each channel or level
        .utf = Func
        .unitDim = 3                        ' the dimension of unit is always 3-D
        .unitSize1 = rate                   ' two numbers are to be stored
        .unitSize2 = rate2
        .unitSize3 = channel
        .units(1) = newUnit(rate, rate2, channel) _
            ' the pooling layer has only one unit, the unit has 3-D weight arrays and contains the same number of elements as _
            the kernel size
    End With
    newPoolingLayer = l
End Function

最后这部分是神经网络的创建函数,newNN()和killNN()用于创建一个神经网络或删除网络并释放内存,initNN()对一个创建好的神经网络进行权值的初始化,NNAddLayer()用于在一个网络中插入一个新的层,不管是哪一种层都用同一个函数,参数不同。

''''''''''''''''''Define Class Neuron Net'''''''''''''
'''Class method of Neuron Nets

Public Function newNN(name As String, inSize As Long, outSize As Long, outF As Byte, Optional annType As Byte = NNTypeMLP, _
Optional inSize2 As Long = 0, Optional inSize3 As Long = 0) As NN
' This function creates an "Empty" Neuron Net that contains only one full connection perceptron layer according to _
    input vector / array size and size of output vector
' the function returns the reference of the created network
' inSize defines the size of input vector, if input is an array, inSize_2 and inSize_3 defines the 2/3-D size
' outSize defines the size of output vector, it is defined that the output of a full connection perceptron layer can _
    ONLY be a 1-dimensional vector (multiple rows and only one column)
' outF defines the unit transfering function of output layer
Dim net As NN, l As layer
Dim curUnit As NNUnit
Dim i As Long
    l = newFullConLayer(outSize, outF, inSize, inSize2, inSize3, annType)   ' the last layer is always a full connection layer
    With net
        .annType = annType
        .annName = name
        .createdOn = Now()
        .createdBy = VBA.Environ("username")
        .annVersion = VERSION
        .cycleTrained = 0
        .inSize = inSize
        .inSize2 = inSize2
        .inSize3 = inSize3
        .OutLayer = l
        .hlayercount = 0
    End With
    With net.TrainParams
        .maxEpochs = 1000
        .maxCycle = 1000000
        .trainAlgorithms = NNTrainSGD
        .learningSpeed = 0.1
        .anneal = NNLearnSpeedANNONE
        .maxValidationROC = 0.95
        .maxTestROC = 0.9
        .minValidationError = 0.1
        .minTestError = 0.1
    End With
    newNN = net
End Function

Public Function initNN(net As NN, Optional ByVal wType As Long = 1, Optional ByVal lLimit As Double = -0.05, Optional ByVal uLimit As Double = 0.05) As NN
' initialize all NNUnit units in the net, wType defines init type of hidden layer units, output units are always set to rand number
' normal net works will be always initialized as small random numbers, 0 initialization is only used in special occasions
Dim outputUCount As Long
Dim lUnitCount As Long
Dim curUnit As NNUnit, curLayer As layer
Dim i As Long, j As Long

    With net
        For i = 1 To .hlayercount + 1                         ' initialize all hidden layers
            If i > .hlayercount Then
                curLayer = .OutLayer
            Else
                curLayer = .hLayer(i)
            End If
            With curLayer
                If .layerType <> NNPoolLayer Then           ' weights of pooling layer should not be initialized or modified
                    If wType = wTypeRand Then
                        lUnitCount = UBound(.units)
                        For j = 1 To lUnitCount
                            .units(j) = initUnit(.units(j), wTypeRand, lLimit, uLimit, .unitDim)       ' set all hidden layer units to be rand number
                        Next j
                    Else
                        lUnitCount = UBound(.units)
                        For j = 1 To lUnitCount
                            .units(j) = initUnit(.units(j), , , , .unitDim)                    ' set all hidden layer units to be 0
                        Next j
                    End If
                End If
            End With
            If i > .hlayercount Then
                .OutLayer = curLayer
            Else
               .hLayer(i) = curLayer
            End If
        Next i
    End With
    initNN = net
End Function

Public Function killNN(net As NN) As Long
' release all resources bound to the net

End Function

Public Function getLayerCount(net As NN) As Long
On Error GoTo errorHandler
    With net
        getLayerCount = .hlayercount + 1
    End With
errorHandler:
    If Err.Number <> 0 Then getLayerCount = 0
End Function

Public Function NNAddLayer(net As NN, ByVal s As Long, ByVal f As Byte, Optional ByVal t As Byte = 1, _
    Optional ByVal p1 As Byte, Optional ByVal p2 As Byte) As NN
' this function adds/inserts a full connection layer before the output layer, the layer can ONLY be added just before _
    the output layer and after all hidden layers previously added
' size of the layer should be provided as argument
' net is the neuron net to be modified
' s is the size (number of units) of the layer, number of weights are defined
' f is used to define the unit transfering function of the added layer
' t is the type of layers to be added, p1 and p2 means kernel size or pooling rate in different circumstances
Dim curUnit As NNUnit, outL As layer, curL As layer
Dim outUnitSize As Long
Dim outUnitCount As Long
Dim curLayerInput As Long, h As Long  ' size of current layer input
Dim i As Long, j As Long

    With net
    ' create and insert a new layer after the last hidden layer
        h = .hlayercount
        If h = 0 Then                        ' insert a new layer before the out layer
            .hlayercount = 1
            ReDim .hLayer(1 To 1)
            Select Case t
            Case 1      ' create normal full connection layer
                .hLayer(1) = newFullConLayer(s, f, .inSize, .inSize2, .inSize3, .annType)
            Case 2      ' create convolutional layer
                .hLayer(1) = newConvLayer(s, p1, .inSize, p2, .inSize2, .inSize3)
            Case 3      ' create pooling layer
                .hLayer(1) = newPoolingLayer(p1, .inSize, p2, .inSize2, .inSize3)
            End Select
        Else                                            ' insert a new layer before the outlayer and after tha last hidden layer
            .hlayercount = h + 1
            ReDim Preserve .hLayer(1 To h + 1)
            With .hLayer(h)
                Select Case t
                Case 1
                    net.hLayer(h + 1) = newFullConLayer(s, f, .outSize1, .outSize2, .outSize3, net.annType)
                Case 2
                    net.hLayer(h + 1) = newConvLayer(s, p1, .outSize1, p2, .outSize2, .outSize3)
                Case 3
                    net.hLayer(h + 1) = newPoolingLayer(p1, .outSize1, p2, .outSize2, .outSize3)
                End Select
            End With
        End If
    ' re-define the output layer if new layer size differs from previous last layer
        outL = .OutLayer
        curL = .hLayer(.hlayercount)
        If curL.outSize1 <> outL.unitSize1 Or curL.outSize2 <> outL.unitSize2 Or curL.outSize3 <> outL.unitSize3 Then
            With .OutLayer
                .unitSize1 = curL.outSize1
                .unitSize2 = curL.outSize2
                .unitSize3 = curL.outSize3
                If .unitSize2 = 0 Then
                    .unitDim = 1
                ElseIf .unitSize3 = 0 Then
                    .unitDim = 2
                Else
                    .unitDim = 3
                End If
                For i = 1 To .outSize1
                    .units(i) = resizeUnit(.units(i), curL.outSize1, curL.outSize2, curL.outSize3)
                Next
            End With
        End If
    End With
    NNAddLayer = net
End Function

在下一篇文章中,我将详细介绍神经网络运行的关键:前向传播和反向传播算法的实现。

posted @ 2018-01-23 00:04  JackiePENG  阅读(8)  评论(0编辑  收藏  举报  来源