5. ML/AI 성능 최적화
5.1 Model Serving 지연시간 최적화
| 최적화 영역 | 전략 | 효과 |
|---|---|---|
| 콜드 스타트 | Minimum Instances >= 1 | 첫 요청 지연 제거 |
| 모델 크기 | ONNX 변환, 양자화 | 로드 시간 50~80% 감소 |
| GPU 선택 | 모델 크기에 맞는 GPU | 과도한 GPU는 비용 낭비 |
| 배치 요청 | Batch Inference 활용 | 개별 요청 대비 10~100x 처리량 |
| Feature Lookup | Online 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 인스턴스 | GPU | GPU 메모리 | 적합한 용도 | 시간당 비용 (USD, 대략) |
|---|---|---|---|---|
| g5.xlarge | A10G × 1 | 24GB | 소규모 파인튜닝, 추론 | $1.01 |
| g5.2xlarge | A10G × 1 | 24GB | 파인튜닝 + 큰 CPU 메모리 | $1.21 |
| g5.12xlarge | A10G × 4 | 96GB | 중규모 분산 학습 | $5.67 |
| g5.48xlarge | A10G × 8 | 192GB | 대규모 분산 학습 | $16.29 |
| p4d.24xlarge | A100 × 8 | 320GB | LLM 파인튜닝, 대규모 모델 | $32.77 |
| p5.48xlarge | H100 × 8 | 640GB | LLM 사전 학습, 초대규모 | $98.32 |
6.2 모델 크기별 GPU 선택 가이드
| 모델 규모 | 파라미터 수 | 최소 GPU 메모리 | 권장 인스턴스 | 비고 |
|---|---|---|---|---|
| 소형 | < 1B | 8~16GB | g5.xlarge (A10G × 1) | scikit-learn, XGBoost도 포함 |
| 중형 | 1~7B | 24~48GB | g5.xlarge ~ g5.2xlarge | Llama 3 8B, Mistral 7B |
| 대형 | 7~13B | 48~96GB | g5.12xlarge (A10G × 4) | Llama 3 13B |
| 초대형 | 13~70B | 160~320GB | p4d.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=True | GPU 대기 시간 감소 |
| 배치 사이즈 | GPU 메모리의 70~80% 활용 | GPU 활용률 극대화 |
| 통신 백엔드 | NCCL (GPU 간), Gloo (CPU) | 그래디언트 동기화 속도 |
| Mixed Precision | torch.cuda.amp | 메모리 50% 절감, 속도 2x |
| Gradient Accumulation | GPU 메모리 부족 시 | 실효 배치 사이즈 증가 |
# 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, Hyperopt | Spark 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)))