我正在嘗試製作一個簡單的前饋神經網絡的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