Простой проект для любого новичка/профессионала в машинном обучении :)

Обычным ночным просмотром некоторых видео на Youtube я посмотрел это видео из Primer об использовании биномиальных распределений, чтобы поймать людей, использующих нечестные/взвешенные монеты:

Это заставило меня задуматься: «Может ли модель машинного обучения выполнять ту же работу так же эффективно, но с взвешенными кубиками?» Давай выясним.

Примечание: я не очень уверен в том, правильно ли я использую слова «кости» или «мертвая кость» в своем письме, поэтому не стесняйтесь смеяться над моими ошибками ›:)

Предпосылки

Как и в большинстве современных проектов по машинному обучению и науке о данных, я в основном использовал Python 3. Что касается библиотек, вам понадобится следующее (скачайте их через pip install):

  • панды (для чтения и просмотра .csv файлов данных)
  • tensorflow и sklearn (библиотеки с готовыми алгоритмами и компонентами машинного обучения)

Подход

Когда я бросился в этот проект вслепую, я решил, что хочу опробовать пару разных моделей для этой задачи, в качестве гарантии на случай, если моя первая с треском провалится. По порядку я выбрал логистическую регрессию, деревья решений и нейронные сети. Я объясню более подробно, что влекут за собой эти модели и как они справляются с задачей с точки зрения их эффективности.

Но перед этим, при любом начинании ML, нам сначала нужны некоторые данные…

Генерация некоторых данных

Я не буду показывать весь скрипт, который я сделал для генерации данных (это изрядное количество кода 😰). Однако вы можете проверить это здесь, на GitHub: https://github.com/mathlord2/catch-cheaters/blob/main/create_data.ipynb.

Однако в простоте я создал вспомогательный класс (названный Die.py в том же репозитории, что и ссылка выше), который имитирует бросок взвешенных или невзвешенных костей. Затем приведенный выше скрипт create_data.ipynb создает несколько экземпляров этого класса для моделирования, и каждый бросок этих костей записывается в наши данные.

Как только вы завершите выполнение этого скрипта, вы можете использовать pandas для вывода некоторых (нормализованных) сгенерированных точек данных:

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

Вероятно, вы уже можете сказать, что существует шаблон для определения того, является ли кубик взвешенным или нет; если вы видите процентное/плавающее значение, которое выше других 5 значений на приличную величину, то, скорее всего, у вас взвешенный кубик. Например, для последнего ряда/кости около 30% его бросков были шестерками, в то время как каждая из других сторон выпадала от 12% до 17% времени.

Настоящий вопрос заключается в том, может ли модель сделать это различие? Давай выясним ;)

Первый раунд: Бой!

Участник конкурса #Uno: Логистическая регрессия

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

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

В этот момент вы, вероятно, жалуетесь на объем кода, который я вставил. Что ж, хорошие новости для вас! С этого момента я не буду показывать вам весь код этого проекта, а только несколько фрагментов наиболее важных строк кода. Я опубликую ссылку на репозиторий моего проекта :)

Создать модель логистической регрессии довольно просто с помощью scikit-learn, когда я разделил данные на наборы для обучения и тестирования:

Итак, модель готова, но как насчет ее производительности? К сожалению, он не слишком хорошо выдерживает испытания…

Выход:

Accuracy: 0.6897666666666666
Precision: 0.6820252161858869
Recall: 0.6970719201187424
F1 Score: 0.689466484268126

Примечание. Если вы хотите узнать, что на самом деле измеряют эти показатели (точность, достоверность, полнота, оценка F1), я предлагаю ознакомиться с этой статьей здесь, написанной Towards Data Science. : https://towardsdatascience.com/accuracy-precision-recall-or-f1-331fb37c5cb9

68%-69% — довольно посредственный результат, если вы спросите меня. Я бы сказал, что логистическая регрессия так же эффективна, как и случайное угадывание 😢

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

Мы могли бы как-то подправить наши данные, чтобы улучшить показатели, но я сомневаюсь, что это того стоило бы, учитывая тот факт, что у нас есть еще две модели, на которые нужно взглянуть 👀

Участник № 二: Дерево решений

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

Все узлы и ветви этого дерева связаны с рассмотрением различных характеристик ваших данных. Они рассчитываются особым образом, о котором я не буду говорить, но я нашел полезную статью от KDnuggets, которая может помочь с этим объяснением (в ней также говорится о другом алгоритме машинного обучения, похожем на деревья решений, который называется случайные леса, которые я также нашел хорошо объясненными): https://www.kdnuggets.com/2022/08/decision-trees-random-forests-explained.html#

К счастью, в коде scikit-learn также предоставляет класс дерева решений, который мы можем использовать:

Как он выдерживает испытания? Используя тот же код метрик, что и выше, мы могли бы получить что-то вроде этого:

Accuracy: 0.9294666666666667
Precision: 0.9240875912408759
Recall: 0.9347563431333065
F1 Score: 0.9293913507741591

92%-93%; проклятие! Огромное улучшение по сравнению с мистером логистической регрессией.

Но почему такая впечатляющая производительность? Что ж, если вы взглянули на статью о деревьях решений, модель использует показатель под названием «энтропия», чтобы определить, какие функции данных должны быть приоритетными для «разделения» на (т. е. функции с более высоким приоритетом считаются ближе к началу/вершине дерева). Другими словами, деревья решений эффективны при анализе отдельных функций для поиска закономерностей в данных, что делает их лучше, чем логистическая регрессия, при обработке нелинейных данных более высокого уровня (таких как наши данные броска костей). в этом сценарии). Таким образом, модель дерева решений способна выяснить, что высокий процент броска для конкретной стороны приводит к «взвешенному» прогнозу.

Теперь об окончательной модели…

Участник конкурса #Trois: Нейронная сеть

Нейронные сети невероятно популярны для машинного обучения, и на то есть веские причины! Модель структурирована как нейронная система человека (отсюда и название): слои нейронов, которые связаны с другими слоями через ответвления. Все нейроны в нейронной сети имеют веса,которые отвечают за выполнение вычислений матричной алгебры на входных данных, которые передаются большему количеству нейронов и, в конечном итоге, к нашему окончательному прогнозу — и эти веса меняются со временем по мере того, как модель тренируется, чтобы улучшить себя. У вас есть полная свобода настраивать нейронную сеть по своему усмотрению (например, количество нейронов и слоев), что делает ее пригодной для многих вариантов использования.

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

  • Входной слой: 6 нейронов (то есть количество признаков в наших данных).
  • Скрытый слой 1: 128 нейронов, вероятность выпадения 50 % (то есть вероятность того, что нейрон в слое будет исключен из вычислений; это помогает снизить вероятность «переоснащение")
  • Скрытый слой 2: 256 нейронов, вероятность отсева 50 %.
  • Скрытый слой 3: 256 нейронов, вероятность выпадения 50 %.
  • Скрытый слой 4: 256 нейронов, вероятность выпадения 50 %.
  • Скрытый слой 5: 64 нейрона, вероятность выпадения 50 %.
  • Выходной слой: 1 нейрон (для нашего прогноза)

В коде (Tensorflow/Keras) наша модель выглядит так:

Это, вероятно, займет несколько минут, но как только вы закончите, результаты, похоже, стоят ожидания:

Accuracy: 0.951867
Precision: 0.976751
Recall: 0.925353

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

Тестирование

Просто чтобы убедиться, что наши три приведенные выше модели действительно соответствуют приведенным выше показателям, я создал короткий тестовый скрипт, который имитирует бросок четырех игральных костей, и модели пытаются предсказать на основе этих результатов, будет ли каждый игральных костей является честным/взвешенным или нет. Я не буду вдаваться в подробности, но не стесняйтесь взглянуть на скрипт здесь: https://github.com/mathlord2/catch-cheaters/blob/main/test_models.ipynb.

Вы заметите, что когда я запускал его, логистическая регрессия давала только половину прогнозов, в то время как модели дерева решений и нейронной сети давали правильные прогнозы («Истинно ” для взвешенных, “False” для невзвешенных).

Заключение

Итак, победителями в нашем конкурсе моделей, похоже, стали модели дерева решений и нейронной сети благодаря их способности эффективно обрабатывать многомерные данные! Подарите им аплодисменты 👏

Кроме того, теперь мы знаем, что машины могут так же эффективно, как и люди, различать взвешенные и невзвешенные кости :)