Базовая утилита помогает поддерживать модульность кода и повышает скорость итерации.

Проекты по науке о данных существуют для воспроизводимого получения знаний из данных.

То есть объединяющая цель всех имеющихся в нашем распоряжении инструментов, от исследовательского анализа данных и визуализации до статистического моделирования и вплоть до передовых архитектур глубокого обучения с гигантскими пространствами параметров, заключается в создании новых знаний. Грубо говоря, мы либо учимся непосредственно посредством умозаключений, либо демонстрируем построением, что наш черный ящик может решить данную проблему. Совершенство требует, чтобы мы были прозрачными в нашем процессе, настолько, чтобы другой человек мог воспроизвести его, тщательно изучить, подправить и улучшить.

GNU Make, классическая и проверенная временем утилита сборки, значительно упрощает достижение этих целей.

В примере проекта, созданном для этого поста, мы загрузим рефераты NeurIPS (выпущенные под CC BY 4.0), немного преобразуем их и, наконец, подгоним под скрытую модель распределения Дирихле. В дальнейшем мы будем использовать Make для организации этого процесса. Среди множества преимуществ мы рассмотрим:

  1. Естественный способ выразить зависимости между шагами в конвейере обучения. (Читайте, «уход от «черт возьми, эти ячейки кода блокнота в правильном порядке…», черт возьми».)
  2. Подталкивание к модульности: простое управление зависимостями побуждает нас разбивать наши процедурные монолиты на крошечные красивые храмы.
  3. Легко использовать утилиты: поскольку Make координирует команды для нас, мы можем использовать все, что установлено на хосте. Хотите обработать текст с помощью sed перед последующими задачами в python? Лимонный сок, Фам.
  4. Более быстрые итерации: Make помогает нам избежать ненужных повторных вычислений, ускоряя наши инновационные циклы и помогая нам строить быстрее.
  5. Более простое внешнее сотрудничество: полагаясь на набор сценариев — а не, например, на записные книжки — и выражая их зависимости, Make помогает нам передать спецификацию нашим друзьям в области обработки данных без особой дополнительной работы.

Пример проекта следует за резаком для печенья DS, выпущенным DrivenData. Этот шаблон впервые показал мне ценность Make for DS, и это сам по себе уровень эффективности.

Что такое сделать

(Опытные пользователи Make, смело пропускайте этот раздел — вам будет скучно.)

Традиционно и по замыслу Make — это инструмент сборки исполняемых файлов. Из GNU.org:

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

Например, обычно можно увидеть файлы Make в проектах C++. Но это его наиболее распространенное использование, не ограничение. Еще от ГНУ:

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

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

Это очень естественно поддается анализу или проектам машинного обучения. Рассмотрим этот довольно стандартный рабочий процесс науки о данных:

  1. Получите некоторые данные, загрузив файл или запросив базу данных.
  2. Обработка данных для очистки, вменения и т. д.
  3. Особенности инженера.
  4. Обучите и сравните ряд архитектур моделей или конфигураций.

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

Легкость выражения

Каждый из этих шагов может быть естественным образом выражен в правиле Make. Например, предположим, что нам нужно загрузить файл типа набор данных NeurIPS из репозитория UCI ML. Мы можем загрузить его, например, с помощью curl. Это создает простое правило Make:

data/raw/NIPS_1987-2015.csv : curl -o $@ https://archive.ics.uci.edu/ml/machine-learning-databases/00371/NIPS_1987-2015.csv

Говоря языком Make, этот целевой файл, который мы хотим создать, не имеет предварительных требований, а только рецепт его создания. Это $@ — это автоматическая переменная Make, которая заполняет имя цели. Перейдите в корень папки демо-проекта, запустите make --recon data/raw/NIPS_1987-2015.csv, и вы должны увидеть команды, которые Make запустит для создания файла:

curl -o data/raw/NIPS_1987-2015.csv https://archive.ics.uci.edu/ml/machine-learning-databases/00371/NIPS_1987-2015.csv

Запустите make data/raw/NIPS_1987-2015.csv без флага --recon, и эта команда действительно запустится. Запустите его еще раз, и вы получите это сообщение, способ Make сказать нам, чтобы мы не тратили время на повторную загрузку:

make: `data/raw/NIPS_1987-2015.csv' is up to date.

Мы видим, что необработанный файл состоит из столбцов, и мы хотим его транспонировать. Мы поместим транспонированную версию в data/interim, набросаем упрощенный скрипт для выполнения операции и выразим зависимость и выполнение в другом правиле:

data/processed/NIPS_1987-2015.csv : src/data/transpose.py data/raw/NIPS_1987-2015.csv $(PYTHON_INTERPRETER) $^ $@

Здесь мы использовали $^, еще одну автоматическую переменную, которая заполняет все предварительные условия. Другой вызов Make с использованием флага --recon, make data/processed/NIPS_1987-2015.csv --recon, покажет нам команды, которые мы увидим в реальном запуске, а именно то, что он создаст целевой файл, используя написанный нами скрипт Python:

python3 src/data/transpose.py data/raw/NIPS_1987-2015.csv data/processed/NIPS_1987-2015.csv

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

models/%_topics.png : src/models/fit_lda.py data/processed/NIPS_1987-2015.csv src/models/prodlda.py $(PYTHON_INTERPRETER) $< $(word 2, $^) $@ --topics $*

Здесь мы написали fit_lda.py для обучения модели и создания облака слов для каждой темы, которое мы фиксируем в целевом файле png. Это упрощено для примера. В типичной профессиональной сборке наши цели будут гораздо более всеобъемлющими: мы включим кривые обучения, сводки ошибок в каждом сегменте нашего набора данных и все остальное, что нам нужно для эмпирического сравнения различных архитектур моделей. Мы также используем правила шаблонов, используя символ % в цели вместе с макросом Make word. Подробнее об этом мы поговорим в разделе о модульности.

Каждый из этих шагов имеет естественные зависимости от предыдущих шагов, и Make дает нам естественный способ их выражения.

Более быстрая итерация

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

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

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

Подобные вещи случаются десятки раз в ходе проекта по науке о данных. Ускорение может значительно увеличить скорость итерации. Эдмунд Лау пишет о скорости итераций в книге The Effective Engineer применительно к разработке программного обеспечения, но ключевой вывод распространяется и на науку о данных: «Чем быстрее вы можете выполнять итерации, тем больше вы можете узнать. ” (Партнерская ссылка.) С моей стороны, отказ от такого ненужного повторения сделал проекты намного более эффективными.

Поощряемая модульность

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

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

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

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

models/%_topics.png : src/models/fit_lda.py data/processed/NIPS_1987-2015.csv src/models/prodlda.py $(PYTHON_INTERPRETER) $< $(word 2, $^) $@ --topics $*

Правило шаблона упрощает объявление правил для создания множества файлов с использованием одного и того же общего шаблона выполнения. В нашем случае мы используем автоматическую переменную $*, чтобы включить основу, соответствующую фактическому выполнению, и мы используем ее как количество тем в нашей модели тем. Объедините это с фальшивой целью all, и мы сможем обучить несколько моделей, используя один запуск make all:

all : models/10_topics.png models/20_topics.png

Затем мы можем сравнить вывод обоих.

Использовать утилиты

Скажем, у вас есть тысяча текстовых файлов, все с одними и теми же 20 строками шаблона, которые необходимо удалить. Скучно, да? Также легко с Make:

data/processed/%.txt : data/raw/%.txt sed 1,20d $^ > $@

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

Простота сотрудничества

По крайней мере, по моему опыту, существует столько же ответов на вопрос «как нам заставить наши модели работать?» как есть организации и архитектуры. У многих (большинства?) организаций есть многолетние данные и унаследованные ограничения, которые нужно учитывать. Это может означать, что, как бы нам ни хотелось внедрить новейшую бесшовную структуру MLOps, мы можем сотрудничать с командами разработчиков программного обеспечения или данных.

Часто эти команды были обучены на собственном опыте: «Можете ли вы помочь команде DS развернуть это?» часто переводится как: «Можете ли вы взять этот плохо документированный блокнот, в котором не задокументировано ни одно из предположений, которые использовались при построении модели, и каким-то образом превратить его в процесс сборки, который можно обслуживать? Встань с противоположной стороны этого забора, пока я перекину его, ктксбай!»

Make — это не волшебная палочка, чтобы отмахнуться от любой из этих проблем. Но хорошо построенный конвейер модели, организованный в Make, уже выражает процесс как DAG. Явно указывая предпосылки, правило Make точно показывает, что должно предшествовать данному шагу. Таким образом, даже если наша сборка модели должна быть перестроена в какой-то другой среде, наш Makefile дает подробную спецификацию этого процесса.

«Конечно, мы можем помочь», — начинают наши друзья-разработчики программного обеспечения или данных, подавляя дрожь. «Можете ли вы рассказать нам о процессе обучения/сборки вашей модели?»

«В целом, если вы клонируете наш репозиторий, переходите к проекту и запускаете make all с флагами -B и --recon, он покажет вам, как выполняются все шаги. Я скину ссылку на репозиторий, подожди...'

Когда они запустят эту команду — флаг -B говорит Make просто переделать все цели, даже если они не устарели, — они увидят все, что мы должны сделать для построения наших моделей:

curl -o data/raw/NIPS_1987-2015.csv https://archive.ics.uci.edu/ml/machine-learning-databases/00371/NIPS_1987-2015.csv python3 src/data/transpose.py data/raw/NIPS_1987-2015.csv data/processed/NIPS_1987-2015.csv python3 src/models/fit_lda.py data/processed/NIPS_1987-2015.csv models/10_topics.png --topics 10 python3 src/models/fit_lda.py data/processed/NIPS_1987-2015.csv models/20_topics.png --topics 20

Почему ты ненавидишь блокноты, чудовище?

Я категорически нет, клянусь. Они невероятно полезны в качестве средств коммуникации — я бы предпочел писать или читать гладкую записную книжку по слайдам в любой день — и для интерактивных записей мыслей в DS. «О, когда я рисую это, мы видим некоторую коллинеарность… давайте посчитаем и напечатаем R-квадрат…» — это то, что я бормочу себе по утрам. Я люблю все блокноты, как IPython, так и физические, заполненные претенциозно, излишне качественной бумагой. (Я был специалистом по английскому языку.)

Я представляю, как Жанетт Уинтерсон заполнила десятки физических тетрадей, пока писала The Passion. (Партнерская ссылка.) Тем не менее, в конце этого процесса она опубликовала роман, краткий и цельный, красивый и очаровательный. Вы, мой дорогой специалист по данным, также являетесь художником амбиций и видения, и мы оказываем себе медвежью услугу, когда путаем наше окончательное средство с набросанными мыслями, которые приводят нас туда.

Если оставить в стороне вымученные метафоры, блокноты — это кошмар системы управления версиями.

Подведение итогов

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

  • Полный пример проекта показывает, как были написаны отдельные сценарии для выполнения каждого из рассмотренных выше шагов.
  • Он построен из этого форма для печенья, невероятно самоуверенного способа создавать проекты, основанные на невероятных, хорошо аргументированных мнениях. Его стоит внимательно прочитать, особенно из-за его краткого аргумента о том, как простые соглашения могут повысить производительность и воспроизводимость вашего моделирования.
  • Эта великая книга может помочь людям погрузиться во все возможности GNU Make, в которые мы только вникли. (Партнерская ссылка.) Его основное внимание уделяется традиционным проектам программного обеспечения, но это не препятствие: он подробно представляет возможности Make и дает подробное руководство. Если вы хотите избавить себя от ловушек, которые возникают при попытке изучить новые технологии исключительно из документации, это может быть хорошим чтением.

Первоначально опубликовано на https://easter.ai 1 марта 2022 г.