Подтвердить что ты не робот

Лучшие практики для превращения jupyter-ноутбуков в скрипты python

Jupyter (iPython) ноутбук заслуженно известен как хороший инструмент для прототипирования кода и интерактивного использования всех видов машинного обучения. Но когда я его использую, я неизбежно сталкиваюсь с следующим:

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

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

  • Просто конвертируйте .ipynb в .py и, только с небольшими изменениями, скопируйте весь конвейер из ноутбука в один python script.

    • '+': быстрый
    • '-': грязный, негибкий, не удобно поддерживать
  • Сделайте одиночный script со многими функциями (приблизительно, 1 функция для каждой одной или двух ячеек), пытаясь включить этапы конвейера с отдельными функциями и назовите их соответственно. Затем укажите все параметры и глобальные константы через argparse.

    • '+': более гибкое использование; более читаемый код (если вы правильно преобразовали логику конвейера в функции)
    • '-': часто трубопровод НЕ расщепляется на логически завершенные части, которые могут стать функциями без каких-либо причуд в коде. Все эти функции обычно необходимо вызывать только один раз в script, а не много раз повторяться внутри циклов, карт и т.д. Кроме того, каждая функция обычно выводит выходные данные всех функций, называемых ранее, поэтому приходится передавать много аргументов каждой функции.
  • То же самое, что и точка (2), но теперь оберните все функции внутри класса. Теперь все глобальные константы, а также выходы каждого метода могут быть сохранены как атрибуты класса.

    • '+': вам не нужно передавать много аргументов каждому методу - все предыдущие выходы, уже сохраненные как атрибуты
    • '-': общая логика задачи все еще не захвачена - это конвейер данных и машинного обучения, а не только класс. Единственная цель для класса должна быть создана, вызывать все методы последовательно один за другим, а затем удаляться. Кроме того, классы довольно долго реализуются.
  • Преобразование ноутбука в модуль python с несколькими скриптами. Я не пробовал это, но я подозреваю, что это самый длинный способ справиться с этой проблемой.

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

Люди, пожалуйста, поделитесь своими идеями и опытом. Вы когда-нибудь сталкивались с этой проблемой? Как вы справились с этим?

4b9b3361

Ответ 1

У нас подобная проблема. Однако мы используем несколько ноутбуков для прототипирования результатов, которые должны стать также несколькими скриптами python.

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

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

Больше всего мы не боимся рефакторинга:)

Ответ 2

Спасатель жизни: когда вы пишете свои записные книжки, постепенно реорганизуйте свой код в функции, написав несколько минимальных assert и строк документации.

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

Базовый пример содержимого ячейки с "минимальными" тестами и строками документов:

def zip_count(f):
    """Given zip filename, returns number of files inside.

    str -> int"""
    from contextlib import closing
    with closing(zipfile.ZipFile(f)) as archive:
        num_files = len(archive.infolist())
    return num_files

zip_filename = 'data/myfile.zip'

# Make sure 'myfile' always has three files
assert zip_count(zip_filename) == 3
# And total zip size is under 2 MB
assert os.path.getsize(zip_filename) / 1024**2 < 2

print(zip_count(zip_filename))

После того как вы экспортировали его в файлы .py, ваш код, вероятно, еще не будет структурирован в классы. Но стоит потратить усилия на то, чтобы провести рефакторинг вашей записной книжки до такой степени, чтобы она имела набор документированных функций, каждая с набором простых assert которые можно легко переместить в tests.py для тестирования с помощью pytest, unittest или чем-то еще. вы. Если это имеет смысл, объединить эти функции в методы для ваших классов будет непросто после этого.

Если все идет хорошо, все, что вам нужно сделать после этого, это написать свой if __name__ == '__main__': и его "ловушки": если вы пишете скрипт для вызова терминалом, вы захотите обработать команду- строковые аргументы: если вы пишете модуль, вам нужно подумать о его API с файлом __init__.py и т.д.

Разумеется, все зависит от предполагаемого варианта использования: существует большая разница между преобразованием ноутбука в небольшой скрипт и превращением его в полноценный модуль или пакет.

Вот несколько идей для документооборота:

  1. Экспортируйте блокнот Jupyter в файл Python (.py) через графический интерфейс.
  2. Удалите "вспомогательные" строки, которые не выполняют реальной работы: операторы print, графики и т.д.
  3. Если нужно, объедините свою логику в классы. Единственная дополнительная работа по рефакторингу должна заключаться в написании строк документации и атрибутов вашего класса.
  4. Напишите ваш сценарий для ввода с помощью if __name__ == '__main__'.
  5. Отдельные операторы assert для каждой из ваших функций/методов и tests.py минимальный набор тестов в tests.py.

Ответ 3

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

from NotebookScripter import run_notebook
run_notebook("./path/to/Notebook.ipynb", some_param="Provided Exteranlly")

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

Внутри клетки.ipynb

from NotebookScripter import receive_parameter

some_param = receive_parameter(some_param="Return this value by default when matching keyword not provided by external caller")

print("some_param={0} within the invocation".format(some_param))

run_notebook() поддерживает файлы.ipynb или.py, что позволяет легко использовать файлы.py, которые могут быть сгенерированы с помощью nbconvert vscode ipython. Вы можете сохранить свой код организованным таким образом, который имеет смысл для интерактивного использования, а также повторно использовать/настраивать его внешне, когда это необходимо.