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

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

Проблема: сохранить качество кода проектов обработки данных

Общие проекты обработки данных имеют более одного шага (или вызова метода), что занимает много времени. Некоторые проекты загружают входные данные из хранилища данных. Другие проекты проходят обучение по модели машинного обучения, что также занимает много времени.

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

Такие проекты, которые имеют много шагов и требуют много времени для выполнения, отнимают много наших ресурсов разработки.

Этот трудоемкий аспект проектов обработки данных затрудняет поддержание качества проектных кодов.

Неадекватные решения

Для повышения эффективности разработки обычно применяются два недостаточных решения.

Добавить временные файлы в репозиторий VCS

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

├── Makefile
├── README.md
├── config
│   ├── __init__.py
│   └── env.py
├── data
│   ├── dictionary.dic
│   ├── preprocessed
│   │   ├── preprocessed_input1.txt
│   │   └── preprocessed_input2.txt
│   ├── models
│   │   ├── validation_model1.dat
│   │   └── validation_model2.dat
...

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

Добавьте процедуры кэширования

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

def generate_id_map(cache_file_path):
    if os.path.exists(cache_file_path) and not self.force:
        with open(cache_file_path, mode='rb') as f:
            return pickle.load(f)
​
    id_map = _generate_map_impl()
​
    if not os.path.exists(cache_file_path) or not self.force:
        with open(cache_file_path, mode='wb') as f:
            pickle.dump(ingredient_id_map, f)
    return id_map

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

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

Убежище

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

Основное использование

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

def generate_large_object(times):
    sleep(1000)
    return map(lambda x: x*2, range(times))

Для кэширования результатов мы просто добавляем украшение, resumable поддерживаемое Hideout.

@resumable()
def generate_large_object(times):
    sleep(1000)
    return map(lambda x: x*2, range(times))

Когда мы запускаем вышеуказанную функцию, Hideout не сохраняет/загружает файлы кеша, только когда для переменной среды HIDEOUT_ENABLE_CACHE установлено значение true, Hideout генерирует файлы кеша в каталоге caches.

Пропустить кэширование указанных этапов

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

Поддержка убежища stage для этой цели. Когда мы хотим подавить определенный этап, мы добавляем имя в переменную окружения HIDEOUT_SKIP_STAGES.

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

Ниже приведена функция, используемая на заключительном этапе.

@resumable(stage="final")
def generate_large_object(data_1, data_2):
    sleep(1000)
    return data_1 + data_2

Например, если мы хотим подавить кеширование для вышеуказанной функции (generate_large_object), мы запускаем команду с HIDEOUT_SKIP_STAGES=final и Hideout пропускаем сохранение и загружаем файлы кеша только для указанного этапа.

Резюме

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