Начните использовать платформу машинного обучения независимо от того, есть ли у вас кластер

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

Мой репозиторий GitHub «kubeflow_k8s» содержит все файлы python и yaml, а также инструкции по установке, необходимые для получения продемонстрированных здесь результатов.

Пример обучения моделиtrain_amazon.py подготавливает текстовые обучающие данные, загружает и совершенствует модель текстовой тональности TensorFlow и выполняет ее вывод для проверки текстовых данных — и все это за один запуск:

python3 train_amazon.py

Цель – создать рабочую среду:

Вот основные шаги.

Кластер
Репозиторий GitHub «Kubeflow_k8s» подробно описывает пошаговый процесс установки и развертывания кластера Kubeflow/Kubernetes на настольном компьютере, проверенный на моей Fedora/32-Core/130 Gib. и Macbook Pro mini/2 ядра/8 Гб.

Рефакторинг в постоянные функциональные компонентыtrain_amazon_refactured.py иллюстрирует разделение train_amazon.py на функциональные блоки без сохранения состояния, которые сохраняют его результаты. Например:

def get_model(output_untrained_model: str):
    hub_layer = hub.KerasLayer(
        "https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1", output_shape = [128],
        input_shape = [], dtype = tf.string, name = 'input', trainable = False
    )

    model = tf.keras.Sequential()
    model.add(hub_layer)
    model.add(tf.keras.layers.Dense(64, activation = 'relu'))
    model.add(tf.keras.layers.Dense(3, activation = 'softmax', name = 'output'))
    model.compile(
        loss = 'categorical_crossentropy',
        optimizer = 'Adam', metrics = ['accuracy']
    )
    model.summary()
    print("\n\nsave untrained_model.pickle\n\n")
    model.save(output_untrained_model)

Функция get_model загружает обученную модель nnlm-en-dim128, добавляет два полносвязных слоя, определяет слой классификатора с тремя классами и сохраняет необученную модель. Убедитесь, что новые выходные данные не изменились после рефакторинга:

python3 train_amazon_refactured.py

Контейнеризация в компоненты и конвейер Kubeflow
kfp_train_amazon.py определяет четыре компонента и конвейер. Сравните новый рецепт контейнера get_model (который под капотом предписывает локальный образ докера и сборку компонентов через декоратор Kubeflow) с предыдущим функциональным компонентом:

@component(
    packages_to_install = ["tensorflow", "tensorflow_hub"],
    output_component_file = "component_nnlm_model.yaml"
)
def nnlm_model_download(untrained_model: Output[Artifact]):
    import tensorflow as tf
    import tensorflow_hub as hub

    hub_layer = hub.KerasLayer(
        "https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1", output_shape = [128], input_shape = [],
        dtype = tf.string, name = 'input', trainable = False
    )

    model = tf.keras.Sequential()
    model.add(hub_layer)
    model.add(tf.keras.layers.Dense(64, activation = 'relu'))
    model.add(tf.keras.layers.Dense(3, activation = 'softmax', name = 'output'))
    model.compile(
        loss = 'categorical_crossentropy', optimizer = 'Adam', metrics = ['accuracy']
    )
    model.summary()
    print("\n\nsave untrained_model.pickle\n\n")
    model.save(untrained_model.path)

Конвейер Kubeflow определяется в «pipeline_amazon.yaml» следующим образом:

@dsl.pipeline(name = "train_amazon_pipeline")
def my_pipeline(epochs: int = 10,
                batch_size: int = 12,
                num_samples: int = 10000,
                url_train: str = "https://www.dropbox.com/s/tdsek2g4jwfoy8q/train.csv?dl=1",
                url_test: str = "https://www.dropbox.com/s/tdsek2g4jwfoy8q/test.csv?dl=1"):
    download_train_data_task = load_dataset(url = url_train, num_samples = num_samples)

    nnlm_model = nnlm_model_download()

    train_model_task = train(
        epochs = epochs, batch_size = batch_size,
        input_labels_artifacts = download_train_data_task.outputs["output_labels_artifacts"],
        input_text_artifacts = download_train_data_task.outputs["output_text_artifacts"],
        input_untrained_model = nnlm_model.outputs["untrained_model"]
    )

    download_test_data_task = load_dataset(url = url_test, num_samples = num_samples)

    eval_model_task = eval_model(
        input_model = train_model_task.outputs["output_model"],
        input_labels_artifacts = download_test_data_task.outputs["output_labels_artifacts"],
        input_text_artifacts = download_test_data_task.outputs["output_text_artifacts"]
    )


kfp.compiler.Compiler(mode = kfp.dsl.PipelineExecutionMode.V2_COMPATIBLE).compile(
    pipeline_func = my_pipeline, package_path = 'pipeline_amazon.yaml'
)

Создайте файл yaml конвейера «pipeline_amazon.yaml»:

python3 kfp_train_amazon.py

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

Конвейеры Kubeflow можно легко развернуть в нескольких производственных средах с помощью — локальных, управляемых и локальных кластеров Kubeflow/Kubernetes, которые, в свою очередь, планируют и сохраняют выполнение конвейера:

Результаты
Kubeflow/Kubernetes — файл «pipeline_amazon.yaml», сгенерированный автоматически, определяет «рабочий процесс» контроллера Kubernetes и контейнеры компонентов:

Точность — «pipeline_amazon.yaml» подходит для данных обучения длиной в десять тысяч предложений с точностью обучения-валидации 0,95 и 0,75 соответственно. Обучение с полными данными устраняет переобучение и точность проверки 97%:

python3 train_amazon_refactured.py

# run time approx. 20mins 5 epochs. Validation:

8883/8883 [=========] - 2020s 227ms/step - loss: 0.1355 - accuracy: 0.9723

MLOps и стресс-тест — мой 32-ядерный 130-гигабитный Fedora 36 может планировать десятки одновременно работающих конвейеров:

python3 kfp_stress.py

с ресурсами Docker, установленными на 20 ядер и 64 ГБ памяти. Незаметно, поскольку этот простой пример полностью загружает обучающие и тестовые данные в память, память является основным узким местом для конвейеров, работающих параллельно. Я использую кеш компонентов Kubeflow, который позволяет пропустить выполнение контейнера для идентичных компонентов и параметров:

Моему 2-ядерному 8-гигабитному mac Book mini удается выполнить всего несколько заданий, но конечная точка Kubeflow часто дает сбой. В состоянии «ожидания» локально установленный кластер Kubeflow/Kubernetes запускает 14 развертываний.

Будущая работа

Контейнеризация — рефакторинг кода в образ докера и создание четырех новых компонентов и конвейеров с использованием контейнеров.

Память — найдите решение для узкого места памяти

Выводы — создать обслуживающий сервис с помощью компонента докера eval-model.

GPU — добавьте обучение GPU Tensorflow и логические выводы, а также оцените ускорение. Планирование новых ресурсов GPU для локального кластера Kubeflow: https://jacobtomlinson.dev/posts/2022/quick-hack-adding-gpu-support-to-kind/

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

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

Advanced MLOps — создайте производственный уровень с другой платформой ML (MLFlow, AirFlow, MetaFlow и др.) и сравните с Kubeflow/Kubernetes.

Advanced MLOps — разверните новую главную и две подчиненные виртуальные машины, установите новый кластер Kubeflow, определите новый планировщик и запланируйте GPU на подчиненных узлах.

Advanced MLOps — установите облачную платформу на новые виртуальные машины (OpenStack или аналогичные) и настройте ресурсы, такие как выделение узлов GPU и автоматическое масштабирование.