Ниже показано, как я организовал свой дизайн и код, когда возился с нейронными сетями. Код здесь (очевидно) псевдокод и примерно следует объектно-ориентированным соглашениям.
Начиная снизу вверх, у вас будет свой нейрон. Каждый нейрон должен иметь возможность хранить веса, которые он присваивает входящим соединениям, буфер для хранения данных входящего соединения и список исходящих ребер. Каждый нейрон должен уметь делать три вещи:
- Способ принимать данные от входящего края
- Метод обработки входных данных и весов для формулирования значения, которое этот нейрон будет отправлять.
- Способ отправки значения этого нейрона на исходящие ребра
По коду это означает:
// Each neuron needs to keep track of this data
float in_data[]; // Values sent to this neuron
float weights[]; // The weights on each edge
float value; // The value this neuron will be sending out
Neuron out_edges[]; // Each Neuron that this neuron should send data to
// Each neuron should expose this functionality
void accept_data( float data ) {
in_data.append(data); // Add the data to the incoming data buffer
}
void process() {
value = /* result of combining weights and incoming data here */;
}
void send_value() {
foreach ( neuron in out_edges ) {
neuron.accept_data( value );
}
}
Далее, мне показалось, что проще всего создать класс Layer, содержащий список нейронов. (Вполне возможно пропустить этот класс, и ваша NeuralNetwork просто будет содержать список списка нейронов. Я считаю, что с организационной точки зрения и с точки зрения отладки проще иметь класс Layer.) Каждый уровень должен предоставлять возможность:
- Заставьте каждый нейрон «зажечь»
- Верните необработанный массив нейронов, вокруг которого находится этот слой. (Это полезно, когда вам нужно делать такие вещи, как ручное заполнение входных данных в первом слое нейронной сети.)
По коду это означает:
//Each layer needs to keep track of this data.
Neuron[] neurons;
//Each layer should expose this functionality.
void fire() {
foreach ( neuron in neurons ) {
float value = neuron.process();
neuron.send_value( value );
}
}
Neuron[] get_neurons() {
return neurons;
}
Наконец, у вас есть класс NeuralNetwork, который содержит список слоев, способ настройки первого слоя с исходными данными, алгоритм обучения и способ запуска всей нейронной сети. В моей реализации я собирал окончательные выходные данные, добавляя четвертый слой, состоящий из одного поддельного нейрона, который просто буферизировал все входящие данные и возвращал их.
// Each neural network needs to keep track of this data.
Layer[] layers;
// Each neural network should expose this functionality
void initialize( float[] input_data ) {
foreach ( neuron in layers[0].get_neurons() ) {
// do setup work here
}
}
void learn() {
foreach ( layer in layers ) {
foreach ( neuron in layer ) {
/* compare the neuron's computer value to the value it
* should have generated and adjust the weights accordingly
*/
}
}
}
void run() {
foreach (layer in layers) {
layer.fire();
}
}
Я рекомендую начать с обратного распространения в качестве алгоритма обучения, поскольку его предположительно проще всего реализовать. Когда я работал над этим, мне было очень трудно найти очень простое объяснение алгоритма, но мой список заметок этот сайт как хороший справочник.
Я надеюсь, что этого достаточно, чтобы вы начали!
person
Alex Miller
schedule
09.01.2011