Соедините 4 с нейросетью: оценка черновика + дальнейшие шаги

Я хотел бы создать движок Connect 4, который работает с использованием искусственной нейронной сети — просто потому, что я очарован ИНС.

Я создал следующий проект структуры ИНС. Будет ли это работать? И правильные ли эти соединения (даже перекрестные)?

альтернативный текст

Не могли бы вы помочь мне составить диаграмму классов UML для этой ИНС?

Я хочу дать ИНС представление платы в качестве входных данных. И на выходе должен быть ход на выбор.

Позже обучение следует проводить с помощью обучения с подкреплением и сигмоидальная функция. Движок будет играть против игроков-людей. И в зависимости от результата игры веса должны корректироваться потом.

Что я ищу...

... в основном проблемы с кодированием. Чем больше он уходит от абстрактного мышления к кодированию, тем лучше.


person caw    schedule 26.12.2010    source источник


Ответы (4)


Ниже показано, как я организовал свой дизайн и код, когда возился с нейронными сетями. Код здесь (очевидно) псевдокод и примерно следует объектно-ориентированным соглашениям.

Начиная снизу вверх, у вас будет свой нейрон. Каждый нейрон должен иметь возможность хранить веса, которые он присваивает входящим соединениям, буфер для хранения данных входящего соединения и список исходящих ребер. Каждый нейрон должен уметь делать три вещи:

  • Способ принимать данные от входящего края
  • Метод обработки входных данных и весов для формулирования значения, которое этот нейрон будет отправлять.
  • Способ отправки значения этого нейрона на исходящие ребра

По коду это означает:

// 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

Существует множество различных способов реализации нейронных сетей, от простых/понятных до высокооптимизированных. В статье Википедии об обратном распространении, на которую вы ссылались, есть ссылки на реализации в C++, C#, Java и т. д., которые могут служить хорошими справочниками , если вам интересно посмотреть, как это сделали другие люди.

Одна простая архитектура будет моделировать и узлы, и соединения как отдельные объекты; узлы будут иметь возможные входящие и исходящие соединения с другими узлами, а также уровни активации и значения ошибок, тогда как соединения будут иметь значения веса.

В качестве альтернативы существуют более эффективные способы представления этих узлов и соединений — например, в виде массивов значений с плавающей запятой, организованных по уровням. Это немного усложняет кодирование, но позволяет избежать создания большого количества объектов и указателей на объекты.

Одно примечание: часто люди включают узел смещения -- в дополнение к обычным входным узлам -- который обеспечивает постоянное значение для каждого скрытого и выходного узла.

person Nate Kohl    schedule 30.12.2010

Я реализовал нейронные сети раньше и вижу несколько проблем с предложенной вами архитектурой:

  1. Типичная многоуровневая сеть имеет соединения от каждого входного узла к каждому скрытому узлу и от каждого скрытого узла к каждому выходному узлу. Это позволяет объединять информацию со всех входов и вносить вклад в каждый выход. Если вы выделите 4 скрытых узла для каждого входа, вы потеряете часть мощности сети для определения взаимосвязей между входами и выходами.

  2. Как вы придумаете значения для обучения сети? Ваша сеть создает сопоставление между позициями на доске и оптимальным следующим ходом, поэтому вам нужен набор обучающих примеров, которые обеспечивают это. Ходы в конце игры легко определить, но как определить, что ход в середине игры является «оптимальным»? (Здесь может помочь обучение с подкреплением)

Последнее предложение состоит в том, чтобы использовать биполярные входные данные (-1 для ложного, +1 для истинного), поскольку это может ускорить обучение. И Нейт Коль делает хорошее замечание: каждый скрытый и выходной узел выиграет от наличия связи смещения (подумайте об этом как о еще одном входном узле с фиксированным значением «1»).

person darkcanuck    schedule 02.01.2011

Ваш дизайн будет сильно зависеть от конкретного типа обучения с подкреплением, который вы планируете использовать.

Самым простым решением было бы использовать обратное распространение. Это делается путем передачи ошибки обратно в сеть (в обратном порядке) и использования функции, обратной (сигмовидной), для определения корректировки каждого веса. После ряда итераций веса будут автоматически скорректированы в соответствии с входными данными.

Генетические алгоритмы являются альтернативой обратному распространению, которые дают лучшие результаты (хотя и немного медленнее). Это делается путем обработки весов как схемы, которую можно легко вставлять и удалять. Схема заменяется мутированной версией (с использованием принципов естественного отбора) несколько раз, пока не будет найдено соответствие.

Как видите, реализация для каждого из них будет кардинально отличаться. Вы можете попытаться сделать сеть достаточно универсальной, чтобы адаптироваться к каждому типу реализации, но это может слишком усложнить ее. Как только вы начнете работать, у вас обычно будет только одна форма обучения (или в идеале ваша сеть уже должна быть обучена).

person k rey    schedule 08.01.2011