Введение в использование Enums в Python для проектов ML
Обзор вашего путешествия
1. Введение
При написании кода Python в проектах машинного обучения почти всегда требуется код конфигурации. Это код, который отслеживает информацию, используемую для настройки других компонентов вашего кода. Хотя это определение, вероятно, не поможет никому, кто еще не знает, что это значит, несколько примеров действительно помогают:
- Гиперпараметры. В машинном обучении гиперпараметры используются для обучения нескольких моделей с небольшими вариациями. Гиперпараметром может быть количество деревьев в случайном лесу. Другим примером является параметр релаксации для регрессии Риджа или Лассо.
- Информация о доступе. При подключении к базе данных или облачной учетной записи хранения требуется информация о доступе. Сюда входят имя, пароль и другие дополнительные свойства системы хранения. Точно так же предположим, что вы подписываетесь на публикацию/подсистему, такую как Kafka или брокер MQTT. Затем имя темы, пароль и номера портов являются необходимым кодом конфигурации.
- Информация о конвейере. При создании конвейера обработки данных необходим код конфигурации. Это могут быть соотношения для разделения тестовых и обучающих наборов или выражение CRON для определения времени запуска конвейера.
Таким образом, код конфигурации может в некоторых случаях помочь получить доступ к внешним системам, таким как базы данных и публикационные/подсистемы. В других случаях это может помочь настроить внутренние системы, такие как модели машинного обучения и конвейеры обработки данных.
Часто имеет смысл хранить код конфигурации в отдельных файлах (в таких форматах, как .env
, .yaml
или .json
). Тем не менее, этот код конфигурации по-прежнему должен быть доступен в коде Python. Есть много способов организовать код конфигурации в ваших скриптах Python 😕
В этом сообщении блога я покажу вам, почему перечисления Python — лучший выбор, чем некоторые из наиболее очевидных способов сделать это. Ранее я сделал видео на YouTube о перечислениях в Python. Вы можете проверить это, если предпочитаете видеоверсию более или менее того же контента:
2. Наивный подход
Прежде чем мы углубимся в то, как Python Enums может сделать код конфигурации более читабельным, давайте сначала рассмотрим наивный подход, который часто используется. Одним из распространенных способов хранения информации о конфигурации является использование глобальных переменных. Допустим, у вас есть модель машинного обучения, которая использует RandomForestClassifier
из scikit-learn
. Вы хотите сохранить количество деревьев в глобальной переменной с именем N_ESTIMATORS
. Это в сочетании с именем параметра, которое принимает RandomForestClassifier
. Вы можете определить эту переменную в верхней части нашего скрипта следующим образом:
# Configuration for RandomForestClassifier N_ESTIMATORS = 100
С единственным параметром конфигурации это не проблема. Однако давайте представим еще несколько:
# Configuration for RandomForestClassifier N_ESTIMATORS = 100 MAX_DEPTH = 8 N_JOBS = 3
Первая незначительная проблема заключается в том, что Python не может понять, что эти глобальные переменные связаны. Это просто разные глобальные переменные. Следовательно, вы не можете перебирать их или коллективно выполнять проверки безопасности без добавления дополнительного кода.
Более серьезная проблема возникает, когда вы пытаетесь работать и с другой моделью в скрипте. Допустим, вы также хотите попробовать использовать AdaBoostClassifier
. Тогда вы можете написать:
# Configuration for RandomForestClassifier N_ESTIMATORS = 100 MAX_DEPTH = 8 N_JOBS = 3 # Configuration for AdaBoostClassifier N_ESTIMATORS = 50 LEARNING_RATE = 1.0
Вы видите, что произошло? Вы случайно перезаписали глобальную переменную N_ESTIMATORS
! Хотя здесь это легко заметить, представьте, что скрипт Python состоит из 500 строк и написан кем-то другим. Возможно, это ускользнуло бы от вас. Python, вероятно, не дал бы никаких указаний на то, что что-то не так. Это не похоже на то, что установка N_ESTIMATORS = 50
для RandomForestClassifier
дает синтаксическую ошибку 😧
Как бы вы решили это? Вы можете ввести расширенное имя, указывающее, к какой модели принадлежит глобальная переменная N_ESTIMATORS
:
# Configuration for RandomForestClassifier N_ESTIMATORS_RANDOM_FOREST_CLASSIFIER = 100 MAX_DEPTH = 8 N_JOBS = 3 # Configuration for AdaBoostClassifier N_ESTIMATORS_ADA_BOOST_CLASSIFIER = 50 LEARNING_RATE = 1.0
Теперь похоже, что глобальная переменная LEARNING_RATE
больше не связана с AdaBoostClassifier
. Можно также добавить суффикс ко всем переменным, чтобы избежать путаницы:
# Configuration for RandomForestClassifier N_ESTIMATORS_RANDOM_FOREST_CLASSIFIER = 100 MAX_DEPTH_RANDOM_FOREST_CLASSIFIER = 8 N_JOBS_RANDOM_FOREST_CLASSIFIER = 3 # Configuration for AdaBoostClassifier N_ESTIMATORS_ADA_BOOST_CLASSIFIER = 50 LEARNING_RATE_ADA_BOOST_CLASSIFIER = 1.0
Помимо слишком длинных имен переменных, вы можете начать видеть, с какими тонкими проблемами вы сталкиваетесь в коде конфигурации. Эти проблемы в худшем случае быстро приводят к возникновению труднопонятных ошибок, а в лучшем — к плохим именам переменных.
Для вызовов базы данных также очень распространено выполнение вызовов к нескольким базам данных в рамках одного сценария. Вы действительно хотите, чтобы переменные имели такие имена, как PRODUCTION_DATABASE_INGESTION_ACCESS_KEY
?
Нет ли лучшего способа справиться с кодом конфигурации в Python?
3 — Enums спешат на помощь!
Python Enums предоставляет более элегантное решение для хранения информации о конфигурации. Перечисления (сокращение от enumerations) — это, по сути, способ определения набора именованных констант. Лучший способ быстро понять Enums — адаптировать код конфигурации машинного обучения из предыдущего раздела. Предыдущий код, который у вас был:
# Configuration for RandomForestClassifier N_ESTIMATORS_RANDOM_FOREST_CLASSIFIER = 100 MAX_DEPTH_RANDOM_FOREST_CLASSIFIER = 8 N_JOBS_RANDOM_FOREST_CLASSIFIER = 3 # Configuration for AdaBoostClassifier N_ESTIMATORS_ADA_BOOST_CLASSIFIER = 50 LEARNING_RATE_ADA_BOOST_CLASSIFIER = 1.0
Теперь это можно записать как:
from enum import Enum class RandomForest(Enum): """An Enum for tracking the configuration of random forest classifiers.""" N_ESTIMATORS = 100 MAX_DEPTH = 8 N_JOBS = 3 class AdaBoost(Enum): """An Enum for tracking the configuration of Ada boost classifiers.""" N_ESTIMATORS = 50 LEARNING_RATE = 1.0
Некоторые преимущества
Давайте быстро посмотрим, какие преимущества дает нам Python Enums:
- Каждый параметр (например,
MAX_DEPTH
) теперь хранится иерархически в модели, для которой он используется. Это гарантирует, что при введении дополнительного кода конфигурации ничего не будет перезаписано. Следовательно, также нет необходимости в слишком длинных именах переменных. - Различные параметры, используемые в
RandomForestClassifier
, теперь сгруппированы вRandomForest
Enum. Таким образом, их можно повторять и коллективно анализировать на безопасность типов. - Поскольку перечисления являются классами, они могут иметь строки документации, как показано выше. Хотя это может быть не обязательно для данного примера, в других примерах это может прояснить, на что ссылается перечисление. Гораздо лучше иметь это в виде строки документации, связанной с классом, а не в свободном комментарии. Во-первых, программное обеспечение для автоматического документирования теперь определит, что строка документации принадлежит перечислению. Для свободного комментария это, вероятно, было бы просто потеряно.
Если теперь вы хотите получить доступ к коду конфигурации дальше по сценарию, вы можете просто написать:
from sklearn.ensemble import RandomForestClassifier RandomForestClassifier( n_estimators=RandomForest.N_ESTIMATORS.value, max_depth=RandomForest.MAX_DEPTH.value, n_jobs=RandomForest.N_JOBS.value )
Это выглядит аккуратно и легко читается 😍
Некоторые возможности Enums для Python
Давайте проиллюстрируем некоторые простые возможности перечислений Python на игрушечном примере:
from enum import Enum class HTTPStatusCodes(Enum): """An Enum that keeps track of status codes for HTTP(s) requests.""" OK = 200 CREATED = 201 BAD_REQUEST = 400 NOT_FOUND = 404 SERVER_ERROR = 500
Ясно, что информация, содержащаяся в перечислении, связана; все они касаются кодов состояния HTTP(s). Учитывая это, теперь вы можете использовать следующий простой код для извлечения имен и значений:
print(HTTPStatusCodes.OK.name) >>> OK print(HTTPStatusCodes.OK.value) >>> 200
Python Enums также позволяет вам вернуться назад: учитывая, что значение кода состояния равно 404
, вы можете найти кодовое имя состояния, просто написав:
print(HTTPStatusCodes(200).name) >>> OK
Вы можете совместно работать с парами имен/значений в Enum, например, используя конструктор списка list()
:
print(list(HTTPStatusCodes)) >>> [ <HTTPStatusCodes.OK: 200>, <HTTPStatusCodes.CREATED: 201>, <HTTPStatusCodes.BAD_REQUEST: 400>, <HTTPStatusCodes.NOT_FOUND: 404>, <HTTPStatusCodes.SERVER_ERROR: 500> ]
Наконец, вы можете собирать и распаковывать перечисления в Python. Для этого просто используйте модель pickle
так же, как и с другими знакомыми объектами в Python:
from pickle import dumps, loads print(HTTPStatusCodes is loads(dumps(HTTPStatusCodes))) >>> True
Это особенно важно для кода конфигурации для моделей машинного обучения. В этом случае есть также известные библиотеки, такие как MLflow, которые элегантно сохраняют код конфигурации для вас.
Конфиденциальный код конфигурации
При использовании конфиденциального кода конфигурации (например, паролей или ключей доступа) НИКОГДА не прописывайте их явно в коде. Они должны быть в отдельном файле, который игнорируется используемой системой контроля версий и импортируется в скрипт.
Это не означает, что их нельзя хранить в Enums внутри кода. Перечисления относятся к организации, и даже цензурированная информация может быть организована. В качестве примера приведено перечисление, представляющее подключение к учетной записи хранения в Microsoft Azure:
from enum import Enum import os class StorageAccount(Enum): ACCOUNT_NAME = "my account name" ACCESS_KEY = os.environ.get('ACCESS_KEY') CONTAINER_NAME = "my container name"
Здесь перечисление StorageAccount
извлекает ACCESS_KEY
из переменной среды. Это может быть установлено, например, в файле .env
. Обратите внимание, что в скрипте Python конфиденциальная информация не раскрывается. Тем не менее вся информация об учетной записи хранения аккуратно организована в виде перечисления.
4 — Подведение итогов
В этом сообщении блога мы увидели, как Python Enums может сделать код конфигурации читабельным, самодокументируемым и менее подверженным ошибкам. Если вы хотите узнать больше о перечислениях, я рекомендую официальное руководство Enum HOWTO и запись в блоге Построение перечислений констант с помощью Enum Python.
Если вас интересуют наука о данных, программирование или что-то среднее между ними, не стесняйтесь добавить меня в LinkedIn и сказать привет ✋
Понравилось, что я написал? Ознакомьтесь с другими моими публикациями, чтобы узнать больше о Python:
- Модернизируйте свой грешный код Python с помощью красивых подсказок по типам
- Визуализация отсутствующих значений в Python невероятно проста
- Представляем обнаружение аномалий/выбросов в Python с помощью PyOD 🔥
- 5 потрясающих функций NumPy, которые могут спасти вас в крайнем случае
- 5 советов экспертов, которые помогут вам резко улучшить свои словарные навыки в Python 🚀