/ml_hw_ai

Домашнее задание по курсу "Машинное обучение ч.2" тема AI

Primary LanguagePython

Домашнее задание по курсу "Машинное обучение", тема: AI

Предлагается реализовать агента для простой многопользовательской игры с другими агентами. Это вариант многораундовой ультимативной игры.

По всем вопросам, связанным с этим заданием, можно обращаться к Алексею Пшеничному: slack:@izhleba, email: izhleba@gmail.com.

Правила игры

Для игры выбирают двух случайных участников. Дальше выбирают в паре участника (proposer), которому дают возможность сделать ультиматум. Например, есть 100 долларов/очков/леденцов. Он (proposer) должен разделить эти плюшки в каком-то соотношении. К примеру, 100 себе и 0 второму участнику (responder). Или 70 себе и 30 второму участнику. Второй участник (responder) может согласиться на этот ультиматум. Тогда произойдёт сделка, каждый получит в соответствии с предложенным разбиением. Либо может отказаться. Тогда оба получают 0.

Проводится множество раундов, после которых сравниваются статистики набранных долларов/очков/леденцов и распределяются места. Набравшему больше всех — первое и т. д.

Метрика успеха

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

Если gains - это массив заработков размера n, то score = (C*m + sum(gains))/(C+len(gains)) где C будет равно 18 (возможны корректировки в будущем), m будет равно 50 (средне значение для примитивного агента).

Использование песочницы

По адресу https://ultimategame.ml/ находится песочница. Сюда можно отправить решение, и посмотреть результаты. С некоторой периодичностью там проходят игры, на момент написания каждые 15 минут. Решения принимаются через Docker-контейнеры, об этом будет написано ниже.

Регистрация

  1. Зайдите на сайт https://ultimategame.ml/ нажмите Login в правом верхнем углу, затем Register.
  2. Укажите необходимые поля (пожалуйста, заполните настоящие имя и фамилию).
  3. На указанный email придёт письмо с активационной ссылкой, пройдите по ней.
  4. После активации можете заходить со своим логином и паролем.

Отправка решения

  1. Зайдите в Submissions и кликните на Make submission
  2. В открывшейся форме в поле Dockerhub image вставьте адрес вашего Docker образа. Адрес указывается без http префикса и выглядит как <username>/<image_name>:<tag>. О способе создания докер образа смотри инструкцию ниже.
  3. В очередной игре участвует самое последнее отправленное решение. Возможно позже появится возможность управлять этим.
  4. Список своих отправленных решений можно увидеть пройдя во вкладку Submissions list.
  5. Поле Pull Status показывает на каком этапе сейчас находится решение.
    • 'Waiting' - ожидается загрузка при следующей игре;
    • 'Downloaded' - образ успешно загружен;
    • 'Downloading error' - не удалось загрузить образ с указанного адреса в dockerhub;
    • 'Exited to fast' - контейнер завершил работу сразу после старта;
    • 'Wrong launch' - контейнер запустился, но работает не корректно;
    • 'Ok' - контейнер корректно отработал игру.
  6. После успешного участия в очередной игре в поле Last Score будет сохранено последнее значение score.
  7. Общую таблицу по последней игре можно найти во вкладке Leaderboard или на главной странице.

Создание своего агента

Простой путь

Для вашего удобства создан класс agent.my_agent.MyAgent, в котором вам нужно написать код агента. Необходимо, чтобы метод offer_action возвращал размер предложения для другого агента, а deal_action результат согласия или несогласия. Метод round_result_action позволяет узнать результаты раунда. Более подробное описание методов и структур данных представлено ниже.

Код шаблона агента:

from agent.base import BaseAgent
from base.protocol import OfferRequest, DealRequest, RoundResult

class MyAgent(BaseAgent):

    def offer_action(self, m: OfferRequest) -> int:
        return 1

    def deal_action(self, m: DealRequest) -> bool:
        return True

    def round_result_action(self, data: RoundResult):
        pass

Структура класса

Любой агент должен наследовать методы от базового класса agent.base.BaseAgent. Описание этих методов:

  • def offer_action(self, data) возвращает размер вашего предложения (int) некоторому агенту в раунде. Данные об агенте и общем размере разделяемых средства находятся в data. Структура класса описана здесь: base.protocol.OfferRequest. Размер вашего предложения offer это то, сколько вы предлагаете оставить другому агенту, в случае принятия предложения, ваша награда будет равна total_amount - offer.
  • deal_action(self, data) возвращает ответ (bool): согласны ли вы на предложение от некоторого агента. Данные об агенте и о размере предложения находятся в data структура класса описана здесь base.protocol.DealRequest
  • def round_result_action(self, data: RoundResult) метод вызывается по окончании раунда, сообщает общие результаты в data; описание: base.protocol.RoundResult.

Структуры данных и их поля

OfferRequest

round_id: int - номер раунда
target_agent_uid: str - идентификатор агента, которому будет отослано предложение
total_amount: int - общее количество, которое необходимо разделить

DealRequest

round_id: int - номер раунда
from_agent_uid: str - идентификатор агента, от которого поступило предложение
total_amount: int - общий размер разделяемых средств
offer: int - предложение от агента

RoundResult

round_id: int - номер раунда
win: bool - True если раунд успешен и предложение принято, в противном случае False
agent_gain: Dict[str, int] - ассоциативный массив с размерами наград, ключ - идентификатор агентов раунда
disconnection_failure: bool - флаг показывает, что во время раунда произошел дисконнект одного из участников

Базовый тест агента

Когда вы создадите агента, перед сабмитом стоит провести базовый тест.

python3 agent_test.py

Если вы получите Base test successfully completed!, значит основные требования к методам выполнены. Можете сабмитить результат.

Сабмит агента

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

Предварительная подготовка

  1. Прежде всего вам нужно установить Docker. Здесь можно найти дистрибутивы для Mac/Win/Linux: https://www.docker.com/get-started

  2. После установки зайдите в терминал и наберите docker run hello-world. Если все пройдет успешно, вы увидите:

Hello from Docker!
This message shows that your installation appears to be working correctly.
  1. Далее вам необходимо зарегистрироваться на https://hub.docker.com/signup. Далее оно будет использоваться как облачное хранилище ваших контейнеров. Запомните login и password далее: они будут использоваться вам еще раз.

  2. После регистрации зайдите в терминал и наберите docker login. Далее введите login и password с которым вы зарегистрировались на Docker Hub. Если все пройдет успешно, вы увидите:

Login Succeeded

Процесс сабмита

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

  1. Сделайте docker login под вашим login в Docker Hub (далее будем обозначать его как dockerhub_login).
  2. Создайте образ docker build -t <dockerhub_login>/ai_agent -f Dockerfile . (не потеряйте точку в конце). Вместо <dockerhub_login> вставьте свой login от Docker Hub. Обратите внимание, образ будет сделан даже без <dockerhub_login>, но сделать push этого образа вы уже не сможете. В случае успеха вы увидите в конце:
Successfully built <идентификатор сборки>
Successfully tagged <dockerhub_login>/ai_agent:latest
  1. Сделайте push в Docker Hub docker push <dockerhub_login>/ai_agent. В случае успеха вы сможете зайти на Docker Hub и увидеть этот контейнер среди своих в списке.

  2. Воспользуйтесь инструкцией по сабмиту решения через песочницу.

Общие правила, рекомендации и советы

  1. Версия python по умолчанию 3.7
  2. Запрещается менять client.py, делать запросы в Интернет и вообще любые манипуляции с сетью или протоколом.
  3. Для вашего удобства проще всего разместить весь код агента в том же файле, где и основной класс, и использовать библиотеки NumPy, scikit-learn. Однако вы можете поместить в контейнер другие необходимые файлы и библиотеки с учетом того, чтобы контейнер не превысил размеры в 500 Mb. Библиотеки добавляются через requirements.txt.
  4. В качестве идентификаторов агента используются строковые представления UUID. После того как подключатся все агенты, сервер оповестит вас о вашем uid, его можно будет узнать из поля agent_id. Обратите внимание: после инициализации класса (вызова __init__) это поле будет None до оповещения от сервера. Однако гарантируется что agent_id будет доступен, когда будут вызываться методы offer_action, deal_action и round_result_action.
  5. Если агент не отвечает более, чем 2 секунды, например из-за долгого цикла обработки в offer_action, он удаляется из обработки. Никакие дальнейшие его сообщения не обрабатываются.
  6. Если нужна длительная инициализация, проведите ее в методе __init__, так как сервер будет одну минуту ждать инициализации всех агентов.
  7. Агент должен не упасть с ошибкой или не дисконнектнуться хотя бы в 1 % раундов, чтобы его очки учитывались.

Следите за обновлениями новостей на сайте и в Slack: могут быть скорректированы правила или добавлены утилиты для повышения удобства.

Дополнительные материалы

Примеры агентов

В папке agent можно найти несколько примеров агентов. Ниже представлены некоторые из них.

Агент без памяти

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

class DummyAgent(BaseAgent):

    def offer_action(self, m):
        return m.total_amount // 2

    def deal_action(self, m):
        if m.offer > 0:
            return True
        else:
            return False

    def round_result_action(self, data):
        pass

Общий алгоритм использования агента в песочнице

  1. Клиент создается через __init__. После создания отсылает серверу сигнал готовности.
  2. Сервер некоторое время ждёт участников и начинает серию раундов.
  3. Случайным образом выбираются пары агентов: один proposer, другой responder.
  4. Сервер спрашивает у proposer размер его предложения (вызывается метод offer_action) и отсылает результат к responder.
  5. У responder вызывается метод deal_action, ответ отсылается на сервер, который решает исход раунда.
  6. В случае окончания раунда, либо дисконнекта одного из участников, сервер отсылает результат агентам и у них вызывается round_result_action.
  7. Это повторяется много раз, пока не наберется достаточная статистика по результатам игр агентов.
  8. Классы агентов всё это время находятся в памяти клиентов и могут хранить состояние предыдущих раундов.

Продвинутая сборка образов

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

docker build --build-arg agent_cls_path=agent.dummy.DummyAgent -t <dockerhub_login>/ai_agent -f Dockerfile .

Если нужны дополнительные библиотеки в сборке, добавьте их в файл requirements.txt и сделайте ребилд с флагом --no-cache.

Посмотреть локальные образы можно командой docker images удалить через docker rmi <имя или идентификатор>

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

Можно использовать тэги для образов, подставив в конце :<тэг>. Таким образом становится легче следить за версиями. Пример:

docker build -t <dockerhub_login>/ai_agent:v1 -f Dockerfile .

При сабмите используйте адрес вместе с тэгом.