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

Данные, используемые в этом руководстве, были созданы Робертом Каттралом и Францем Оппахером и размещены в репозитории машинного обучения Калифорнийского университета в Ирвине: https://archive.ics.uci.edu/ml/datasets/Poker+Hand

Чтобы неявно обучать правилам покерной руки, мы просто введем таблицу различных комбинаций из 5 карт и соответствующую метку типа покерной руки (например: Royal Flush или 4 вида). Это будут наши тренировочные данные.

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

Авторы набора данных были достаточно любезны, чтобы уже разделить данные на наборы для обучения и тестирования. Разделение этих данных на тренировку / тест было проведено очень осторожно, потому что классы (тип покерной руки) невероятно несбалансированы. Фактически, более 50% всех перестановок 5 карт из колоды вообще не являются покерной комбинацией. С другой стороны, Royal Flush представляет только 0,000154% возможных перестановок. Если бы была взята случайная выборка всех возможных рук, мы могли бы упустить редкие классы в обучающих данных, что сделало бы невозможным когда-либо предсказать эти классы.

Примечание: существует ровно 311 875 200 перестановок 5 карт из колоды. Мы будем работать с (относительно) небольшой репрезентативной выборкой из всей области.

В документации к набору данных приводится следующая разбивка как обучения, так и тестирования в зависимости от типа покерной руки:

Учебный набор (25010 рядов):

- 0: ничего нет, 12493 экземпляра (49,95202% / 50,117739%)
- 1: одна пара, 10599 экземпляров, (42,37905% / 42,256903%)
- 2: две пары, 1206 экземпляров, ( 4,82207% / 4,753902%)
- 3: три одинаковых, 513 экземпляров, (2,05118% / 2,112845%)
- 4: прямые, 93 экземпляра, (0,37185% / 0,392465%)
- 5: флеш, 54 раз, (0,21591% / 0,19654%)
- 6: фулл-хаус, 36 раз, (0,14394% / 0,144058%)
- 7: каре, 6 раз , (0,02399% / 0,02401%)
- 8: Стрит-флеш, 5 раз, (0,01999% / 0,001385%)
- 9: Роял-флеш, 5 раз, (0,01999% / 0,000154%)

Примечание: проценты в круглых скобках представляют (пропорция в наборе данных / пропорция во всех перестановках карточек)

Набор для тестирования (1000000 строк):

- 0: ничего нет, 501209 экземпляров, (1.000063)
- 1: одна пара, 422498 экземпляров, (0,999832)
- 2: две пары, 47622 экземпляра, (1,001746)
- 3: три одинаковых, 21121 экземпляров, (0,999647)
- 4: прямые, 3885 экземпляров, (0,989897)
- 5: Flush, 1996 экземпляров, (1,015569)
- 6: Фулл-хаус, 1424 раз, (0,988491)
- 7: Каре, 230 раз, (0,957934)
- 8: Стрит-флеш, 12 раз, (0,866426)
- 9: Роял флеш, 3 экземпляра, (1.948052)

Примечание: авторы набора данных решили отображать соотношение относительных пропорций в виде чисел для набора для тестирования, но они имеют то же значение, что и в описаниях данных для обучения.

Примеры стрит-флешей и флеш-роялей чрезмерно представлены в обучающих данных. Даже один случай Royal Flush из 25 010 записей был бы чрезмерным, но нам нужно несколько разных случаев, чтобы вообще предсказать любой. Позже мы увидим, как это повлияет на результаты.

Начиная

Я начал этот проект, используя язык программирования python в Jupyter Notebook, но в итоге использовал записную книжку google colab. Самое замечательное в Google Colab то, что любой, у кого есть учетная запись Google, может начать кодировать прямо сейчас. Вы можете использовать практически любое устройство (Windows / Mac / Android) и обрабатывать свой ноутбук в облаке. Чтобы познакомиться с google colab, посетите: https://colab.research.google.com/notebooks/welcome.ipynb#

Как только вы войдете в свой блокнот (Jupyter или google colab), импортируйте следующие библиотеки. Я отметил версии, которые у меня в настоящее время есть в Google Colab, в конце этого руководства.

Импортировать данные

Следующим шагом является использование pandas для импорта файлов данных с расширением «.data», но проверка с помощью текстового редактора показывает, что они, по сути, представляют собой файлы значений, разделенных запятыми (csv). Функция pandas read_csv () импортирует их как фреймы данных (таблицы).

После импорта каждого файла мы разделили столбцы предикторов (масти и значения для каждой из 5 карт) на наши переменные X и столбец меток (тип покерной руки) на наши переменные Y.

Пример данных:

Данные обучения и данные тестирования имеют одинаковый формат (столбцы) с разным количеством строк.

Столбцы с 0 по 9 представляют масти и значения каждой карты в шаблоне «Костюм», «Значение», «Костюм», «Значение» и т. Д.

  • Масти - от 1 до 4, представляющие: червы, пики, бубны, трефы.
  • Значения - от 1 до 13, представляющие: Туз, Двойка, Тройка,…, Дама, Король.

Столбец 10 - это код типа покерной руки, как показано ранее.

Построение и обучение нашей модели

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

Этот поиск привел к следующему руководству, предоставленному Tensorflow, которое я бы рекомендовал попробовать после того, как вы его закончите: https://www.tensorflow.org/tutorials/keras/basic_classification

Модель относится к типу Последовательная, что означает, что один слой последовательно переходит в следующий. Моя первоначальная модель была по сути копией модели, показанной в приведенном выше примере. Однако он не показал хороших результатов при классификации всех типов покерных комбинаций (даже несмотря на то, что он мог очень хорошо идентифицировать классы «Ничего в руке» и «Одна пара»). Я решил добавить слой в середине из 64 нейронов, что привело к тому, что модель работала значительно лучше с большинством типов покерных рук.

Каждый слой в этой конкретной модели использует тип Плотный (что означает, что между каждым слоем есть плотно связанные нейроны). Первые два уровня используют функцию активации выпрямленного линейного блока ReLU, которая по существу эквивалентна функции f (x) = max (0, x). Первый слой состоит из 128 нейронов, которые затем передаются слою из 64 нейронов.

Последний слой содержит 10 нейронов, которые активируются с помощью функции softmax . softmax преобразует действительные числа в нормализованные вероятности, так что результат из 10 чисел дает в сумме 1 (100%). Мы можем выбрать самую высокую вероятность в качестве предсказанного класса (типа покерной руки) для каждого элемента данных (набор из 5 карт).

Наконец, мы компилируем модель и выбираем оптимизатор и функцию потерь. В этом руководстве я не буду углубляться в оптимизатор, а лишь скажу, что использовал adam, который является популярным вариантом. Функция потерь была выбрана как sparse_categorical_crossentropy. Это предпочтительная функция потерь, когда выходной класс помечен целочисленным кодом, как в наших данных. Мы можем рассматривать потери как меру того, насколько хороша модель. Например, если бы мы выполняли модель линейной регрессии, наша функция потерь была бы суммой квадратных ошибок (∑ (y_predicted - y_actual) ²).

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

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

Готово, готово, впору…

После интенсивных тренировок…

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

Данные тестирования

Этот шаг требует только одного интересного метода для прогнозирования типов покерных комбинаций для каждого элемента данных в тестовых данных. После этого мы выполняем небольшие манипуляции с фреймами данных pandas, чтобы улучшить результат и создать таблицу ниже.

В результате чего:

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

Но насколько неправильными были ошибки?

Мы можем взглянуть на таблицу наших прогнозов по сравнению с реальными покерными руками в тестовых данных, используя функцию confusion_matrix () в sci-kit learn. Функция принимает фактические значения y и предсказанные значения y и создает массив подсчетов. Затем мы очищаем и маркируем массив, нанося его на график с помощью функции heatmap () seaborn.

И получаем…

Начиная с самой плохо предсказанной покерной руки, флеш-рояля, мы можем видеть, что было 3 настоящих флеш-рояля, 2 предсказывались как обычные флеши, а 1 предсказывался как ничто в руке. Последнее кажется довольно плохим, но флеш-рояль - это всего лишь одна карта, чтобы быть ничем в руке. Глядя на крайний правый столбец, было 20 предсказанных Роял Флеш, что связано с вышеупомянутой передискретизацией в данных обучения. 14 из этих рук на самом деле были флешами, а 1 - стрит. Поскольку Royal Flush - это особый стрит-флеш, это не так уж и далеко, как могло бы быть.

Класс, который хорошо предсказывался (но не идеально), был Фулл-хаус. Мы можем видеть, что большая часть (379) фактических фул-хаусов была предсказана как три одинаковых, что является составной частью фулл-хауса.

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

Дальнейшее развитие

Если вы прочитали это руководство и знаете, как можно улучшить эту модель, оставьте комментарий ниже. Я рада услышать ваши мысли и дать им шанс.

Использованная литература:

Версии моей библиотеки:

  • тензорный поток: 1.14.0-rc1
  • керас: 2.2.4-тс
  • панды: 0.24.2
  • число: 1.16.4
  • matplotlib: 3.0.3
  • морской: 0.9.0
  • sklearn: 0,21,2