top of page
Gambar penulisCornellius Yudha Wijaya

Model Retraining Otomatis Sederhana dengan GitHub Actions

Diperbarui: 16 Jul 2024

Model Retraining Otomatis Sederhana dengan GitHub Actions
Photo by Praveen Thirumurugan on Unsplash

Model Machine Learning dapat memberikan nilai yang sangat besar bagi bisnis. Namun, mengembangkannya bukanlah aktivitas yang selesai satu kali saja. Sebaliknya, ini adalah proses berkelanjutan agar model tetap memberikan nilai. Inilah yang melatarbelakangi munculnya MLOps.


Kombinasi prinsip CI/CD (Continuous Integration and Continuous Deployment,) dan pengembangan machine learning disebut MLOps, yang bertujuan untuk memberikan nilai berkelanjutan dengan model tersebut.


Salah satu cara agar model Machine Learning memberikan manfaat secara terus-menerus adalah dengan melatih ulang (retaining) model saat diperlukan. Misalnya jika terdeteksi adanya data drift. Kita bisa melakukan otomatisasi retraining model dengan mengatur lingkungan untuk pemicu retraining.


Kita bisa menggunakan tool GitHub yang disebut GitHub Actions untuk memfasilitasi proses retraining ini. Tool ini adalah fitur dari GitHub untuk platform CI/CD yang digunakan untuk mengotomatisasi proses pengembangan perangkat lunak dari repository GitHub.


Artikel ini akan mengajarkan kita cara melakukan otomatisasi retraining model yang dikendalikan dengan GitHub Actions. Bagaimana caranya? Mari kita mulai.



Persiapan

Pada proyek ini kita akan melakukan pengembangan dan demonstrasi otomatisasi model sederhana. Secara keseluruhan, Struktur proyek s akan terlihat seperti diagram di bawah ini.

Model Retraining Otomatis Sederhana dengan GitHub Actions

Mari kita mulai dengan menyiapkan repository GitHub untuk menggunakan GitHub Actions dalam repo ini. Anda dapat membuat repository kosong dengan nama apa saja. Untuk saya, saya membuat repository ini.


Selain itu, kita akan mensimulasikan deployment model dengan Docker. Untuk itu, mari instal Docker Desktop. Daftarlah ke Dockerhub jika Anda belum terdaftar.


Kemudian, mari kita buat GitHub Personal Access Token (PAT) dengan cakupan Repo dan Workflow. Simpan token tersebut di suatu tempat, dan kembali ke repository kosong yang baru saja Anda buat. Pergi ke pengaturan dan pilih “Secret and variables”. Selanjutnya, buat Repository secrets yang berisi PAT, Docker username, dan Docker password.

Model Retraining Otomatis Sederhana dengan GitHub Actions
Gambar oleh penulis

Pull repository GitHub ke lokal Anda atau platform mana pun yang Anda gunakan. Setelah siap, mari kita siapkan keseluruhan struktur untuk tutorial ini. Di IDE favorit Anda, buat folder seperti yang ditunjukkan di bawah ini.

diabetes-project/
├── data/
├── notebooks/
├── scripts/
├── models/
├── .github/
│   └── workflows/
|─────────────

Jika folder ada sudah siap, kita akan mengatur virtual environment. Ini adalah praktik terbaik karena kita ingin memiliki environment yang terisolasi. Pergi ke folder root Anda dan gunakan kode berikut di CLI.

python -m venv your_environment_name

Kemudian, Anda dapat mengaktifkan virtual environment dengan menjalankan kode di bawah ini.

your_environment_name\Scripts\activate

Setelah kita mengaktifkan virtual environment, kita akan menginstal semua paket yang diperlukan untuk tutorial ini. Buat file dengan nama requirements.txt di folder root Anda dan isi dengan paket berikut.

fastapi
uvicorn
pandas
scikit-learn
matplotlib
seaborn
evidently

Kita akan menginstal paket-paket ini di virtual environment ketika file requirements.txt sudah siap.

pip install -r requirements.txt

Sekarang semua persiapannya sudah selesai dan kita bisa melanjutkan mengembangkan model dan memulai otomatisasi retraining model.



Pengembangan Model (Model Development)

Tutorial ini akan menggunakan Open-Source Diabetes dataset (CC0: Public Domain) sebagai dataset kita. Download dataset tersebut dan letakkan di folder Data. Untuk saya, saya mengganti nama dataset tersebut menjadi data.csv tetapi Anda dapat mengubahnya sesuai dengan preferensi Anda.


Kita akan melakukan pengembangan model awal di Jupyter Notebook. Buat notebook Anda dan letakkan di folder notebooks. Kemudian, mari mulai dengan membaca dataset.

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

data_path = '..//data//data.csv'

df = pd.read_csv(data_path)

Kita tidak akan fokus pada eksplorasi data karena artikel ini berfokus pada demonstrasi kemampuan GitHub Actions untuk otomatisasi retraining. Saya telah menyertakan bagian eksplorasi data di notebook jika Anda ingin melihatnya.


Untuk saat ini, mari lanjut ke data preprocessing dan inisiasi pipeline. Kita akan menggunakan data pipeline untuk mensimulasikan proses pengembangan standar.

from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler

X = df.drop('Outcome', axis=1)
y = df['Outcome']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

numeric_features = X.columns
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='mean')),
    ('scaler', StandardScaler())
])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features)
    ])

Setelah pipeline siap, kita akan menggunakan algoritma Random Forest sebagai model machine learning kita. Anda bisa memilih model lain yang sesuai dengan tujuan Anda.

from sklearn.ensemble import RandomForestClassifier

pipeline = Pipeline(steps=[
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

pipeline.fit(X_train, y_train)

Mari kita evaluasi model dan lihat bagaimana performanya.

from sklearn.metrics import classification_report

y_pred = pipeline.predict(X_test)

# Evaluasi model
report = classification_report(y_test, y_pred)
print(report)
Model Retraining Otomatis Sederhana dengan GitHub Actions

Secara keseluruhan, performanya masih bisa diterima. Sebenarnya masih bisa jadi lebih baik lagi, tapi mari kita lanjutkan dengan model ini dan simpan di folder models.

import pickle

with open('..//models//pipeline.pkl', 'wb') as f:
    pickle.dump(pipeline, f)

Ketika model sudah siap, kita akan menerapkannya di produksi. Kita akan menerapkannya sebagai API dan menggunakan Docker untuk mengkontainerisasi model tersebut.


Untuk menerapkan model sebagai API, mari buat file bernama app.py dan simpan di folder scripts. Gunakan kode berikut di dalam file untuk membuat model menjadi API.

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import pickle
import pandas as pd

app = FastAPI()

columns = ['Pregnancies', 'Glucose', 'BloodPressure', 
'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age']

dict_res = {0: 'Not-Diabetes', 1: 'Diabetes'}

pipeline_path = 'models/pipeline.pkl'
with open(pipeline_path, 'rb') as pipeline_file:
    pipeline = pickle.load(pipeline_file)

class DataInput(BaseModel):
    data: list

@app.post("/predict")
async def predict(input_data: DataInput):
    try:
        df = pd.DataFrame(input_data.data, columns=columns)
        predictions = pipeline.predict(df)
        results = [dict_res[pred] for pred in predictions]
    
        return {"predictions": results}
    
    except Exception as e:
        print("Error:", str(e))
        raise HTTPException(status_code=400, detail=str(e))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Mari kita uji apakah kita bisa mengakses model API. Pertama, kita akan menjalankan kode berikut di CLI untuk memulai aplikasi.

uvicorn scripts.app:app --host 0.0.0.0 --port 8000

Kemudian, jalankan kode berikut di Jupyter Notebook Anda untuk menguji API.

import requests

url = "http://localhost:8000/predict"

data = {
    "data": [
        [1, 85, 66, 29, 0, 26.6, 0.351, 31]
    ]
}

response = requests.post(url, json=data)
print(response.json())

Pastikan posisi data yang Anda berikan ke API sama dengan data training. Jika API berfungsi dengan baik, kita akan membuat Docker image dan mem-push-nya ke hub.


Pertama, mari buat dockerfile di folder root. Isi file tersebut dengan kode berikut.

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY scripts/app.py app.py
COPY models models

EXPOSE 8000

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Pada kode di atas, kita akan mengatur Python environment dan menyalin file yang diperlukan ke dalam kontainer untuk menjalankan API sambil mendengarkan di port 8000.


Dengan dockerfile tersebut, kita bisa membuat image dengan kode berikut.

docker build -t username/image_name -f Dockerfile .
docker login -u username
docker push username/image_name:latest

Ganti username dengan username Dockerhub Anda dan image_name dengan nama aplikasi yang Anda inginkan. Anda harus melihat image Anda di Dockerhub, seperti milik saya, jika berhasil.


Jadi, mengapa kita mengkontainerisasi model API kita di Docker dan mem push-nya ke Dockerhub? Ini memastikan konsistensi di semua environment tempat Anda akan menjalankan aplikasi.


Ini juga menunjukkan betapa hebatnya GitHub Actions di bagian berikutnya pada retraining model dan mem-push-nya kembali ke kontainer ini. Oleh karena itu, kita hanya perlu melakukan pull image untuk menerapkan model.


Jalankan kode di bawah ini untuk melakukan pull image dari Dockerhub dan menjalankannya di local environment Anda.

docker login -u username
docker pull username/image_name:latest
docker run -d -p 8000:8000 username/image_name:latest

Dengan ini, kita telah memiliki model di produksi. Di bagian selanjutnya, kita akan melihat bagaimana melatih ulang model melalui GitHub Actions dengan pemicu tertentu.



Retraining Model dengan GitHub Actions

Seperti yang telah saya sebutkan, model machine learning adalah proyek yang berkelanjutan jika kita ingin mendapatkan manfaat dari model. Ini karena kita tidak dapat mengharapkan model terus menghasilkan kualitas yang sama setiap saat, terutama jika terjadi data drift.


Pada tutorial ini, kita akan belajar cara melakukan retraining model otomatis ketika terdeteksi data drift dalam dataset produksi. Pertama, mari kita lihat bagaimana kita bisa mendeteksi drift dalam dataset kita.


Mari kita simulasikan drift dalam dataset dengan kode berikut.

import numpy as np

def introduce_drift(data, drift_features, drift_amount=0.1, random_seed=42):
    np.random.seed(random_seed)
    drifted_data = data.copy()
    
    for feature in drift_features:
        if feature in data.columns:
            drifted_data[feature] += np.random.normal(loc=0, scale=drift_amount, size=data.shape[0])
    
    return drifted_data
    
features_to_drift = ['Glucose', 'BloodPressure', 'SkinThickness', 'Pregnancies']

drifted_data = introduce_drift(X_test, features_to_drift, drift_amount=50)
drifted_data = drifted_data.reset_index(drop = True)

Pada kode di atas, kita memodifikasi beberapa kolom dalam test data. Anda dapat mengubah-ubah drift_amount untuk mengontrol seberapa banyak data yang mengalami drift.


Kita akan memerlukan data training (referensi) dan data drift (baru) untuk tutorial kita. Saya juga akan menyimpan kolom target yang akan kita gunakan nanti untuk contoh retraining.

reference_data['Outcome'] = y_train.reset_index(drop = True)
drifted_data['Outcome'] = y_test.reset_index(drop = True)

drifted_data.to_csv('..//data//new_data.csv', index=False)
reference_data.to_csv('..//data//reference_data.csv', index=False)

Menggunakan Evidently (saya tidak berafiliasi dalam bentuk apa pun dengan Evidently), kita akan memeriksa apakah data produksi telah mengalami drift dibandingkan dengan data referensi. Kita bisa melakukannya dengan kode berikut.

from evidently.metric_preset import DataDriftPreset
from evidently.report import Report

data_drift_report = Report(metrics=[
    DataDriftPreset(),
])

data_drift_report.run(current_data=drifted_data.drop('Outcome', axis =1), 
reference_data=reference_data.drop('Outcome', axis =1), column_mapping=None)
report_json = data_drift_report.as_dict()
drift_detected = report_json['metrics'][0]['result']['dataset_drift']
Model Retraining Otomatis Sederhana dengan GitHub Actions

Hasilnya menunjukkan adanya dataset drift yang akan kita gunakan nanti untuk otomatisasi retraining.


Untuk mensimulasikan deteksi drift, mari buat file dengan nama drift_detection.py dan simpan di folder scripts. Isi file dengan kode di bawah ini.

import pandas as pd
from evidently.metric_preset import DataDriftPreset
from evidently.report import Report

reference_data = pd.read_csv('data/reference_data.csv')
new_data = pd.read_csv('data/new_data.csv')

data_drift_report = Report(metrics=[
    DataDriftPreset()
])

data_drift_report.run(reference_data=reference_data.drop('Outcome', axis =1), 
                      current_data=new_data.drop('Outcome', axis =1), column_mapping=None)

report_json = data_drift_report.as_dict()
drift_detected = report_json['metrics'][0]['result']['dataset_drift']

if drift_detected:
    print("Data drift detected. Retraining the model.")
    with open('drift_detected.txt', 'w') as f:
        f.write('drift_detected')
else:
    print("No data drift detected.")
    with open('drift_detected.txt', 'w') as f:
        f.write('no_drift')

Pada kode di atas, kita menyimpan hasil boolean ke file drift_detected.txt dan mencetak informasi apakah drift terdeteksi atau tidak. Jika terdeteksi drift, kita ingin melakukan retraining model. Untuk itu, kita juga perlu menyiapkan skrip pelatihan.


Buat file bernama train_model.py di folder scripts dan isi dengan kode berikut.

import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
import pickle

reference_data = pd.read_csv('data/reference_data.csv')
new_data = pd.read_csv('data/new_data.csv')

df= pd.concat([reference_data, new_data], ignore_index=True)

X = df.drop('Outcome', axis=1)
y = df['Outcome']

numeric_features = X.columns
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='mean')),
    ('scaler', StandardScaler())
])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features)
    ])

pipeline = Pipeline(steps=[
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

pipeline.fit(X, y)

with open('models/pipeline.pkl', 'wb') as f:
    pickle.dump(pipeline, f)

Kode di atas akan menggabungkan data training dan data drift menjadi model pelatihan baru, yang kemudian digunakan untuk melatih model baru. Ini hanya pendekatan yang disederhanakan, karena data training di dunia nyata akan memerlukan lebih banyak persiapan, dan model baru akan memerlukan evaluasi yang tepat.


Namun, dengan semua skrip yang telah siap, kita akan menyiapkan GitHub Actions untuk retraining model ketika drift terdeteksi. Kita harus menyiapkan file YAML yang berisi semua konfigurasi yang diperlukan untuk retraining.


Jadi, mari buat file bernama mlops_pipeline.yml di folder .github\workflows. Pastikan nama folder benar; GitHub Actions memerlukan nama yang tepat. Isi mlops_pipeline.yml dengan kode di bawah ini.

name: Diabetes Retraining Pipeline with Data Drift Detection

on:
  push:
    paths:
      - 'data/new_data.csv'
permissions:
  contents: write
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2 
      with:
        python-version: 3.9

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

    - name: Run data drift detection
      run: |
        python scripts/drift_detection.py
      continue-on-error: true 

    - name: Check for data drift
      id: check_drift
      run: |
        if grep -q 'drift_detected' drift_detected.txt; then
          echo "Data drift detected."
          echo "drift=true" >> $GITHUB_ENV
        else
          echo "No data drift detected."
          echo "drift=false" >> $GITHUB_ENV
        fi
      shell: bash

    - name: Model Retraining if Data Drift detected
      if: env.drift == 'true'
      run: |
        python scripts/train_model.py

    - name: Commit and push updated model
      if: env.drift == 'true'
      env:
        GIT_COMMITTER_NAME: github-actions
        GIT_COMMITTER_EMAIL: github-actions@github.com
      run: |
        git config --global user.name "github-actions"
        git config --global user.email "github-actions@github.com"
        git remote set-url origin https://x-access-token:${{ secrets.ACTIONS_PAT }}@github.com/username/image_name.git
        git add models/pipeline.pkl
        git commit -m "Update model after retraining on $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
        git push

    - name: Build Docker image
      if: env.drift == 'true'
      run: |
        docker build -t username/image_name -f dockerfile .

    - name: Log in to Docker Hub
      if: env.drift == 'true'
      run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin

    - name: Push Docker image to Docker Hub
      if: env.drift == 'true'
      run: |
        docker push username/image_name:latest

    - name: Notify about the process
      run: |
        if [[ "$GITHUB_ENV" == *"drift=false"* ]]; then
          echo "No data drift detected. No retraining necessary."
        else
          echo "Data drift detected. Model retrained and deployed."
        fi
      shell: bash

Keseluruhan struktur konfigurasi yang kita buat dalam YAML di atas ditunjukkan pada gambar di bawah ini.

Model Retraining Otomatis Sederhana dengan GitHub Actions

Pemicu untuk GitHub Actions yang kita gunakan adalah ketika file new_data.csv di-push ke dalam folder data. Namun, retraining model hanya akan berjalan ketika drift terdeteksi. Jika model dilatih ulang, kita akan mem-push-nya kembali ke repository GitHub dan Docker Hub.


Jangan lupa untuk mengganti setiap username/image_name identifikasi Docker menjadi milik Anda. Anda juga bisa membuat Repository Secrets jika identifikator selalu sama.

Setelah semua file siap, Anda harus mem-push-nya ke repository GitHub Anda.


Kemudian, Anda bisa mencoba membuat data drift baru dan menyimpannya sebagai new_data.csv dan mencoba mem-push-nya sekali lagi ke repository.


Pergi ke tab Action di repository GitHub Anda. Jika berhasil dijalankan, Anda akan melihat satu job bernama build dengan status Success.

Model Retraining Otomatis Sederhana dengan GitHub Actions

Klik pada job tersebut untuk mendapatkan semua detail proses. Anda bisa melihat informasi setiap langkah untuk memahami proses atau melihat jika ada yang gagal dijalankan.

Model Retraining Otomatis Sederhana dengan GitHub Actions

Jika Anda masuk ke folder models di repository, Anda bisa memeriksa apakah model telah diperbarui. Kita menggunakan pesan commit untuk diinformasikan ketika model telah di-retraining.


Model Retraining Otomatis Sederhana dengan GitHub Actions

Anda juga bisa memeriksa repository Docker Hub Anda untuk melihat apakah image telah diperbarui.


Itulah semua yang Anda perlukan untuk menggunakan GitHub Actions untuk menyederhanakan proses retraining model. Anda bisa menyesuaikan semua skrip sesuai kebutuhan Anda, misalnya, pemicu, kondisi retraining, dataset, dan banyak lainnya.


Jika Anda memerlukan semua kode yang kita gunakan dalam artikel ini, saya telah mem-push-nya ke repository ini.


Kesimpulan


Dalam artikel ini, kita telah belajar cara menggunakan GitHub Actions untuk mengotomatisasi proses retraining model. Dengan mengatur konfigurasi dengan file YAML dan menentukan pemicu, kita bisa dengan mudah menggunakan GitHub Actions untuk menyederhanakan proses yang diperlukan.




74 tampilan0 komentar

Postingan Terkait

Lihat Semua

コメント


bottom of page