Немного предисловия
Начав изучать язык программирования Golang, мне стало интересно, что может этот язык в сфере машинного обучения. Тогда я начал искать примеры кода какой-либо НС на этом языке. К сожалению, ничего толкового найти не получилось. И вот тогда я решил переписать НС из этой статьи под GO.
Нейросеть
Задача нейронной сети — решить, что делать персонажу, исходя из 3х параметров:
- Количество здоровья (от 1 до 100)
- Наличие оружия
- Количество врагов
В зависимости от результата, может быть принято одно из следующих решений:
- Атаковать
- Красться
- Убегать
- Ничего не делать
Примеры:
Здоровье | Оружие | Враги | Решение |
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 | Ничего не делать |
Подготовка
Мы будем использовать библиотеку GoNN.
Установка:
go get github.com/fxsjy/gonn/gonn
Приступим!
Для начала зададим импорт:
import (
"fmt"
"github.com/fxsjy/gonn/gonn"
)
Теперь приступим к созданию нейронной сети:
func CreateNN() {
// Создаём НС с 3 входными нейронами (столько же входных параметров),
// 16 скрытыми нейронами и
// 4 выходными нейронами (столько же вариантов ответа)
nn := gonn.DefaultNetwork(3, 16, 4, false)
// Создаём массив входящих параметров:
// 1 параметр - количество здоровья (0.1 - 1.0)
// 2 параметр - наличие оружия (0 - нет, 1 - есть)
// 3 параметр - количество врагов
input := [][]float64 {
[]float64{0.5, 1, 1}, []float64{0.9, 1, 2}, []float64{0.8, 0, 1},
[]float64{0.3, 1, 1}, []float64{0.6, 1, 2}, []float64{0.4, 0, 1},
[]float64{0.9, 1, 7}, []float64{0.6, 1, 4}, []float64{0.1, 0, 1},
[]float64{0.6, 1, 0}, []float64{1, 0, 0} }
// Теперь создаём "цели" - те результаты, которые нужно получить
target := [][]float64 {
[]float64{1, 0, 0, 0}, []float64{1, 0, 0, 0}, []float64{1, 0, 0, 0},
[]float64{0, 1, 0, 0}, []float64{0, 1, 0, 0}, []float64{0, 1, 0, 0},
[]float64{0, 0, 1, 0}, []float64{0, 0, 1, 0}, []float64{0, 0, 1, 0},
[]float64{0, 0, 0, 1}, []float64{0, 0, 0, 1} }
// Начинаем обучать нашу НС.
// Количество итераций - 100000
nn.Train(input, target, 100000)
// Сохраняем готовую НС в файл.
gonn.DumpNN("gonn", nn)
}
Теперь нам нужно написать функцию, которая выберет ответ нейрона с самым большим весом.
func GetResult(output []float64) string {
max := -99999
pos := -1
// Ищем позицию нейрона с самым большим весом.
for i, value := range output {
if (value > max) {
max = value
pos = i
}
}
// Теперь, в зависимости от позиции, возвращаем решение.
switch pos {
case 0: return "Атаковать"
case 1: return "Красться"
case 2: return "Убегать"
case 3: return "Ничего не делать"
}
return ""
}
И теперь пишем функцию main.
func main() {
CreateNN()
// Загружем НС из файла.
nn := gonn.LoadNN("gonn")
// Записываем значения в переменные:
// hp - здоровье (0.1 - 1.0)
// weapon - наличие оружия (0 - нет, 1 - есть)
// enemyCount - количество врагов
var hp float64 = 0.7
var weapon float64 = 1.0
var enemyCount float64 = 1.0
// Получаем ответ от НС (массив весов)
out := nn.Forward([]float64{ hp, weapon, enemyCount })
// Печатаем ответ на экран.
fmt.Println(GetResult(out))
}
В данном случае ответ — «Атаковать»
Заключение
Как видим, процесс работы с нейронными сетями на Golang такой же, как и с другими языками программирования. Эксперементируйте с этой сетью и создавайте свои!
Комментарии (18)
WebConn
29.11.2017 15:38Есть мнение, что на таком большом количестве итераций такая сеть просто заучит входные данные и выход на новых будет не очень адекватным.
На самом деле, интересней узнать, как у gonn обстоят дела с платформо-зависимыми оптимизациями. Может ли оно использовать gpu для математики, например. Вообще, для подобной статьи неплохо предложить сравнение с другими библиотеками.grossws
29.11.2017 18:44Есть мнение, что на таком большом количестве итераций такая сеть просто заучит входные данные и выход на новых будет не очень адекватным.
Эта сеть, очевидно, переобучится со свистом. Другое дело, что здесь нет никакого сплита на train/test или train/dev/test и оценки базовых параметров получившегося результата (хотя бы банальные fp, fn, precision, recall и f1).
RPG18
29.11.2017 19:25gonn написан на Go без CGO, поэтому не может задействовать GPU и SSE/MMX/и т.д.
quasilyte
30.11.2017 11:35Через Go ассемблер можно использовать и SSE, и AVX, а MMX я бы просто не рекомендовал использовать. :)
И это добро реально используют при оптимизации, например, стандартной библиотеки Go.RPG18
30.11.2017 12:43Знаю. Вот пример использования. Но в 2017 писать на ассемблере ML библиотеки, это грустно.
JekaMas
29.11.2017 20:35+2В Golang очень плохо с такими вещами, как cuda.
Я на go три года и… освоил python для ml, dl. Он лучше. В го очень неудобно и многословно, когда речь заходит про данные и ml — нет именованных параметров, значений по умолчанию, нет коротких конструкций для работы со списками и хэштаблицами, отсутствие удобного функционального подхода, вызовы cgo стали пошустрее, но до python еще далеко.
Всему свой инструмент. Для ds — это python или java.
grossws
29.11.2017 18:42Как видим, процесс работы с нейронными сетями на Golang такой же, как и с другими языками программирования.
"Такой же" — это наличие метода
train
? Что-то не очень видно настроек количества скрытых слоёв, всяких развлечений типа dropout'а и подобных регуляризаций, метода спуска и критерия остановки (кроме количества итераций).
Если посмотреть в их примеры, то количество сугубо вспомогательного кода, некритичного к производительности (начальная загрузка и обработка данных) превышает все разумные пределы. Проще и лаконичней написать на Си с использованием cilkplus.
Кому это gonn'о нужно после python'а с приличными библиотеками? Умеет ли оно хотя бы в нативный blas/mkl?
QtRoS
29.11.2017 20:22Как я понимаю мотив библиотеки — Python это эксперименты, а Go продакшн (на самом деле надо понимать, что все годные ML-либы в Python'е на C++ или numpy сделаны, поэтому по скорости не уступают, но это не суть). Мне кажется идея такая, но это неточно.
grossws
29.11.2017 20:39Столь примитивная библиотека выглядит просто смешно как решение для прода (сравните со всякими TensorFlow, Theano, Torch).
Естественно, на python'е в ML почти всегда используется обвязка над нативными реализациями того же BLAS'а. Иии..? Почему это является внезапно минусом?
А насчёт "не уступают" мне интересно увидеть бенчмарк, который это подтвердит. Против numpy+icc+mkl или numpy+gcc+atlas (собранный под конкретный хост, а не из репозитория). Я сильно сомневаюсь, что оно сможет догнать даже numpy+gcc+openblas.
JekaMas
29.11.2017 20:42+2Библиотека как 4 года не поддерживается (2 года назад readme поправили только).
Думаю, кто-то просто пробовал golang и учился. Обучение идет в один поток, параллельная версия медленнее однопоточной (в readme указано). Просто учебный проект, на мой взгляд.
artemmityushov
29.11.2017 20:11Что-то мне не понятно, он просто выбирает из матрицы готовых решений?
В чем фишка использования нейросетей то в данном случае?SenDu Автор
29.11.2017 20:11Это простой пример. И он не выбирает из матрицы. Это обучение.
artemmityushov
30.11.2017 08:43А вот здесь вопрос — как и чему он обучается? По мне так это просто красивое слово «обучается», как добавление слова «блокчейн» ко всему чему можно.
SenDu Автор
02.12.2017 10:58Он обучается на простых примерах. Это как показать человеку, чем он должен заниматься: сначала говорим, какие инструменты и материалы у него есть, а потом говорим, что у него должно получиться. Представим, что этот человек в принципе такими вещами не занимался. Поэтому он будет учиться это делать. И со временем, после обучения, он сможет делать то, что у него просят.
Тут аналогично: я обучил нейросеть на простых примерах, а она теперь сама может «решать», что будет лучше, даже вводить числа з 6 знаками и больше.
peinguin
30.11.2017 09:32Я новичок в ML. Не могли бы вы рассказать почему скрытых нейронов именно 16? И как вы выбирали это значение?
SenDu Автор
02.12.2017 11:07Честно, не знаю, почему автор этой нейронной сети (я просто перевёл её на другой ЯП) выбрал именно число 16. В общем, количество скрытых нейронов подбирается экспериментально. В зависимости от желаемого результата, надо корректировать это значение.
GH0st3rs
Не знаю как у вас работает этот пример, но как минимум следующие исправления надо внести (чтобы компилятор не ругался)
SenDu Автор
Спасибо! Не внимательно переписал код, уже исправил.