Быстрая нейронная сеть для каждого

Данная статья продемонстрирует возможность легко написать свою нейронную сеть на языке Javа. Дабы не изобретать велосипед, возьмем уже хорошо проработанную библиотеку Fast Artificial Neural Network. Использование нейронных сетей в своих Java-проектах — реально. Часто можно услышать упреки в адрес Java касательно скорости выполнения. Хотя разница не так велика — подробно об этом можно узнать в публикации «Производительность C++ vs. Java vs. PHP vs. Python. Тест «в лоб»». Мы будем использовать обертку вокруг библиотеки FANN.

Задача


Необходимо написать систему, которая сможет принимать решения за персонажа, который может встретить одного или несколько врагов. Системе может быть известно:
  • здоровье персонажа в процентах;
  • наличие пистолета;
  • количество врагов.

Ответ должен быть в виде одного из действий:
  • атаковать;
  • бежать;
  • прятаться (для внезапной атаки);
  • ничего не делать.

Для обучения составим таблицу «уроков»:
Здоровье пистолет Враги Действие
50% 1 1 Атаковать
90% 1 2 Атаковать
80% 0 1 Атаковать
30% 1 1 Прятаться
60% 1 2 Прятаться
40% 0 1 Прятаться
90% 1 7 Бежать
60% 1 4 Бежать
10% 0 1 Бежать
60% 1 0 Ничего
100% 0 0 Ничего

Подготовка


Первое, что нужно сделать — собрать и установить libfann.
Затем скачать fannj и jna.

Сделаем файл, который будет содержать набор «уроков»:

11 3 4
0.5 1 1
1 0 0 0
0.9 1 2
1 0 0 0
0.8 0 1
1 0 0 0
0.3 1 1
0 1 0 0
0.6 1 2
0 1 0 0
0.4 0 1
0 1 0 0
0.9 1 7
0 0 1 0
0.5 1 4
0 0 1 0
0.1 0 1
0 0 1 0
0.6 1 0
0 0 0 1
1.0 0 0
0 0 0 1

Теперь обучим нашу ИНС и сохраним ее в файл:

   public static void main(String[] args) {
        //Для сборки новой ИНС необходимо создасть список слоев
        List<Layer> layerList = new ArrayList<Layer>();
        layerList.add(Layer.create(3, ActivationFunction.FANN_SIGMOID_SYMMETRIC, 0.01f));
        layerList.add(Layer.create(16, ActivationFunction.FANN_SIGMOID_SYMMETRIC, 0.01f));
        layerList.add(Layer.create(4, ActivationFunction.FANN_SIGMOID_SYMMETRIC, 0.01f));
        Fann fann = new Fann(layerList);
        //Создаем тренера и определяем алгоритм обучения
        Trainer trainer = new Trainer(fann);
        trainer.setTrainingAlgorithm(TrainingAlgorithm.FANN_TRAIN_RPROP);
        /* Проведем обучение взяв уроки из файла, с максимальным колличеством
           циклов 100000, показывая отчет каждую 100ю итерацию и добиваемся
        ошибки меньше 0.0001 */
        trainer.train(new File("train.data").getAbsolutePath(), 100000, 100, 0.0001f);
        fann.save("ann");
    }

Пояснение


Layer


ИНС состоит из слоев нейронов. Первый слой — это нейроны «рецепторы» или нейроны входных данных. Последний слой нейронов выходных данных. Все остальные — это скрытые слои. В нашем случае первый слой имеет 3 нейрона:

уровень здоровья (0.1-1.0);
наличие оружия (1-есть, 0-нету);
количество врагов.

Fann


Объект класса Fann это и есть нейронная сеть, которая создается на основе созданных ранее слоев.

Trainer


Объект класса тренер инкапсулирует алгоритмы обучения нейронной сети переданной при создании тренера. После обучения не забываем сохранить ее в файл.

Проверка результатов


Для проверки нашего обучения воспользуемся следующим кодом:

   public static void main(String[] args) {
        Fann fann = new Fann("ann");
        float[][] tests = {
                {1.0f, 0, 1},
                {0.9f, 1, 3},
                {0.3f, 0, 8},
                {1, 1, 8},
                {0.1f, 0, 0},
        };
        for (float[] test:tests){
            System.out.println(getAction(fann.run(test)));
        }
    }
    
    private static String getAction(float[] out){
        int i = 0;
        for (int j = 1; j < 4; j++) {
            if(out[i]<out[j]){
                i = j;
            }
        }
        switch (i){
            case 0:return "атаковать";
            case 1:return "прятаться";
            case 2:return "бежать";
            case 3:return "ничего не делать";
        }
        return "";
    }

У меня получились такие результаты:
Здоровье пистолет Враги Действие
100% Нет 1 Атаковать
90% Есть 3 Прятаться
30% нет 8 Бежать
100% Есть 8 Бежать
10% Нет 0 Ничего не делать

Буду рад услышать конструктивную критику.
Поделиться публикацией

Комментарии 12

    +3
    Спасибо за интересную статью. Следует однако отметить, что в ней вы рассмотрели довольно простую задачу. Попробуйте, например, задачу распознавания образов, и все сразу станет намного интереснее. Плюс Fann насколько я понимаю не параллелится, а жаль, потому что обучение нейронных сетей можно существенно ускорить на многоядерных процессорах.
      0
      Спасибо. Обязательно попробую. После чего поделюсь результатами. FANN мне понравилась тем что ее очень легко использовать. Есть еще интересный проект OpenANN. Но пока руки не дошли попробовать ее вставить в java проект.
        +1
        Лет 6 назад использовал joone framework для распознавания капчи — java библиотека без нативного кода. У нее был простой api, много типов поддерживаемых типов сетей, визуальный редактор для конструирования и обучения искусственной нейронной сети. Но он давно не поддерживается.
        Более новая и библиотека Encog, поддерживала в том числе работу на GPGPU
          0
          А чем закончилось все? Каков был процент распознавания?
            0
            На тот момент в связи с глюкавостью сервиса, при перезапросе URL с капчей он перегенерировал картинку с теми же цифрами, но с другой деформацией. Благодаря этой оплошности разработчиков, я не помню чтобы у меня была хоть одна ошибка при распознавании из 15 разных картинок с одним и тем же фактическим содержимим капчи
      +3
      не совсем понял зачем тут нейронная сеть для элементарной задачи. Уверен что для данной задачей можно обойтись пучком if-ов
        0
        Это простой пример для демонстрации возможностей ИНН и библиотеки.
      • НЛО прилетело и опубликовало эту надпись здесь
          0
          Спасибо за удобную вводную статью. Я использовал те же исходные данные (набор + параметры сети), но результаты получил другие.
            0
            Непонятна структура файла уроков.
            Что такое:
            >11 3 4

            Почему в обучающем файле всего 3 колонки, где колонка с результатом?
              0
              11 — количество «уроков»
              3 — количество входных параметров
              4 — количество вариантов результата

              Нужно рассматривать по две строки:
              0.5 1 4
              0 0 1 0
              

              Означает, что когда на выход поступит последовательность «50% жизни, пистолет, 4 врага», то результат будет равен «3» ( активен третий «бит» из четырёх ). Что будет означать это, мы определили позже:
               case 2:return "бежать";
              
              0
              Добрый вечер, пытаюсь засунуть эту библиотеку в android, но всё бесполезно, уже кучу форумов облазил, постоянно падает при инициализации:
              java.lang.UnsatisfiedLinkError: Can't obtain static method fromNative(Method, Object) from class com.sun.jna.Native at com.sun.jna.Native.initIDs(Native Method)

              List<Layer> layerList = new ArrayList<Layer>();
              layerList.add(Layer.create(1, ActivationFunction.FANN_SIGMOID_SYMMETRIC, 0.01f));
              layerList.add(Layer.create(10, ActivationFunction.FANN_SIGMOID_SYMMETRIC, 0.01f));
              fann = new Fann(layerList);// на этой строке падает
              


              в gradle вот
              implementation group: 'net.java.dev.jna', name: 'jna', version: '4.1.0'
              implementation group: 'com.googlecode.fannj', name: 'fannj', version: '0.6'

              Может кто-нибудь знает в чём проблема?

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

              Самое читаемое