Как обучить нейронную сеть для игры в «Крестики-нолики»?
Написал сеть, которая играет в "Крестики-нолики". Встал вопрос - как обучить? Первое что попробовал - рандом. Поставил ее против рандома 70% выигрышей 10% ничьей. Попробовал сыграть против обученной таким способом сети сам - по сути выучила она как следует только правила игры. Напролом пытается выстроить ряд из трех ноликов, при этом абсолютно не замечая мои ходы.
Это никуда не годится. Поставил играть друг против друга две сети - 100% ничьи. Стал наказывать за ничьи как за проигрыши - 50\50 побед при любом числе прогонов. Решил попробовать сначала обучить сеть правилам игры с помощью рандома, а потом поставить играть против другой сети точно так же обученной - в результате либо 50\50, либо с определенным шансом одна сеть обгоняет другую в самом начале и выигрывает 100% игр. Я пробовал играть с сетью после обучения каждым из этих способов - играют плохо(
Как же можно программно обучить такую нейронную сеть? Играть с ней самому либо вручную забивать все комбинации - не для этого я учился писать код. Обучить как-то должно быть возможно, ведь, как известно, правильно действуя в крестиках ноликах, можно получить 100% непроигрышей. А 80% против жалкого рандома - это как-то не айс :\
По сути - самая простая нейронная сеть. Два слоя по кол-ву клеток каждый (т.е. по 9 нейронов в каждом) в первом слое все нейроны по умолчанию не выдают никакого сигнала, если противник сети ставит туда метку - то нейрон начинает выдавать 1. С каждого нейрона первого слоя информация поступает на входы всех нейронов второго слоя. Нейроны выходного (второго слоя) соответствующие клеткам в которых уже стоит метка искусственно блокируются, если в этой клетке стоит метка противника - нейрон всегда будет выдавать 0, независимо от входа, если метка поставленная самой сетью - всегда 1. Так же информация с каждого выходного нейрона поступает на входы всех остальных выходных нейронов, при этом при пересчете сети если выходной нейрон не заблокирован то информация с него игнорируется. Все веса изначально равны 10, шаг обучения - 0.01. При пересчете нейрона все ненулевые сигналы поступившие на вход (программно это представлено отдельным объектом хранящим ссылку на другой нейрон с которого надо взять сигнал и текущий вес связи) сохраняются. Если сигнал с этого нейрона оказывается максимальным (сигналы с заблокированных нейронов при сравнении игнорируются) то сеть возвращает его номер как принятое решение и сохраняет сохраненные в нем сигналы. В конце раунда мы сообщаем нейронной сети о результате и она в соответствии с ним увеличивает или уменьшает веса все сохраненных сигналов на шаг обучения. Как то так.
Крестики-нолики не интересная задача, так как число состояний очень мало и фактически их с ручкой и бумажкой можно легко все составить. Получится вход - текущее состояние, выход - куда и чем походить. Тут даже нейросеть не нужна. И потом, любую игру можно свести к ничьей, что объясняет результат, полученный при игре одной НС с другой.
Вот, думаю, с нардами или шашками будет куда интереснее. Или крестики-нолики на большом поле, где нужно N крестиков/ноликов поставить в ряд хотя бы.
@opium@Fesor@Funbit Я знаю что главное их предназначение -распознавать картинки. Но я узнал про эту тему и сразу идти в сложные задачи не захотел, кроме того там есть полно библиотек которые инкапсулируют все работу сети. Я же хотел сам написать сеть и что называется собственными глазами увидеть процесс ее обучения. Тут задача была не "как бы решить крестики нолики?" а "что бы простенькое придумать для нейронной сети?" в комментариях к вопросу описал конфигурацию.
@opium Кстати что вы имеете ввиду "от текущего времени"? Если вы имеете ввиду сам объект представляющий рандом - он один, каждый раз когда требуется сделать ход я запрашиваю у него числа до тех пор пока не выдаст номер любой из пустых клеток.
@saroff как раз таки распознование картинок, кластеризация, восстановление изображений от шумов, это то, что можно легко сделать на нейронках. Как минимум потому что вопрос хорошо освещен в литературе и т.д. С играми и т.д. все несколько сложнее ибо информации намного меньше. Так что думаю вам все же стоит с картинок начать. В крайнем случае со звука, это все же чуть сложнее.
Так же вы должны понять, что нейронные сети... они разные бывают. Вам стоит начать с задачи и методов решения, а уже есть писать под вариант решения.
С точнки зрения программирования нейронные сети очень простые, и все сводится к простым математическим выражениям, так что привязки особой к языкам программирования либо не будет, либо не стоит этого так уж сильно бояться.
Для начала попробуйте реализовать перцептрон, разобраться как он работает. Затем можете поковырять сети с другими конфигурациями. Так же погуглите на тему задач прогнозирования, по идее вам туда надо копать.
Так что за топология-то у нейросети? Разные сети обучают по-разному. Вангую, что 3-4 слойная сеть справится с задачей. Обучить можно ОРО (обратное распространение ошибки). Наверное.
Вы не обучите нормально такую сеть, поскольку не учитываются взаиморасположения крестиков и ноликов. Добавьте скрытый слой хотя бы с 1 нейроном (желательно не персептроном), что-бы получить более приемлимый результат.
Также советую заменить на выходе персептроны на что-либо другое, чтобы компьютер мог выбрать куда походить лучше. Сейчас у вас, как я понял, просто стоит 1 там, где можно походить.
Почему же не учитываются? На каждый нейрон выхода поступает 1. Информация с первого слоя которая говорит о том, где стоят вражеские метки 2. Информация с этого же слоя, с помощью которой он знает, где стоят дружеские метки. По сути каждый нейрон выхода имеет полную информацию о состоянии поля на данный момент.
На выходе не единичка. На выходе рассчитываются сигналы нейронов путем сложения всех сигналов подающихся на вход, помноженных на веса (каждый на свой) потом сеть находит максимальный сигнал на выходе, при этом игнорируя заблокированные нейроны, и выносит решение поставить туда метку.
@saroff состояние поля - это хорошо, но не забывайте, что игра должна стремиться к победе и поле у вас расположено не в ряд, а квадратом. Вам нужна информация о поле немного другого рода, а именно состояния соседних клеток для каждой из них. К тому же, наравне со стремлением к выигрышу, сеть должна стремиться не проиграть, что порой важнее.
Все же советую добавить промежуточный.
@Iworb Я просто не совсем понимаю как должен быть организован промежуточный слой.
А в принципе, ну и что, что в ряд? По идее же при обучении она должна обучится, что если вот здесь и здесь стоят метки противника, нужно поставить сюда, иначе проигрыш. Нейронной сети, как мне по крайней мере кажется, совсем не обязательно знать как именно клетки расположены на плоскости. Может быть мы решим извратится и сыграть в что то похожее на крестики нолики, но с клетками в ряд Оо. В любом случае в процессе обучения даже такая сеть вполне самостоятельно способна запомнить это. Что если пришли сигналы от вот этого и вот этого нейронов, то она может поставить метку вот сюда и выиграть, какая для нее разница, где эти три клетки будут находится?
@saroff Если идете полным перебором всех возможных комбинаций, то 1) конечно научится, но 2) а зачем тогда она нужна? Ваших 1 и 0 на входах/выходах мало, если вы хотите по ограниченному набору заставить программу "думать".
Скрытый слой нужен именно для принятия таких неявных решений, как "стремиться победить" и "стремиться не проиграть". Для каждой задачи обычно подбирают свое наилучшее количество нейронов, скрытых слоев и активационные функции. Скрытый слой практически ничем не отличается от входного/выходного, можно даже связи такие-же задать и поставить тоже 9 нейронов. Однозначно, попытаться стоит. И да, если у вас булева активационная функция, то советую ее заменить на logsig.