[MLOps] MinIO를 활용한 YOLO 학습

RCC.AI·2025년 1월 6일

MLOps

목록 보기
10/15

MinIO를 활용한 YOLO 학습 방법을 알아보기 위해 ChatGPT를 활용하였다.

Me : minio 버킷에 저장된 데이터를 바탕으로 darknet yolov4를 학습하고 싶은데 mlflow를 활용해야 할까? 아니면 python 으로 구현할 수 있을까?

ChatGPT : MinIO에 저장된 YOLOv4 학습용 데이터를 활용하기 위해 반드시 MLflow를 사용할 필요는 없습니다. MLflow는 주로 실험 추적(Experiment Tracking), 모델 관리, 배포 등에 특화된 도구이며, 데이터 다운로드 및 모델 학습 자체는 Python 코드로 충분히 구현할 수 있습니다.


MLflow vs Python 직접 구현

  1. MLflow 활용 시:

    • 모델 및 실험 관리에 편리한 UI와 기능 제공
    • 학습 파라미터, 메트릭, 모델 아티팩트 추적 가능
    • 데이터 다운로드 및 전처리 로직은 별도로 Python으로 작성 필요
    • 학습 자체를 MLflow가 대신 해주는 것은 아님. MLflow는 주로 "관리"와 "추적" 역할을 함.
  2. Python 직접 구현 시:

    • boto3나 MinIO Python SDK 등을 사용해 MinIO 버킷에서 이미지 및 라벨 데이터를 직접 다운로드.
    • YOLOv4 Darknet 학습 스크립트(C++/Make 기반)를 호출하기 전에 필요 파일을 로컬 디렉토리에 배치 가능.
    • 전처리, 학습 스크립트 실행, 결과 분석 및 저장 등을 하나의 Python 스크립트로 통합 가능.
    • MLflow 없이도 간단한 로깅(로그 파일, CSV, JSON)으로 실험 관리 가능.

구현 전략

  1. MinIO에서 데이터 가져오기:

    import boto3
    import os
    
    s3_client = boto3.client(
        's3',
        endpoint_url='http://127.0.0.1:9000',
        aws_access_key_id='your-access-key',
        aws_secret_access_key='your-secret-key'
    )
    
    bucket_name = "mybucket"
    prefix = "my-yolo-dataset/"
    
    # 로컬 디렉토리에 데이터 다운로드
    local_data_dir = "./data"
    os.makedirs(local_data_dir, exist_ok=True)
    
    response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
    while True:
        for obj in response.get('Contents', []):
            key = obj['Key']
            filename = key.replace(prefix, "")
            if filename:  # prefix 자체가 아닌 경우
                local_path = os.path.join(local_data_dir, filename)
                # 하위 디렉토리 구조 반영
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
                s3_client.download_file(bucket_name, key, local_path)
        
        if response.get('IsTruncated'):
            response = s3_client.list_objects_v2(
                Bucket=bucket_name,
                Prefix=prefix,
                ContinuationToken=response.get('NextContinuationToken')
            )
        else:
            break
  2. YOLOv4 학습 스크립트 실행:
    데이터 다운로드 후 YOLOv4 Darknet 학습을 위한 .cfg, .data, .names 파일, 이미지 및 라벨 세팅을 마친 뒤 아래처럼 Darknet을 실행할 수 있습니다.

    ./darknet detector train data/obj.data cfg/yolov4.cfg yolov4.conv.137

    Python에서 subprocess를 통해 Darknet 학습 호출:

    import subprocess
    
    command = ["./darknet", "detector", "train", "data/obj.data", "cfg/yolov4.cfg", "yolov4.conv.137"]
    subprocess.run(command, check=True)

MLflow를 사용하는 경우

  • MLflow Tracking 서버에 접속하여 실험 파라미터, 메트릭(Precision, Recall, mAP) 등을 로깅할 수 있습니다.
  • 학습 완료 후 mlflow.log_metric, mlflow.log_artifact 등을 통해 모델 가중치 파일을 MLflow에 업로드하고 버전 관리할 수 있습니다.

그러나 이 과정은 선택 사항입니다. 학습 파이프라인 자체는 MLflow 없이도 수행할 수 있으며, 필요하다면 나중에 MLflow를 추가해 실험 관리 기능을 확장할 수 있습니다.


결론

  • MLflow는 필수 아님: YOLOv4 학습을 위해 MinIO 데이터 다운로드, Darknet 학습 실행은 Python 스크립트로 충분히 구현 가능.
  • MLflow는 필요 시 실험 추적 및 관리 기능을 제공하는 툴로 선택적 사용.
  • 단순히 MinIO에서 데이터 가져와 YOLOv4 학습을 자동화하려면 Python 코드로 end-to-end 파이프라인 구성이 가능.

Me : Python 스크립트로 학습을 하고 MLflow는 필요 시 실험 추적 및 관리 기능을 제공하는 툴로 활용하고 싶은데 구체적으로 어떻게 구현해야 할까?

ChatGPT : 아래 예제는 Python 스크립트 내부에서 MinIO로부터 YOLO 데이터셋을 가져와 darknet YOLOv4 모델을 학습한 뒤, MLflow를 통해 실험 파라미터 및 결과(메트릭, 모델 가중치 파일)를 기록하는 구현 전략을 제시합니다. 이 예제는 하나의 큰 파이프라인으로, 필요 시 MLflow 추적을 켜거나 끌 수 있습니다.


구성 개요

  1. MinIO에서 YOLO 데이터셋 다운로드:
    boto3를 사용해 MinIO로부터 이미지와 annotation 파일 등을 다운로드합니다.

  2. YOLO 학습 스크립트 실행:
    subprocess를 이용해 YOLOv4 학습을 위한 Darknet 실행 파일을 호출합니다.

  3. 학습 결과 처리:
    Darknet 학습 로그를 파싱하거나, 최종 결과(가중치 파일, mAP 계산 결과 등)를 확인합니다.

  4. MLflow로 로깅:

    • mlflow.set_experiment()를 통해 실험 관리
    • mlflow.log_param()으로 학습 파라미터 기록
    • mlflow.log_metric()으로 mAP, Loss 등 메트릭 기록
    • mlflow.log_artifact()로 최종 가중치 파일 등 산출물 업로드

MLflow Tracking UI를 통해 웹 브라우저에서 실험 이력을 확인할 수 있습니다.


예제 코드 구조

import os
import subprocess
import boto3
import mlflow

# ========== 설정 부분 ==========
MINIO_ENDPOINT = "http://127.0.0.1:9000"
ACCESS_KEY = "your-access-key"
SECRET_KEY = "your-secret-key"
BUCKET_NAME = "mybucket"
DATA_PREFIX = "my-yolo-dataset/"
LOCAL_DATA_DIR = "./data"  # YOLO 학습용 데이터 저장 디렉토리

DARKNET_PATH = "./darknet"
CONFIG_FILE = "cfg/yolov4.cfg"
DATA_FILE = "data/obj.data"
PRETRAINED_WEIGHTS = "yolov4.conv.137"
EXPERIMENT_NAME = "YOLOv4_Experiment"
USE_MLFLOW = True  # 필요시 True/False로 MLflow 로깅 on/off
# ============================

def download_data_from_minio():
    """MinIO에서 YOLO 데이터셋을 다운로드."""
    s3_client = boto3.client(
        's3',
        endpoint_url=MINIO_ENDPOINT,
        aws_access_key_id=ACCESS_KEY,
        aws_secret_access_key=SECRET_KEY
    )
    os.makedirs(LOCAL_DATA_DIR, exist_ok=True)

    continuation_token = None
    while True:
        if continuation_token:
            response = s3_client.list_objects_v2(
                Bucket=BUCKET_NAME,
                Prefix=DATA_PREFIX,
                ContinuationToken=continuation_token
            )
        else:
            response = s3_client.list_objects_v2(
                Bucket=BUCKET_NAME,
                Prefix=DATA_PREFIX
            )
        
        for obj in response.get('Contents', []):
            key = obj['Key']
            relative_path = key[len(DATA_PREFIX):]
            if relative_path:  # prefix 자체가 아닌 경우
                local_path = os.path.join(LOCAL_DATA_DIR, relative_path)
                os.makedirs(os.path.dirname(local_path), exist_ok=True)
                s3_client.download_file(BUCKET_NAME, key, local_path)
        
        if response.get('IsTruncated'):
            continuation_token = response.get('NextContinuationToken')
        else:
            break

def run_darknet_training():
    """Darknet YOLOv4 학습을 subprocess로 실행."""
    command = [
        DARKNET_PATH, "detector", "train",
        DATA_FILE, CONFIG_FILE, PRETRAINED_WEIGHTS,
        "-dont_show", "-map"  # -map 옵션으로 mAP 계산
    ]
    # Darknet이 학습 중 출력하는 로그를 파일에 저장
    with open("training.log", "w") as log_file:
        subprocess.run(command, check=True, stdout=log_file, stderr=log_file)

def parse_darknet_log():
    """
    Darknet의 training.log를 파싱해 mAP 등 메트릭 추출.
    darknet detector train 명령어로 학습 완료 시 로그에 mAP가 표시됨.
    로그 파싱 로직은 실제 로그 형식에 맞게 구현 필요.
    예: 'mean average precision (mAP@0.5) = 0.836' 형태를 검색
    """
    map_value = None
    with open("training.log", "r") as f:
        lines = f.readlines()
    for line in lines:
        if "mean average precision (mAP@0.5)" in line:
            # 라인 예: 'mean average precision (mAP@0.5) = 0.836...'
            parts = line.strip().split("=")
            if len(parts) > 1:
                try:
                    map_value = float(parts[1].strip().split()[0])
                except:
                    pass
    return map_value

def find_final_weights():
    """
    Darknet 학습이 완료되면 backup/ 폴더(또는 cfg에 지정한 output 폴더)에 최종 weights 파일이 생성됨.
    예: backup/yolov4_final.weights 
    """
    # 사용자 환경에 맞게 수정 필요
    backup_dir = "./backup"
    final_weights = None
    for f in os.listdir(backup_dir):
        if f.endswith("_final.weights"):
            final_weights = os.path.join(backup_dir, f)
            break
    return final_weights


def main():
    # 1. 데이터 다운로드
    download_data_from_minio()

    # 2. MLflow 설정
    if USE_MLFLOW:
        mlflow.set_tracking_uri("http://127.0.0.1:5000")  # MLflow tracking server URI (변경 가능)
        mlflow.set_experiment(EXPERIMENT_NAME)

    # 학습 파라미터 예시
    learning_rate = 0.001
    batch_size = 64

    with mlflow.start_run() if USE_MLFLOW else nullcontext():  # mlflow 사용 안할 경우 dummy context
        # 파라미터 로깅
        if USE_MLFLOW:
            mlflow.log_param("learning_rate", learning_rate)
            mlflow.log_param("batch_size", batch_size)
            mlflow.log_param("config_file", CONFIG_FILE)
            mlflow.log_param("data_file", DATA_FILE)

        # 3. Darknet 학습 실행
        run_darknet_training()

        # 4. 학습 결과 파싱 (mAP 등)
        map_value = parse_darknet_log()
        if map_value is not None and USE_MLFLOW:
            mlflow.log_metric("mAP", map_value)

        # 5. 최종 모델 가중치 파일 로깅
        final_weights = find_final_weights()
        if final_weights and USE_MLFLOW:
            mlflow.log_artifact(final_weights, artifact_path="model")

        # 필요하다면 기타 결과물(예: confusion matrix 이미지, log 파일)도 로깅 가능
        if USE_MLFLOW:
            mlflow.log_artifact("training.log", artifact_path="logs")


# mlflow 미사용 시 with 구문에서 예외 처리용 context
from contextlib import contextmanager
@contextmanager
def nullcontext():
    yield

if __name__ == "__main__":
    main()

설명

  1. MinIO 데이터 다운로드:
    download_data_from_minio() 함수에서 모든 YOLO 데이터셋 파일을 로컬 ./data 디렉토리에 가져옵니다.

  2. Darknet 실행:
    run_darknet_training()에서 subprocess.run()으로 Darknet 훈련 명령을 실행합니다. 학습 로그는 training.log 파일에 기록합니다.

  3. MLflow 설정:
    mlflow.set_tracking_uri()로 MLflow 서버 위치를 지정하고, mlflow.set_experiment()로 실험 이름을 지정합니다. mlflow.start_run() 블록 안에서 log_param(), log_metric(), log_artifact()를 호출하면 해당 실험에 결과가 기록됩니다.

  4. 결과 파싱 및 로깅:
    parse_darknet_log()에서 mAP를 추출하고 mlflow.log_metric("mAP", map_value)로 기록합니다. 최종 가중치 파일(_final.weights)을 찾아 mlflow.log_artifact()로 업로드합니다.

  5. 옵션:
    USE_MLFLOW = TrueFalse로 바꾸면 MLflow 없이도 스크립트 실행이 가능합니다.


MLflow UI 확인

MLflow Tracking UI를 실행했다면 (예: mlflow ui 실행 후 http://127.0.0.1:5000 접속), 해당 실험(YOLOv4_Experiment)에 대한 러닝 로그, 파라미터, 메트릭, 아티팩트를 웹 UI에서 확인할 수 있습니다.


이렇게 Python 스크립트로 데이터 다운로드, 학습 실행, 결과 파싱까지 진행하고, 필요한 경우 MLflow로 로깅을 활성화하여 모델 실험 관리 기능을 추가할 수 있습니다.

profile
따라가기도 벅찬 AI Engineer 겸 부앙단

0개의 댓글