Лучшие практики для превращения записных книжек jupyter в скрипты Python

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

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

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

  1. Просто преобразуйте .ipynb в .py и, с небольшими изменениями, жестко запрограммируйте весь конвейер из записной книжки в один скрипт Python.

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

    • '+': more flexible usage; more readable code (if you properly transformed the pipeline logic to functions)
    • '-': часто конвейер НЕ разделяется на логически завершенные части, которые могут стать функциями без каких-либо причуд в коде. Все эти функции обычно необходимо вызывать только один раз в сценарии, а не вызывать много раз внутри циклов, карт и т. Д. Кроме того, каждая функция обычно принимает выходные данные всех функций, вызванных ранее, поэтому нужно передавать много аргументов каждой функция.
  3. То же, что и пункт (2), но теперь все функции обертываются внутри класса. Теперь все глобальные константы, а также выходные данные каждого метода могут быть сохранены как атрибуты класса.

    • '+': you needn't to pass many arguments to each method -- all the previous outputs already stored as attributes
    • '-': общая логика задачи все еще не зафиксирована - это конвейер данных и машинного обучения, а не только класс. Единственная цель для класса - создать, последовательно вызвать все методы, один за другим, а затем удалить. Вдобавок к этому классы довольно длинные для реализации.
  4. Преобразуйте блокнот в модуль Python с помощью нескольких скриптов. Я не пробовал это делать, но подозреваю, что это самый долгий способ решения проблемы.

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

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


person kurtosis    schedule 24.08.2015    source источник
comment
Вы просто хотите извлечь код Python из файла Notebook?   -  person kxxoling    schedule 26.08.2015
comment
Я делал это пару раз - именно так я упоминал в пункте (1). У него есть недостатки. Во-первых, негибкий жесткий код. Во-вторых, обычно вы не хотите выполнять каждую ячейку в записной книжке одну за другой (может быть много чисто визуализационных или просто избыточных ячеек), поэтому вам все равно нужно очистить код после экспорта - такой же объем работы, как если бы вы просто скопировали ячейки вручную.   -  person kurtosis    schedule 26.08.2015
comment
Вы можете написать сценарий для извлечения всего кода Python, а не вручную. Это то, что вы хотите?   -  person kxxoling    schedule 26.08.2015
comment
Простой экспорт записной книжки в файл .py делает именно это - извлекает весь код Python. Но это не совсем то, что я хочу. Я бы хотел сделать из него хороший сценарий в целом: с прозрачным кодом, с классами и функциями, когда это необходимо, с гибким api (например, argparse) и т. Д. Как лучше всего обернуть «конвейер данных» в код логика?   -  person kurtosis    schedule 26.08.2015
comment
@kutosis Смотрите ... Это может зависеть от использования, если он будет запускаться только один раз, оставьте это как скрипт в порядке. Если требуется гибкость, вы можете обернуть ее в модуль или исполняемый файл, как хотите, в классе или функции не имеет значения.   -  person kxxoling    schedule 26.08.2015
comment
Относительно этой части вашего вопроса: предположим, что я разработал весь конвейер машинного обучения в jupyter, который включает выборку необработанных данных из различных источников, очистку данных, разработку функций. Приходилось ли вам использовать Quilt для управления своими данными, например кодом? Наш магазин машинного обучения использует его, и это очень полезно.   -  person tatlar    schedule 26.07.2018


Ответы (4)


Спасатель жизни: по мере написания блокнотов постепенно реорганизуйте код в функции, создавая несколько минимальных 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 или чем-то еще. Если это имеет смысл, после этого очень легко объединить эти функции в методы для ваших классов.

Если все пойдет хорошо, все, что вам нужно сделать после этого, - это написать свой _8 _ и его «крючки»: если вы пишете скрипт, который будет вызываться терминалом, вам понадобится обрабатывать аргументы командной строки, если вы пишете модуль, о котором нужно подумать свой API с __init__.py файлом и т. д.

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

Вот несколько идей для рабочего процесса от записной книжки к сценарию:

  1. Экспортируйте блокнот Jupyter в файл Python (.py) через графический интерфейс.
  2. Удалите «вспомогательные» строки, которые не выполняют реальной работы: print операторы, графики и т. Д.
  3. При необходимости объедините свою логику в классы. Единственная необходимая дополнительная работа по рефакторингу - это написание строк документации и атрибутов вашего класса.
  4. Напишите входы вашего скрипта с if __name__ == '__main__'.
  5. Разделяйте свои assert операторы для каждой из ваших функций / методов и конкретизируйте минимальный набор тестов в tests.py.
person François Leblanc    schedule 21.07.2017

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

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

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

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

person Radek    schedule 30.12.2015
comment
Это звучит как хороший подход: кажется, невозможно провести рефакторинг непосредственно в блокноте - будь то ipython / jupyter, zeppelin, spark-notebook… что угодно. Это остановка. Таким образом, ваша характеристика ноутбука как конфигурации является дополнительным аспектом к моему мнению, что это был инструмент для презентаций, а не инструмент для разработки. - person WestCoastProjects; 04.07.2017
comment
@javadba Для этого нам понадобится интегрированная с языком IDE, ориентированная как на производственное использование, так и на исследование. - person CMCDragonkai; 11.04.2018

Недавно я сделал модуль (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's 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 из ipython vscode. Вы можете организовать свой код таким образом, чтобы это было удобно для интерактивного использования, а также повторно использовать / настраивать его извне, когда это необходимо.

person Ben    schedule 10.12.2018

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

Я бы пошел по этому пути:

  1. Добавьте несколько тестов в свой конвейер, для конвейеров машинного обучения это немного сложно, но если ваш ноутбук обучает модель, вы можете использовать метрики производительности, чтобы проверить, работает ли ваш конвейер по-прежнему (точность вашего теста может быть = 0,8, но убедитесь, что вы определили допустимый диапазон, поскольку число вряд ли будет одинаковым для каждого прогона)
  2. Разбейте свой единственный блокнот на более мелкие, выходные данные одного должны использоваться для другого. Как только вы создадите разделение, убедитесь, что вы добавили несколько тестов для каждой записной книжки отдельно. Чтобы управлять этим последовательным выполнением, вы можете использовать бумажную фабрику для выполнения ваших записных книжек или инструмент управления рабочим процессом, такой как < href = "https://github.com/ploomber/ploomber" rel = "nofollow noreferrer"> ploomber, который интегрируется с papermill, может разрешать сложные зависимости и имеет ловушку для запуска тестов при выполнении записной книжки ( Отказ от ответственности: я автор ploomber)
  3. Когда у вас есть конвейер, состоящий из нескольких записных книжек, который проходит все ваши тесты, вы можете решить, хотите ли вы продолжать использовать формат ipynb или нет. Я бы порекомендовал оставить в виде записных книжек только те задачи, которые имеют расширенный вывод (например, таблицы или графики), остальное можно преобразовать в функции Python, которые более удобны в обслуживании.
person Edu    schedule 17.03.2020