Как написать искусственный интеллект на java

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

Время на прочтение
3 мин

Количество просмотров 216K

Данная статья продемонстрирует возможность легко написать свою нейронную сеть на языке 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 Ничего не делать

Буду рад услышать конструктивную критику.

Post Views: 13 168

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

  • Искусственные нейроны
  • Весы(weights) и смещения(biases)
  • Активационные функции(activation functions)
  • Слои нейронов(layers)
  • Реализация нейронной сети на Java

Раскрывая нейронные сети

Во-первых, термин «нейронные сети» может создать снимок мозга в вашем сознании, в частности для тех, кто ранее познакомился с ним. В действительности это правда, мы считаем мозг — большая и естественная нейронная сеть. Однако что мы можем сказать об искусственных нейронных сетях (ANN — artificial neural network)? Хорошо, он начинается с антонима естественный и первая мысль, которая приходит в нашу голову — это картинка искусственного мозга или робота учитывает термин «искусственный«. В этом случае, мы так же имеем дело с созданием структуры, похожей и вдохновленной человеческим мозгом; поэтому это названо искусственным интеллектом. Поэтому читатель, который не имел прошлого опыта с ANN, сейчас может думать, что книга учит, как строить интеллектуальные системы, включая искусственный мозг, способный эмулировать человеческое сознание, используя Java программы, не так ли? Конечно мы не будем покрывать создание искусственного мышления машин как в трилогии Матрицы; однако эта книга растолкует несколько неимоверных способностей и что могут эти структуры. Мы предоставим читателю Java исходники с определением и созданием основных нейросетевых структур, воспользоваться всеми преимуществами языка программирования Java.

Почему искусственные нейронные сети?

Мы не можем начать говорить про нейросети без понимания их происхождения, включая также термин. Мы используем термины нейронные сети (NN) и ANN взаимозаменяемо в этой книге, хотя NN более общий, покрывая также
естественные нейронные сети. Таким образом, что же такое на самом деле ANN? Давайте изучим немного историю этого термина.

В 1940-ых нейрофизиолог Warren McCulloch и математик Walter Pits спроектировали первую математическую реализацию искусственного нейрона, комбинируя нейронаучный фундамент с математическими операциями. В то время многие исследования осуществлялись на понимании человеческого мозга и как и если бы мог смоделирован, но в пределах области неврологии. Идея McCulloch и Pits была реально уникальна, потому что добавлен математический компонент. Далее, считая, что мозг состоит из миллиардов нейронов, каждый из них взаимосвязан с другими миллионами, в результате чего в некоторых триллионах соединениях, мы говорим о гигантской структуре сети. Однако, каждый нейрон очень простой, действуя как простой процессор, способный суммировать и распространять сигналы.

На базе этого факта, McCulloch и Pits спроектировали простую модель для одного нейрона, первоначально симулируя человеческое зрение. Доступные калькуляторы или компьютеры в то время были очень редкими, но способные иметь дело с математическими операциями достаточно хорошо; с другой стороны, даже современные задачи, такие как компьютерное зрение и распознавание звуков не очень легко программируются без специальных фреймворков, основанных на математических операциях и функциях. Тем не менее, человеческий мозг может выполнять эти последние задачи эффективнее чем первые, и этот факт реально побуждает ученых исследователей.

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

Задачи, быстро решаемые человеком Задачи, быстро решаемые компьютером
Классификация изображений

Распознавание голоса

идентификация лиц

Прогнозирование событий на основе предыдущего опыта

Комплексные вычисления

Исправление грамматических ошибок

Обработка сигналов

Управление операционной системой

Как устроены нейронные сети

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

How neural networks are arranged - Neural Network Programming with ...

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

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

Самый базовый элемент — искусственный нейрон

Доказано, что естественные нейроны — обработчики сигналов поскольку они получают микросигналы в дендритах, что вызывает сигнал в аксонах в зависимости от их силы или величины. Мы можем поэтому подумать, что нейрон как имеющий сборщик сигналов во входах(inputs) и активационную единицу в выходе(output), что вызывает сигнал, который будет передаваться другим нейронам. Таким образом, мы можем определить  искусственную нейронную структуру, как показано на следующем рисунке:

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

Давая жизнь нейронам — активационная функция

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

Четыре самых используемых активационных фунций:

  • Сигмоида(Sygmoid)
  • Гиперболический тангенс(Hyberbolic tangent)
  • Жесткая пороговая функция(Hard limiting threshold)
  • Линейная(linear)

Уравнения и графики ассоциирующиеся с этими функциями, показаны
в следующей таблице:

Фундаментальные величины — весы(weights)

В нейронных сетях, синапсы представляют собой соединения между нейронами и имеют возможность усиливать или смягчать нейронные сигналы, например, перемножать сигналы, таким образом улучшать их. Итак, путем модификации нейронных сетей, нейронные весы(weights) могут повлиять на нейронный вывод(output), следовательно нейронная активация может быть зависима от ввода и от весов. При условии, что inputs идут от других нейронов или от внешнего мира, весы(weights) считаются установленными нейронными соединениями между нейронами. Таким образом, с тех пор как весы являются внутренними для нейронных сетей, мы можем считать их как знания нейронных сетей, предоставленные изменения весов будут изменять возможности нейронных сетей и поэтому — действия.

Важный параметр — смещение

Искуственный нейрон может иметь независимый элемент, который добавляет специальный сигнал для активации функции.

Части образующие целое — слои

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

Нейронные сети могут быть составлены из нескольких соединенных слоев, которые называются многослойными сетями. Обычные нейронные сети могут быть разделены на 3 класса:
1. Input layer;
2. Hidden layer;
3. Output layer;
На практике, дополнительный нейронный слой добавляет другой уровень
абстракции внешней стимуляции, тем самым повышая способность
нейронных сетей представлять больше комплексных данных.

Каждая нейросеть имеет как минимум входной/выходной слой независимо от количества слоев. В случае с многослойной сетью, слои между входом и выходом названы скрытыми.

Изучение архитектуры нейронных сетей

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

1. Нейронные соединения:
1.1 Однослойные(monolayer) сети;
1.2 Многослойные(multilayer) сети;
2. Поток сигналов:
2.1 Сети прямой связи(Feedforward networks);
2.2 Сети обратной связи(Feedback networks);

Однослойные сети

Нейронная сеть получает на вход сигналы и кормит их в нейроны, которые в очереди продуцируют выходные сигналы. Нейроны могут быть соединены с другими с или без использования рекуррентности. Примеры таких архитектур: однослойный персептрон, Adaline(адаптивный линейный нейрон), самоорганизованная карта, нейронная сеть Элмана(Elman) и Хопфилда.

Многослойные сети

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

Радиальные базисные функции и многослойные персептроны – хорошие примеры этой архитектуры. Такие сети реально полезны для апроксимации реальных данных в функцию, специально спроектированной для представлении этих данных. Более того, благодаря тому, что они имеют много слоев обработки, эти сети адаптивны для изучения из нелинейных данных, возможности отделить их или легче определять знания, которые воспроизводят или распознают эти данные.

Сети прямой связи(feedforward networks)

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

Сети обратной связи(Feedback networks)

Когда нейронная сеть имеет некоторый вид внутреннего рецидива, это значит, что сигналы вернулись обратно в нейрон или слой, который уже получил и обработал сигнал, сеть – это тип feedback-а. Посмотрите на картинку:

Специальная причина добавить рекуррентность в сеть – это выработка динамического поведения, в частности когда сеть адресует проблемы, включая временные ряды или распознавание образов, которые требуют внутреннюю память для подкрепления обучающего процесса. Тем не менее, такие сети особенно трудны в тренировке, в конечном счете не в состоянии учиться. Многие feedback сети – однослойные, такие как сети Элмана(Elman) и Хопфилда(Hopfield), но возможно и построить рекуррентную многослойную сеть, такие как эхо и рекуррентные многослойные персептронные сети.

От незнания к знаниям — процесс обучения

Нейронные сети обучаются благодаря регулировке соединений между нейронами, а именно весов. Как уже упоминалось в структуре нейронных секций, весы представляют собой знания нейронных сетей. Разные весы призывают сеть вырабатывать разные результаты для тех же входных данных. Таким образом, нейронная сеть может улучшить эти результаты, адаптируя эти весы следуя обучающемуся правилу. Основная схема обучения показана на следующем рисунке:
From ignorance to knowledge – learning process - Neural Network ...
Процесс, показанный на предыдущей схеме, называется контролируемое обучение(supervised learning), потому что это желаемый вывод, но нейронные сети могут обучаться только входных данных, без желаемого результата(контролируемое обучение). Во второй главе, «Как обучаются нейронные сети», мы собираемся глубже погрузиться в процесс обучения нейронных сетей.

Давайте начнем реализацию! Нейронные сети на практике

В этой книге мы покроем все процессы реализации нейронных сетей на Java. Java — это объектно-ориентированный язык  программирования, созданный в 1990-ые маленькой группой инженеров из Sun Microsystems, позже приобретенной компанией Oracle в 2010-ых. Сегодня, Java представлена во многих устройствах, которые участвуют в нашей повседневной жизни. В объектно-ориентированном языке, таком как Java, мы имеем дело склассами и объектами. Класс — план чего-то в реальной жизни, а объект — образец такого плана, например, car(класс, ссылающийся на все машины) и my car(объект, ссылающийся на конкретную машину — мою). Java классы обычно состоят из атрибутов и методов(или функций), которые включают принципы объектно-ориентированного программирования(ООП). Мы собираемся кратко рассмотреть эти принципы без углубления в них, поскольку цель этой книги — просто спроектировать и создать нейронные сети с практической точки зрения. В этом процессе четыре принципа уместны и нуждаются в рассмотрении:

  • Абстракция: Перевод проблем и правил реальной жизни в сферу программирования, рассматривая только их уместные особенности и отпуская детали, которые часто мешают разработке.
  • Инкапсуляция: Аналогично инкапсуляции продукта, при которой некоторые соответствующие функции раскрыты открыто (публичные(public) методы), в то время как другие хранится скрытым в пределах своего домена (частного(private) или защищенного(protected)), избегая неправильное использование или избыток информации.
  • Наследование: В реальной мире, много классов этих объектов представляют собой атрибуты и методы в иерархической манере; например, велосипед может быть супер-классом для машин и грузовиков.Таким образом, в ООП эта концепция позволяет из одного класса перенимать все свойства в другой класс, тем самым избегая переписывания кода.
  • Полиморфизм: Во многом схожа с наследованием, но с изменениями в методах со схожими сигнатурами, представляющие разные поведения в разных классах.

Используя концепции нейронных сетей, представленные в этой главе и коцепции ООП, мы сейчас собираемся проектировать самый первый класс, реализующий нейронную сеть. Как можно увидеть, нейронная сеть состоит из слоев, нейронов, весов, активационных функций и смещений, и трех типов слоев: входные, скрытые и выходные. Каждый слой может иметь один или несколько нейронов. Каждый нейрон соединен друг с другом входом/выходом или другими нейронами, и эти соединения называются весами.

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

Имя класса: Neuron

Атрибуты

private ArrayList<Double> listOfWeightIn Переменная ArrayList дробных чисел представляет список входных весов
private ArrayList<Double> listOfWeightOut Переменная ArrayList дробных чисел представляет список выходных весов

Методы

public double initNeuron() Инициализирует функции listOfWeightIn, listOfWeightOut с псевдослучайными числами
Параметры: нет
Возвращает: Псевдослучайное число
public ArrayList<Double> getListOfWeightIn() Геттер ListOfWeightIn
Параметры: нет
Возвращает: список дробных чисел, сохраненной в переменной ListOfWeightIn
public void setListOfWeightIn(ArrayList<Double> listOfWeightIn) Сеттер ListOfWeightIn
Параметры: список дробных чисел, сохранненных в объекте класса
Возвращает: ничего
public ArrayList<Double> getListOfWeightOut() Геттер ListOfWeightOut
Параметры: нет
Возвращает: список дробных чисел, сохраненной в переменной ListOfWeightOut
public void setListOfWeightOut(ArrayList<Double> listOfWeightOut) Сеттер ListOfWeightOut
Параметры: список дробных чисел, сохранненных в объекте класса
Возвращает: ничего
Реализация класса: файл Neuron.java
Имя класса: Layer
Заметка: Этот класс абстрактный и не может быть проинициализирован.
Атрибуты
private ArrayList<Neuron> listOfNeurons Переменная ArrayList объектов класса Neuron
private int numberOfNeuronsInLayer Целочисленное значение для хранения количества нейронов, которая является частью слоя.
Методы
public ArrayList<Neuron> getListOfNeurons() Геттер listOfNeurons
Параметры: нет
Возвращает: listOfNeurons
public void setListOfNeurons(ArrayList<Neuron> listOfNeurons) Сеттер listOfNeurons
Параметры: listOfNeurons
Возвращает: ничего
public int getNumberOfNeuronsInLayer() Геттер numberOfNeuronsInLayer
Параметры: нет
Возвращает: numberOfNeuronsInLayer
public void setNumberOfNeuronsInLayer(int numberOfNeuronsInLayer) Сеттер numberOfNeuronsInLayer
Параметры: numberOfNeuronsInLayer
Возвращает: ничего
Реализация класса: файл Layer.java
Имя класса: InputLayer
Заметка: Этот класс наследует атрибуты и методы от класса Layer
Атрибуты
Нет
Методы
public void
initLayer(InputLayer inputLayer)
Инициализирует входной слой с дробными псевдорандомными числами
Параметры: Объект класса InputLayer
Возвращает: ничего
public void
printLayer(InputLayer inputLayer)
Выводит входные весы слоя
Параметры: Объект класса InputLayer
Возвращает: ничего
Реализация класса: файл InputLayer.java
Имя класса: HiddenLayer
Заметка: Этот класс наследует атрибуты и методы от класса Layer
Атрибуты
Нет
Методы
public ArrayList<HiddenLayer>
initLayer(
HiddenLayer hiddenLayer,
ArrayList<HiddenLayer> listOfHiddenLayers,
InputLayer inputLayer,
OutputLayer outputLayer
)
Инициализирует скрытый слой(и) с дробными псевдослучайными числами
Параметры: Объект класса HiddenLayer, список объектов класса HiddenLayer, объект класса InputLayer, объект класса OutputLayer
Возвращает: список скрытых слоев с добавленным слоем
public void
printLayer(ArrayList<HiddenLayer> listOfHiddenLayers)
Выводит входные весы слоя(ев)
Параметры: Список объектов класса HiddenLayer
Возвращает: ничего
Реализация класса: файл HiddenLayer.java
Имя класса: OutputLayer
Заметка: Этот класс наследует атрибуты и методы от класса Layer
Атрибуты
Нет
Методы
public void initLayer(OutputLayer outputLayer) Инициализирует выходной слой с дробными псевдорандомными числами
Параметры: Объект класса OutputLayer
Возвращает: ничего
public void printLayer(OutputLayer outputLayer) Выводит входные весы слоя
Параметры: Объект класса OutputLayer
Возвращает: ничего
Реализация класса: файл OutputLayer.java
Имя класса: NeuralNet
Заметка: Значения в топологии нейросети фиксированы в этом классе(два нейрона во входном слое, два скрытых слоя с тремя нейронами в каждом, и один нейрон в выходном слое).
Напоминание: Это первая версия.
Атрибуты
private InputLayer inputLayer Объект класса InputLayer
private HiddenLayer hiddenLayer Объект класса HiddenLayer
private ArrayList<HiddenLayer> listOfHiddenLayer Переменная ArrayList объектов класса HiddenLayer. Может иметь больше одного скрытого слоя
private OutputLayer outputLayer Объект класса OutputLayer
private int numberOfHiddenLayers Целочисленное значение для хранения количества слоев, что является частью скрытого слоя
Методы
public void initNet() Инициализирует нейросеть. Слои созданы и каждый список весов нейронов созданы случайно
Параметры: нет
Возвращает: ничего
public void printNet() Печатает нейросеть. Показываются каждое входное и выходное значения каждого слоя.
Параметры: нет
Возвращает: ничего
Реализация класса: файл NeuralNet.java

Огромное преимущество ООП — легко документировать программу в унифицированный язык моделирования(UML). Диаграммы классов UML представляют классы, атрибуты, методы, и отношения между классами очень простым и понятным образом, таким образом, помогая программисту и/или заинтересованным сторонам понять проект в целом. На следующем рисунке представлена самая первая версия диаграммы классов проекта: Сейчас давайте применим эти классы, чтобы получить некоторые результаты.

Показанный следующий код имеет тестовый класс, главный метод объектом класса NeuralNet, называнный n. Когда этоn метод вызывается (путем выполнения класса), он вызывает initNet () и printNet () методы из объекта n, генерирующие следующий результат, показанный на рисунке справа после кода. Он представляет собой нейронную сеть с двумя нейронами во входном слое, три в скрытом слое и один в выходном слое:

public class NeuralNetTest {
    public static void main(String[] args) {
        NeuralNet n = new NeuralNet();
        n.initNet();
        n.printNet();
    }
}

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

В сумме

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

От переводчика

Оригинал книги: Neural Network Programming with Java

Каковы лучшие инструменты для начала машинного обучения Java?

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

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

ИИ для людей

ИИ – это широкая и крутая область, которая существует уже некоторое время, но всегда чувствуется немного вне досягаемости и сделана специально для ученых. Если вы хотели создать систему искусственного интеллекта, вам нужно было самостоятельно реализовать основные алгоритмы и научить их определять шаблоны, понимать образы и обрабатывать естественный язык.

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

Начало работы машины

Чтобы упростить задачу, мы решили выделить 3 проекта, которые помогут вам начать:

  1. Deeplearning4J (DL4J) – открытая, распределенная и коммерческая библиотека глубокого обучения для JVM
  2. BID Data Project – набор шаблонов, которые обеспечивают быстрое, крупномасштабное машинное обучение и анализ данных.
  3. Нейроф – Объектно-ориентированная нейронная сеть

Кстати, недавно мы опубликовали еще один список интересных библиотек GitHub с открытым исходным кодом, которые привлекли наше внимание. Проверьте это .

1. DL4J – глубокое обучение

DL4J – это инструмент, который поможет вам в процессе настройки глубоких нейронных сетей, которые состоят из нескольких слоев. Это приносит глубокое изучение JVM наряду с быстрым прототипированием и настройкой в ​​масштабе, сосредотачиваясь больше на соглашении, чем на конфигурации.

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

DL4J написан на Java, что делает его совместимым с любым языком JVM, таким как Clojure, Scala или Kotlin, и интегрируется с Hadoop и Spark.

Возможные варианты использования включают системы оценки или рекомендации (CRM, adtech, предотвращение оттока), прогнозную аналитику или даже обнаружение мошенничества. Если вы ищете пример из реальной жизни, вы можете проверить Rapidminer . Это платформа данных с открытым исходным кодом, которая использует DL4J для оптимизации процессов прогнозной аналитики для своих пользователей.

Настроить новую нейронную сеть так же просто, как создать новый объект:

1

2

3

4

5

6

7

8

9

ultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()

     .iterations(1)

     .weightInit(WeightInit.XAVIER)

     .activation("relu")

     .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)

     .learningRate(0.05)

     .backprop(true)

     .build();

2. Проект BID Data

Проект BID Data Project предназначен для тех из вас, кто имеет дело с большим количеством данных и чувствителен к производительности. Этот проект UC Berkeley представляет собой набор аппаратных, программных и конструктивных шаблонов, которые обеспечивают быстрый и крупномасштабный анализ данных.

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

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

Две другие библиотеки – это BIDMat , библиотека быстрой матричной алгебры, которая фокусируется на интеллектуальном анализе данных, и BIDParse , анализатор естественного языка с GPU-ускорением. Другие библиотеки в этом проекте включают инструменты визуализации, а также библиотеки, которые позволят вам работать на Spark или даже на Android.

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

3. Нейроф

Neuroph – это облегченная среда Java, используемая для разработки общих архитектур нейронных сетей. Фреймворк предоставляет библиотеку Java вместе с инструментом GUI (называемым easyNeurons), и вы можете использовать его для создания и обучения ваших собственных нейронных сетей в программах Java.

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

Вы можете попробовать онлайн демоверсию Neuroph и посмотреть, как она на самом деле работает. Оповещение о спойлере: интерфейс выглядит старым и устаревшим, но с ним можно создавать приятные вещи. Кроме того, он получил награду Duke’s Choice Award за 2013 год .

сети 768x522

Сетевой график

А как насчет других?

Если эти 3 проекта не ваша чашка чая, и вы ищете что-то немного другое для вашего проекта, не беспокойтесь. Если вы ищете в «GitHub» « Машинное обучение », вы найдете 1,506 репозиториев Java, которые могут дать вам правильный инструмент.

Например, интересный проект от Airbnb – аэрозоль ; Библиотека машинного обучения, созданная для дружелюбного отношения к человеку.

Начало работы с новой технологией всегда является источником неприятностей. Если вам понадобится помощь с вашими исключениями, обязательно воспользуйтесь инструментом анализа ошибок Takipi .

Последние мысли

Каждые несколько лет вокруг AI появляется новый шум. На этот раз он получил подкрепление в виде машинного обучения, интеллектуального анализа данных, нейронных сетей и т. Д., И мы все за это. Тот факт, что эти библиотеки с открытым исходным кодом, означает, что информация и возможности доступны, и все, что вам нужно сделать, это подумать, что можно сделать с помощью этой возможности.

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

  • 23.07.2017

Реализуем искуственный интеллект в виде нейронной сети на Java.

Проект Neuroph по-сути имеет две реализации:

  1. Программа с графическим интерфейсом для наглядного отображения работы нейронной сети, ее обучения и тестирования.
  2. Набор классов для интегрирования в приложение Java

Neuroph studio

Для того, чтобы создать и обучить нейронную сеть с помощью Neuroph Studio, необходимо выполнить следующие шаги:

  1. Create Neuroph Project
  2. Create Perceptron network
  3. Create training set (Training -> New Training Set)
  4. Train network
  5. Test trained network

Интеграция нейронной сети в Java приложение

Пример создания небольшой нейронной сети на Neuroph:

[java]
package neural;

import javax.sql.rowset.serial.SerialArray;

import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.nnet.Perceptron;
import org.neuroph.nnet.learning.HopfieldLearning;
import org.neuroph.util.TransferFunctionType;

public class Neural {

public static void main(String[] args) {
// TODO Auto-generated method stub
NeuralNetwork&amp;lt;HopfieldLearning&amp;gt; nNetwork = new Perceptron(2, 1);

DataSet trainingSet =
new DataSet(2, 1);

trainingSet. addRow (new DataSetRow (new double[]{0, 0},
new double[]{0}));
trainingSet. addRow (new DataSetRow (new double[]{0, 1},
new double[]{1}));
trainingSet. addRow (new DataSetRow (new double[]{1, 0},
new double[]{1}));
trainingSet. addRow (new DataSetRow (new double[]{1, 1},
new double[]{1}));
// learn the training set
nNetwork.learn(trainingSet);
// save the trained network into file
nNetwork.save(«or_perceptron.nnet»);
System.out.println(«end»);

// set network input
nNetwork.setInput(0, 0);
// calculate network
nNetwork.calculate();
// get network output
double[] networkOutput = nNetwork.getOutput();

for (double i : networkOutput)
System.out.println(i);

}

}
[/java]

Глава 2: Наша первая Нейронная Сеть

Сперва, давайте начнем с ответа на простой вопрос: Что такое Нейрон? Давайте рассмотрим формальное определение:

Нейрон, или нейрон, или нервная клетка — электрически возбудимая клетка, которая принимает, обрабатывает и передает информацию при помощи электрических и химических сигналов [1]

Как вы могли заметить, у Нейрона есть три основные функции:

  • Передача результата после обработки

Эти три функции отлично сочетаются с тремя ключевыми компонентами настоящего Нейрона, посмотрите на следующий рисунок Нейрона:

Эти три компонента один к одному соответствуют основным функциям, исполняющимся Нейроном::

  • Дендриты — получает сигнал и передает его в тело клетки (нейрона)

  • Сома (тело) — обрабатывает сигнал (body)

  • Аксон — посылает сигнал другим нейронам

Теперь мы можем воссоздать математическую модель Нейрона:

Веса – каждый вес отражает дендрит и отвечает за прием сигнала из окружения. Поскольку у каждого дендрита есть свой размер (форма), существует некое отклонение между сигналом, передающимся дендриту, и сигналом, передающимся от дендрита в сому. Потому нам и нужен вес для каждой связи. Вес, по сути, говорит нам, как дендрит влияет на сигнал. Как только все сигналы собираются и перемножаются с весом, они суммируются и передаются в сому.

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

Со всеми полученными знаниями мы наконец готовы приступить к написанию кода.

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

public interface Neuron {

* Должен быть вызван, когда Нейрон получает входной сигнал от связанного

* К примеру, давайте рассмотрим следующую сеть:

* Если NeuronA или NeuronB или NeuronC посылают сигнал в NeuronD, метод

* @param from — Нейрон, который посылает сигнал.

void forwardSignalReceived(Neuron from, Double value);

default void connect(Neuron neuron, Double weight) {

this.addForwardConnection(neuron);

neuron.addBackwardConnection(this, weight);

void addForwardConnection(Neuron neuron);

void addBackwardConnection(Neuron neuron, Double weight);

Чтобы сделать сеть нам необходимо обозначить 2 типа нейронов:

public class InputNeuron implements Neuron {

private Set<Neuron> connections = new HashSet<>();

public void forwardSignalReceived(final Neuron from, final Double value) {

connections.forEach(n -> n.forwardSignalReceived(this, value));

public void addForwardConnection(final Neuron neuron) {

public void addBackwardConnection(final Neuron neuron, final Double weight) { } // No op

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

  • Интерфейс для Функции Активации;

  • Нашу первую простейшую Функцию Активации;

public interface ActivationFunction {

Double forward(final Double x);

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

public class StepFunction implements ActivationFunction {

public Double forward(Double x) {

return x >= 1. ? 1. : 0.;

Если вы построите график функции, то он будет выглядеть так:

Сейчас мы готовы обсудить самую сложную часть этой главы – реализацию нашего первого настоящего Нейрона. Мы разберем его по частям. Для начала давайте опишем все поля, которые нам потребуются: Представляет соединения от нейрона к нейронам, от которых он получает сигналы. Например, в следующей сети:

private final ActivationFunction activationFunction;

* Представляет соединения от нейрона к нейронам, от которых он получает

* сигналы. К примеру, в следующей сети:

* backwardConnections map будет выглядеть так:

private final Map<Neuron, Double> backwardConnections = new HashMap<>();

* Представляет набор Нейронов, которым текущий нейрон посылает сигнал, здесь

* нет необходимости в весах.

private final Set<Neuron> forwardConnections = new HashSet<>();

* inputSignals используется для хранения сигналов от других Нейронов. Ключи в

* этом словаре должны быть абсолютно идентичны ключам в

* {@link #backwardConnections}. Как только все сигналы получены Нейроном,

* он может начинать обрабатывать их.

private final Map<Neuron, Double> inputSignals = new HashMap<>();

* Количество сигналов, уже полученных.

* Как только это количество достигнет размера {@link #inputSignals} словаря,

* нейрон готов к обработке входных сигналов и отправке сигнала дальше.

private volatile int signalReceived;

private final double bias;

* Хранит результаты последнего отправленного сигнала от Нейрона к другим

* Нейронам. Это в основном необходимо для выходных Нейронов,

* поскольку у них не хватает других Нейронов для отсылки сигнала, и в то же

* время должна быть возможность получить это значение.

private volatile double forwardResult;

К этому моменту должно стать очевидно, как работает добавление связей:

public void addForwardConnection(final Neuron neuron) {

forwardConnections.add(neuron);

public void addBackwardConnection(final Neuron neuron, final Double weight) {

backwardConnections.put(neuron, weight);

inputSignals.put(neuron, Double.NaN);

И главная функция, делающая всю магию:

public void forwardSignalReceived(final Neuron from, final Double value) {

inputSignals.put(from, value);

// Следующий if – проверка на то, являлся ли текущий сигнал последним

// оставшимся для получения. Если это так и все входные сигналы были

// получены, Нейрон может начинать их обработку и испускать сигнал сам

if (backwardConnections.size() == signalReceived) {

// Нейрону для обработки входных сигналов необходимо 4 шага:

// 1. Посчитать input = W * X + b

// 2. Посчитать output = f(input), где f – функция активации

// 3. Отправить output другим нейронам

// 4. Аннулировать состояние

// Посчитать W * X + b – сумма всех входных сигналов, умноженных на

// соответствующий ему вес.

// Отклонение (bias) добавляется в конце.

double forwardInputToActivationFunction

.mapToDouble(connection ->

// inputSignals хранит сигнал, где

// connection.getValue() дает вам

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

// Таким образом это и есть X * W.

inputSignals.get(connection.getKey())

= activationFunction.forward(

forwardInputToActivationFunction);

forwardResult = signalToSend;

// Шаг #3 Поскольку сигнал посчитан, теперь мы можем отослать

Одна вещь, которую мы опускаем – Builder. Поскольку паттерн builder не имеет ничего общего с DeepLearning мы просто предположим, что Builder существует для нашего класса, но мы не показываем фрагмент кода здесь.

Сейчас мы можем создать нашу первую нейронную сеть (Neural Network, NN). Для начала давайте опишем нейронную сеть, которую мы собираемся построить. Наша первая NN будет предсказывать, нужно ли идти на вечеринку или нет. Для построения сети у нас должны быть данные для обучения сети. Поскольку мы не знаем, как тренировать нашу сеть, мы будем делать это вручную, наблюдая за шаблонами в наших данных и пытаясь подобрать такие веса, которые позволят сети делать верные предсказания. Итак, здесь есть то, что мы знаем о факторах, влияющих на опыт:

  1. 1.

    Факт того, будет ли наш лучший друг на вечеринке или нет;

  2. 2.

    Наличие любимого напитка (давайте назовем его «Vodka» :) );

  3. 3.

    Погода (солнечно или нет);

Сейчас мы можем создать комбинацию всех этих факторов и желаемое поведение для каждого из них. Чтобы сделать изображение более простым, мы используем 1 или 0, чтобы представить каждый фактор. 1 будет обозначать присутствие фактора, а 0 – его отсутствие. Так как мы говорим о трех факторах, входом для нашей NN будет 3 цифры (1 или 0 каждая), например, следующий массив отображает вход с информацией о том, что наш лучший друг будет на вечеринке, но не будет любимого напитка и погода будет не солнечная, в следствии чего мы не пойдем на вечеринку:

Еще один пример с информацией об отсутствии лучшего на вечеринке, но будет напиток и солнечная погода на улице – нужно идти на вечеринку.

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

[1, 0, 0] => 0
[0, 1, 0] => 0
[0, 0, 1] => 0
[1, 1, 0] => 1
[1, 0, 1] => 1
[0, 1, 1] => 1
[1, 1, 1] => 1

Как наша NN будет выглядеть? На самом деле, все просто. У нас будет 3 входных нейрона и 1 выходной нейрон. Пожалуй, можно сразу перейти к изображению NN, оно проще для понимания:

Эта сеть называется «полносвязная однослойная сеть». Звучит пугающе, я думаю, что многие люди в науке сознательно пытаются создать такое впечатление :) В любом случае, мы собираемся разделить название на части и последовательно обсудить. Полносвязная просто означает, что у каждого нейрона в слое есть связь с каждым нейроном предшествующего слоя. Слой – абстракция, которой не существует в настоящем мозге, просто упрощение, позволяющее инженерам проще изображать нейронные сети. Каждый слой – коллекция нейронов, у которых нет связи друг с другом. Нейроны в слое получают сигналы только от предшествующего слоя (если такой существует) и посылают сигналы в следующий слой (если такой существует). Глядя на нашу картинку, кто-то может подумать, что это двуслойная сеть. Исторически так сложилось, что входной слой не считают (или, может быть, мы считаем от 0, тогда это имеет смысл). Если мы не считаем входной слой, эта сеть, безусловно, однослойная.

На этом этапе у нас есть абсолютно все, что нам необходимо, чтобы продолжить реализацию нашей первой NN: данные, архитектура сети и даже веса, которые мы будем использовать. Так давайте реализуем:

// Создаем три входных нейрона

InputNeuron inputFriend = new InputNeuron();

InputNeuron inputVodka = new InputNeuron();

InputNeuron inputSunny = new InputNeuron();

ConnectedNeuron outputNeuron

// Мы не обсудили builder, впрочем, это простейший Java builder,

// если необходимо, то вы можете посмотреть реализацию

= new ConnectedNeuron.Builder()

.activationFunction(new StepFunction())

// Создаем связи между нейронами

inputFriend.connect(outputNeuron, 0.5);

inputVodka.connect(outputNeuron, 0.5);

inputSunny.connect(outputNeuron, 0.5);

// Посылаем сигналы в сеть

inputFriend.forwardSignalReceived(null, 1.);

inputVodka.forwardSignalReceived(null, 1.);

inputSunny.forwardSignalReceived(null, 0.);

// Получаем результат и печатаем его:

double result = outputNeuron.getForwardResult();

System.out.printf(«Prediction: %3fn», result)

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

Нейронная сеть — это трендовая и процветающая ниша в IT-мире. Если вам кажется, что нейронные сети, искусственный интеллект и машинное обучение — это «что-то» из далекого будущего, то вы очень сильно ошибаетесь. Любой пользователь интернета каждый день сталкивается с нейронными сетями и даже не подозревает об этом. Ищете информацию через поиск в Гугле? Или, может, просите Алису подсказать вам погоду в вашей местности? И то и то — это и есть работа нейросети и искусственного интеллекта.

 

Нейронная сеть на Java

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

 

Проекты на Java, связанные с нейронной сетью

Есть множество крупных, средних и мелких проектов, разработанных на Java и применяющих возможности искусственного интеллекта. Вот несколько интересных примеров:

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

  2. JOONE Neural Engine. Это платформа, написанная на Java, которую используют для программирования, обучения и тестирования нейронных сетей из различных областей.

  3. Robocode. Это популярная обучающая игра, написанная на Java и рассчитанная на тех, кто хочет обучиться Java-программированию и базовым подходам в робототехнике и работе с нейронными сетями. Суть игры — разработать свой собственный роботанк, запрограммировать его стратегическое развитие на основе своей интуиции и искусственного интеллекта, состязаться с роботанками прочих java-программистов.

  4. WEKA Machine Learning Suite. Это платформа, разработанная на Java, использующая технологию нейросетей для прототипирования различных подходов в машинном обучении для бизнес-деятельности в сфере анализирования больших объемов информации.

  5. Чат-бот Alice. Данный бот является одним из самых продвинутых чат-ботов современности, который способен поддержать разговор с человеком на его собственном языке. Представленный чат-бот несколько раз выигрывал премию первенства AI Loebner, которая присуждается лучшим бот-программам, соревнующимся в качественном преодолении тестирования по системе Тьюринга.

 

Нейронная сеть на Java: инструменты

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

Сегодня мы поговорим о наиболее популярных инструментах.

 

Полезные платформы

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

  1. Weka. Мы уже обсуждали эту платформу чуть выше. Это эффективный вспомогательный ресурс, созданный на Java, который использует в своей работе возможности искусственного интеллекта. На этой платформе вы можете попрактиковаться в работе с машинным обучением в сфере аналитики информации.

  2. Knime. Эта платформа ориентирована на разработку и обучение программ с фармацевтическим уклоном или в сфере бизнес-аналитики.

  3. RapidMiner. Помогает визуализировать и обрабатывать данные, используемые в сфере машинного обучения.

  4. Elki. Представленная платформа поможет разработать приложение, которое будет использовать в своей основе интеллектуальный анализ информации любых объемов.

 

Java-библиотеки

  1. Apache Jena. Это библиотека, которая помогает создавать приложения, основываясь на возможностях RDF.

  2. PowerLoom. Это библиотека, которая помогает создавать приложения, сформированные на каких-либо конкретных знаниях, анализируя которые нужно использовать метод дедукции, вычисляя правильный вывод, построенный на «вбитых» в базу данных правил и условий.

  3. D3web. Еще одна библиотека для программирования приложений на основе анализа имеющихся «внутри» знаний, которые должны уметь рассуждать и применять различные решения в зависимости от конкретной ситуации.

  4. Neuroph — специальная библиотека для формирования нейронных сетей и программирования машинного обучения.

  5. DeepLearning4j. Это профессиональная библиотека для обучения JVM и программирования нейронных сетей.

  6. Apache OpenNLP — библиотека для программирования нейронных сетей для обрабатывания текстовых записей на человеческом языке.

  7. Stanford CoreNLP — инструмент для программирования нейронных сетей для обработки человеческого языка.

  8. Java-ML — это целый набор инструментария для программистов, практикующих машинное обучение.

  9. Jenetics — это дистрибутив, рассчитанный на программистов, которым нужна нейронная сеть на Java для программирования генетических и эволюционных алгоритмических программ.

  10. ECJ 23 — еще одна мощная библиотека для генетического программирования.

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

 

Заключение

Нейронная сеть на Java — это достаточно популярное событие, поэтому вполне естественно, когда у вас есть знания этого языка программирования, попробовать себя в нишах искусственного интеллекта. Искусственный интеллект и построение нейронных сетей — это сфера, которая развивается быстрее остальных IT-ниш. А это значит, что спрос на хороших специалистов из этой сферы будет только расти.

Реализация нейронной сети на Java

Пример реализации простой нейронной сети, обучаемой по алгоритму обратного распространенния ошибки.

NNet package

Layer.java

Базовый интерфейс любого нейронного слоя. Размер слоя — количество нейронов содержащихся в слое и соотвественно размер выходного вектора слоя.

package nnet;
import java.io.Serializable;

/**
 * Интерфейс нейронного слоя
 */
public interface Layer extends Serializable {
    /**
     * Получает размер входного вектора
     * @return Размер входного вектора
     */
    int getInputSize();

    /**
     * Получает размер слоя
     * @return Размер слоя
     */
    int getSize();

    /**
     * Вычисляет отклик слоя
     * @param input Входной вектор
     * @return Выходной вектор
     */
    float[] computeOutput(float[] input);
}

Network.java

Реализация базовой функциональности нейронной сети, состоящей из нескольких слоев.

package nnet;

import java.io.*;

/**
 * Базовая реализация нейронной сети
 */
public class Network implements Serializable {
    /**
     * Конструирует нейронную сеть с заданными слоями
     * @param layers Нейронные слои
     */
    public Network(Layer[] layers) {
        // проверки
        if (layers == null || layers.length == 0) throw new IllegalArgumentException();
        // проверим детально
        final int size = layers.length;
        for (int i = 0; i < size; i++)
            if (layers[i] == null || (i > 1 && layers[i].getInputSize() != layers[i - 1].getSize()))
                throw new IllegalArgumentException();

        // запомним слои
        this.layers = layers;
    }

    /**
     * Получает размер входного вектора
     * @return Размер входного вектора
     */
    public final int getInputSize() {
        return layers[0].getInputSize();
    }

    /**
     * Получает размер выходного вектора
     * @return Размер выходного вектора
     */
    public final int getOutputSize() {
        return layers[layers.length - 1].getSize();
    }

    /**
     * Получает размер сети
     * @return Размер сети
     */
    public final int getSize() {
        return layers.length;
    }

    /**
     * Получает нейронный слой по индексу
     * @param index Индекс слоя
     * @return Нейронный слой
     */
    public final Layer getLayer(int index) {
        return layers[index];
    }

    /**
     * Вычисляет отклик сети
     * @param input Входной вектор
     * @return Выходной вектор
     */
    public float[] computeOutput(float[] input) {
        // проверки
        if (input == null || input.length != getInputSize())
                throw new IllegalArgumentException();

        // вычислим выходной отклик сети
        float[] output = input;
        final int size = layers.length;
        for (int i = 0; i < size; i++)
            output = layers[i].computeOutput(output);

        // вернем выход
        return output;
    }

    /**
     * Сохраняет нейронну сеть в файл
     * @param fileName Имя файла
     */
    public void saveToFile(String fileName) {
        // проверки
        if (fileName == null) throw new IllegalArgumentException();

        // сохраняем
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(fileName));
            outputStream.writeObject(this);
            outputStream.close();
        }
        catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * Загружает нейронную сеть из файла
     * @param fileName Имя файла
     * @return Нейронную сеть
     */
    public static Network loadFromFile(String fileName) {
        // проверки
        if (fileName == null) throw new IllegalArgumentException();

        // загружаем
        Object network = null;
        try {
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName));
            network = inputStream.readObject();
            inputStream.close();
        }
        catch (Exception e) {
            throw new IllegalArgumentException(e);
        }

        // отдадим сеть
        return (Network)network;
    }

    /**
     * Слои
     */
    private Layer[] layers;
}

BackpropLayer.java

Интерфейс нейронного слоя обучаемого по алгоритму обратного распространения ошибки. Метод randomize задает начальные случайные значения весов в слое. Метод computeBackwardError вычисляет ошибку в обратном направлении: то есть ту, которая пришла на вход слоя от предыдущего слоя. В качестве параметров computeBackwardError принимает входной вектор, который подавался на вход и вектор ошибки для этого слоя. Метод же adjust подгоняет веса нейроннов в сторону уменьшения ошибки.

package nnet;

/**
 * Интерфейс слоя обучаемого по алгоритму обратного распространения ошибки
 */
public interface BackpropLayer extends Layer {
    /**
     * Придает случайные значения весам нейронов
     * @param min Минимальное значение
     * @param max Максимальное значение
     */
    void randomize(float min,float max);

    /**
     * Выичисляет следующий вектор ошибки в обратном направлении
     * @param input Входной вектор
     * @param error Вектор ошибки
     * @return Следующий вектор ошибки в обратном направлении
     */
    float[] computeBackwardError(float[] input,float[] error);

    /**
     * Подгоняет веса нейронов в сторону уменьшения ошибки
     * @param input Входной вектор
     * @param error Вектор ошибки
     * @param rate Скорость обучения
     * @param momentum Моментум
     */
    void adjust(float[] input,float[] error,float rate,float momentum);
}

BackpropNetwork.java

Реализация нейронной сети, обучаемой по алгоритму обратного распространенния ошибки.

package nnet;

/**
 * Нейронная сеть обучаемая по алгоритму обратного распространения ошибки
 */
public final class BackpropNetwork extends Network {
    /**
     * Констрирует нейронную сеть с заданными слоями
     * @param layers
     */
    public BackpropNetwork(Layer[] layers) {
        // передадим родакам
        super(layers);
        // рандомизируем веса
        randomize(0,0.3f);
    }

    /**
     * Придает случайные значения весам нейроннов в сети
     * @param min
     * @param max
     */
    public void randomize(float min,float max) {
        // придаем случайные значения весам в сети
        final int size = getSize();
        for (int i = 0; i < size; i++) {
            Layer layer = getLayer(i);
            if (layer instanceof BackpropLayer) ((BackpropLayer)layer).randomize(min,max);
        }
    }

    /**
     * Обучает сеть паттерну
     * @param input Входной вектор
     * @param goal Заданный выходной вектор
     * @param rate Скорость обучения
     * @param momentum Моментум
     * @return Текущую ошибку обучения
     */
    public float learnPattern(float[] input,float[] goal,float rate,float momentum) {
        // проверки
        if (input == null || input.length != getInputSize() || 
                goal == null || goal.length != getOutputSize()) throw new IllegalArgumentException();

        // делаем проход вперед
        final int size = getSize();
        float[][] outputs = new float[size][];

        outputs[0] = getLayer(0).computeOutput(input);
        for (int i = 1; i < size; i++)
            outputs[i] = getLayer(i).computeOutput(outputs[i - 1]);

        // вычислим ошибку выходного слоя
        Layer layer = getLayer(size - 1);
        final int layerSize = layer.getSize();
        float[] error = new float[layerSize];
        float totalError = 0;

        for (int i = 0; i < layerSize; i++) {
            error[i] = goal[i] - outputs[size - 1][i];
            totalError += Math.abs(error[i]);
        }

        // обновим выходной слой
        if (layer instanceof BackpropLayer)
            ((BackpropLayer)layer).adjust(size == 1 ? input : outputs[size - 2],error,rate,momentum);

        // идем по скрытым слоям
        float[] prevError = error;
        Layer prevLayer = layer;

        for (int i = size - 2; i >= 0; i--,prevError = error,prevLayer = layer) {
            // получим очередной слой
            layer = getLayer(i);
            // вычислим для него ошибку
            if (prevLayer instanceof BackpropLayer)
                error = ((BackpropLayer)prevLayer).computeBackwardError(outputs[i],prevError);
            else
                error = prevError;
            // обновим слой
            if (layer instanceof BackpropLayer)
                ((BackpropLayer)layer).adjust(i == 0 ? input : outputs[i - 1],error,rate,momentum);
        }

        // вернем суммарную ошибку
        return totalError;
    }
}

SigmoidLayer.java

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

package nnet;

/**
 * Сигмоидальный слой
 */
public final class SigmoidLayer implements BackpropLayer {
    /**
     * Вес
     */
    private final int WEIGHT = 0;

    /**
     * Дельта
     */
    private final int DELTA = 1;

    /**
     * Констрирует сигмоидальный слой
     * @param inputSize Размер входного вектора
     * @param size Размер слоя
     * @param bipolar Флаг биполярного слоя
     */
    public SigmoidLayer(int inputSize,int size,boolean bipolar) {
        // проверки
        if (inputSize < 1 || size < 1) throw new IllegalArgumentException();

        // создаем слой
        matrix = new float[size][inputSize + 1][2];

        // запомним параметры
        this.inputSize = inputSize;
        this.bipolar = bipolar;
    }

    /**
     * Конструирует биполярный слой
     * @param inputSize Размер входного вектора
     * @param size Размер слоя
     */
    public SigmoidLayer(int inputSize,int size) {
        this(inputSize,size,true);
    }

    public int getInputSize() {
        return inputSize;
    }

    public int getSize() {
        return matrix.length;
    }

    public float[] computeOutput(float[] input) {
        // проверки
        if (input == null || input.length != inputSize)
                throw new IllegalArgumentException();

        // вычислим выход
        final int size = matrix.length;
        float[] output = new float[size];
        for (int i = 0; i < size; i++) {
            output[i] = matrix[i][0][WEIGHT];
            for (int j = 0; j < inputSize; j++)
                output[i] += input[j] * matrix[i][j + 1][WEIGHT];
            if (bipolar)
                output[i] = (float)Math.tanh(output[i]);
            else
                output[i] = 1 / (1 + (float)Math.exp(-output[i]));
        }

        // вернем оклик
        return output;
    }

    public void randomize(float min,float max) {
        final int size = matrix.length;
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < inputSize + 1; j++) {
                matrix[i][j][WEIGHT] = min + (max - min) * (float)Math.random();
                matrix[i][j][DELTA] = 0;
            }
        }
    }

    public float[] computeBackwardError(float[] input,float[] error) {
        // проверки
        if (input == null || input.length != inputSize ||
                error == null || error.length != matrix.length) throw new IllegalArgumentException();

        // вычислим входящую ошибку
        float[] output = computeOutput(input);
        final int size = matrix.length;
        float[] backwardError = new float[inputSize];

        for (int i = 0; i < inputSize; i++) {
            backwardError[i] = 0;
            for (int j = 0; j < size; j++)
                backwardError[i] += error[j] * matrix[j][i + 1][WEIGHT] *
                        (bipolar ? 1 - output[j] * output[j] : output[j] * (1 - output[j]));
        }

        // вернем ошибку
        return backwardError;
    }

    public void adjust(float[] input,float[] error,float rate,float momentum) {
        // проверки
        if (input == null || input.length != inputSize || 
                error == null || error.length != matrix.length) throw new IllegalArgumentException();

        // обновляем веса
        float[] output = computeOutput(input);
        final int size = matrix.length;

        for (int i = 0; i < size; i++) {
            final float grad = error[i] * (bipolar ? 1 - output[i] * output[i] : output[i] * (1 - output[i]));
            // обновляем нулевой вес
            matrix[i][0][DELTA] = rate * grad + momentum * matrix[i][0][DELTA];
            matrix[i][0][WEIGHT] += matrix[i][0][DELTA];
            // обновим остальные веса
            for (int j = 0; j < inputSize; j++) {
                matrix[i][j + 1][DELTA] = rate * input[j] * grad + momentum * matrix[i][j + 1][DELTA];
                matrix[i][j + 1][WEIGHT] += matrix[i][j + 1][DELTA];
            }
        }
    }

    /**
     * Размер входного вектора
     */
    private final int inputSize;

    /**
     * Флаг биполярного слоя
     */
    private final boolean bipolar;

    /**
     * Матрица слоя
     */
    private float[][][] matrix;
}

WTALayer.java

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

package nnet;

/**
 * WTA слой
 */
public final class WTALayer implements BackpropLayer {
    /**
     * Конструирует WTA слой заданного размера и уровнем доверия
     * @param size Размер слоя
     * @param minLevel Уровень доверия
     */
    public WTALayer(int size,float minLevel) {
        // проверки
        if (size < 1) throw new IllegalArgumentException();
        // запомним параметры слоя
        this.size = size;
        this.minLevel = minLevel;
    }

    public int getInputSize() {
        return size;
    }

    public int getSize() {
        return size;
    }

    public float[] computeOutput(float[] input) {
        // проверки
        if (input == null || input.length != size) throw new IllegalArgumentException();

        // найдем победителя
        int winner = 0;
        for (int i = 1; i < size; i++)
            if (input[i] > input[winner]) winner = i;

        // готовим ответ
        float[] output = new float[size];

        // проверим на минимальный уровень расхождения
        if (minLevel > 0) {
            float level = Float.MAX_VALUE;
            for (int i = 0; i < size; i++)
                if (i != winner && Math.abs(input[i] - input[winner]) < level)
                    level = Math.abs(input[i] - input[winner]);
            if (level < minLevel) return output;
        }

        // говорим кто победитель
        output[winner] = 1;

        // вернем отклик
        return output;
    }

    public void randomize(float min,float max) {
    }

    public float[] computeBackwardError(float[] input,float []error) {
        // проверки
        if (input == null || input.length != size || error == null ||
                error.length != size) throw new IllegalArgumentException();

        // расчитываем ошибку
        float[] backwardError = new float[size];
        float[] output = computeOutput(input);

        for (int i = 0; i < size; i++)
            backwardError[i] = error[i] + output[i] - input[i];

        // вернем входящую ошибку
        return backwardError;
    }

    public void adjust(float[] input,float[] error,float rate,float momentum) {
    }

    /**
     * Получает минимальный уровень между победителем и всеми остальными нейронами
     * @return Минимальный уровень между победителем и всеми остальными нейронами
     */
    public float getMinLevel() {
        return minLevel;
    }

    /**
     * Устанавливает минимальный уровень между победитлем и всеми остальными нейронами
     * @param minLevel Новый минимальный уровень
     */
    public void setMinLevel(float minLevel) {
        this.minLevel = minLevel;
    }

    /**
     * Размер слоя
     */
    private final int size;

    /**
     * Минимальный уровень между победителем и всеми остальными нейронами
     */
    private float minLevel;
}

Понравилась статья? Поделить с друзьями:
  • Как написать искоса или искосо
  • Как написать исковое заявление примеры
  • Как написать исковое заявление на установление отцовства
  • Как написать исковое заявление на управляющую компанию
  • Как написать исковое заявление на раздел имущества