Skip to main content

5. ML/AI 성능 최적화

5.1 Model Serving 지연시간 최적화

최적화 영역전략효과
콜드 스타트Minimum Instances >= 1첫 요청 지연 제거
모델 크기ONNX 변환, 양자화로드 시간 50~80% 감소
GPU 선택모델 크기에 맞는 GPU과도한 GPU는 비용 낭비
배치 요청Batch Inference 활용개별 요청 대비 10~100x 처리량
Feature LookupOnline Table 활용Feature 조회 < 10ms
# Model Serving 엔드포인트 설정 (최적화 버전)
import mlflow

# 모델 등록 시 최적화 힌트
mlflow.pyfunc.log_model(
    artifact_path="model",
    python_model=my_model,
    pip_requirements=["scikit-learn==1.3.0"],  # 최소 의존성
    metadata={"serving_optimization": "latency"}
)

5.2 배치 추론 병렬화

import mlflow
from pyspark.sql.functions import struct

# Spark UDF로 배치 추론 병렬화
model_uri = "models:/fraud_detection/production"
predict_udf = mlflow.pyfunc.spark_udf(spark, model_uri)

# 대용량 데이터셋에 병렬 추론 적용
predictions = (
    spark.table("catalog.schema.transactions")
    .withColumn("prediction", predict_udf(struct("feature1", "feature2", "feature3")))
)

# 최적화 팁: 파티션 수를 코어 수의 2~3배로 설정
predictions.repartition(spark.sparkContext.defaultParallelism * 2)

5.3 Vector Search 검색 속도 최적화

최적화 항목권장 설정이유
임베딩 차원768~1536 (모델에 따라)불필요하게 큰 차원은 속도 저하
인덱스 유형Delta Sync Index자동 동기화, 운영 부담 최소
엔드포인트 크기데이터 크기에 비례1M 문서 미만: Small, 이상: Medium+
필터링메타데이터 필터 활용검색 범위 축소로 속도/정확도 향상
# Vector Search 쿼리 최적화
results = index.similarity_search(
    query_text="고객 이탈 예측 방법",
    columns=["content", "source", "page"],  # 필요한 컬럼만 반환
    filters={"category": "ml", "year": 2025},  # 메타데이터 필터로 범위 축소
    num_results=5  # 필요한 만큼만 요청
)

6. GPU 클러스터 사이징

6.1 GPU 인스턴스 비교

GPU 인스턴스GPUGPU 메모리적합한 용도시간당 비용 (USD, 대략)
g5.xlargeA10G × 124GB소규모 파인튜닝, 추론$1.01
g5.2xlargeA10G × 124GB파인튜닝 + 큰 CPU 메모리$1.21
g5.12xlargeA10G × 496GB중규모 분산 학습$5.67
g5.48xlargeA10G × 8192GB대규모 분산 학습$16.29
p4d.24xlargeA100 × 8320GBLLM 파인튜닝, 대규모 모델$32.77
p5.48xlargeH100 × 8640GBLLM 사전 학습, 초대규모$98.32

6.2 모델 크기별 GPU 선택 가이드

모델 규모파라미터 수최소 GPU 메모리권장 인스턴스비고
소형< 1B8~16GBg5.xlarge (A10G × 1)scikit-learn, XGBoost도 포함
중형1~7B24~48GBg5.xlarge ~ g5.2xlargeLlama 3 8B, Mistral 7B
대형7~13B48~96GBg5.12xlarge (A10G × 4)Llama 3 13B
초대형13~70B160~320GBp4d.24xlarge (A100 × 8)Llama 3 70B, DBRX
거대70B+640GB+p5.48xlarge (H100 × 8)사전 학습용
참고 GPU 메모리 계산 공식: 모델 파라미터를 float16(2바이트)으로 로드할 때 필요한 GPU 메모리 = 파라미터 수 × 2바이트. 예: 7B 모델 = 7 × 10^9 × 2 = 14GB. 학습 시에는 옵티마이저 상태와 그래디언트로 인해 3~4배 추가 메모리가 필요합니다.

6.3 GPU 활용률 모니터링

# GPU 사용률 확인 (노트북에서)
import subprocess
result = subprocess.run(['nvidia-smi', '--query-gpu=utilization.gpu,memory.used,memory.total',
                        '--format=csv,noheader,nounits'], capture_output=True, text=True)
print(result.stdout)

# GPU 사용률 지속 모니터링 (학습 중)
# nvidia-smi dmon -s u -d 5  # 5초 간격으로 사용률 출력
GPU 활용률진단조치
< 30%GPU 낭비 (데이터 로딩 병목)데이터 로더 최적화, num_workers 증가
30~70%적정 (약간의 여유)배치 사이즈 증가 시도
70~95%최적 활용현재 설정 유지
> 95%OOM 위험배치 사이즈 축소 또는 큰 GPU

7. 분산 학습 성능 튜닝

7.1 TorchDistributor 활용

from pyspark.ml.torch.distributor import TorchDistributor

def train_fn():
    import torch
    import torch.distributed as dist
    from torch.nn.parallel import DistributedDataParallel as DDP
    
    # 분산 환경 초기화 (TorchDistributor가 자동 설정)
    local_rank = int(os.environ.get("LOCAL_RANK", 0))
    device = torch.device(f"cuda:{local_rank}")
    
    model = MyModel().to(device)
    model = DDP(model, device_ids=[local_rank])
    
    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
    
    for epoch in range(10):
        for batch in train_loader:
            loss = model(batch)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
    
    return model

# Databricks에서 분산 학습 실행
distributor = TorchDistributor(
    num_processes=4,     # 총 GPU 수
    local_mode=False,    # 멀티 노드
    use_gpu=True
)
model = distributor.run(train_fn)

7.2 분산 학습 성능 최적화

최적화 항목전략효과
데이터 로딩num_workers=4, pin_memory=TrueGPU 대기 시간 감소
배치 사이즈GPU 메모리의 70~80% 활용GPU 활용률 극대화
통신 백엔드NCCL (GPU 간), Gloo (CPU)그래디언트 동기화 속도
Mixed Precisiontorch.cuda.amp메모리 50% 절감, 속도 2x
Gradient AccumulationGPU 메모리 부족 시실효 배치 사이즈 증가
# Mixed Precision Training (메모리 절감 + 속도 향상)
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()

for batch in train_loader:
    optimizer.zero_grad()
    
    with autocast():  # FP16으로 연산
        output = model(batch)
        loss = criterion(output, labels)
    
    scaler.scale(loss).backward()  # 그래디언트 스케일링
    scaler.step(optimizer)
    scaler.update()

# Gradient Accumulation (큰 배치 효과를 작은 GPU로)
accumulation_steps = 4
for i, batch in enumerate(train_loader):
    loss = model(batch) / accumulation_steps
    loss.backward()
    
    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()
주의 분산 학습 주의사항: 노드 간 네트워크 대역폭이 중요합니다. Databricks에서 멀티 노드 학습 시 EFA(Elastic Fabric Adapter) 또는 고대역폭 네트워크가 지원되는 인스턴스(p4d, p5)를 사용하세요. 네트워크 병목이 있으면 GPU가 유휴 상태로 대기합니다.

8. Spark ML vs 단일 노드 성능 비교

8.1 선택 가이드

기준단일 노드 (scikit-learn, XGBoost)Spark ML
데이터 크기< 100GB (메모리 내)100GB+ (분산 처리)
모델 복잡도높음 (딥러닝, 복잡한 앙상블)낮~중 (선형, 트리, 기본 앙상블)
Feature 수1000+100~1000
학습 속도빠름 (단일 노드 최적화)데이터 분산 오버헤드
하이퍼파라미터 튜닝Optuna, HyperoptSpark ML CrossValidator
MLflow 통합네이티브네이티브
# 단일 노드: Pandas + scikit-learn (데이터 < 100GB)
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
import mlflow

# Spark 테이블을 Pandas로 변환 (데이터가 메모리에 들어갈 때)
df = spark.table("catalog.schema.features").toPandas()

with mlflow.start_run():
    model = GradientBoostingClassifier(n_estimators=100)
    model.fit(df[features], df[target])
    mlflow.sklearn.log_model(model, "model")

# 분산: Spark ML (데이터 > 100GB)
from pyspark.ml.classification import GBTClassifier
from pyspark.ml.feature import VectorAssembler

assembler = VectorAssembler(inputCols=features, outputCol="features")
gbt = GBTClassifier(featuresCol="features", labelCol="target", maxIter=100)

pipeline = Pipeline(stages=[assembler, gbt])
model = pipeline.fit(spark.table("catalog.schema.features"))

8.2 하이브리드 패턴

# 대용량 데이터에서 Feature Engineering은 Spark로,
# 학습은 단일 노드로 하는 하이브리드 패턴

# Step 1: Spark로 대규모 Feature Engineering (분산)
features_df = (
    spark.table("catalog.schema.raw_data")  # 1TB
    .withColumn("feature_1", ...)
    .withColumn("feature_2", ...)
    .groupBy("entity_id")
    .agg(...)
)
features_df.write.saveAsTable("catalog.schema.features")  # 50GB

# Step 2: 학습용 샘플링 (50GB → 5GB)
sample_df = features_df.sample(fraction=0.1).toPandas()

# Step 3: 단일 노드에서 학습 (최적화된 라이브러리 활용)
import xgboost as xgb
model = xgb.XGBClassifier(tree_method="gpu_hist", gpu_id=0)
model.fit(sample_df[feature_cols], sample_df[target_col])

# Step 4: 전체 데이터에 Spark UDF로 추론 (분산)
predict_udf = mlflow.pyfunc.spark_udf(spark, "models:/my_model/production")
predictions = features_df.withColumn("prediction", predict_udf(struct(*feature_cols)))