Создайте покедекс с глубоким обучением

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

Грунтовка

Прежде чем я пойду дальше, краткое введение в покемонов для тех, кто не знаком. Покемоны — это существа, похожие на животных, которых можно поймать и обучить сражаться с другими покемонами. У каждого покемона есть элементный тип или два, которые обозначают, какие типы движений, которые он может использовать, являются самыми сильными, и против каких типов покемонов он будет иметь преимущество или недостаток в бою. Например, Пикачу, культовый талисман франшизы Pokemon, относится к типу электрических. Это означает, что у него есть слабость к покемонам наземного типа и преимущество перед водными и летающими покемонами. В играх можно ловить диких покемонов и записывать данные об их типах и другую информацию с помощью похожего на энциклопедию устройства под названием Pokedex. Одной из целей большинства игр является захват и запись данных о каждом покемоне на земле, но как мы можем определить тип покемона, просто взглянув на него? Это основное внимание в этой статье.

Данные

Мы будем использовать спрайт-изображения покемонов в различных итерациях или поколениях игр про покемонов из бесплатного репозитория Github под названием PokeAPI с собственностью изображений Nintendo. Мы также будем использовать набор данных от Kaggle, в котором перечислены типы и номера каждого покемона в национальном Pokedex, который мы будем использовать для индексации и маркировки наших изображений. Всего 18 видов. Предыдущие читатели могут вспомнить, что мы использовали этот набор данных, чтобы определить лучшую команду покемонов для использования в двух недавних играх.

Изображения организованы следующим образом:

sprites
\- pokemon
    \- other
        \- dream world (SVGs)
        \- official artwork (PNGs)
    \- versions
        \- generation i
            \- red and blue (PNGs with back, gray, transparent, back-gray variants)
            \- yellow (PNGs with back, gbc, gray, transparent, back-gbc, back-gray, back-transparent variants)
        \- generation ii
            \- crystal (PNGs with back, shiny, back-shiny, transparent, transparent-shiny, back-transparent, back-transparent-shiny variants)
            \- gold (PNGs with back, shiny, transparent, back-shiny variants)
            \- silver (PNGs with back, shiny, transparent, back-shiny variants)
        \- generation iii
            \- emerald (PNGs with shiny variants)
            \- fire red and leaf green (PNGs with back, shiny, back-shiny variants)
            \- ruby and sapphire (PNGs with back, shiny, back-shiny variants)
        \- generation iv
            \- diamond and pearl (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
            \- heart gold and soul silver (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
            \- platinum (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
        \- generation v
            \- black and white (PNGs with back, female, shiny, back-female, back-shiny, shiny-female, animated variants)
        \- generation vi
            \- omega ruby and alpha sapphire (PNGs with female, shiny, shiny-female variants)
            \- x and y (PNGs with female, shiny, shiny-female variants)
        \- generation vii
            \- ultra sun and ultra moon (PNGs with female, shiny, shiny-female variants)
            \- icons (PNGs)
        \- generation viii
            \- icons (PNGs with female variants)
    \- default PokeAPI sprites (PNGs with back, female, shiny, back-female, back-shiny, shiny-female variants)
\- items
    \- default PokeAPI items (PNGs)

Некоторые ключевые термины для уточнения: блестящий относится к покемонам с альтернативной окраской, чем их обычный вид. Например, вместо культового желтого Пикачу блестящий Пикачу имеет выгоревшую на солнце оранжевую окраску. Женский покемон говорит сам за себя: некоторые покемоны имеют разную внешность в зависимости от их биологического пола. Самка Пикачу, например, имеет хвост в форме сердца вместо хвоста молнии. Наконец, задний относится к заднему спрайту покемона, который пользователь использует во время битвы покемонов против противника, чей передний спрайт показан. Эти разные представления об одном и том же покемоне будут очень полезны при обучении нашей модели, потому что по состоянию на 2021 год существует всего 898 покемонов, что само по себе не составляет большой набор данных. Включая спрайты из разных игр и поколений, принимая во внимание как передние, так и задние спрайты, блестящих покемонов и покемонов с половой диморфностью, мы получаем гораздо больший набор данных, который будет очень полезен при обучении нашей модели. На этой ноте давайте перейдем к тому, как мы будем использовать эти данные для обучения нашего классификатора.

Подход к машинному обучению

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

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

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

Как и в softmax, необработанные результаты подвергаются экспоненциальному преобразованию, но обратите внимание, что эти вероятности рассчитываются независимо друг от друга (т. Е. Их сумма не обязательно должна равняться 1). Таким образом, вероятность того, что, скажем, Чаризард — покемон огненного/летающего типа — будет огненным типом, не имеет отношения к его вероятности быть летающим, и наоборот. Таким образом, у нас может быть 83 % вероятности того, что это огонь, и 74 % вероятность того, что это будет полет. Поэтому мы будем использовать эту функцию при оценке наших прогнозов и соответствующей точной настройке модели на этапе обучения.

Другое соображение, которое мы должны принять во внимание, — это то, как мы будем оценивать нашу модель. По тем же причинам, по которым мы не можем использовать функцию softmax для получения вероятностей, мы не можем просто использовать традиционную метрику точности как средство того, насколько хорошо наша модель работает во время обучения и проверки; точность — это, по сути, доля случаев, которые мы правильно классифицируем. Математически это определяется как:

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

Вместо этого мы будем использовать показатель F1-score. Это среднее гармоническое точность и отзыв, которые измеряют долю правильно предсказанных положительных случаев среди всех предсказанных положительных результатов и правильно предсказанных положительных случаев среди всех реальных положительных случаев. , соответственно:

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

Еще две вещи о счете F1, прежде чем мы перейдем к кодовой части этой статьи. Во-первых, указанная выше оценка F1 индивидуальна для каждого типа. Чтобы получить общее представление о том, насколько хорошо работает наш классификатор во всех этих классах, мы можем взять среднее значение баллов F1. Сделать это можно несколькими способами, но подробнее можно прочитать здесь и здесь. В нашем случае мы возьмем глобальное среднее значение F1, называемое макропоказателем F1. Эта метрика одинаково взвешивает все типы и, таким образом, не так подвержена влиянию дисбаланса классов, что является проблемой с нашими данными: например, у нас есть чрезмерное представительство Водных и Нормальных типов, но мало призрачных и ледяных покемонов. Равномерно взвешивая типы, мы не сильно подвержены влиянию преобладающих типов в нашем наборе данных. Во-вторых, мы будем использовать функцию потерь, называемую soft-F1-score, для оценки эффективности нашей модели. Это модифицированная версия оценки F1, которая сделана дифференцируемой (важное предварительное условие для функции потерь, допускающее обратное распространение). Для краткости я не буду вдаваться в подробности здесь, но подробнее об этом можно прочитать здесь и здесь.

Реализация модели

Мы будем использовать библиотеку TensorFlow для программирования нашей нейронной сети. Начну с того, что большая часть реализации модели была адаптирована из учебника Ашрефа Майзы по классификации с несколькими метками с помощью TensorFlow, в дополнение к другим замечательным ресурсам, ссылки на которые будут приведены в конце этой статьи.

Начнем с загрузки наших библиотек, которых будет довольно много:

Далее мы проведем некоторую очистку данных в нашем словаре типов покемонов и их индексов в Pokedex.

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

Напомним, что, поскольку количество отдельных покемонов довольно мало, мы используем спрайты покемонов из разных поколений и игр, в дополнение к бэк-спрайтам, блестящим вариантам и женским покемонам в качестве встроенного способа увеличения размера наших тренировочных данных. . Таким образом, наш обучающий набор данных состоит из 15 000 изображений.

Далее мы будем бинаризировать наши типы с помощью горячего кодирования и конвертировать наши данные в формат, пригодный для классификации по нескольким меткам, используя tf.data API.

Labels:
0. Bug
1. Dark
2. Dragon
3. Electric
4. Fairy
5. Fighting
6. Fire
7. Flying
8. Ghost
9. Grass
10. Ground
11. Ice
12. Normal
13. Poison
14. Psychic
15. Rock
16. Steel
17. Water
sprites/sprites/pokemon/versions/generation-iv/diamond-pearl/125.png [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
sprites/sprites/pokemon/versions/generation-i/yellow/back/gbc/67.png [0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
sprites/sprites/pokemon/versions/generation-ii/silver/180.png [0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

Покемон под sprites/sprites/pokemon/versions/generation-iv/diamond-pearl/125.png — это покемон электрического типа, называемый Electabuzz, поэтому в векторе бинарного типа он имеет 1 в индексе 3, соответствующем этому типу. Точно так же sprites/sprites/pokemon/versions/generation-i/yellow/back/67.png и sprites/sprites/pokemon/versions/generation-ii/silver/180.png относятся к типу Fighting, Machoke, и типу Electric, Flaaffy, соответственно, с соответствующим форматированием их векторов меток. Наконец, мы отформатируем этот набор данных таким образом, чтобы изображения и метки были связаны с ним таким образом, который может обрабатывать TensorFlow.

Теперь, когда наши наборы данных для обучения и проверки подготовлены и отформатированы соответствующим образом, мы, наконец, готовы создать и обучить нашу модель! Несмотря на то, что нам удалось расширить наш набор данных, мы будем использовать трансферное обучение, чтобы повысить его производительность и сократить время обучения. При трансферном обучении мы используем предварительно обученную модель на гораздо большем наборе данных — и тот, который не обязательно должен быть связан с нашим собственным — в качестве основы нашей модели для идентификации классов в новом контексте. Одна из приятных особенностей TensorFlow заключается в том, что у них есть богатая база данных предварительно обученных моделей, которые вы можете легко импортировать, особенно для компьютерного зрения, как в нашем случае. Единственное, что нам нужно добавить, это плотный скрытый слой и выходной слой, соответствующий каждому из 18 типов покемонов, а затем мы можем скомпилировать и обучить модель.

Это заняло чуть более 15 минут на моем ПК с графическим процессором NVIDIA GTX 1660 Super. Глядя на производительность обучения и проверки, у нас, кажется, нет значительного переобучения, с окончательными макро-оценками F1 0,5413 и 0,4612 для обучения и проверки соответственно, хотя производительность показывает уменьшение отдачи в течение последних нескольких эпох как для обучения, так и для проверки. и проверочные наборы данных, но тем не менее довольно хорошие. Аналогично, окончательные значения потерь для обучения и проверки составляют 0,54 и 0,5976 соответственно.

Теперь давайте протестируем модель. Я специально тренировал его на первых 5 поколениях покемонов, потому что набор данных от Kaggle идет до поколения 6, поэтому мне было любопытно, насколько хорошо он будет работать с новыми покемонами, не видя их изображения ни на этапе обучения, ни на этапе проверки. Я также добавлю, что с поколения 6 и далее покемоны перешли от обычных 2D-спрайтов к 3D-моделям покемонов, поэтому будет тем интереснее посмотреть, как наша модель справляется с этими данными, которых она раньше не видела.

Delphox
Type
['Fire', 'Psychic']

Prediction
['Fire', 'Psychic']



Clauncher
Type
['Water']

Prediction
['Water']



Noivern
Type
['Flying', 'Dragon']

Prediction
['Dragon', 'Flying', 'Psychic', 'Water']



Quilladin
Type
['Grass']

Prediction
['Grass']



Gogoat
Type
['Grass']

Prediction
[]



Hawlucha
Type
['Fighting', 'Flying']

Prediction
['Flying', 'Psychic']



Goomy
Type
['Dragon']

Prediction
['Water']



Sylveon
Type
['Fairy']

Prediction
['Flying', 'Water']

Мы видим, что наш классификатор неплохо справляется с предсказанием типов этих покемонов. Я был особенно впечатлен тем, что он идеально передал типы Delphox (покемон, похожий на лису). Он изо всех сил пытался правильно предсказать тип Феи Сильвеона и отказался от Гогота, поскольку вероятность ни одного из типов не превышала 0,5 (замечу, однако, что Фея впервые была добавлена ​​как 18-й тип в этом поколении, с несколькими старыми покемонами). либо задним числом изменив их тип с Нормального на Волшебный, либо прикрепив его к их первоначальному монотипу). Давайте посмотрим на некоторых старых покемонов, таких как Пикачу и Чаризард:

Pikachu
Type
['Electric']

Prediction
['Water']



Charizard
Type
['Fire', 'Flying']

Prediction
['Flying', 'Normal', 'Psychic']

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

В этой статье мы узнали, как научить нейронную сеть предсказывать типы покемонов, используя разнообразие спрайтов и форм в различных итерациях игр на протяжении многих лет. Мы получили довольно хорошие прогнозы на покемонов из новых игр с 3D-моделями. Тем не менее, есть еще много возможностей для улучшения. Например, можно выполнить подвыборку, чтобы обуздать проблему дисбаланса классов и еще больше уменьшить нашу функцию потерь. Мы также могли бы оценить производительность модели, используя различные функции потерь, такие как бинарная кросс-энтропия вместо мягких потерь F1, которые чаще используются в задачах классификации с несколькими метками. Также есть недавний препринт на arXiv, который представляет другую функцию потерь, полученную из оценки F1 для классификации с несколькими метками, называемую sigmoidF1, которая может быть многообещающей.

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

Рекомендации

[1] https://towardsdatascience.com/multi-label-image-classification-in-tensorflow-2-0-7d4cf8a4bc72
[2] https://github.com/PokeAPI/sprites
[3] https://www.kaggle.com/abcsds/pokemon
[4] https://glassboxmedicine.com/2019/05/26/classification-sigmoid-vs- softmax/
[5] https://medium.com/arteos-ai/the-differences-between-sigmoid-and-softmax-activation-function-12adee8cf322
[6] https ://github.com/ashrefm/multi-label-soft-f1
[7] https://towardsdatascience.com/the-unknown-benefits-of-using-a-soft-f1-loss -in-classification-systems-753902c0105d
[8] https://arxiv.org/pdf/2108.10566.pdf