为什么我的卷积神经网络在几次迭代后返回 NaN?

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

我目前正在用 Java 编写自己的卷积神经网络。首先,我实现了完全连接的层,它工作得非常好(它与 MNIST 数据集一起工作正常)。 现在我也实现了卷积层并尝试了一个非常简单的例子:

Network nn = new Network(new SGD(0.01), new CrossEntropy(), new Convolution(6, 3, 3, 2),  new Convolution(4, 2, 2, 1), new Flatten());
    ConvTrainPair[] trainPairs = new ConvTrainPair[] {
        new ConvTrainPair(Cube.ones(6, 6, 3), Vector.from(0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.8))
    };
    
    //System.out.println(nn.run(new Cube[] {Cube.ones(6, 6, 3)}));
    for(int e = 0; e < 100000000; ++e) {
        nn.train(trainPairs, 2);
        Matrix out = (Matrix) nn.run(new Cube[] {Cube.ones(6, 6, 3)});
        System.out.println("out: " + out);
    }

但不知何故,经过几次迭代后,网络只返回 NaN,我不知道为什么。该网络只是一个简单的网络,具有一个卷积层(输入大小为 6、输入深度为 3、内核大小为 3、numKernels 2),另一个卷积层(输入大小为 4、输入深度为 2、内核大小2 和 numKernels 1) 以及最终的 Flatten 层,它将输出转换为矩阵。我正在使用随机梯度下降和交叉熵损失。

我很确定错误一定出在我的卷积层代码中:

public class Convolution extends Layer {
private Matrix[][] filters, filterGradients;
private Matrix[] bias, biasGradients; 
private Cube[] lastInputBatch;
private Trainer trainer;
private int trainerWW, trainerWH, trainerBL;

public Convolution(int inputSize, int inputDepth, int filterSize, int numFilters) {

    int outputSize = inputSize - filterSize + 1;
    this.trainerWW = numFilters * filterSize;
    this.trainerWH = inputDepth * filterSize;
    this.trainerBL = (numFilters + outputSize) * outputSize; 
    
    this.bias = new Matrix[numFilters];
    this.filters = new Matrix[numFilters][inputDepth];
    for(int i = 0; i < numFilters; ++i) {
        bias[i] = Matrix.random(outputSize, outputSize);
        
        for(int j = 0; j < inputDepth; ++j) {
            filters[i][j] = Matrix.random(filterSize, filterSize);
        }
    }
}



@Override
public void init(Optimizer optimizer) {
    this.trainer = new Trainer(trainerWW, trainerWH, trainerBL, optimizer);
}



@Override
public Object feedforward(Cube[] batch) {
    this.lastInputBatch = batch;
    Cube[] out = new Cube[batch.length];
    
    for(int b = 0; b < batch.length; ++b) {
        Cube current = batch[b];
        Matrix[] fMaps = new Matrix[filters.length];
        for(int i = 0; i < filters.length; ++i) {
            fMaps[i] = bias[i];
            for(int j = 0; j < filters[i].length; ++j) {
                fMaps[i].addE(crossCorrelate(current.data[j], filters[i][j]));
            }
        }
        
        out[b] = new Cube(fMaps);
    }
    
    return out;
}



@Override
public Object backward(Cube[] deltaBatch, boolean needsActivationGradient) {
    Cube[] inputDeltaBatch = new Cube[deltaBatch.length];
    for(int b = 0; b < deltaBatch.length; ++b) {
        Cube delta = deltaBatch[b];
        Cube lastInput = lastInputBatch[b];
        
        filterGradients = new Matrix[filters.length][filters[0].length];
        biasGradients = new Matrix[filterGradients.length];
        Matrix[] inputGradients = new Matrix[filters[0].length];
        for(int i = 0; i < filterGradients.length; ++i) {
            for(int j = 0; j < filterGradients[i].length; ++j) {
                Matrix filter = filters[i][j];
                filterGradients[i][j] = crossCorrelate(lastInput.data[j], delta.data[i]);
                if(i == 0) inputGradients[j] = new Matrix(lastInput.width(), lastInput.height());
                inputGradients[j].addE(crossCorrelate(delta.data[i].padding(filter.w - 1), filter.rotate180()));
            }
            biasGradients[i] = delta.data[i];
        }
        
        inputDeltaBatch[b] = new Cube(inputGradients);
    }
    
    return inputDeltaBatch;
}

public static Matrix crossCorrelate(Matrix input, Matrix kernel) {
    int nmW = input.w - kernel.w + 1;
    int nmH = input.h - kernel.h + 1;
    Matrix nm = new Matrix(nmW, nmH);
    
    for(int i = 0; i < nmW; ++i) {
        for(int j = 0; j < nmH; ++j) {
            for(int a = 0; a < kernel.w; ++a) {
                for(int b = 0; b < kernel.h; ++b) {
                    nm.e[i][j] += input.e[i + a][j + b] * kernel.e[a][b];
                }
            }
        }
    }
    
    return nm;
}



@Override
public void optimize(int episode) {
    double lr = 0.001;
    for(int i = 0; i < filters.length; ++i)  {
        for(int j = 0; j < filters[i].length; ++j) {
            //System.out.println(filters[i][j]);
            filters[i][j].subE(filterGradients[i][j].mul(lr));
        }
    }
    
    
    for(int i = 0; i < bias.length; ++i) {
        bias[i].subE(biasGradients[i].mul(lr));
    }
    
    //System.out.println();
    
} }

我已经尝试只使用一个卷积层来解决问题,但我不明白为什么。我也尝试改变学习率和损失函数,但没有解决问题。我还在两个卷积层之间以及扁平层之后插入了一些激活函数。使用 sigmoid 函数,它不再返回 NaN,但它似乎也没有通过训练学到任何东西。使用 tanH 和 softmax 函数,它仍然返回 Nan。

如果您知道问题可能是什么,我将非常感激。

提前谢谢您!

java deep-learning neural-network conv-neural-network overfitting-underfitting
1个回答
0
投票

嗨,我也有同样的错误enter image description here

你发现错误了吗?

© www.soinside.com 2019 - 2024. All rights reserved.