Не играйте в игрушечные модели; будьте готовы к продакшену ваших работ!

16 августа: От скриптов к API прогнозирования

23 февраля: От cURL к автоматизированному рабочему процессу

Как продвинутые новички, мы знаем довольно много: концепции EDA, машинного обучения, архитектуры моделей и т. Д.… Мы можем написать большой блокнот Jupyter, нажать «Перезагрузить и запустить все» и получить самые современные результаты. Хорошо, что дальше? Как другие могут использовать / воспроизвести / масштабировать ваши результаты?

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

Для каждого из них есть еще много чего, но мы остановимся только на основах, покажем преимущества и объясним процесс мышления! Цель состоит в том, чтобы свести к минимуму вероятность того, что инженеры закатят глаза при разговоре с вами.

Блокнот Jupyter

Jupyter Notebook хорош для исследования и создания прототипов, но мы не можем оставаться там навсегда. Ни один инженер [или никто] не хочет читать ваш код и переписывать его. Даже если у вас есть инженерные ресурсы, это просто замедляет весь процесс🙁

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

«››› Репозиторий на GitHub ‹********************************************************************************************************************************************************************************************************************************************************************************************

Все кредиты этой великой записной книжки в репо принадлежат оригинальному создателю!

Скрипты: ipynb - ›.py

Почему скрипт? Почему мы не можем просто использовать интерактивный блокнот, когда он работает нормально? Позвольте мне предложить несколько сценариев, чтобы обосновать, почему может быть хорошей идеей использовать сценарии, выходящие за рамки исследования!

  1. Если вы хотите запустить 20 больших моделей параллельно, откроете ли вы 20 ноутбуков?
  2. Предположим, вы хотите открыть 20 блокнотов, как вы настроите гиперпараметры? Перейти к каждой записной книжке и найти связанные ячейки?
  3. Предположим, вы вручную настроили 20 блокнотов, как вы проанализируете результаты? Перейти к каждой записной книжке?
  4. Предположим, вы можете обработать 20 блокнотов для первых 3 шагов. Что, если у вас есть отличная идея, но вам нужно поэкспериментировать с 200 наборами гиперпараметров, готовы ли вы повторить шаги 1–3?
  5. Предположим, вы обнаружили ошибку в исходной записной книжке и вам нужно все заново запустить. Готовы ли вы повторить шаги 1–3?

Если вы на все ответите ДА, то эта статья НЕ для вас, до свидания и хорошего дня 😊

Я структурирую конвейер так, чтобы разбить его на etl.py, train.py и прогноз.py. Каждый сценарий несет свою ответственность и старается быть как можно более атомарным, чтобы его могли разрабатывать отдельные товарищи по команде, если ответственность ясна.

etl.py: преобразовать необработанные данные, выполнить некоторые преобразования, создать train.csv и test.csv в папке данных [train_test_split]

train.py: используйте данные в train.csv, экспортируйте модель в папку модели [model.fit (X, y)]

predict.py: используйте данные в test.csv и обученную модель, экспортируйте прогнозы и оцените [model.predict (X, y)]

# File structure
.
├── README.md
├── __init__.py
├── autoformat.sh
├── data
│   ├── predict.csv
│   ├── test.csv
│   ├── train.csv
│   └── winequality.csv
├── log
│   ├── etl.log
│   ├── predict.log
│   └── train.log
├── model
│   └── model.pkl
├── notebook
│   └── prediction-of-quality-of-wine.ipynb
├── requirement.txt
└── scripts
    ├── config.yml
    ├── etl.py
    ├── predict.py
    ├── test_train.py
    ├── test_utility.py
    ├── train.py
    └── utility.py

Если я что-то напутаю, я не буду пытаться понять, почему [представьте мутацию состояния в Jupyter из-за случайного запуска ячеек], я просто снова запущу скрипт и посмотрю.

# Run in command line, as simple as this :)
$ python3 ./scripts/etl.py
$ python3 ./scripts/train.py
$ python3 ./scripts/predict.py

Отлично, мы можем загружать и запускать скрипты в командной строке! Но, как мы упоминали в сценариях выше [№2], как тогда мы можем настроить гиперпараметры? _?

Файл конфигурации в качестве ввода: нажмите

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

# config.yml example, I only use this to configure the scripts
etl:
  raw_data_file: "data/winequality.csv"
  processed_path: "data"
  test_size: 0.2
  random_state: 42
train:
  processed_train: "data/train.csv" 
  ensemble_model: "RandomForestClassifier"
  model_config: {n_estimators: 300}
  model_path: "model/model.pkl"
predict:
  model_path: "model/model.pkl"
  processed_test: "data/test.csv"
  predicted_file: "data/predict.csv"
  export_result: True

Внутри скрипта я создал служебную функцию для анализа файла конфигурации.

def parse_config(config_file):
    with open(config_file, "rb") as f:
        config = yaml.safe_load(f)
    return config

Затем мы можем загружать конфигурации в сценарии расширяемым способом!

@click.command()
@click.argument("config_file", type=str, default="scripts/config.yml")
def etl(config_file):
    config = parse_config(config_file)
    raw_data_file = config["etl"]["raw_data_file"]
    processed_path = Path(config["etl"]["processed_path"])
.......DO SOMETHING.......

Если у нас есть 20 моделей с похожей архитектурой, мы можем просто подготовить 20 конфигураций, вот и все.

Без печати: ведение журнала

Теперь мы можем комбинировать скрипт и конфигурацию в среде командной строки! Как я могу получить результаты? В мире Jupyter я буду печатать и смотреть, но это означает, что мне нужно сканировать длинный длинный блокнот, что, если у меня много моделей? Есть ли какой-нибудь конкретный способ собрать результаты в файл?

Отпусти печать, учись ведению журнала! Модуль ведения журнала в Python делает то, что предполагает его название, ведет журнал в Python. Мы можем определять и регистрировать то, что нас интересует, например, на каком этапе скрипта [для отладки], сводке показателей [для оценки], я извлек часть из прогнозирования и оценки модели в качестве примера

Этот фрагмент кода сгенерирует журнал, аналогичный приведенному ниже.

Узнайте больше о ведении журнала из этой статьи! Https://www.machinelearningplus.com/python/python-logging-guide/

В сторону:

Ведение журнала - это здорово, но все же, если у нас будет 100 экспериментов, мы получим 100 файлов журнала 🙁 Даже если они конкретны, мы можем не захотеть читать их один за другим, есть ли лучший способ оценить? Ниже приведены некоторые возможные решения:

  • Еще один сценарий, использующий grep или регулярное выражение, анализирует и извлекает ключевую информацию [но что, если кто-то изменит формат или написание !?]
  • Еще один инструмент - касса MLFlow или ModelDB! [Ведение журнала - лишь одна из функций обоих продуктов]

Воспроизводимость имеет значение: Conda env

Эй, теперь все хорошо! Я могу отправлять сообщения на GitHub, а мои друзья / коллеги могут клонировать и запускать свой компьютер 😊

Ой, подождите ……… их компьютер …… .. что, если мы используем разные версии? Это обычное дело, потому что у каждого пакета есть много версий, например: sklearn. API / интерфейсы могут со временем меняться [и нарушать код 🙁], как мы можем гарантировать, что мы используем один и тот же пакет?

Краткий ответ: Conda env, посмотрите статью Мэтта

Ниже я перечисляю некоторые полезные команды, которых достаточно для наших нужд.

# In terminal
# Create the environment
$ conda create — name YOU_CHANGE_THIS python=3.7 -y
# Activate it
$ conda activate YOU_CHANGE_THIS
# Install the needed package
$ pip install –r requirements.txt
# # ## # ## # ## # ## # ## # ##
# # # Do your development # # #
# # ## # ## # ## # ## # ## # ##
# Deactivate and back to normal environment
$ conda deactivate
# This will remove the environment, do it when you finish the project?
$ conda remove –name YOU_CHANGE_THIS –all

* в файле requirements.txt всегда нужно указывать версию! Если вы не укажете его, он попытается загрузить последнюю версию, но ваша последняя версия может быть не моей последней и потеряет весь смысл использования Conda env.

# this is what I have inside requirements.txt
pytest==5.3.5
pandas==1.0.1
Click==7.0
scikit-learn==0.22.1
black==19.10b0
isort==4.3.21
PyYAML==5.2

Не нарушайте предыдущий код: pytest

В мире программного обеспечения и машинного обучения изменения - это константа, мы что-то меняем, когда работаем. Но как мы можем гарантировать, что новое изменение не нарушит существующее (особенно те, которые вы написали за несколько месяцев до этого)?

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

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

Чтобы запустить модульный тест, просто запустите pytest в терминале, и вы должны увидеть следующий результат:

Круто, мы можем запустить pytest до того, как отправим на GitHub и попытаемся сохранить работоспособную программу! Но человек ненадежен, что, если мы забудем запустить pytest перед тем, как нажать и сломать код !? Как мы можем защитить себя от этой ситуации?

Если возможно, не полагайтесь на людей: непрерывная интеграция

Что, если сама программа может запускать модульные тесты автоматически, когда мы отправляем / создаем пул-реквест? Затем он может остановить слияние, если не сможет пройти тесты! Если вы слышали о непрерывной интеграции, это часть функции! Популярными продуктами на рынке являются CircleCI / TravisCI / jenkins, но я ленив, я не хочу выполнять дополнительную работу по настройке, если она не нужна, поэтому мы будем использовать для этого относительно новый GitHub Action 😊

В прошлом, я думаю, инженеры были настолько умны, что как они могли запомнить синтаксис и придумать такой сценарий [он и так уже простой] самостоятельно? Не думайте так, большинство людей просто копируют и изменяют шаблон, как я [есть шаблон под названием Опубликовать пакет Python в рабочих процессах CI в GitHub Actions]

Теперь вы это видите! Скрипты, версии пакетов и pytest работают вместе, чтобы гарантировать правильность версий пакетов и работоспособность программы! Они защитят нас, когда мы будем разрабатывать инкрементально, они скажут НЕТ, если код нарушит модульные тесты. Если мы обнаружим ошибку, мы можем включить ее в качестве еще одного тестового примера, чтобы убедиться, что мы не повторяем одну и ту же ошибку дважды!

Можете ли вы сделать то же самое в Jupyter Notebook с таким же уровнем уверенности?

Стиль кода имеет значение: черный, isort

Вы слышали о pep8? Писать Python - это стилевое соглашение, есть много (хороших) моментов, но, честно говоря, пожалуйста, не проверяйте меня на этом, мой мозг не может их всех запомнить!

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

$ pycodestyle --first optparse.py
optparse.py:69:11: E401 multiple imports on one line
optparse.py:77:1: E302 expected 2 blank lines, found 1
optparse.py:88:5: E301 expected 1 blank line, found 0

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

Simply run this and the script will do the rest!
# If you get permission error, you can try
# chmod +rx autoformat.sh
$ ./autoformat.sh

Выводы

У местных сейчас все хорошо! Я могу отправить их на GitHub, а мои друзья могут клонировать и запускать его на своем компьютере.

НО! Мы знаем, что важные (плохие) сообщения обычно начинаются с НО / ОДНАКО, например: отклонения заявления о приеме на работу 🙁

НО что, если мы хотим сделать модель производственной? _?

В: Во-первых, что это значит под производством?

A: В узком смысле это означает потребление модели, я даю вам ввод, вы возвращаете мне вывод [возможно, в форме JSON], например: конечная точка сети / API. Нам нужен сервер [другой компьютер] для размещения моделей и конечных точек, я не хочу использовать свой компьютер для его размещения, иначе весь продукт будет зависеть от моего бедного Macbook 🙁

Я выделил два слова выше, и это как раз проблемы.

1. Как найти другой компьютер для размещения моделей?

Краткий ответ: либо купите компьютер, либо арендуйте его у поставщика облачных услуг [например, AWS, Azure, GCP], тогда мы сможем заявить, что знаем ОБЛАЧНЫЕ ВЫЧИСЛЕНИЯ 😊

2. Что делать, если на другом компьютере установлена ​​не MacOS, как я могу убедиться, что мой код работает на нем?

Краткий ответ: докер

В заключение мы:

a. start from a Jupyter Notebook
b. Code conversion [ipynb to .py]
c. Make the scripts configurable [Click]
d. Include logging [logging]
e. Make sure the local environment is the same [Conda env]
f. Include unit test and basic CI [pytest, GitHub Action]
g. Autoformat the script style [black, isort]

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

.
├── README.md
├── __init__.py
├── .github/workflows         [f]
├── autoformat.sh             [g]
├── data
│   ├── predict.csv           [b]
│   ├── test.csv              [b]
│   ├── train.csv             [b]
│   └── winequality.csv
├── log
│   ├── etl.log               [d]
│   ├── predict.log           [d]
│   └── train.log             [d]
├── model
│   └── model.pkl             [b]
├── notebook
│   └── prediction-of-quality-of-wine.ipynb [a]
├── requirement.txt           [e]
└── scripts
    ├── config.yml            [c]
    ├── etl.py                [b, c]
    ├── predict.py            [b, c]
    ├── test_train.py         [f]
    ├── test_utility.py       [f]
    ├── train.py              [b, c]
    └── utility.py

Мы упомянули, но не уточнили:

- ›MLFlow от Databricks / ModelDB от VertaAI

Мы можем обсудить еще кое-что:

  • Развертывание модели в облаке [AWS]
  • Контейнеризация [Docker]

Теперь у нас есть прочная основа, эти скрипты в той форме, в которой мы можем использовать их в дальнейшем! Представьте, как это можно сделать с помощью Jupyter Notebook?

Я понимаю, каково это, когда кто-то сказал, что вам следует попробовать ABC / XYZ, но они не объясняют, почему и как развиваться с нуля, я надеюсь, что эта статья может немного помочь :)

Если статья окажется полезной, вы можете оставить комментарии

ИЛИ вы можете поставить мое репо!

ИЛИ мой LinkedIn [Добро пожаловать, но, пожалуйста, оставьте несколько слов, чтобы указать, что вы не зомби]!

Большую часть этого я узнал во время стажировки в Manifold AI.