我在自己的 CNN 中被反向传播困住了

问题描述 投票:0回答:0

请帮助我,我的错误在哪里?

我在没有任何外部库的情况下用 C# 编写自己的 CNN,因为我想了解神经网络的工作原理。但现在我面临的问题是,在训练过程中,无论学习率如何,第一个卷积层上的过滤器值都趋向于-2000或更少,神经网络本身似乎被冻结了,甚至不会改变预测输入数据发生变化。

首先,我会说基本上我弄清楚了卷积是如何发生的,原则上,神经网络是如何工作的,但是理解错误的反向传播对我来说是一个真正的考验。

事实是,当我从感知器的层接收到误差向量时,我将其转换为最后一个卷积层的输出张量维度的张量,然后我执行池化的逆运算,将每个值乘以导数,结果我得到了一个准备反向折叠的张量。 在此卷积之前,我通过将这一层中的输入张量与误差张量进行卷积来找到过滤器的梯度。 接下来,我将此梯度应用于过滤器,并将误差张量应用于偏差。在我看来,这就是我的神经网络偏差无法操作的主要问题,因为我不完全了解如何更新偏差。

UPD:我看到如果我不在卷积方法中对张量应用偏差,则不会发生第一次反向传播后 cnn 的冻结。

我在这里展示主要代码:

主要反向传播方式:

public void BackPropagation(double expectedAnswer, double learningRange) {
            try {
                for (var i = 0; i < PerceptronLayers[^1].Neurons.Length - 1; i++) 
                    if (i != (int)expectedAnswer) 
                        PerceptronLayers[^1].NeuronsError[i] = -PerceptronLayers[^1].Neurons[i] * 
                                                                  NeuronActivate.GetDerivative(PerceptronLayers[^1].Neurons[i]);
                    else PerceptronLayers[^1].NeuronsError[i] = (1.0 - PerceptronLayers[^1].Neurons[i]) * 
                                                                NeuronActivate.GetDerivative(PerceptronLayers[^1].Neurons[i]);

                for (var i = PerceptronLayers.Length - 2; i >= 0; i--) {
                    PerceptronLayers[i].NeuronsError = PerceptronLayers[i].Weights.GetTranspose() * PerceptronLayers[i + 1].NeuronsError;
                    for (var j = 0; j < PerceptronLayers[i].Neurons.Length; j++)
                        PerceptronLayers[i].NeuronsError[j] *= NeuronActivate.GetDerivative(PerceptronLayers[i].NeuronsError[j]);
                }
                
                for (var i = 0; i < PerceptronLayers.Length - 1; ++i)
                    PerceptronLayers[i].SetWeights(learningRange, PerceptronLayers[i + 1]);
                
                var errorTensor  = new Vector(PerceptronLayers[0].NeuronsError).AsTensor(
                    ConvolutionLayers.Last().Output.Channels[0].Body.GetLength(0),
                    ConvolutionLayers.Last().Output.Channels[0].Body.GetLength(1), 
                    ConvolutionLayers.Last().Output.Channels.Count);

                for (var i = ConvolutionLayers.Length - 1; i >= 0; i--) {
                    var inputTensor     = ConvolutionLayers[i].Input;
                    var prevErrorTensor = errorTensor.GetSameChannels(inputTensor);

                    prevErrorTensor = Pooling.BackMaxPool(prevErrorTensor, ConvolutionLayers[i].NotPooled, 
                        Configuration.ConvolutionConfigurations[i].PoolSize); 
                    prevErrorTensor *= NeuronActivate.GetDerivative(prevErrorTensor);
                    
                    var filterGradient =
                        Convolution.GetConvolution(inputTensor, 
                                new[] { prevErrorTensor.AsFilter() }, 1).GetSameChannels(ConvolutionLayers[i].Filters[0]).AsFilter();

                    for (var f = 0; f < ConvolutionLayers[i].Filters.Length; f++) {
                            ConvolutionLayers[i].Filters[f] -= filterGradient * learningRange;
                        for (var bias = 0; bias < ConvolutionLayers[i].Filters[f].Bias.Count; bias++) 
                            ConvolutionLayers[i].Filters[f].Bias[bias] -= prevErrorTensor.Channels[bias].GetSum() * learningRange;
                    }

                    errorTensor = Convolution.GetConvolution(Padding.GetPadding(prevErrorTensor.GetSameChannels(ConvolutionLayers[i].Filters[0]), 
                        (ConvolutionLayers[i].Filters[0].Channels[0].Body.GetLength(0) - 1)/2), ConvolutionLayers[i].FlipFilters(), 1);      
                }
            }
            catch (Exception e) {
                MessageBox.Show($"{e}");
                throw;
            }
        }

卷积方法:

private static Matrix GetConvolution(Matrix matrix, Matrix filter, int stride, double bias) {
            var xFilterSize = filter.Body.GetLength(0);
            var yFilterSize = filter.Body.GetLength(1);
            
            var matrixSize = matrix.Body.GetLength(0);
            var conMat = new Matrix(matrixSize - xFilterSize + 1, matrixSize - yFilterSize + 1);
            
            for (var i = 0; i < conMat.Body.GetLength(0); i += stride) {
                for (var j = 0; j < conMat.Body.GetLength(1); j += stride) {
                    var subMatrix = matrix.GetSubMatrix(i, j, i + xFilterSize, j + yFilterSize);
                    conMat.Body[i,j] += (filter * subMatrix).GetSum() + bias;
                }
            }
            
            return conMat;
        }
        
        public static Tensor GetConvolution(Tensor tensor, Filter[] filters, int stride) {
            var newTensor  = new Tensor(new List<Matrix>());

            for (var i = 0; i < filters.Length; i++) {
                var tempMatrix = new Matrix(tensor.Channels[0].Body.GetLength(0) - filters[0].Channels[0].Body.GetLength(0) + 1,
                tensor.Channels[0].Body.GetLength(0) - filters[0].Channels[0].Body.GetLength(0) + 1);

                for (var j = 0; j < tensor.Channels.Count; j++) {
                    tempMatrix += GetConvolution(tensor.Channels[j], filters[i].Channels[j], stride, filters[i].Bias[j]); 
                }

                newTensor.Channels.Add(tempMatrix); 
            }
                                
            return newTensor;
        }

求导法:

public static double GetDerivative(double value) => value is < 0 or > 1 ? 0 : 1;

通道数不同的两个张量的通道变化:

public Tensor GetSameChannels(Tensor reference) {
        var newTensor = this;

        if (newTensor.Channels.Count != reference.Channels.Count) {
            newTensor = newTensor.Channels.Count < reference.Channels.Count
                ? newTensor.IncreaseChannels(reference.Channels.Count - newTensor.Channels.Count)
                : newTensor.CropChannels(reference.Channels.Count);
        }

        return newTensor;
    }

    private Tensor IncreaseChannels(int channels) {
        var tensor = new Tensor(Channels);
        for (var i = 0; i < channels; i++) tensor.Channels.Add(Channels[^1]);
        return tensor;
    }
    
    private Tensor CropChannels(int channels) {
        var matrix = new List<Matrix>();
        for (var i = 0; i < channels; i++) {
            matrix.Add(Channels[i]);
        }
        return new Tensor(matrix);
    }

无偏教学后滤波器的权重:

-997,3155 -997,3192500000001 -997,2990000000001 -997,3140000000002 -997,3402500000001 -1044,7554999999998 -1044,7914999999998 -1044,7344999999998 -1044,8004999999998 -1044,7464999999997 -1067,75425 -1067,8045 -1067,7737499999998 -1067,7984999999999 -1067,791 -1079,0785 -1079,0425 -1079,0755 -1079,0462499999999 -1079,0964999999999 -1087,373 -1087,30775 -1087,3385 -1087,3055 -1087,355 -7,9106939999999994 -7,927943999999999 -7,953443999999999 -7,969193999999999 -7,946693999999999 -8,290214 -8,304464000000001 -8,286464 -8,338964 -8,322464 -8,472092 -8,531341999999999 -8,469842 -8,493841999999997 -8,508841999999998 -8,593831999999999 -8,563832 -8,560082 -8,591581999999999 -8,562332 -8,678007999999998 -8,627007999999998 -8,639757999999999 -8,677257999999998 -8,630007999999998 -0,005331552000000016 -0,026331552000000008 -0,019581552000000016 -0,039081552 -0,019581552000000016 -0,048117712 -0,021117712000000004 -0,033867712000000015 0,0006322879999999822 0,002132287999999983 -0,03234073600000001 -0,0030907360000000067 -0,045090736 -0,018840736000000004 -0,040590736 -0,018812656000000018 -0,047312656 -0,003812656000000013 -0,045062656 0,0029373439999999945 -0,056092064000000004 -0,046342063999999995 -0,015592064000000003 -0,050842064 -0,066592064 0,042989347584 0,029489347583999997 0,030239347583999997 0,073739347584 0,033989347584 .......
c# neural-network conv-neural-network backpropagation
© www.soinside.com 2019 - 2024. All rights reserved.