Production AI 구축: 3부 MLOps 여정 - Pt.2

발행: (2026년 1월 19일 오전 01:57 GMT+9)
8 min read
원문: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the post. Could you please paste the content you’d like translated (excluding any code blocks or URLs you want to keep unchanged)? Once I have the article text, I’ll provide a Korean translation while preserving the original formatting and markdown.

훈련 실험실: Google Colab 설정

먼저, 작업할 공간이 필요합니다. AI를 훈련시키는 것은 컴퓨터에게 마라톤을 뛰게 하는 것과 같아 매우 힘듭니다. 우리는 Google Colab을 사용합니다. 무료 T4 GPU를 제공해 주어 Adire 모델을 훈련시키는 데 필요한 “엔진”이 되기 때문입니다.

필수 라이브러리 설치

# ========================================
# Cell 1: Environment Setup
# ========================================
!pip install -q diffusers==0.25.0 transformers==4.36.0 \
             accelerate==0.25.0 peft==0.7.1 bitsandbytes

# Verify that the GPU is available
import torch
assert torch.cuda.is_available(), "No GPU found! Check your Colab settings."
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f}GB")

훈련 스크립트 다운로드

# ========================================
# Cell 2: Download the 'Recipe'
# ========================================
# Grab a proven script from the HuggingFace team.
!wget -q https://raw.githubusercontent.com/huggingface/diffusers/v0.36.0/examples/dreambooth/train_dreambooth_lora.py

훈련 실행 구성

# ========================================
# Cell 3: The Secret Sauce (Configuration)
# ========================================
CONFIG = {
    "model": "runwayml/stable-diffusion-v1-5",
    "output_dir": "./lora_weights",
    "instance_data_dir": "./training_images",
    "instance_prompt": "a photo in nigerian_adire_style",
    "resolution": 512,
    "train_batch_size": 1,
    "gradient_accumulation_steps": 4,   # “save up” steps to act like a bigger batch
    "learning_rate": 1e-4,
    "lr_scheduler": "constant",
    "max_train_steps": 800,             # 800 iterations is usually the sweet spot
    "lora_rank": 4,
    "lora_alpha": 4,
    "seed": 42
}

훈련 시작

# ========================================
# Cell 4: Ignition!
# ========================================
!accelerate launch train_dreambooth_lora.py \
  --pretrained_model_name_or_path="{CONFIG['model']}" \
  --instance_data_dir="{CONFIG['instance_data_dir']}" \
  --output_dir="{CONFIG['output_dir']}" \
  --instance_prompt="{CONFIG['instance_prompt']}" \
  --resolution={CONFIG['resolution']} \
  --train_batch_size={CONFIG['train_batch_size']} \
  --gradient_accumulation_steps={CONFIG['gradient_accumulation_steps']} \
  --learning_rate={CONFIG['learning_rate']} \
  --lr_scheduler="{CONFIG['lr_scheduler']}" \
  --max_train_steps={CONFIG['max_train_steps']} \
  --use_8bit_adam \
  --checkpointing_steps=100 \
  --validation_prompt="{CONFIG['instance_prompt']} sunset over Lagos" \
  --seed={CONFIG['seed']}

엔진 튜닝: 하이퍼파라미터 분석

CONFIG에서 특정 숫자를 선택한 이유가 궁금할 수 있습니다. AI 학습은 요리와 비슷합니다—소금 한 꼬집이 너무 많으면 수프가 망쳐집니다.

하이퍼파라미터이유
학습률 (1e-4)너무 높으면 → 모델이 “패닉” 상태가 되어 아무것도 배우지 못합니다. 너무 낮으면 → 학습이 며칠씩 끌립니다.
실제 배치 크기 (1 × 4)우리는 한 번에 하나의 이미지만 학습하지만 네 단계에 걸쳐 그래디언트를 누적하여 GPU 메모리를 초과하지 않으면서 학습을 안정적으로 유지합니다.
LoRA 랭크 (4)가볍고 빠릅니다. 랭크 16이면 파일 크기가 약 4배 커지지만 품질 향상은 미미합니다. 효율성이 목표입니다.

Source:

The Factory: Building the MLOps Pipeline

이제 노트북을 벗어나 실제 소프트웨어 시스템을 구축합니다. 프로덕션 환경에서는 파일을 수동으로 복사‑붙여넣기 하고 싶지 않으므로 ZenML을 사용해 컨베이어 벨트를 만듭니다.

우리 파이프라인에는 세 가지 주요 “직원”이 있습니다:

  • Evaluator – 모델이 실제로 Adire 패턴을 생성하는지, 아니면 잡음만 내는지 확인합니다.
  • Promoter – 테스트 점수를 보고 모델이 고객에게 충분히 좋은지 판단하는 “매니저”.
  • Deployer – 모델을 포장해 클라우드로 배포합니다.

Step 1: The Evaluator (Quality Control)

이 단계에서는 새로 학습된 모델을 로드하고, 몇 장의 이미지를 생성한 뒤, 지연 시간을 측정하고, 이미지가 프롬프트와 얼마나 잘 맞는지 평가합니다. 모든 통계는 MLflow에 영구 기록됩니다.

@step(enable_cache=False)
def evaluate_model(model_path: str, test_prompts: List[str]) -> Dict[str, float]:
    """
    Load the Stable Diffusion model + LoRA weights, generate images,
    time the generation, and compute a quality score using CLIP.
    """
    # Load the base model and LoRA adapters
    pipe = StableDiffusionPipeline.from_pretrained(
        "runwayml/stable-diffusion-v1-5",
        torch_dtype=torch.float16,
        safety_checker=None,
    )
    pipe.unet.load_attn_procs(model_path)

    results = {}
    for i, prompt in enumerate(test_prompts):
        # Measure generation time
        start = time.time()
        image = pipe(prompt).images[0]
        gen_time = time.time() - start

        # Compute a CLIP‑based similarity score (higher = better)
        quality = compute_clip_score(image, prompt)

        results[f"prompt_{i}_time"] = gen_time
        results[f"prompt_{i}_quality"] = quality

    # Log to MLflow (example)
    mlflow.log_metrics(results)
    return results

Step 2: The Promoter (Gatekeeper)

@step
def promote_model(metrics: Dict[str, float], threshold: float = 0.75) -> bool:
    """
    Decide whether the model passes quality gates.
    Returns True if the average quality score exceeds `threshold`.
    """
    quality_keys = [k for k in metrics if "quality" in k]
    avg_quality = sum(metrics[k] for k in quality_keys) / len(quality_keys)

    mlflow.log_metric("avg_quality", avg_quality)
    return avg_quality >= threshold

Step 3: The Deployer (Shipping)

@step
def deploy_model(model_path: str, approved: bool) -> None:
    """
    If `approved` is True, push the model to a model registry
    (e.g., Hugging Face Hub, S3, or a custom endpoint).
    """
    if not approved:
        logger.info("Model did not meet quality thresholds – not deploying.")
        return

    # Example: push to Hugging Face Hub
    repo_id = "my-org/adire-lora"
    huggingface_hub.create_repo(repo_id, exist_ok=True)
    upload_folder(
        repo_id=repo_id,
        folder_path=model_path,
        commit_message="Deploy new Adire LoRA weights",
    )
    logger.info(f"Model deployed to https://huggingface.co/{repo_id}")

Full ZenML Pipeline

from zenml import pipeline

@pipeline
def adire_training_pipeline(
    evaluator,
    promoter,
    deployer,
    model_path: str,
    test_prompts: List[str],
    quality_threshold: float = 0.75,
):
    metrics = evaluator(model_path=model_path, test_prompts=test_prompts)
    approved = promoter(metrics=metrics, threshold=quality_threshold)
    deployer(model_path=model_path, approved=approved)

파이프라인을 실행하면 다음과 같은 순서가 진행됩니다:

  1. Train – Colab에서 LoRA를 학습합니다.
  2. Evaluate – 보류된 프롬프트 세트로 모델을 평가합니다.
  3. Promote – 품질 임계값을 만족하는 경우에만 승격합니다.
  4. Deploy – 승인된 모델을 원격 레지스트리에 배포하여 downstream에서 사용할 수 있게 합니다.

Source:

TL;DR

  • Colab → 반복적인 LoRA 학습을 위한 무료 GPU.
  • CONFIG → 속도와 품질을 위해 신중하게 선택된 하이퍼파라미터.
  • ZenML 파이프라인 → 자동화된 평가, 게이팅 및 배포.

이 설정을 통해 원시 Adire 이미지를 지속적으로 개선되는 생성 모델로 전환하는 재현 가능하고 프로덕션 준비된 “팩토리”를 갖게 됩니다. 🚀

Akan

clip_score(image, prompt)

metrics = {"avg_time": gen_time, "avg_quality": quality}
mlflow.log_metrics(metrics)  # Keep a receipt!
return metrics

Step 2: The Promoter (The Decision Maker)

이것이 우리의 자동 품질 게이트입니다. 우리는 엄격한 규칙을 설정합니다: 품질이 0.75 이하이거나 그림을 그리는 데 30초보다 오래 걸리면 모델은 **“해고”**됩니다. 통과하면 “프로덕션” 상태로 승격됩니다.

@step
def promote_model(metrics: Dict[str, float], thresholds: Dict[str, float]):
    # Does it meet our standards?
    checks = {
        "quality_check": metrics["avg_quality"] >= thresholds["quality"],
        "speed_check": metrics["avg_time"] <= thresholds["speed"],
    }
    return all(checks.values())
Back to Blog

관련 글

더 보기 »

네트워크 엔지니어

소개: 나는 네트워크 엔지니어가 되기 위한 여정을 시작하고 있다. 첫 번째 단계는 Cisco Certified Network Associate(CCNA) 인증을 취득하는 것이다, fol...

Linux Internals 모두가 *꼭* 이해해야 하는

Linux 내부 구조: 모든 DevOps 엔지니어가 반드시 이해해야 할 내용, “Linux를 안다”는 수준을 넘어 DevOps를 주장한다면, Linux는 단순히 OS가 아니라 여러분의 runtime, debugger, firewall, …이다.

히토리

배경: 나는 Claude Code를 사용해 에이전트 모드에서 Hitori 게임 구현을 작성했다. Linux 버전의 Hitori를 발견하고 그 아키텍처를 연구한 후…