我正在尝试制作一个简单的前馈神经网络的Java端口。
这显然涉及到大量的数值计算,所以我试图尽可能优化我的中央循环。结果应该在float
数据类型的限制内是正确的。Java:微优化数组操作
我当前的代码如下所示(错误处理&初始化删除):
/**
* Simple implementation of a feedforward neural network. The network supports
* including a bias neuron with a constant output of 1.0 and weighted synapses
* to hidden and output layers.
*
* @author Martin Wiboe
*/
public class FeedForwardNetwork {
private final int outputNeurons; // No of neurons in output layer
private final int inputNeurons; // No of neurons in input layer
private int largestLayerNeurons; // No of neurons in largest layer
private final int numberLayers; // No of layers
private final int[] neuronCounts; // Neuron count in each layer, 0 is input
// layer.
private final float[][][] fWeights; // Weights between neurons.
// fWeight[fromLayer][fromNeuron][toNeuron]
// is the weight from fromNeuron in
// fromLayer to toNeuron in layer
// fromLayer+1.
private float[][] neuronOutput; // Temporary storage of output from previous layer
public float[] compute(float[] input) {
// Copy input values to input layer output
for (int i = 0; i < inputNeurons; i++) {
neuronOutput[0][i] = input[i];
}
// Loop through layers
for (int layer = 1; layer < numberLayers; layer++) {
// Loop over neurons in the layer and determine weighted input sum
for (int neuron = 0; neuron < neuronCounts[layer]; neuron++) {
// Bias neuron is the last neuron in the previous layer
int biasNeuron = neuronCounts[layer - 1];
// Get weighted input from bias neuron - output is always 1.0
float activation = 1.0F * fWeights[layer - 1][biasNeuron][neuron];
// Get weighted inputs from rest of neurons in previous layer
for (int inputNeuron = 0; inputNeuron < biasNeuron; inputNeuron++) {
activation += neuronOutput[layer-1][inputNeuron] * fWeights[layer - 1][inputNeuron][neuron];
}
// Store neuron output for next round of computation
neuronOutput[layer][neuron] = sigmoid(activation);
}
}
// Return output from network = output from last layer
float[] result = new float[outputNeurons];
for (int i = 0; i < outputNeurons; i++)
result[i] = neuronOutput[numberLayers - 1][i];
return result;
}
private final static float sigmoid(final float input) {
return (float) (1.0F/(1.0F + Math.exp(-1.0F * input)));
}
}
我正在与-server选项的JVM,而截至目前我的代码是25%和慢50%之间比类似的C代码。我能做些什么来改善这种情况?
谢谢
马丁Wiboe
编辑#1:看到回复的大量后,我也许应该澄清在我们的场景中的数字。在典型的运行过程中,该方法将被称为约50,000次不同的输入。一个典型的网络会有numberLayers = 3层,分别有190,2和1个神经元。因此最内层的循环将具有大约2*191+3=385
的迭代次数(当计数第0层和第1层中添加的偏置神经元时)
编辑1:在此线程中实现了各种建议之后,我们的实现实际上与C版一样快在〜2%之内)。感谢所有的帮助!所有的建议都很有帮助,但由于我只能将一个答案标记为正确的答案,因此我会将它提交给@Durandal,以提供阵列优化,并且是唯一预先计算for
循环标题的答案。
你分析过它吗?知道它花费大部分时间在哪里会很有趣。 – 2010-06-08 00:04:35
同意分析。不要注意它,并猜测需要改进的地方。 – Donnie 2010-06-08 00:12:29
这样的代码是否容易并行化?如果是这样的话,编写它的多线程版本将拥有大量时间的单线程版本。我一直在那里,用Java重写正确的多线程Quicksort。在16核心机器上观看是个喜悦:http://stackoverflow.com/questions/2210185(它压缩了默认的Java API排序算法**大**时间)。除此之外,我看到了一些微观优化,但是我对神经网络的了解不够充分。 (最近它已经很难买单CPU机器,例如我不知道苹果是否仍然销售单CPU CPU) – SyntaxT3rr0r 2010-06-08 00:18:54