Найдите баланс между быстрыми результатами и высококачественным кодированием.
Хорошо, давайте проясним с самого начала: этот пост НЕ предназначен для людей, которые выполняют разовые, разрозненные проекты, такие как участие в соревнованиях Kaggle или выполнение хобби-проектов на ноутбуках Jupyter для изучения профессии. Ценность одноразового, быстрого и грязного кода сценария здесь очевидна - и имеет свое место. Скорее, он предназначен для практиков машинного обучения, работающих в производственной среде. Так что, если вы работаете в команде машинного обучения, которая изо всех сил пытается справиться с техническим долгом, выкачивая модели машинного обучения, это для вас.
Типичный рабочий процесс

Вот посылка: вы инженер / аналитик / ученый, работающий в сфере машинного обучения / дистанционного обучения / искусственного интеллекта / искусственного интеллекта, работает в стартапе / МСП / предприятии. Ваша работа - взять кучу случайных данных из любого места и создать ценность. Что вы делаете? Вы садитесь, каким-то образом переносите данные на свой локальный компьютер и неизбежно делаете что-то вроде:
jupyter notebook
Или вы идете в колаб, если вам нравится и правила конфиденциальности вашей команды позволяют это.
Ниже приводится история, которую я уже столько раз видел - конечно же, псевдокод.
import pandas as pd import xyzlibraryforml# CELL 1: Read df = pd.read_*("/path/to/file.*") df.describe()# INSERT HERE: a 100 more cells deleted and updated to explore data.# CELL 2: Split train, eval = split_the_data() # basic# INSERT HERE: trying to figure out if the split worked# CELL 3: Preprocess # nice, oh lets normalize preprocess(train) preprocess(eval)# exploring preprocessed data, same drill as before# CELL 4: Train model = some_obfuscated_library.fit(train, eval) # not being petty# if youre lucky here, just look at accuracy. otherwise:# CELL 5: Evaluate complicated_evaluation_methods(model)# INSERT HERE: do this a 1000 times# CELL 6: Export (i.e. pickle it) export_model()
Итак, вы закончили, верно? Вот и все - бум. Результаты тестов отличные. Давайте передадим его оператору для развертывания в производственной среде. Перерыв на обед и Reddit до конца дня!
Хорошо, я сильно преувеличиваю? Да. Неужели для некоторых предприятий это близко к истине? Также да.
Так в чем проблема?
Проблема в том, что этот блокнот, представленный выше, представляет собой клубок технического долга, который будет продолжать расти, если не будет ликвидирован достаточно рано. Давайте разберемся, что с этим было не так:
Не двигаться к более широкой картине
Когда вы помещаете обобщаемую логику в одноразовые блоки блокнотов без версий, это лишает вашу команду возможности использовать ее в своих интересах. Например, логика загрузки / извлечения ваших данных из вашего статического источника данных. Конечно, pd.read_json сейчас просто, но что будет, если формат изменится? Хуже того, что произойдет, если данные увеличатся и разделятся на несколько файлов? Хуже того, что будет, если он больше не поместится в памяти? Что произойдет, если ваш коллега столкнется с той же проблемой - она, вероятно, пройдет через те же циклы, что и вы, даже не зная, что это уже решенная проблема. Конечно, есть решения для всех этих проблем, но собираетесь ли вы решать их в своем локальном блокноте?
Ответ, вероятно, отрицательный (если вы не Netflix или что-то в этом роде). Обычно логично извлечь загрузку в логически отдельный сервис. Таким образом, вы можете абстрагироваться от фактического извлечения данных от себя и на поддерживаемый уровень, прозрачный для всех. Например, это может быть некоторая форма feature store, которая собирает все различные потоки данных в вашей организации в одну точку, которая затем может быть использована в определенных API всеми членами вашей команды.
То же самое относится к предварительной обработке, обучению и оценке в приведенном выше сценарии.
Построение логики локально (и поэтому снова и снова)
Как и в приведенном выше примере, когда вы пишете код для исследования данных, вы генерируете массу отличных вещей - визуализации, статистические фреймы данных, очищенные и предварительно обработанные данные и т. Д. Случайный, произвольный порядок выполнения записных книжек Jupyter гарантирует, что путь, выбранный для реализации эти артефакты навсегда теряются при переопределении локальных переменных и самопроизвольных перезапусках ядра. Хуже того, логика имеет сложные, легко переопределяемые конфигурации, встроенные глубоко внутри самого кода, что значительно усложняет воссоздание артефактов.
Послушайте, я понимаю - я делаю это постоянно. Обработка данных - это случайный процесс измельчения, который будет беспорядочным. Но установка некоторой структуры для отслеживания ваших конвейеров исследования данных окупится. Как и при комментировании вашего кода, больше всего выиграет от отслеживания вы сами. Кроме того, ваша команда будет работать быстрее и избежать лишней работы, если эти артефакты и мини-ответы на вопросы будут автоматически сделаны прозрачными и понятными для всех.
Не владеющий развертыванием
Последняя часть этого ноутбука, вероятно, вызывает наибольшее разочарование. Я действительно не верю, что работа человека, пишущего этот блокнот, заканчивается экспортом модели для операций. Это не имеет никакого смысла.
Прежде всего, вместе с ней должна идти функция preprocessing_fn(), иначе перекос при обучении-подаче приведет к сбою вашей модели с самого начала. Во-вторых, откуда оператору знать, какие допущения вы сделали при построении модели? Собираетесь ли вы написать обширную документацию о том, какие данные входят, как они должны быть предварительно обработаны и в какой форме они должны быть при развертывании на конечной точке? Я имею в виду, что теперь есть автоматизированные способы сделать это - так что владейте развертыванием!
Один аспект, которого не хватает в приведенном выше сценарии, - это игнорирование измерения производительности модели. Большинство специалистов по данным, которых я знаю, не волнует, насколько велика модель, сколько памяти она потребляет для прогнозов и насколько быстро / эффективно она развертывается. Модель не приносит ценности, если она не соответствует критериям производительности конечного приложения. Опять же, человек, разрабатывающий модели, должен владеть своим конечным развертыванием.
Предложения
Самый простой способ исправить это - разработать структуру, в которой команда машинного обучения может сбалансировать одноразовую исследовательскую разработку кода с поддерживаемым кодом, которым легко поделиться. Если вы собираетесь это сделать, вы можете иметь в виду следующее:
Создавайте четко определенные интерфейсы (например, разлагайте на конвейеры)
Компоненты split, transform, train, evaluate, deploy вашего рабочего процесса являются логически независимыми объектами / услугами. Создайте архитектуру, в которой отдельные компоненты рабочего процесса машинного обучения будут абстрагированы от конкретных реализаций. Это может быть определение фактического объектно-ориентированного стиля интерфейсов или просто обеспечение того, чтобы ваше репо имело некоторую форму структуры, в которую каждый может легко вносить свой вклад и расширять. Вначале это не обязательно должно быть ракетостроение, но это очень поможет.
Здесь в игру вступает понятие конвейеров машинного обучения: конвейеры - это абстрактные представления, которые определяют серию задач обработки данных. Использование pipelines поможет вашей команде выделить логические объекты в рабочем процессе и обеспечить независимый поток данных. Это неизбежно приведет к созданию более надежной и удобной в обслуживании кодовой базы. Кроме того, определение таких конвейеров машинного обучения гарантирует, что вы сможете автоматизировать непрерывное обучение устаревших моделей на новых данных по мере их поступления. Однако для этого вам также необходимо отслеживать метаданные данных (см. Ниже).
Составьте план для своих метаданных машинного обучения
Каждый эксперимент, который вы запускаете, дает метаданные ML: кто его запускал, когда он запускался, какие данные вводились, где хранятся результаты и т. Д. Убедитесь, что вы сопоставили их и предоставили удобный способ добавления в это хранилище. Важно отметить: я говорю не только об отслеживании экспериментов. Есть много замечательных библиотек, которые помогут в отслеживании метаданных, ориентированных на модель, то есть метрик и т. Д. Однако метаданными, ориентированными на данные, часто пренебрегают, особенно если данные постоянно меняются. Такие вещи, как управление версиями данных, статистика, визуализации, какое начальное число использовалось при случайном разделении - и тому подобное. Должен быть простой способ отслеживать различные маршруты, по которым ваши данные используются в процессе разработки.
Убедитесь, что ваши рабочие нагрузки могут выполняться в любой среде
Запускать рабочую нагрузку на одной машине всегда будет проще, чем запускать один и тот же код в любой произвольной среде. Я знаю, что Docker недоступен и труден для многих людей, занимающихся машинным обучением, но, по крайней мере, создайте requirements.txt и добавьте __init__.py! В идеале следует поместить код в контейнеры и проводить эксперименты на какой-либо платформе оркестровки. Выполнение этого одного шага сейчас избавит вас от многих проблем, если вы масштабируете и автоматизируете все это для работы с большими данными.
Не отделяйте развертывание от обучения
На данный момент это, пожалуй, самое простое из всех простых предложений. Сквозное владение привело к целой революции DevOps 20 лет назад, и это не исчезло и в разработке машинного обучения. Обеспечьте плавный механизм для передачи обученной модели в конечную точку и убедитесь, что сотрудники Ops сидят в одной комнате (не всегда буквально) с вашими разработчиками машинного обучения. Внедрите процессы, в которых каждый понимает конечную цель производства. По возможности автоматизируйте вещи.
Не идите на компромисс в отношении повторяемости и прослеживаемости
Вы знаете, когда люди начинают кодировать на Python, а затем переходят на C ++ или Java, и они не понимают таких понятий, как указатели и статическая типизация? Они думают: «Какой смысл давать переменной тип, Я знаю, что это такое, и почему меня заставляют это делать?» Что ж, извините, что нарушаю это вам, но указатели и статическая типизация имеют цель - их знание защищает ваш код от ваших собственных ошибок и обеспечивает высококачественный надежный вывод. Абсолютная гибкость может быть плохой вещью, особенно для разработчиков, которые склонны ошибаться в сторону лени (как я).
Что-то очень похожее происходит в ноутбуках Jupyter - свобода запуска любого произвольного кода в любом порядке дает свободу, но заставляет вас терять очень важное понятие повторяемости и отслеживаемости, два краеугольных камня любой надежной, готовой к производству инженерной дисциплины. Пожалуйста, по крайней мере убедитесь, что ваши записные книжки выполняются сверху вниз и воспроизводятся. Загроможденные и случайно упорядоченные блокноты Jupyter следует наказывать длинными тирадами, подобными этой на собраниях Code Review.
Один из способов обеспечить обе черты - извлечь settings вашего кода из реализации. Это подводит меня к следующему пункту ...
Отдельная конфигурация от реализации
Определенно сложно отделить конфигурацию от реальной реализации кода. Тем не менее, это еще одна из тех вещей, которые окупаются в долгосрочной перспективе. Мы уже писали об этом раньше, но резюмируем: разделение конфигурации позволяет автоматизировать повторяющиеся задачи, повышает предсказуемость результатов и обеспечивает воспроизводимость. В идеале конфигурацию следует рассматривать как код, версионировать и поддерживать.
Заключение
Специалисты по машинному обучению во многих организациях сильно заинтересованы в том, чтобы быстро добиваться результатов для получения первых результатов. Однако это приводит к накоплению технического долга, который со временем замедляет работу команды. Решение состоит в том, чтобы с самого начала следовать правильным принципам разработки программного обеспечения и опираться на рекомендации, чтобы найти баланс между быстрыми результатами и высококачественной разработкой программного обеспечения.
Приведенные выше мысли - это очень личные уроки, которые я извлек за последние 4 года, внедряя модели в производство. Это ни в коем случае не исчерпывающий список, и вы можете использовать его в качестве шаблона для тестирования производственной архитектуры или как образец для разработки своей собственной.
Мы использовали эти факторы в качестве руководящих принципов для ZenML, нашей платформы MLOps с открытым исходным кодом. Поэтому, прежде чем начинать с нуля, ознакомьтесь с ZenML на GitHub: https://github.com/maiot-io/zenml и не забудьте поставить нам звезду, если вам нравится то, что вы видите!