Пыталась написать алгоритм для обучения бота, который выбирает лучшее будущее состояние игры и "ходит" в него.
Алгоритм выглядит примерно так:
1. сыграть N партий, ошибаясь с шансом eps.
2. каждую партию, записывать как две последовательности состояний от лица двух игроков т.е. в одной последовательности чётные номера, в другой - нечётные. (конечное состояние "переворачиваю" и кладу в противоположный от списка с обычным конечным состоянием список).
3. для каждого состояния вычисляю соответствующее значение v'[i] = v[i] + alpha*(r[i] + gamma * v[i+1] - v[i]), где v = оценка состояния с помощью встроенного в бота нейронки, а r[i] - награда, соответствующая состоянию.
4. учу нейронку, как обычную feed-forward модель с помощью небольшого (порядка 10) количества итераций градиентного спуска по функции потерь (v - v')^2.
5. очищаю список записей партий.
6. повторяю большое количество раз .
Для игры "ручки" (у тебя есть N ручек, можешь взять 1/2/3 и передать ход сопернику, если возьмёшь последнюю - проиграл) данный алгоритм работал вполне разумно. Но когда я попыталась применить ту же логику для крестиков-ноликов, то ничего не вышло. Пробовала в качестве моделей перцептроны с 2 или 1 скрытыми слоями (на вход всем подаются значения +-1 или 0 в зависимости от клетки) и другие. В качестве нелинейной функции использовала leak relu, который "загнут" в x=+-1, т.к. нейронка должна стремиться выдавать значения +-1, соответственно. Да, спустя действительно огромное количество партий бот начинает сводить в ничью при игре против меня, но он не смог доучиться до состояния, при котором бы 5 партий подряд сыграл бы в ничью.
В чём может быть проблема? В самом алгоритме? В неправильно настроенных параметрах обучения? В архитектуре нейронки?
P.S. использую c++ для всего этого, поэтому лучше приводить в пример псевдокод.
1. Функция активации всегда нелинейная (поэтому leak relu должен быть ломаной, не должен быть простой прямой).
2. Активации разных слоев могут быть разными - в скрытых слоях leak relu, в финальном - сигмоида
3. Один скрытый слой мало. Пробуйте 2-3 хотя бы.
4. Пробуйте разные скорости обучения (0,001)
5. Число итераций градиентного спуска может и побольше нужно делать.
А точно всё это нужно самостоятельно писать на C++? Может лучше взять готовые реализации библиотек обучения (PyTorch, tensorflow)? Проект ONNX https://ru.wikipedia.org/wiki/ONNX позволяет экспортировать модели (но сам я пока не попробовал). Так же никто не запрещает из программы на C++ вызывать питоновский скрипт (как внешнюю программу).
1. Само собой, пробовала leak relu двух видов: с одним углом и двумя (на 0 и 1)
2. Тоже пробовала
3. И это тоже, вроде, но с увеличением числа скрытых слоёв ситуация лучше не становилась, вроде
4. Тоже пробовала, в основном как раз и брала 0.001
5. Возможно и так, но на более серьёзных задачах боюсь представить, сколько лет тогда надо будет обучать
Писала на c++, т.к. питон не знала. Не знаю и до сих пор, по крайней мере, не знаю, как на нём собирать проекты для запуска на других устройствах при условии, чтобы итоговый проект файл не весил >100мб. А с плюсами ситуация попроще, хоть и не знаю их полностью до сих пор, программы на них хоть научилась собирать нормально + уже освоила графические библиотеки под них.
Экспорт моделей может и помог бы, но он не подходит в случаях, когда модель надо будет доучивать уже при работе программы. Да и вызывать питоновский скрипт тоже смысла немного - тогда уже вообще весь проект проще будет на нём написать.