Learning Machine Challenge 1

В августе 2001 года на семнадцатой международной совместной конференции по искусственному интеллекту (IJCAI 01) израильская компания Artificial Intelligence N.V. анонсировала открытый чемпионат обучающихся программ (Learning Machine Challenge). Координатором проекта выступил главный научный специалист компании и победитель Loebner Prize in Artificial Intelligence ’96 Джейсон Хатченс (Jason Hutchens).

Artificial Intelligence N.V. – частная ислледовательская компания работающая над созданием программы способной обучаться естественному языку так как это делают люди. Цель спонсорства чемпионата для компании заключалась в получении общего алгоритма обучения на основе языковых сценариев. Нужно отметить, что последнее обстоятельство зачастую упускалось из виду участниками чемпионата, что приводило в дальнейшем к разнообразным коллизиям. Срок подачи заявок был ограничен 30 октября 2001 года. Тем не менее, предложение вызвало большой энтузиазм и 94 участника успели подготовить 196 програм-игроков и принять участие в чемпионате. Итоги LMC1 были объявлены в январе 2002 года. Одновременно с этим было анонсировано проведение LMC2. Однако, к этому времени большинство участников уже разобралось, что LMC2 будет узко направленным на обучение естественному языку и, видимо, слабо веря в интеллектуальность чат-ботов, не проявило должной активности. В результате проведение LMC2 было свернуто.

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

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

Вы посылаете один из известных вам символов во внешний мир.

Вы получаете один из известных вам символов извне.

Затем вы получаете очки за сделанный ход.

Сможете ли вы научиться хорошо играть? Даже если изначально вы ничего не знаете о противнике и игре в которую будете играть? LMC1 призвано найти лучшую самообучающуюся программу способную сделать это.

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

Как это работает?

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

В начале новой игры программа-судья информирует каждую программу-участника о наборе возможных ходов. Каждый возможный ход представляется в виде строки (длиной не более 32 символов), и весь набор может включать от двух до восьмидесяти строк. Например, в одной из игр может быть только три возможных хода: "камень”, ”бумага” и ”ножницы” . Другие игры могут иметь больший набор ходов, такой как 26 строчных букв алфавита или 10 цифр (все, естественно, на английском).

В процессе игры может произойти три вещи. Ваша программа может получить ход противника через входной канал (input channel). Она может получить запрос на генерацию ответного хода на выходном канале (output channel). Она может быть информирована о полученных очках (score channel). Все эти события могут происходить в любом порядке. Так например, ваша программа может получить три хода противника подряд, запрос на генерацию двух ответных ходов и затем информацию о полученных очках. Эта последовательность зависит от конкретной игры и известна лишь программе-судье. Более того, ходы получаемые вашей программой могут, а могут и не принадлежать противнику (т.н. single-player games, например, игра repeat-after-me “повторяй за мной” где программа участник должна научиться повторять последовательность ходов сделанных противником, хотя реально никакого противника нет, ходы генерирует программа-судья).

Наглядный пример игры (RoShamBo или Камень-Ножницы-Бумага)

После сообщения о том, что набор возможных ходов состоит из трех строк “rock”, "paper", "scissors", программа-судья просит вашу программу сгенерировать ход. Без ведома вашей программы, она заставляет сделать то же самое вашего противника. Когда обе программы-участника сделали свои ходы судья посылает на вход каждой программы ход ее противника. Затем она определяет победителя используя алгоритм: "бумага бьет камень, камень бьет ножницы, ножницы бьют бумагу” и ничья в случае когда противники сделали одинаковый ход. Затем каждому участнику сообщаются полученные им очки в виде одного из чисел {-1, 0, 1} (поражение, ничья, выигрыш)(в общем случае это непрерывная величина в диапазоне –1..1). Игра состоит из многих тысяч (реально было по 3-5 тыс.) раундов, давая вашей программе возможность обучаться на своем опыте. Каждая программа, естественно, должна стремиться набрать максимальную сумму очков (забегая вперед, следует сказать, что очки не накапливаются от игры к игре. В каждой игре определяется победитель по сумме набранных очков, а победитель чемпионата определяется по числу выиграных игр).

Для участия в LMC1 ваша программа должна использовать простой API. Этот API позволяет вашей программе общаться с программой-судьей используя стандартные процедуры консольного ввода/вывода.

У такого подхода есть множество преимуществ. Он позволяет вам тестировать вашу программу напрямую, используя в качестве программы-судьи самого себя. Также, он позволяет вам использовать любой язык программирования на выбор. Наконец, это платформонезависимый подход – мы можем работать с исполняемыми модулями и Windows и Linux.

В разделе http://www.a-i.com/show_tree.asp?id=81&level=2&root=75 - Testing Zone мы предоставили вам программы-примеры написанные на различных языках . Эти программы полностью реализуют рассматриваемый API, и вы можете использовать их в качестве основы вашей программы.

Спецификация интерфейса

Ваша программа представляет собой “черный ящик” общающийся с внешним миром через несколько разных “каналов”. Эти каналы таковы:

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

@channel data

где channel – имя канала, а data – данные специфичные для каждого канала. Заметьте, что имя канала и данные разделены одиночным символом пробела, а вся строка должна заканчиваться символом EOL.

Input channel

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

[@input ]string

где string – строка без пробелов, представляющая один из множества возможных ходов.

Output channel

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

[@output ]string

где string – строка без пробелов, представляющая один из множества возможных ходов.

Score channel

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

@score number

где number – непрерывная величина в диапазоне от –1.0 до +1.0.

Command channel

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

Для начала, программа-судья может запросить корректного завершения вашей программы. Синтаксис этой команды имеет вид:

@command exit

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

Программа-судья может сообщить вашей программе о начале новой игры с новым противником. Синтаксис этой команды имеет вид:

@command new

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

Программа-судья может информировать вашу программу о наборе возможных ходов, используя команду symbol. Синтаксис этой команды имеет вид:

@command symbol string

где string – строка не содержащая пробелов, представляющая один из возможных ходов в данной игре.

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

@command play

Когда ваша программа получает команду play, она должна послать один известных ей символов на свой выходной канал, используя синтаксис: [@output ]string.

Info channel

Информационный канал позволяет вашей программе посылать сообщения программе-судье. Имеется всего два типа таких сообщений и мы рассмотрим их одно за другим.

Сообщение exit говорит программе-судье, что ваша программа собирается завершиться. Синтаксис этого сообщения имеет вид:

@info exit

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

Сообщение name информирует программу-судью о том под каким именем будет выступать ваша программа (это имя не обязательно должно совпадать с именем исполняемого файла). Синтаксис этого сообщения имеет вид:

@info name string

где string – имя вашей программы (не должно содержать пробелов). Ваша программа должна проинформировать программу-судью о своем имени прежде чем делать что-либо другое.

Комментарии

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

Предупреждение

Так как рассматриваемый API является консольно-ориентированным, ваша программа не должна использовать консольный ввод/вывод в каких-либо других целях! Несоблюдение этого правила может нарушить работу программы-судьи!

Спецификация прведения турниров

Программа-судья проводит турнир, общаясь с соревнующимися программами-участниками через описанный выше консольный API. При этом она следует определенному сценарию:

  1. Когда программа-судья запускает вашу программу в первый раз, она ждет от вашей программы сообщения о ее имени (@info name string). Ваша программа должна послать это сообщение в течение 10 секунд с момента запуска.
  2. Программа-судья выдает команду new, начиная новую игру.
  3. Программа-судья передает полный набор возможных ходов данной игры вашей программе используя @command symbol string. Каждая такая команда посылает одну строку представляющую один из возможных в данной игре ходов. Программа-судья должна передать как минимум два возможных в игре хода. После этого начинается игра (новые возможные ходы не могут вводиться по ходу игры).
  4. Программа-судья запрашивает вашу программу о ее ходе, либо посылает вашей программе ход противника, либо информирует ее о получаемых очках.
  5. Четвертый шаг данного сценария может повторяться много раз, давая вашей программе возможность обучаться на своем опыте. Например, вашей программе может быть пердложено сделать два хода прежде чем она получит очки, а затем она может получить три хода противника подряд, прежде чем сделать ход самой.
  6. Когда игра закончена, программа-судья может инициировать новую игру начиная с шага 2 данного сценария, либо завершить вашу программу командой exit.

Пример турнира

Мы продемонстрируем API и сценарий проведения турнира на примере работы программы-судьи в игре Камень-Ножницы-Бумага. Для ясности, мы покажем ответы лишь одной из программ-участников, полагая, что вторая постоянно делает ход "rock".

Judge Program

Player Program

#Judge Program Ready

#Player Program Ready

 

@info name SimpleLearner

@command new

 

@command symbol rock

 

@command symbol paper

 

@command symbol scissors

 

@command play

 
 

@output scissors

@input rock

 

@score -1

 

@command play

 
 

@output rock

@input rock

 

@score 0

 

@command play

 
 

@output paper

@input rock

 

@score 1

 

@command play

 
 

@output paper

@input rock

 

@score 1

 

@command exit

 
 

@info exit

Мы должны подчеркнуть, что приведенная последовательность (программа-судья просит игрока сделать ход, посылает ему ход противника и затем полученные очки) является специфической для игры Камень-Ножницы-Бумага. Другие игры могут работать по-другому (в частности, очки выдаваемые программе-участнику могут зависеть не только от того что произошло с момента предыдущей раздачи очков, но и от более далеко отстоящих событий ).

Возможно вас повергло в уныние приведенное описание интерфейса, которому должна следовать ваша программа. Не отчаивайтесь, по ссылке http://www.a-i.com/data/Challenge.zip находится zip-архив с примерами игроков, для одного из которых (SmartStupid) имеется полная реализация интерфейса на C. Наконец, могу предложить вам свою реализацию. Все что вам останется сделать это:

  1. Вписать имя под которым будет выступать ваша программа.
  2. Написать обработчик события OnInput (происходит когда программа получает ход противника)
  3. Написать обработчик события OnScore (происходит когда программе сообщается о полученных ею очках)
  4. Написать обработчик события OnCommandPlay (ядро вашей программы, здесь вы должны совершить ход по требованию программы-судьи).
  5. Написать обработчик события OnCommandNew (начинается новая игра, подготовьтесь!)
  6. Написать обработчик события OnCommandExit (вас просят выйти, уберите за собой).

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

  1. Comment(char* format,...) – послать комментарий, просто как printf (снабжать строку лидирующим символом # не надо, это делается автоматически).
  2. Exit() – завершить программу, функция сама уведомляет программу-судью о завершении и сама завершает вашу программу предварительно вызвав обработчик события OnCommandExit.
  3. Output(char* symbol) – послать ваш ход программе-судье.

В качестве парметра последней функции вы должны передать строку, но не беда если вы храните ходы в виде индексов массива возможных ходов. Две функции работы с этим массивом (алфавитом) GetSymbolByIndex(int index) и GetIndexOfSymbol(char* symbol) позволяют легко переводить одно в другое. И наконец функция алфавита GetSize() позволяет узнать его размер (количество возможных ходов в игре).

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

Игры и LMC

Необходимо понимать, что правила LMC1 лишь стандартизируют способ взаимодействия игровых самообучающихся программ. Эти правила вообще ничего не говорят о самих играх. Мы можем полагать, что для проведения LMC подойдут любые игры, которые могут быть естественно выражены с помощью стандартного интерфейса LMC. Этот интерфейс позволяет естественным образом реализовать любой игровой процесс представляющий собой не более чем последовательный поток событий. Поясню, что я имею ввиду на примере. Так рассмотренная игра Rock-Paper-Scissors полностью соответветствует интерфейсу LMC. Действительно, в этой игре нет никаких игровых объектов с их признаками, состояниями и т.д. в ней просто происходят некоторые события. Не такова игра TicTacToe (крестики-нолики на поле 3x3). Хотя ее и можно реализовать с помощью интерфейса LMC (что и было сделано организаторами), это выглядит противоестественным. Набор возможных ходов в этой реализации состоит из трех строк: "1", "2", "3". Игроку постоянно предлагается сделать два хода подряд, которые интерпретируются программой-судьей как X и Y координаты игрового поля соответственно. Трудно представить себе более противоестественный протокол для этой игры чем интерфейс LMC. Мало того, что нарушено заявление организаторов о "бессодержательности” символов представляющих возможные ходы (как видим это множество чисел с метрикой на нем, что абсолютно не доступно пониманию игрока), игрок еще и никак “не видит” игрового поля. В силу ограниченности интерфейса, он не может судить о близости фишек на игровом поле и образуемых ими фигурах, не может даже понять занята ли определенная ячейка игрового поля или свободна. Представьте себя на месте игрока играющего в эту игру по протоколу LMC. Пусть для полноты картины набор возможных ходов представлен тройкой "rock", "paper", "scissors". Сомневаюсь, что даже человек с его интеллектом смог бы научиться хорошо играть в эту простейшую игру посредством данного интерфейса.

LMC и ИИ

Не смотря на скудность интерфейса, LMC, на мой взгляд, представляет интерес для исследований в области ИИ. Мы, конечно не сможем создать в данном формате некоторую, пусть и простую но автономную (реализующую все интеллектуальные функции живых организмов) систему, но сможем глубже исследовать одну из сторон деятельности интеллекта. А именно, способность находить и использовать закономерности в одномерном потоке данных. Средствами интерфейса LMC эту задачу можно еще и редуцировать до чистого прогнозирования. То есть, оценки можно выдавать не за победу в какой-то игре а за правильное предсказание действий противника. Наконец, ничто не мешает нам нарастить возможности интерфейса (о чем ниже) и перейти к работе с многомерным потоком данных.

Возможные расширения интерфейса LMC

Возвращаясь к рассмотренным проблемам интерфейса в реализации игры TicTacToe, можно наметить пути их решения. Мы могли бы сообщать игроку в начале игры не только о возможных ходах, но и о существовании некоторых факторов, влияющих на ход игры. Фактор – это переменная, которая изменяет свое значение в зависимости от некоторых игровых событий и влияет на последующие игровые события. Для рассматриваемой игры такими факторами могли бы служить 9 переменных принимающих зачения {0, 1} и символизирующих занятость той или иной ячейки игрового поля. Хотя это не решает всех проблем интерфейса, но может существенно помочь игроку (в реализации TicTacToe за каждую попытку сделать ход в занятую ячейку игрок штрафуется на одно очко) и несколько приблизить интерфейс к естественному для данной игры. С введением факторов, учитывая их возможную взаимозависимость, мы переходим к двумерному потоку данных. Образно выражаясь, при прогнозировании и принятии решения, нам придется смотреть не только назад (в предысторию), но и по сторонам (на текущие значения факторов). Факторы полезны и необходимы в любой ситуации когда некоторое событие вызывает изменение состояния системы способное повлиять на дальнейшее развитие событий. Являясь сторонником той мысли, что не следует требовать от системы ИИ решения задач с которыми не справляется, или справляется другими средствами, естественный интеллект, я отвергаю возражения о том, что в факторах нет необходимости т.к. мол можно просто запомнить событие изменяющее состояние системы и учитывать этот факт в дальнейшей деятельности. По утрам мы не запоминаем что наступил день, в течение дня мы просто постоянно видим что "он есть".

Другим полезным, на мой взгляд, расширением интерфейса LMC мог бы служить переход от не очень выразительного представления возмож&#