Этот пост также был опубликован в Блоге разработчиков IBM.

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

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

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

В этой статье я поделюсь уроками и передовыми практиками, которые я извлек из своих недавних проектов по машинному обучению. Хотя я называю это подходом Сделай сам, некоторые могут назвать его Путь пещерного человека. Я прекрасно понимаю, что в настоящее время существует множество платформ для отслеживания и управления экспериментами, но пользоваться ими не всегда возможно или удобно. Некоторые платформы требуют, чтобы вы проводили свои эксперименты на их платформе. Иногда вы не можете делиться какой-либо конфиденциальной информацией за пределами вашей организации — не только наборами данных, но также результатами и кодом. Многие платформы требуют платной подписки, что в некоторых случаях также может быть проблемой. Иногда вам просто нужен полный контроль над подходом к управлению экспериментом и данными.

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

Отслеживание ваших действий

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

Само собой разумеется, что ваш экспериментальный код должен контролироваться исходным кодом. Тем не менее, при использовании современных интерактивных сред, таких как Jupyter Notebooks, легко поддаться искушению провести быстрые эксперименты на лету, не внося изменений в GIT или любую другую систему управления версиями. Старайтесь избегать этого, насколько это возможно. Может быть, это только я, но я предпочитаю использовать приличную IDE и простые скрипты Python для проведения экспериментов. Я могу использовать блокнот для первоначального исследования данных, но вскоре после того, как первоначальный скелет модели готов, я переключаюсь на полноценный скрипт Python, который также позволяет выполнять отладку, рефакторинг и т. д.

2. Используйте идентифицируемые эксперименты

Но вы знаете, что? Исходного контроля недостаточно. Даже если все контролируется исходным кодом, может быть утомительно просматривать историю репозитория и понимать, какой источник использовался для запуска эксперимента 12 дней назад. Я хотел бы предложить дополнительную практику, которую я называю «Копирование при записи». Дублируйте последний файл/папку сценария эксперимента перед каждым новым экспериментом и вносите изменения в новый файл. Сделайте свои эксперименты узнаваемыми, добавив порядковый номер к каждому эксперименту в имени исходного файла. Например, animal_classifier_009.py для эксперимента №9. И да, это работает и для блокнотов: вы можете создать блокнот для каждого эксперимента. Это означает, что вам нужен только файл diff, чтобы понять, что изменилось между экспериментами № 9 и № 12. Хранилище дешевое, а размер исходного кода всех ваших экспериментов, вероятно, ничтожно мал по сравнению с размером ваших данных.

3. Автоматические снимки исходного кода

Еще один совет — автоматически делать снимок кода эксперимента при каждом запуске. Вы можете легко сделать это внутри самого скрипта эксперимента, запустив код начальной загрузки, который копирует исходный файл/папку в каталог с отметкой времени начала эксперимента в его имени. Это сделает вашу стратегию отслеживания экспериментов надежной, даже если у вас возникнет соблазн проводить эксперименты «на лету» без коммитов или копирования при записи, описанных выше (так называемые «грязные коммиты»).
Например, при запуске эксперимента aanimal_classifier_009.py мы создаем папку out/animal_classifier_009/2021_11_03–12_34_12/source и сохраняем снимок соответствующего исходный код внутри.

4. Относитесь к параметрам конфигурации так же, как к исходному коду

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

Для встраивания конфигурации эксперимента вы можете использовать простой Python, словарь, json, yaml или любой другой удобный формат. Просто убедитесь, что вы зафиксировали файлы конфигурации вместе с кодом эксперимента. Похоже ли жесткое кодирование на запах кода? Ну, не в этом случае. Если вы принимаете внешние параметры времени выполнения — обязательно записывайте их значения!

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

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

5. Отслеживание дерева эволюции эксперимента

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

Однако это может вызвать проблемы, если вы забудете обновить примечания перед запуском эксперимента. Я предлагаю простую электронную таблицу, подобную этой:

На листе вы также можете зафиксировать другую информацию или параметры, которые вы использовали в этом эксперименте, и, конечно же, — результаты эксперимента. Но мы коснемся этого позже.

Отслеживание того, что произошло

6. Сохранить вывод консоли/журнала

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

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

7. Отслеживание результатов эксперимента

Вы можете использовать несколько метрик для количественной оценки качества вашей модели. Например, точность, прецизионность, полнота, F-оценка, AUC. Убедитесь, что вы отслеживаете их в отдельном структурированном файле результатов, который вы можете автоматически обрабатывать позже для отображения диаграмм и т. д.

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

8. Выполните несколько повторов для стохастических моделей

Вы хотите, чтобы ваши результаты были воспроизводимыми, но все же избегайте получения вводящих в заблуждение результатов из-за случайности. Решение заключается в повторении со случайными семенами. Избегайте использования фиксированных случайных начальных значений, если ваши модели являются стохастическими. То же самое относится к перетасовке, понижению дискретизации или любой операции, которая содержит случайный элемент. Например, если вы используете SciKitLearn, всегда запускайте свои модели с параметром random_state=none. Выполняйте несколько повторений в каждом эксперименте и усредняйте результаты целевых показателей оптимизации во всех повторениях, чтобы получить стабильные цифры. Вы можете использовать такие показатели, как Стандартная ошибка среднего (SEM), чтобы оценить, насколько близко среднее значение ваших повторов к истинному среднему значению генеральной совокупности (если бы вы могли запускать бесконечное количество повторов). . Значение показателя SEM уменьшается по мере увеличения количества повторений. Это поможет вам обрести уверенность и понять, действительно ли ваши последние результаты лучше, или это может быть просто удача, и вам следует увеличить количество повторений, чтобы быть уверенным. Как правило, когда ваша модель становится более зрелой/стабильной, ваши оптимизации, вероятно, будут иметь меньшее влияние, и вам может потребоваться увеличить количество повторений.

Отслеживание входных и промежуточных наборов данных

9. Отслеживание входных наборов данных

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

10. Избегайте или отслеживайте промежуточные наборы данных

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

Краткое содержание

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