Португальская версия доступна здесь

В этой статье мы рассмотрим пример использования GitHub Actions в проекте машинного обучения (ML) 🫰.

Введение

Надежность — важный момент при развертывании моделей машинного обучения в продуктивной среде. Мы можем повысить надежность, скорость, качество и воспроизводимость с помощью путей CI/CD — непрерывной интеграции (CI) и непрерывного развертывания ( CD) — позволяет разработчикам автоматизировать процесс тестирования, сборки и развертывания кода.

GitHub Actions — это мощная платформа для реализации рабочих процессов CI/CD непосредственно в ваших репозиториях GitHub, мы сделаем пример, используя проект Simplified ML.

Конвейеры CI/CD для машинного обучения?

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

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

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

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

Но в конце концов, что такое GitHub Actions?

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

Практика — GitHub Actions для машинного обучения

Договоримся, что будем делать 😉

Прежде чем объяснять, как мы собираемся создавать конвейеры CI/CD с помощью GitHub Actions, давайте договоримся о том, каковы будут обязанности конвейера CI и CD:

Конвейеры непрерывной интеграции (CI) Обязанности

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

Обязанности конвейеров непрерывного развертывания (CD)

  • Храните артефакты в среде хранения
  • Опубликовать график запуска модели
  • Инициировать запуск модели для проверки правильности выполнения

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

Шаг 1. Создание базового проекта машинного обучения для примера

Поскольку мы собираемся моделировать CI/CD в проекте, нам нужно создать два файла примера в корне репозитория, файл train_model.py, который будет содержать обучающий код для модели для прогнозирования типа вина с использованием набора данных о вине из scikit-learn и файл requirements.txt, который будет содержать необходимые пакеты для выполнения проекта ML.

Шаг 1.1. Создайте обучающий файл модели ✍️

Создайте файл в корне репозитория train_model.py со следующим содержимым:

from sklearn.datasets import load_wine
from sklearn.ensemble import RandomForestClassifier
import numpy as np
import pickle

def load_dataset():
    """
    Loads the Wine dataset from Scikit-Learn.
    Returns:
    X (np.array): Input data.
    y (np.array): Target labels.
    """
    X, y = load_wine(return_X_y = True)
    return X, y

def train_model(X : np.array, y : np.array) -> RandomForestClassifier:
    """
    Trains a random forest model on the Wine dataset.
    Parameters:
    X (np.array): Input data.
    y (np.array): Target labels.
    returns:
    model (RandomForestClassifier): The trained model.
    """
    # Set the model
     model = RandomForestClassifier(n_estimators = 2 , max_depth = 1 , random_state = 1 )
    # Train the model
     model = model.fit(X, y)
    # Evaluate the training model
    print (f"Training Mean Accuracy : {model.score(X, y)} " )
    return model

def save_model( model, filepath ):
    """
    Saves the trained model to a pickle file.
    Parameters:
    model (any): The trained model to be saved.
    filepath (str): The file path where the trained model will be saved.
    """
    with open (filepath, 'wb' ) as f:
        pickle.dump(model, f)

def main():
    """
    The main function that performs the model training steps.
    This function loads the Wine dataset using the load_dataset() function,
    trains a random forest model on the loaded data using the train_model() function,
    and returns the trained model.
    """
    X, y = load_dataset()
    model = train_model(X, y)
    save_model(model, 'model.pkl')
    
    return

if __name__ == "__main__":
    main()

Шаг 1.2. Создайте необходимый файл пакетов для модели ✍️

Создайте файл в корне репозитория requirements.txt со следующим содержимым:

pytest==7.3.1
scikit-learn==1.2.2
numpy==1.24.3

Шаг 2. Создание файла рабочего процесса GitHub

Рабочие процессы GitHub Actions определяются в файлах .yml, которые находятся в каталоге .github/workflows в корне вашего репозитория GitHub.

Давайте создадим этот файл вместе, частично добавляя части, пока мы не завершим наши пайплайны CI/CD.

✍️ Создайте в репозитории новый файл с именем ml_workflow.ymldirectory , содержащий следующее содержимое. Начнем с создания конвейера непрерывной интеграции (CI) .github/workflows

# This is a basic workflow to help you get started with Actions
name: ML CI/CD Pipeline

# Controls when the workflow will run
on:
  # Triggers the workflow on push events but only for the "dev" branch
  push:
    branches: [ "dev" ]

# A workflow run is made up of one or more jobs that can run 
# sequentially or in parallel
jobs:
  # This workflow will contain two jobs, CI and CD.
  CI:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, 
      # so your job can access it
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v2
        with:
          python-version: '3.8.x'

      # Runs a single command using the runners shell
      - name: Run Hello
        run: echo Hello, Medium!

В этом примере рабочий процесс называется ML CI/CD Pipeline, он будет запускать нужные нам действия всякий раз, когда есть push na branch dev, работающий на Linux-машине с дистрибутивами ubuntuи python 3.8. Мы хотим, чтобы он просто сказал нам: «Привет, Medium! ».

Тестирование Шаг 2

Закоммитьте файл ml_workflow.yml в ветку dev, после этого войдите в репозиторий GitHub через браузер и перейдите на вкладку Actions, как на принтскрине ниже.

Вы увидите, как выполняется рабочий процесс, как показано на изображении ниже.

Нажмите на этот рабочий процесс, обратите внимание, что шаг Run Helloвыполнен успешно!

Шаг 3: Настройка среды

Для моделей Python часто требуются определенные пакеты для запуска, в нашем примере мы будем использовать scikit-learn и numpy.

✍️ Добавив этот фрагмент в файл .github/workflows/ml_workflow.yml рабочих процессов действий github, вы уже сможете установить пакеты, которые хотите для своего проекта Python, они были перечислены в файле requirements.txtмы, созданном на шаге 1.2!

- name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

Шаг 4. Разработка тестов для обучения модели

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

В нашем примере давайте проверим, что ученый не пропустил шаг, который ожидался в обучающем коде модели train_model.py, проверив два основных момента:

  • Наличие стандартных методов для контролируемого контекста моделирования, методов load_dataset, train_modelи save_model.
  • Правильное сохранение файла pickle с обученной моделью.

✍️ Создать в корне репозитория именованный файл unit_test_train_model.pyвнутри папки tests со следующим содержимым:

import pytest 
import os 
import train_model 

def  test_load_dataset_exists (): 
    """ 
    Tests whether the load_dataset method exists in the train module. 

    This test will check for the existence of the load_dataset method in the train module. 
    If the method does not exist, the test will fail with a message indicating that method load_dataset does not exist. 
    """ 
    assert  hasattr (train_model, 'load_dataset' ), "Method load_dataset does not exist" 

def  test_train_model_exists (): 
    """ 
    Tests whether method train_model exists in module train.

    This test will check for the existence of the train_model method in the train module. 
    If the method does not exist, the test will fail with a message indicating that the train_model method does not exist. 
    """ 
    assert  hasattr (train_model, 'train_model' ), "Method train_model does not exist" 

def  test_save_model_exists (): 
    """ 
    Tests whether method save_model exists in module train. 

    This test will check for the existence of the save_model method in the train module. 
    If the method does not exist, the test will fail with a message indicating that the save_model method does not exist. 
    """ 
    assert  hasattr (train_model,), "Method save_model does not exist" 

@pytest.fixture( scope= "module" ) 
def  train_and_save_model (): 
    """ 
    Fixture to train and save the model before running the test test_model_file_exists. 

    This function calls the main() function from the train module to train and save the model. 
    After testing is complete, remove the 'model.pkl' file. 
    """
     train_model.main() 
    yield
     os.remove( 'model.pkl' ) 

def  test_model_file_exists ( train_and_save_model ): 
    """ 
    Tests whether the model.pkl was saved correctly.

    This test, which depends on the train_and_save_model fixture, will check for the existence of the model.pkl file in the current directory. 
    If the file does not exist, the test will fail with a message indicating that the model.pkl file was not saved correctly. 
    """ 
    assert os.path.isfile( 'model.pkl' ), "The model.pkl file was not saved correctly"

Шаг 5: Шаг CI — запуск тестов

Добавьте следующий фрагмент в файл рабочих процессов действий github .github/workflows/ml_workflow.yml, вы уже сможете запускать различные тесты в конвейере CI, мы создали модульные тесты для проверки наличия стандартных методов в файле unit_test_train_model.py, который мы только что создали.

# Install Python packages dependencies from requirements.txt file
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      # Execute unit tests for train_model.py script
      - name: Run model training unit tests
        run: |
          python -m pytest tests/unit_test_train_model.py

Тестирование Шаг 5

Сделайте коммит в ветке dev текущих файлов репозитория, после этого зайдите в репозиторий GitHub через браузер и перейдите на вкладку Actions, как это сделано в шаге «Testing Step 2».

Мы видим, что код был выполнен успешно, так как наш файл train_model.py имеет все методы load_dataset, train_model и save_model, а также может успешно сохранить файл model.pkl.

Шаг 6: Шаг компакт-диска — развертывание

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

Ниже я приведу каждый файл ml_workflow.yml для повышения практичности при тестировании, ✍️ замените этот файл на старый, чтобы убедиться, что все в порядке.

Примечание. Шаги Upload artifacts, Schedule execution и Run deploy tests были упрощены в дидактических целях, поскольку эти шаги зависят от используемой инфраструктуры развертывания и могут включать учетные данные. Если вы хотите, чтобы я предоставил пример кода с использованием Azure, хлопните в ладоши этому сообщению

# This is a basic workflow to help you get started with Actions
name: ML CI/CD Pipeline

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "dev" ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  CI:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v2
        with:
          python-version: '3.8.x'

      # Runs a single command using the runners shell
      - name: Run Hello
        run: echo Hello, Medium!

      # Install Python packages dependencies from requirements.txt file
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      # Execute unit tests for train_model.py script
      - name: Run model training unit tests
        run: |
          python -m pytest tests/unit_test_train_model.py

  CD:
    needs: CI
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v2
        with:
          python-version: '3.8.x'

      # Install Python packages dependencies from requirements.txt file
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      
      # Run train model script
      - name: Train model
        run: |
          python train_model.py

      # Upload the model saved
      - name: Upload artifacts
        run:
          echo "You can choose where you want to storage your artifacts"

      # Schedule the execution of the model
      - name: Schedule execution
        run:
          echo "You can upload your new Airflow DAG to a specific folder "

      # Execute the model once to test if it does run
      - name: Run deploy tests
        run:
          echo "Here, you can execute a pytest to check if artifacts and the DAG were uploaded"
          echo "After this, you can ask Airflow to execute a first model run"

Тестирование Шаг 6

Зафиксируйте devтекущие файлы репозитория в ветку, включая файл ml_workflow.ymlпредоставленный на шаге 5, после этого войдите в репозиторий GitHub через браузер и перейдите на вкладку «Действия», как это сделано на шаге «Тестирование шага 2».

Теперь вы заметите, что CD зависит от CI, потому что добавление аргумента «needs: CI» на этапе CD гарантирует, что CD будет выполнен только в случае успешного завершения CI!

Нажав на шаг компакт-диска в действиях Github, мы получим статус успеха на всех этапах! 🥹

Нарушение (преднамеренно 😎) проекта ML

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

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

Имитация сбоя разработки: давайте вставим параметр n_neighborsпри создании экземпляра RandomForestClassifier, этот параметр не существует для этого класса. Кроме того, давайте изменим имя сгенерированного файла .pkl с model.pkl на my_beautiful_model.pkl.

Замените файл train_model.py содержимым ниже:

from sklearn.datasets import load_wine
from sklearn.ensemble import RandomForestClassifier
import numpy as np
import pickle

def load_dataset():
    """
    Loads the Wine dataset from Scikit-Learn.

    Returns:
    X (np.array): Input data.
    y (np.array): Target labels.
    """
    X, y = load_wine(return_X_y = True)
    return X, y

def train_model(X : np.array, y : np.array) -> RandomForestClassifier:
    """
    Trains a random forest model on the Wine dataset.

    Parameters:
    X (np.array): Input data.
    y (np.array): Target labels.

    returns:
    model (RandomForestClassifier): The trained model.
    """

    # Define the model
     model = RandomForestClassifier(n_estimators = 15 ,
                                   max_depth = 3 ,
                                   n_neighbors = 120 ,
                                   random_state = 1 )

    # Train the model model
     = model.fit(X, y)

    # Evaluate the training model
    print(f"Training Mean Accuracy: {model.score(X, y)} " )

    return model

def save_model ( model, filepath ):
    """
    Saves the trained model to a pickle file.

    Parameters:
    model (any): The model trained model to be saved.
    filepath (str): The file path where the trained model will be saved.
    """
    with open (filepath, 'wb' ) as f:
        pickle.dump(model, f)

def main():
    " ""
    The main function that performs model training steps.

    This function loads the Titanic dataset using the load_dataset() function,
    trains a random forest model on the loaded data using the train_model() function,
    and returns the trained model.
    """
     X, y = load_dataset()
    model = train_model(X, y)
    save_model(model, 'my_beautiful_model.pkl' )
    
    return


if __name__ == "__main__" :
    main()

Зафиксируйте devтекущие файлы репозитория в ветку, включая файл train_model.pyвыше, после этого войдите в репозиторий GitHub через браузер и перейдите на вкладку «Действия», как это сделано в шаге «Тестирование шаг 2».

Мы видим, что CI сломался, как и ожидалось, и по этой причине CD не запустился.

При доступе к шагу CI мы получаем предупреждение о TypeError: неожиданный аргумент ключевого слова ‘k_neighbors’ , на самом деле этот параметр не существует в классе RandomForestClassifier. 😇

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

Последние мысли

Внедрение CI/CD в проекты машинного обучения с помощью GitHub Actions не только оптимизирует процесс разработки, но и значительно повышает качество сгенерированных моделей и надежность построенных систем.

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

Надеюсь, в этой статье я дал вам четкое представление о том, как настроить конвейеры CI/CD для проектов машинного обучения с помощью GitHub Actions. Применение CI/CD к машинному обучению — это большой шаг к MLOps, дисциплине применения лучших практик DevOps в жизненном цикле машинного обучения. 😁

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

Благодарим вас за внимание к этой статье, и мы надеемся, что теперь вы лучше подготовлены для создания и управления собственным конвейером машинного обучения CI/CD с помощью GitHub Actions. 🤗

Давайте подключим

Вам понравилось содержание? Давайте выпьем кофе, добавьте меня в LinkedIn, чтобы обменяться идеями и поделиться знаниями!

https://www.linkedin.com/in/iagobrandao

Рекомендации

[1] Гитхаб (2023). Документация по действиям GitHub. Доступно на https://docs.github.com/pt/actions