⏱️ 예상 읽기 시간: 25분

서론

Large Language Model(LLM)의 성능을 극대화하려면 초기 사전 훈련(pre-training) 이후의 post-training 과정이 핵심입니다. NVIDIA NeMo RL은 이러한 post-training을 위한 확장 가능한 툴킷으로, Supervised Fine-Tuning(SFT), Direct Preference Optimization(DPO), Reward Model(RM) 훈련을 효율적으로 지원합니다.

본 튜토리얼에서는 NVIDIA NeMo RL을 활용하여 실제 LLM 강화학습 파이프라인을 구축하고 실행하는 방법을 완전히 마스터해보겠습니다.

📋 주요 학습 내용

  • NeMo RL 핵심 개념: SFT, DPO, RM, GRPO 이해
  • 환경 설정: 로컬 macOS 개발환경 구축
  • 단일 노드 훈련: 기본 모델 훈련 실습
  • 멀티 노드 확장: 대규모 분산 훈련 설정
  • 모델 평가: 훈련된 모델 성능 검증
  • 실전 활용: 프로덕션 환경 배포 준비

🔧 시스템 요구사항

하드웨어 요구사항

  • GPU: NVIDIA GPU (CUDA 지원) - RTX 3080 이상 권장
  • 메모리: 최소 16GB RAM, 32GB 권장
  • 저장공간: 최소 50GB 여유 공간

소프트웨어 요구사항

  • 운영체제: macOS 12.0+ 또는 Linux
  • Python: 3.8-3.11
  • CUDA: 11.8 이상 (GPU 사용 시)
  • Docker: 최신 버전 (선택사항)

NeMo RL 핵심 개념 이해

1. Supervised Fine-Tuning (SFT)

SFT는 사전 훈련된 모델을 특정 작업에 맞게 조정하는 지도학습 방식입니다.

# SFT 기본 설정 예시
sft_config = {
    "model_name": "meta-llama/Llama-3.2-1B-Instruct",
    "dataset": "helpsteer_data",
    "batch_size": 32,
    "learning_rate": 5e-5,
    "epochs": 3
}

2. Direct Preference Optimization (DPO)

DPO는 인간의 선호도를 직접 모델에 반영하는 혁신적인 방법입니다.

# DPO 설정 예시
dpo_config = {
    "preference_dataset": "helpsteer3",
    "beta": 0.1,  # KL divergence 가중치
    "sft_loss_weight": 0.1,
    "preference_average_log_probs": True
}

3. Reward Model (RM)

RM은 모델 출력의 품질을 평가하는 보조 모델입니다.

# RM 훈련 설정
rm_config = {
    "base_model": "meta-llama/Llama-3.2-1B-Instruct",
    "preference_data": "helpsteer3",
    "ranking_loss": "cross_entropy"
}

환경 설정 및 설치

1. 레포지토리 클론 및 서브모듈 초기화

# NeMo RL 레포지토리 클론
git clone https://github.com/NVIDIA-NeMo/RL.git
cd RL

# 서브모듈 초기화 (중요!)
git submodule update --init --recursive

2. 파이썬 환경 설정

# Python 버전 확인
python --version  # 3.8-3.11 확인

# UV 패키지 매니저 설치 (권장)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 가상환경 생성 및 활성화
uv venv nemo-rl-env
source nemo-rl-env/bin/activate

3. 의존성 설치

# 기본 의존성 설치
uv pip install -e .

# GPU 지원을 위한 추가 패키지
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# 평가 도구 설치
uv pip install --extra eval

4. 환경 변수 설정

# ~/.zshrc 또는 ~/.bashrc에 추가
export NEMO_RL_HOME="$HOME/RL"
export CUDA_VISIBLE_DEVICES=0  # 사용할 GPU 지정
export PYTHONPATH="$NEMO_RL_HOME:$PYTHONPATH"

# 환경 변수 적용
source ~/.zshrc

SFT (Supervised Fine-Tuning) 실습

1. 기본 SFT 실행

단일 GPU에서 Llama 3.2 1B 모델을 훈련해보겠습니다.

# 기본 SFT 실행
uv run python examples/run_sft.py

2. 설정 커스터마이징

더 많은 GPU를 활용하여 더 큰 모델을 훈련해보겠습니다.

# 8B 모델로 8GPU 훈련
uv run python examples/run_sft.py \
  policy.model_name="meta-llama/Llama-3.1-8B-Instruct" \
  policy.train_global_batch_size=128 \
  sft.val_global_batch_size=128 \
  cluster.gpus_per_node=8 \
  checkpointing.checkpoint_dir="results/sft_llama8b" \
  logger.wandb_enabled=True \
  logger.wandb.name="sft-llama8b-tutorial"

3. 데이터셋 커스터마이징

자체 데이터셋을 사용하려면 다음과 같이 설정합니다.

# custom_sft_config.yaml
data:
  dataset_name: "custom_dataset"
  dataset_path: "/path/to/your/dataset"
  prompt_template: "Human: {input}\n\nAssistant: {output}"
  
sft:
  max_epochs: 5
  save_interval: 500
  val_check_interval: 100
# 커스텀 설정으로 실행
uv run python examples/run_sft.py --config custom_sft_config.yaml

DPO (Direct Preference Optimization) 실습

1. 기본 DPO 실행

HelpSteer3 데이터셋을 사용한 선호도 기반 훈련을 시작해보겠습니다.

# 단일 GPU DPO 실행
uv run python examples/run_dpo.py

2. 고급 DPO 설정

# 고급 DPO 설정으로 실행
uv run python examples/run_dpo.py \
  policy.model_name="meta-llama/Llama-3.1-8B-Instruct" \
  policy.train_global_batch_size=256 \
  dpo.sft_loss_weight=0.1 \
  dpo.preference_average_log_probs=True \
  dpo.beta=0.1 \
  cluster.gpus_per_node=8 \
  checkpointing.checkpoint_dir="results/dpo_llama8b" \
  logger.wandb_enabled=True \
  logger.wandb.name="dpo-llama8b-tutorial"

3. DPO 모니터링

# TensorBoard로 훈련 과정 모니터링
tensorboard --logdir results/dpo_llama8b/tensorboard

# Weights & Biases 대시보드 확인
# https://wandb.ai 에서 프로젝트 확인

Reward Model (RM) 훈련

1. 기본 RM 훈련

# 단일 GPU RM 훈련
uv run python examples/run_rm.py

2. 멀티 GPU RM 훈련

# 8GPU RM 훈련
uv run python examples/run_rm.py \
  cluster.gpus_per_node=8 \
  checkpointing.checkpoint_dir="results/rm_llama1b" \
  logger.wandb_enabled=True \
  logger.wandb.name="rm-llama1b-tutorial"

3. RM 성능 평가

# 훈련된 RM 평가
uv run python examples/eval_rm.py \
  --model_path results/rm_llama1b/final \
  --test_dataset helpsteer3_test

모델 평가 및 변환

1. 체크포인트 변환

PyTorch DCP 형식에서 Hugging Face 형식으로 변환합니다.

# DCP → Hugging Face 변환
uv run python examples/converters/convert_dcp_to_hf.py \
    --config results/dpo_llama8b/step_170/config.yaml \
    --dcp-ckpt-path results/dpo_llama8b/step_170/policy/weights/ \
    --hf-ckpt-path results/dpo_llama8b/hf

2. 모델 평가 실행

# 변환된 모델 평가
uv run python examples/run_eval.py \
    generation.model_name=$PWD/results/dpo_llama8b/hf \
    generation.temperature=0.7 \
    generation.top_p=0.9 \
    data.dataset_name=math500 \
    eval.num_tests_per_prompt=16 \
    cluster.gpus_per_node=8

3. 벤치마크 테스트

# MATH-500 벤치마크 테스트
uv run python examples/run_eval.py \
    --config examples/configs/evals/math_eval.yaml \
    generation.model_name=agentica-org/DeepScaleR-1.5B-Preview \
    generation.temperature=0.6 \
    generation.top_p=0.95 \
    generation.vllm_cfg.max_model_len=32768 \
    data.dataset_name=math500 \
    eval.num_tests_per_prompt=16 \
    cluster.gpus_per_node=8

멀티노드 분산 훈련

1. Slurm 클러스터 설정

# multi_node_sft.sh
#!/bin/bash
NUM_ACTOR_NODES=2

COMMAND="uv run ./examples/run_sft.py \
  --config examples/configs/sft.yaml \
  cluster.num_nodes=2 \
  cluster.gpus_per_node=8 \
  checkpointing.checkpoint_dir='results/sft_llama8b_2nodes' \
  logger.wandb_enabled=True \
  logger.wandb.name='sft-llama8b-multinode'" \
CONTAINER=YOUR_CONTAINER \
MOUNTS="$PWD:$PWD" \
sbatch \
    --nodes=${NUM_ACTOR_NODES} \
    --account=YOUR_ACCOUNT \
    --job-name=nemo-rl-sft \
    --partition=YOUR_PARTITION \
    --time=4:0:0 \
    --gres=gpu:8 \
    ray.sub

2. Kubernetes 클러스터 설정

# k8s-nemo-rl-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: nemo-rl-training
spec:
  template:
    spec:
      containers:
      - name: nemo-rl
        image: nvcr.io/nvidia/nemo:24.01.01
        command: ["uv", "run", "python", "examples/run_sft.py"]
        args:
          - "--config"
          - "examples/configs/sft.yaml"
          - "cluster.num_nodes=2"
          - "cluster.gpus_per_node=8"
        resources:
          limits:
            nvidia.com/gpu: 8
        volumeMounts:
        - name: data-volume
          mountPath: /workspace/data
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: nemo-rl-data
      restartPolicy: Never

실전 활용 및 최적화

1. 성능 최적화 팁

# config/optimization.yaml
trainer:
  gradient_clip_val: 1.0
  accumulate_grad_batches: 4
  precision: 16-mixed  # Mixed precision 사용
  
model:
  micro_batch_size: 8
  global_batch_size: 256
  activation_checkpointing: true
  
optimizer:
  name: adamw
  lr: 3e-4
  weight_decay: 0.01
  betas: [0.9, 0.95]

2. 메모리 최적화

# 메모리 효율적인 훈련
uv run python examples/run_sft.py \
  model.activation_checkpointing=true \
  model.sequence_parallel=true \
  model.tensor_model_parallel_size=2 \
  trainer.precision=16-mixed

3. 데이터 파이프라인 최적화

# data_config.py
data_config = {
    "num_workers": 8,
    "pin_memory": True,
    "persistent_workers": True,
    "prefetch_factor": 2,
    "drop_last": True
}

문제 해결 및 디버깅

1. 일반적인 오류 해결

# 서브모듈 초기화 오류
ModuleNotFoundError: No module named 'megatron'

# 해결방법
git submodule update --init --recursive
NRL_FORCE_REBUILD_VENVS=true uv run examples/run_sft.py

2. GPU 메모리 부족

# 배치 크기 조정
uv run python examples/run_sft.py \
  model.micro_batch_size=4 \
  trainer.accumulate_grad_batches=8

3. 디버그 모드 실행

# 상세 로그와 함께 실행
NEMO_LOG_LEVEL=DEBUG uv run python examples/run_sft.py \
  trainer.limit_train_batches=10 \
  trainer.limit_val_batches=5 \
  trainer.max_epochs=1

고급 활용 사례

1. 커스텀 데이터셋 구성

# custom_dataset.py
from torch.utils.data import Dataset
import json

class CustomPreferenceDataset(Dataset):
    def __init__(self, data_path):
        with open(data_path, 'r') as f:
            self.data = json.load(f)
    
    def __len__(self):
        return len(self.data)
    
    def __getitem__(self, idx):
        item = self.data[idx]
        return {
            'prompt': item['prompt'],
            'chosen': item['chosen'],
            'rejected': item['rejected']
        }

2. 커스텀 평가 메트릭

# custom_metrics.py
def calculate_preference_accuracy(predictions, labels):
    """선호도 예측 정확도 계산"""
    correct = sum(pred == label for pred, label in zip(predictions, labels))
    return correct / len(predictions)

def calculate_reward_correlation(predicted_rewards, human_ratings):
    """예측 보상과 인간 평가 간 상관관계"""
    import numpy as np
    return np.corrcoef(predicted_rewards, human_ratings)[0, 1]

3. 하이퍼파라미터 튜닝

# hyperparameter_search.py
import optuna

def objective(trial):
    lr = trial.suggest_float('learning_rate', 1e-5, 1e-3, log=True)
    beta = trial.suggest_float('dpo_beta', 0.01, 1.0)
    batch_size = trial.suggest_categorical('batch_size', [16, 32, 64, 128])
    
    # 모델 훈련 및 평가
    score = train_and_evaluate(lr=lr, beta=beta, batch_size=batch_size)
    return score

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)

성능 벤치마크 및 비교

1. 베이스라인 모델과 비교

# benchmark.py
models = {
    'baseline': 'meta-llama/Llama-3.2-1B-Instruct',
    'sft_trained': 'results/sft_llama1b/hf',
    'dpo_trained': 'results/dpo_llama1b/hf',
    'rm_trained': 'results/rm_llama1b/hf'
}

for name, model_path in models.items():
    score = evaluate_model(model_path)
    print(f"{name}: {score}")

2. 메트릭 수집 및 분석

# metrics_analysis.py
import matplotlib.pyplot as plt
import pandas as pd

def plot_training_curves(log_dir):
    """훈련 곡선 시각화"""
    df = pd.read_csv(f"{log_dir}/metrics.csv")
    
    fig, axes = plt.subplots(2, 2, figsize=(12, 8))
    
    # Loss curves
    axes[0,0].plot(df['step'], df['train_loss'], label='Train')
    axes[0,0].plot(df['step'], df['val_loss'], label='Validation')
    axes[0,0].set_title('Loss Curves')
    axes[0,0].legend()
    
    # Reward curves (for DPO/RM)
    axes[0,1].plot(df['step'], df['reward_accuracy'])
    axes[0,1].set_title('Reward Accuracy')
    
    # Learning rate
    axes[1,0].plot(df['step'], df['learning_rate'])
    axes[1,0].set_title('Learning Rate Schedule')
    
    # GPU utilization
    axes[1,1].plot(df['step'], df['gpu_utilization'])
    axes[1,1].set_title('GPU Utilization')
    
    plt.tight_layout()
    plt.savefig(f"{log_dir}/training_analysis.png")

프로덕션 배포 준비

1. 모델 최적화

# 추론 최적화된 모델 생성
uv run python examples/optimize_for_inference.py \
  --model_path results/dpo_llama8b/hf \
  --output_path results/dpo_llama8b/optimized \
  --optimization_level 3

2. Docker 컨테이너화

# Dockerfile
FROM nvcr.io/nvidia/nemo:24.01.01

WORKDIR /workspace
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
RUN pip install -e .

EXPOSE 8000
CMD ["python", "serve_model.py"]

3. 서비스 API 구성

# serve_model.py
from fastapi import FastAPI
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

app = FastAPI()

# 모델 로드
model_path = "results/dpo_llama8b/hf"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    torch_dtype=torch.float16,
    device_map="auto"
)

@app.post("/generate")
async def generate_text(prompt: str, max_length: int = 512):
    inputs = tokenizer(prompt, return_tensors="pt")
    with torch.no_grad():
        outputs = model.generate(
            **inputs,
            max_length=max_length,
            do_sample=True,
            temperature=0.7,
            top_p=0.9
        )
    
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    return {"response": response}

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

결론

NVIDIA NeMo RL은 LLM의 post-training을 위한 강력하고 확장 가능한 플랫폼입니다. 본 튜토리얼을 통해 다음과 같은 내용을 마스터했습니다:

🎯 주요 성과

  • SFT, DPO, RM 훈련 방법론 이해 및 실습
  • 단일/멀티 노드 환경에서의 확장 가능한 훈련
  • 모델 평가성능 최적화 기법
  • 프로덕션 배포를 위한 모델 최적화

🚀 다음 단계

  • GRPO (Generalized Preference Optimization) 고급 기법 탐구
  • 커스텀 데이터셋 구성 및 도메인 특화 모델 개발
  • 대규모 클러스터 환경에서의 분산 훈련 최적화
  • A/B 테스팅을 통한 모델 성능 검증

💡 추가 리소스

NVIDIA NeMo RL을 활용하여 여러분만의 고성능 LLM을 구축해보세요! 🚀


🧪 실습 테스트 스크립트

본 튜토리얼과 함께 제공되는 테스트 스크립트를 활용하여 NeMo RL 환경을 쉽게 설정할 수 있습니다.

1. 기본 설치 테스트

# 테스트 스크립트 다운로드 및 실행
curl -O https://raw.githubusercontent.com/thakicloud/tutorials/main/nemo-rl-test/test-nemo-rl-setup.sh
chmod +x test-nemo-rl-setup.sh
./test-nemo-rl-setup.sh

2. Alias 설정

# 편리한 단축 명령어 설정
curl -O https://raw.githubusercontent.com/thakicloud/tutorials/main/nemo-rl-test/setup-nemo-rl-aliases.sh
chmod +x setup-nemo-rl-aliases.sh
./setup-nemo-rl-aliases.sh

# 설정 적용
source ~/.zshrc

# 사용법 확인
nemo-help

3. 실제 테스트 결과

macOS Sonoma 14.7 환경에서 테스트 완료

# 시스템 환경
macOS Sonoma 14.7
Python 3.11.8
UV 0.4.15

# 테스트 결과
✓ Python 버전이 호환됩니다 (3.8-3.11)
✓ Git이 설치되어 있습니다
✓ UV 패키지 매니저가 설치되어 있습니다
✓ 레포지토리 클론 및 서브모듈 초기화 완료
✓ 가상환경 생성 및 활성화 완료
✓ 기본 의존성 설치 완료
✓ PyTorch CPU 버전 설치 완료
✓ NeMo RL 모듈 임포트 성공
✓ 설정 모듈 로드 성공
✓ 모든 예제 스크립트 문법 검사 통과

4. 개발환경 버전 정보

개발환경:
  OS: macOS Sonoma 14.7
  Python: 3.11.8
  UV: 0.4.15
  PyTorch: 2.1.0+cpu
  CUDA: N/A (CPU 테스트)

테스트 통과 항목:
  - 환경 요구사항 검증
  - 레포지토리 클론 및 서브모듈
  - 가상환경 생성
  - 의존성 설치
  - 모듈 임포트
  - 설정 파일 확인
  - 예제 스크립트 문법 검사

5. GPU 환경 추가 설정

GPU가 있는 환경에서는 다음 추가 설정을 진행하세요:

# CUDA 버전 확인
nvidia-smi

# GPU 지원 PyTorch 설치
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# 실제 모델 훈련 테스트
nemo-sft-1b trainer.max_epochs=1 trainer.limit_train_batches=10

다음 포스트 예고: NeMo RL과 Kubernetes를 활용한 대규모 분산 훈련 아키텍처 구축 가이드를 준비 중입니다. 구독하고 최신 튜토리얼을 받아보세요!