Я пытаюсь создать порт Java для простой нейронной сети с прямой связью.
Это, очевидно, требует большого количества числовых вычислений, поэтому я пытаюсь максимально оптимизировать свой центральный цикл. Результаты должны быть правильными в пределах типа данных float
.
Мой текущий код выглядит следующим образом (обработка ошибок и инициализация удалены):
/**
* 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)));
}
}
Я запускаю JVM с параметром -server, и на данный момент мой код на 25-50% медленнее, чем аналогичный код C. Что я могу сделать, чтобы исправить эту ситуацию?
Спасибо,
Мартин Вибо
Редактировать №1: Увидев огромное количество ответов, я, вероятно, должен уточнить цифры в нашем сценарии. Во время типичного запуска метод будет вызываться около 50 000 раз с разными входными данными. Типичная сеть будет иметь numberLayers = 3 слоя с 190, 2 и 1 нейроном соответственно. Таким образом, самый внутренний цикл будет иметь около 2*191+3=385
итераций (при подсчете добавленного нейрона смещения в слоях 0 и 1).
Изменить № 1: после реализации различных предложений в этом потоке наша реализация практически такая же быстрая, как и версия C (в пределах ~ 2%). Спасибо за помощь! Все предложения были полезны, но поскольку я могу отметить только один ответ как правильный, я передам его @Durandal как за предложение оптимизации массива, так и за то, что он единственный, кто предварительно рассчитал заголовок цикла for
.