我目前正在用 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。
如果您知道问题可能是什么,我将非常感激。
提前谢谢您!