Не буду вдаваться в подробности того как устроена нейросеть, об этом можно почитать в интернете или в книгах. прочитав книги Тарик Рашид "Создаем нейронную сеть" и я Эндрю Траск "Грокаем глубокое обучение" решил поэкспериментировать.

Алгоритм построения нейросети взял из книги Эндрю Траска. Решил сделать программку, которая бы по заданным параметрам создавала нейронную сетку, держала ее в памяти и давала возможность доступа к ней на обучение и получение результата.

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

Подготовим среду для тестирования.

Манипуляции буду производить на CentOS7.

wget http://download.opensuse.org/repositories/home:/bayrepo/CentOS_7/home:bayrepo.repo -O /etc/yum.repos.d/home:bayrepo.repo
yum install bayrepo-neuro-farm -y

Пакет содержит файлы bayrepo_neuro_farm, libbayrepo_nfl.a и neuro_web_client.h. Первый - это сам сервер, второй файл - это библиотека, чтоб можно было написать клиента на C для связи с сервером и последний файл - это заголовки, для работы с библиотекой.

Запуск сервера

Для запуска необходимо:

cd ~
mkdir neuro_test
cd neuro_test
/usr/sbin/bayrepo_neuro_farm

сервер прицепится к адресу 127.0.0.1:10111. Теперь его можно посетить, вбив в браузере строку: http://127.0.0.1:10111

Для чего создавалась папка mkdir neuro_test, дело в том, что сервер сохраняет данные о своих нейросетях в каталог, в котором он будет запущен, т.е в нашем случае в ~/neuro_test.

Вид главной страницы:

Наглавной странице можно увидеть ссылку для добавления сети и добавить новую сеть:

номер поля Нзвание Описание
1 Name имя будущей сети
2 Inputs число входов нейросети
3 Outputs число выходов нейросети
4 Hidden Число скрытых слоев
3 Nets of hidden число нод в скрытом слое нейросетки
3 Learn speed скокрость обучения сети
3 Activation func функция активации по умолчанию, для слоев с определением NODEF
3 Drop out если коэффициент нейрона меньше 0, то сбрасывать коэффициент этого нейрона в 0
3 список слоев и функции активации к ним NODEF, RELU, SIGMOID, TANH

А вот как выглядит новая нейросеть созданная программой и не обученная:

Матрицы коэффициентов представлены графически.

Каждая сетка получает номер, по которому к этой сетке можно в последующем обратиться.

Одним из важных факторов нейросети - это сформировать такую информацию на вход и получить выход. близкий истинному значению.

Пример "XOR"

Решим задачу - нужно сделать сеть и научить ее таким правилам:

Вход 1 Вход 2 Выход
0 0 0
0 1 1
1 0 1
1 1 0

Из входных данных видно, что сеть должна иметь 2 входных параметра и 1 выходной. На вход подается 0.0 или 1.0, на выходе получаем число. Если число < 0.5, то считаем выход равный 0, иначе - выход равен 1. Коффициент обучения 0.1-0.5. Слишком большой коэффциент может привести к насыщению элементов сети, поэтому рекомендуется экспериментально подбирать этот параметр. Число скрытых слоев или 0 или 1 для текущей задачи, т.к число входных данных небольшое. Слишком большое число скрытых слоев приведет к очень долгому обучению, как показала практика, в этом случае ошибка по слоям распростарняется очень медленно.

Вот как выглядит программа клиент для связи xor клиента с нейросетью:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "neuro_web_client.h"

int main(int argc, char* argv[]) {
    double inputs[2];
    double outputs[1];
    char curl_error[1024];

    if (argc < 3) {
        exit(0);
    }

    inputs[0] = (double) atoi(argv[1]);
    inputs[1] = (double) atoi(argv[2]);

    web_client_init("http://127.0.0.1:10111");

    web_send_inputs_to_net(4, (double *) &inputs, 2, (double *) &outputs, 1, curl_error);

    web_client_clean();
    printf("%d^%d=%d\n", (int) inputs[0], (int) inputs[1],
            (outputs[0] >= 0.5) ? 1 : 0);

    return 0;
}

и вот результат работы:

а вот как выглядит результат после обучения. Обучение проходило на тестовом наборе, в 10000 эпох.

Все отлично, сеть научилась по входным данным предсказывать выходные.

Теперь усложним задачу

Лабиринт

Пусть есть условие. Есть лабиринт, структура которго создается, лабиринт ограничен сиволом #, в лабиринте есть стены, обозначенные символом *. Есть главный герой, обозначенный символом &. Он должен добраться до флага - символ 7. Пройденный путь помечается символом +. Направление куда хочет сделать следующий шаг герой обозначается символом @.

Вот как выглядит один из созданных примеров лабиринта:

#################
#           *   #
#           * 7 #
#  *******      #
#               #
#   **********  #
#      *        #
#      *        #
#      *        #
#     @*        #
#  +++&*        #
#################

Главный герой может идти только вверх, вниз, влево или вправо. Нельзя становиться на клеточку обозначенную # или * - т.е конец лабиринта и стена. Там где уже ранее ходил, т.е где проставлен +, по возможности не ходить. Главный герой знает расстояние от себя до искогомо флага 7. Главнйый герой видит только на одну клеточку вперед, то что дальше одной клетки - ему не известно. Имея такие данные, нужно обучить нейростеть находить путь.

я решил так распределить входные данные:

вход назначение
0 есть ли стена или конец лабиринта от героя справа 0 - нет, 1 - есть
1 есть ли стена или конец лабиринта от героя слева 0 - нет, 1 - есть
2 есть ли стена или конец лабиринта от героя сверху 0 - нет, 1 - есть
3 есть ли стена или конец лабиринта от героя снизу 0 - нет, 1 - есть
4 проходил ли герой по клетке справа 0 - нет, 1 - да
5 проходил ли герой по клетке слева 0 - нет, 1 - да
6 проходил ли герой по клетке сверху 0 - нет, 1 - да
7 проходил ли герой по клетке снизу 0 - нет, 1 - да
8 квадрат расстояния до флага, если герой переместится правее
9 квадрат расстояния до флага, если герой переместится левее
10 квадрат расстояния до флага, если герой переместится выше
11 квадрат расстояния до флага, если герой переместится ниже

последние четыре входа рассчитываются по формуле:

distance^2 = (xh-x_finish)^2+(yh-y_finish)^2

потом находится самая большая величина из 4-х: max_distance = distance^2 и все ячейи заполняются по формуле:

1-(distance^2/max_distance)

т.е получим, что самая далекая точка получит входной коэффициент 0, а самая близкая получит самый больший коэффициент между 0 и 1.

Выходы же нейросети - их четыре. Указывают в каком направлении герой должен двигаться. 0 - впаво, 1 - лево, 2 - верх, 3 - низ. Тот выход, значение которого будет максимальным и указывает направление движения.

Все. Создаем нейросеть с 12 входами, 4 выхода и 2 скрытых слоя по 15 элементов. скороть обучения 0.1-0.2. Функция активации TANH. Экспериментально определил, что большой коэффициент обучения заводит нейросеть в насыщение и она перестает обучаться.

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

После обучения на 5000 комбинаий путей, вот как начинает вести себя герой:

В перовм варианте, предпоследний поиск пути оказался неудачным, т.к герою нужно было возвращаться по уже пройденному пути, а сетка к такому была не готова. Т.к. тренировочные данные не предусматривали проход по пути. который был уже пройден и нейростеть приняла решение идти в стену. При доработке кобинаций обучения по уже пройденному маршруту, думаю, нейросеть справится и с таким.

Вобщем, получилось весьма интеерсно.

Код лабиринта - для тех, кто дочитал:

ссылка на код на github

Добавить комментарий

Следующая запись Предыдущая запись