This content originally appeared on DEV Community and was authored by Jonas Kim
책 머신러닝 시스템 설계과 그에 대한 요약본 Summary of Designing Machine Learning Systems을 참고하였습니다.
6 - 모델 개발 및 오프라인 평가
초기 학습 피처 세트가 준비되면, 드디어 모델 개발을 시작할 수 있습니다. 피처 엔지니어링과 모델 개발은 반복적인 과정임을 명심하십시오.
이 장에서는 다음 내용을 다룹니다.
- 모델 선택, 개발 및 학습
- ML 모델 선택 기준
- 모델 선택의 일부로서의 앙상블(Ensembles)
- 모델 개발 중 실험 추적 및 버전 관리
- ML 모델 디버깅
- 분산 학습 모드: 데이터 병렬성, 모델 병렬성, 파이프라인 병렬성
- AutoML: 자동 하이퍼파라미터 튜닝, 자동 아키텍처 탐색, 학습된 옵티마이저
- 오프라인 평가: 최상의 모델을 선택하기 위해 모델 대안을 평가하는 방법
- 베이스라인: 모델을 비교할 대상이 필요합니다.
- 전반적인 ML 지표를 넘어서는 오프라인 평가 방법: 프로덕션에 보내기 전에 모델의 견고성(robustness), 공정성(fairness), 건전성(sanity) 평가하기.
모델 선택, 개발 및 학습
ML 모델 선택 기준
모델 선택 결정을 내릴 때는 여러 측면의 균형을 맞춰야 합니다. 모델 선택은 과학이라기보다는 예술에 가깝습니다. 이 장은 고려해야 할 사항에 대한 몇 가지 가이드라인과 팁을 제공합니다.
팁 1: 적재적소에 올바른 도구 사용하기
당면한 문제에 적합한 모델에만 집중하여 모델 선택의 탐색 공간을 좁히십시오. 예를 들어:
- 사기 탐지 시스템을 구축하려는 경우, 이것이 고전적인 이상 탐지(anomaly detection) 문제이며 이 문제 범주에 일반적으로 사용되는 모델이 KNN, isolation forest, 클러스터링, 신경망 등임을 알아야 합니다.
- NLP 텍스트 분류 문제를 맡았다면, 옵션은 일반적으로 나이브 베이즈(naive-Bayes), 로지스틱 회귀, 순환 신경망(recurrent neural networks), 그리고 BERT나 GPT와 같은 트랜스포머 기반 모델입니다.
팁 2: 직면한 문제 유형에 대한 팀의 "성숙도 단계" 인지하기
주어진 문제에 대한 ML 채택에는 4가지 단계가 있습니다. 한 단계의 솔루션은 다음 단계의 솔루션을 평가하기 위한 베이스라인으로 사용될 수 있습니다.
- 1단계: Pre-ML (ML 이전): 이 유형의 문제를 처음 해결하는 경우입니다. 문제에 대한 첫 번째 시도는 "가장 인기 있는" 항목을 추천하거나 시간순으로 정렬하는 것과 같은 간단한 휴리스틱일 수 있습니다. ML이 아닌 솔루션만으로도 충분하다는 것을 발견할 수도 있습니다.
- 2단계: 가장 간단한 ML 모델: 이 문제에 대한 첫 번째 ML 모델이 될 것입니다. 팁 6: 가장 간단한 모델로 시작하기를 참조하십시오.
- 3단계: 간단한 모델 최적화: 목적 함수 최적화, 하이퍼파라미터 탐색, 피처 엔지니어링, 데이터 추가, 앙상블 생성 등을 통해 간단한 모델을 더 좋게 만듭니다.
- 4단계: 복잡한 모델: 간단한 모델의 한계에 도달했을 때 복잡한 모델로 이동합니다. 이 단계에서는 모델이 어떻게 노후화되는지(decay) (예: 재학습이 얼마나 자주 필요한지) 파악하여 재학습 파이프라인을 구축하고자 합니다.
팁 3: 학습 지표를 넘어서는 모델 속성 고려하기
정확도, F1, 로그 손실(log loss)과 같은 직접적인 학습 관련 성능 지표가 일반적으로 모델 선택에 사용됩니다. 그러나 이것들만이 고려해야 할 유일한 지표는 아닙니다.
필요한 데이터의 양, 모델의 계산 요구 사항, 학습 시간, 추론 지연 시간(inference latency), 모델 해석 가능성(interpretability)과 같은 다른 속성에도 신경 써야 합니다.
결정을 내리기 위해 선택하는 지표 세트는 문제에 따라 다릅니다. 예를 들어, 어떤 유스케이스에서는 정확도를 약간 희생하더라도 지연 시간이 매우 중요합니다.
팁 4: SOTA(State-of-the-art)의 함정 피하기
"SOTA" 성능을 가진 모델은 대개 연구에서 직접 나옵니다. 그러나 연구자들은 종종 기존의 미리 정의된 데이터셋을 사용하여 학술적인 환경에서만 모델을 평가합니다.
연구에서 SOTA 성능을 가졌다는 사실이 여러분이 구현하기에 모델이 충분히 빠르거나, 충분히 저렴하다는 것을 의미하지는 않습니다. 마찬가지로, 그것이 여러분의 데이터에서도 동일한 성능을 가질 것임을 의미하지도 않습니다.
SOTA 모델보다 훨씬 저렴하고 간단하면서도 문제를 해결할 수 있는 솔루션이 있다면, 더 간단한 솔루션을 사용하십시오.
팁 5: "고전 ML은 끝났다"는 함정 피하기
요즘 신경망이 언론의 많은 주목을 받고 있습니다. 하지만 그렇다고 해서 고전적인 ML 모델이 사라지는 것은 아닙니다. 고전적인 모델은 여전히 프로덕션 환경에서, 특히 지연 시간이나 설명 가능성(explainability)이 중요할 때, 광범위하게 사용됩니다.
또한, 고전적인 모델과 신경망 모델이 함께(in tandem) 배포되는 것을 보는 것도 드문 일이 아닙니다. 예를 들어:
- 신경망 모델과 결정 트리를 사용한 앙상블 구성.
- k-means와 같은 고전적인 모델을 사용하여 피처를 추출하고, 이를 NN의 입력으로 사용.
- BERT나 GPT-3 같은 사전 학습된 신경망을 사용하여 임베딩을 생성하고, 이를 로지스틱 회귀와 같은 고전적인 모델에 입력.
"신경망"이 아니라는 이유만으로 고전적인 모델을 무시하지 마십시오.
팁 6: 가장 간단한 모델로 시작하기
- 간단한 모델을 사용하면 프레이밍(framing)과 데이터를 조기에 검증할 수 있습니다.
- 간단한 모델은 배포하기 더 쉽습니다. 모델을 조기에 배포하면 다음과 같은 것들을 검증할 수 있습니다.
- 피처의 엔드-투-엔드(end-to-end) 동작
- 예측 파이프라인과 학습 파이프라인 간의 일관성
- 시스템의 자연 레이블 탐지 능력 (문제에 해당 사항이 있다면)
- 시스템의 데이터 분포 변화(data distribution shifts) 및 재학습 트리거 탐지 능력
- 간단하게 시작하여 더 복잡한 구성 요소를 천천히 추가하면 모델을 이해하고 디버깅하기가 더 쉬워집니다.
- 간단한 모델은 복잡한 모델과 비교할 수 있는 베이스라인이 됩니다.
- 간단한 모델과 노력이 적게 드는(low-effort) 모델은 다릅니다. 사전 학습된 BERT 모델을 사용하는 것은 (작업이 이미 완료되었기 때문에) 노력이 적게 들지만, 모델이 간단한 것은 아닙니다.
- 때로는 간단한 모델 대신 노력이 적게 들고 복잡한 모델로 시작하는 것이 합리적일 수 있습니다. 그러나 그런 경우에도 간단한 모델을 베이스라인으로 갖는 것은 가치가 있습니다. 왜냐하면 복잡한 모델을 개선하는 것은 매우 어렵기 때문입니다.
팁 7: 모델 선택 시 인간의 편향 피하기
선택하기 전에 다른 아키텍처들에게 비슷한 수준의 노력을 기울였는지 확인하십시오: 특정 아키텍처에 흥미를 느낀 엔지니어는 그것을 실험하는 데 더 많은 시간을 할애할 것입니다. 결과적으로, 덜 흥미를 느끼는 아키텍처에 비해 해당 아키텍처에서 더 나은 성능을 찾을 가능성이 높습니다.
팁 8: 현재의 좋은 성능 VS 미래의 좋은 성능 평가하기
- 지금 당장 최고의 모델이라고 해서 두 달 후에도 최고의 모델이 될 것이라는 의미는 아닙니다. 예: 지금 데이터가 거의 없다면 결정 트리가 가장 잘 작동할 수 있습니다. 두 달 후 데이터가 더 많아지면 NN이 더 잘 작동할 수 있습니다.
- 모델에 더 많은 데이터를 제공함에 따라 모델의 성능은 포화(saturate)되는 경향이 있습니다. 간단한 모델은 복잡한 모델보다 더 빨리 포화됩니다. 학습 곡선(learning curves)을 사용하여 모델이 가능한 한 많이 학습했는지(즉, 포화되었는지) 또는 가까운 미래에 개선될 잠재력이 여전히 있는지 평가할 수 있습니다.
- 모델이 포화되었다면, 더 복잡한 모델을 시도해 볼 때일 수 있습니다.
- 일부 팀은 챔피언-챌린저(champion-challenger) 방식으로 간단한 모델(현재 좋은 성능)과 복잡한 모델(미래에 좋은 성능)을 배포합니다. 더 많은 데이터가 확보됨에 따라 복잡한 모델을 계속 학습시키고, 복잡한 모델의 성능이 더 좋아지면 교체합니다.
- 모델을 선택할 때, 가까운 미래의 개선 잠재력과 그러한 개선을 달성하기 위한 난이도를 의사 결정에 고려할 수 있습니다.
팁 9: 트레이드오프(Trade-offs) 평가하기
모델 선택은 트레이드오프로 가득 차 있습니다. 특정 유스케이스에 무엇이 중요한지 이해하면 정보에 입각한 결정을 내리는 데 도움이 됩니다. 몇 가지 일반적인 트레이드오프는 다음과 같습니다.
- False positives(FP) VS false negatives(FN) 트레이드오프. 예: 모델이 위험한 질병을 진단하는 경우, false negatives를 최소화하는 모델을 선호할 수 있습니다.
계산 집약도 VS 성능 트레이드오프: 더 크고 복잡한 모델이 더 정확할 수 있지만, 실행하는 데 더 강력한 기계와 특수 하드웨어가 필요할 수 있습니다.
지연 시간 VS 성능 트레이드오프: 더 크고 복잡한 모델이 더 정확할 수 있지만, 추론을 생성하는 속도가 더 느릴 것입니다. 일부 유스케이스에는 엄격한 지연 시간 요구 사항이 있습니다.
해석 가능성 VS 성능 트레이드오프: 모델 복잡성이 증가함에 따라 성능은 향상되는 경향이 있지만 모델의 해석 가능성은 감소합니다.
팁 10: 모델의 가정(Assumptions) 이해하기
모든 ML 모델에는 일부 내재된(baked-in) 가정이 있습니다. 이러한 가정을 이해하고 데이터가 이러한 가정을 충족하는지 조사하면 모델 선택에 지침이 될 수 있습니다.
모델에 의해 만들어지는 몇 가지 일반적인 가정의 샘플:
- 예측 가정: 모델은 X로부터 Y를 예측하는 것이 실제로 가능하다고 가정합니다. 때때로 데이터는 단지 무작위이며 학습할 패턴이 없습니다.
- 독립적이고 동일하게 분포된(Independent and Identically distributed, IID): 신경망을 포함한 많은 모델이 모든 예제가 IID라고 가정합니다.
- 평활성(Smoothness): 모든 지도 ML 모델은 입력 X를 추론 Y로 변환하기 위해 학습될 수 있는 평활(smooth) 함수가 있다고 가정합니다. 즉, 매우 유사한 입력 X와 X'는 비례적으로 가까운 출력을 생성해야 합니다.
- 다루기 쉬움(Tractability): X를 입력, Z를 X의 잠재 표현(latent representation)이라고 할 때, 생성 모델(Generative models)은 P(Z|X)를 계산하는 것이 다루기 쉽다고 가정합니다.
- 경계(Boundaries): 선형(linear) 방법은 결정 경계(decision boundaries)가 선형이라고 가정합니다. 커널(Kernel) 방법은 결정 경계가 사용된 커널의 모양을 따른다고 가정합니다.
- 조건부 독립(Conditional independence): 나이브 베이즈 분류기 기반 모델은 클래스가 주어졌을 때 피처 값들이 서로 독립이라고 가정합니다.
- 정규 분포: 많은 모델이 데이터가 정규 분포를 따른다고 가정합니다.
앙상블 (Ensembles)
앙상블을 사용하는 것은 단일 모델에 비해 지속적으로 성능 향상을 보여준 방법입니다. 앙상블에서는 여러 기본 학습기(base learners)가 학습되고 각각 예측을 출력합니다. 최종 예측은 다수결(majority vote)과 같은 휴리스틱을 사용하여 도출됩니다.
- 2021년 Kaggle 대회의 22개 우승 솔루션 중 20개가 앙상블을 사용했습니다. SQuAD 2.0의 상위 20개 솔루션은 앙상블입니다.
앙상블 방법은 배포 및 유지 관리가 더 어렵기 때문에 프로덕션 환경에서는 덜 선호됩니다. 그러나 작은 성능 향상이 막대한 재정적 이득으로 이어질 수 있는 작업에서는 여전히 일반적입니다.
앙상블을 만들 때, 기본 학습기 간의 상관관계가 적을수록 앙상블이 더 좋아집니다. 따라서, 앙상블을 위해 매우 다른 유형의 모델을 선택하는 것이 일반적입니다. 예를 들어, 하나의 트랜스포머 모델, 하나의 순환 신경망, 하나의 그래디언트 부스티드 트리(gradient-boosted tree)로 구성된 앙상블을 만들 수 있습니다.
- 투표에서 동점(tie)을 피하기 위해 홀수 개의 기본 학습기를 사용하는 것도 일반적입니다.
앙상블을 만드는 방법에는 배깅(bagging), 부스팅(boosting), 스태킹(stacking)의 3가지가 있습니다. 이러한 기술들은 결합될 수도 있습니다.
MLWave(전설적인 Kaggle 팀)의 앙상블 가이드에서 앙상블을 만드는 방법에 대한 훌륭한 조언을 찾을 수 있습니다.
배깅 (Bagging)
- 배깅은 많은 이점을 가집니다. 학습 안정성 향상, 정확도 향상, 분산(variance) 감소, 과적합 방지.
-
직관: 복원 추출(sample with replacement)을 사용하여 다른 데이터셋을 만듭니다. 각 데이터셋에서 분류 또는 회귀 모델을 학습시킵니다.
- 복원 추출은 각 부트스트랩(bootstrap)이 서로 독립적으로 생성되도록 보장합니다 (즉, 기본 학습기 간의 상관관계가 적어짐).
-
사용 시기: 불안정한 방법(예: NN, 분류 및 회귀 트리, 선형 회귀)의 안정성을 향상시킬 때.
- k-최근접 이웃(k-nearest neighbours)과 같은 안정적인 방법에서는 성능을 약간 저하시킬 수 있습니다.
부스팅 (Boosting)
-
직관:
- 1. 원본 데이터셋에서 첫 번째 약한 분류기(weak classifier)를 학습시키는 것으로 시작합니다.
- 2. 샘플들은 첫 번째 모델이 얼마나 잘 분류했는지에 따라 가중치가 재조정(re-weighted)됩니다. 잘못 분류된 예제는 더 높은 가중치를 갖습니다.
- 3. 가중치가 재조정된 샘플로 두 번째 분류기를 학습시킵니다.
- 4. 분류기 2의 출력에 따라 샘플 가중치가 다시 재조정됩니다.
- 5. 분류기 2에서 재조정된 가중치를 사용하여 세 번째 분류기를 학습시킵니다.
- 6. 필요한 만큼 반복합니다.
- 7. 기존의 모든 분류기들의 가중치 조합(weighted combination)으로 최종 강력한 분류기(strong classifier)를 형성합니다. 학습 오류가 작은 분류기가 더 높은 가중치를 갖습니다.
-
부스팅 알고리즘:
- GBM (Gradient Boosted Machines)
- XGBoost: 많은 ML 대회에서 선택되는 알고리즘.
- LightGBM: 분산 병렬 학습을 허용하는 XGBoost의 대안 (대규모 데이터셋에 좋음).
스태킹 (Stacking)
직관: 서로 다른 기본 학습기(일반적으로 서로 매우 다른 성격의)를 학습시킵니다. 최종 예측을 생성하기 위해, 기본 학습기들의 예측을 어떻게 결합할지 학습하는 메타 학습기(meta-learner)를 사용합니다.
- 메타 학습기는 다수결(분류) 또는 평균(회귀)과 같은 간단한 휴리스틱일 수 있습니다.
- 또는 로지스틱 회귀(분류)나 선형 회귀 모델(회귀)과 같은 또 다른 (일반적으로 더 간단한) 모델일 수도 있습니다.
- #todo : 메타 학습기가 각 기본 학습기가 어떤 유형의 샘플에 능숙한지 학습할 수 있도록 피처에 대한 접근 권한도 부여하는 것이 일반적인가?
실험 추적 및 버전 관리
실험을 구성하는 설정 파라미터와 해당 실험이 생성하는 산출물(artefacts)을 추적하는 것은 모델 선택을 수행하고 실험 파라미터(데이터, 파라미터, 모델) 변경이 출력 모델의 성능에 어떤 영향을 미치는지 이해하는 데 핵심입니다.
실험 추적(Experiment tracking)과 버전 관리(versioning)는 서로 밀접하게 연관되어 있으며 보통 하나의 개념처럼 이야기되는 2가지 다른 개념입니다.
- 실험 추적: 실험의 진행 상황과 결과를 추적하는 프로세스.
- 버전 관리: 실험을 재현(replicate)할 수 있도록 모든 설정 파라미터를 로깅하는 프로세스.
- 원래 실험 추적을 위해 만들어졌던 많은 도구(예: MLFlow, Weights & Biases)가 이제 버전 관리를 포함하고 있으며, 그 반대(예: DVC)도 마찬가지입니다.
적극적인 실험 추적 및 버전 관리는 재현성(reproducibility)에 도움이 되지만, 이를 보장하지는 않는다(ENSURE IT)는 점을 명심하십시오.
실험 추적
- 실험 추적을 통해 ML 엔지니어는 학습 과정을 효과적으로 세심하게 관리할 수 있습니다; 이것은 학습의 매우 큰 부분입니다.
- 추적할 수 있는 몇 가지 예시는 다음과 같습니다.
- 학습 및 각 평가 분할(eval splits)의 손실 곡선(Loss curve).
- 테스트 분할을 제외한 모든 분할에서 관심 있는 모델 성능 지표: 정확도, F1, 퍼플렉시티(perplexity).
- <샘플, 정답, 예측> 로그. 건전성 확인(sanity check)을 위해 수동 분석이 필요한 경우 유용합니다.
- 초당 스텝 수로 평가한 모델 속도. 텍스트 기반 모델을 다루는 경우, 초당 처리된 토큰 수.
- 시스템 성능 지표: 메모리 사용량, CPU/GPU 사용량. 병목 현상을 식별하고 자원 낭비를 피하는 데 도움이 됩니다.
- 모델 성능에 영향을 미치는 모든 파라미터 및 하이퍼파라미터의 시간에 따른 값. 예: 학습률(learning rate), 그래디언트 놈(gradient norms) (전역 및 레이어별), 가중치 놈(weight norm).
- 이론적으로는, 가능한 모든 것을 추적하는 것이 나쁜 생각은 아닙니다. 실제로는, 모든 것을 추적하면 노이즈에 압도당하고 주의가 산만해질 것입니다.
버전 관리
- ML 시스템은 부분적으로 코드이고, 부분적으로 데이터입니다. 둘 다 버전 관리해야 합니다.
- 데이터 버전 관리는 다음과 같은 이유로 어려운 부분입니다.
- 데이터는 코드보다 훨씬 크기 때문에 코드 버전 관리 도구를 재사용할 수 없습니다. 이전 버전으로 롤백(roll back)하기 위해 데이터셋을 여러 번 복제하는 것은 실현 불가능합니다.
- 데이터 버전 관리 시 정확히 무엇이
diff를 구성하는지에 대해 여전히 혼란이 있습니다.- 모든 변경 사항을 추적해야 하는가, 아니면 체크섬(checksum)만 추적해야 하는가?
- 2021년 기준, DVC와 같은 도구는 전체 디렉토리의 체크섬만 등록합니다.
- 버전 관리되는 데이터에서 병합 충돌(merge conflicts)이 무엇인지 명확하지 않습니다.
- GDPR 또는 유사한 규정의 적용을 받는 사용자 데이터를 사용하는 경우, 데이터 삭제 요청을 준수하는 것이 불가능해집니다.
ML 모델 디버깅
ML 모델 디버깅은 여러 가지 이유로 어렵고 좌절스럽습니다.
- ML 모델은 조용히 실패합니다(fail silently). 예측은 계속하지만 예측이 틀립니다. 때때로 모델에 버그가 있다는 것조차 모릅니다.
- 버그가 수정되었는지 확인하는 것은 답답할 정도로 느립니다. 모델을 재학습한 다음 새 모델로 샘플을 재평가해야 할 수도 있습니다. 때로는 프로덕션에 배포해야만 알 수 있습니다.
- ML 모델에는 데이터, 레이블, 피처, ML 알고리즘, 코드, 인프라 등 여러 팀에서 발생할 수 있는 많은 실패 지점(points of failure)이 있습니다.
불행히도, ML 디버깅에 대한 과학적인 접근 방식은 없습니다. 그러나 일반적으로 좋은 관행으로 간주되는 몇 가지 검증된(tried and true) 기술이 있습니다.
-
단순하게 시작하여 점진적으로 구성 요소 추가: 이렇게 함으로써 현재 하고 있는 일이 성능에 도움이 되는지 해가 되는지 볼 수 있습니다. 이는 또한 예상되는 동작에 대한 직관을 얻는 데 도움이 됩니다.
- 오픈 소스 SOTA 구현을 복제하여 자체 데이터를 연결하는 것은 안티패턴(anti-pattern)입니다. 그것이 작동할 가능성은 매우 낮으며, 작동하지 않을 경우 디버깅하기가 매우 어렵습니다.
- 단일 배치에 과적합(Overfit)시키기: 모델의 간단한 구현을 마친 후, 작은 데이터 배치에 과적합시킨 다음 동일한 데이터에 대해 평가를 실행하여 가능한 가장 높은 정확도(또는 가장 낮은 손실)를 얻는지 확인합니다. 작은 과적합된 배치에서 매우 높은 정확도를 얻을 수 없다면, 무언가 잘못되었을 수 있습니다.
- 무작위 시드(random seed) 설정: ML 모델은 무작위성을 사용하는 곳이 많습니다. 무작위성은 성능 변화가 무작위성 때문인지 변경 사항 때문인지 알 수 없게 하여 실험을 비교하고 재현하기 어렵게 만듭니다. 여러 실험에서 일관된 무작위 시드를 사용하면 비교 가능성을 유지하는 데 도움이 됩니다.
- 더 많은 기술은 Adrej Karpathy의 포스트 "신경망 학습 레시피(A Recipe for Training Neural Networks)"에서 볼 수 있습니다.
분산 학습 (Distributed Training)
메모리에 맞지 않는 데이터로 모델을 학습시키는 것은 흔한 일입니다. 이런 일이 발생하면, 전처리(예: 정규화), 셔플링, 배치 구성 및 학습을 위한 알고리즘이 여러 머신에서 병렬로 수행되어야 합니다.
ML 엔지니어로서, 분산 학습과 ML 확장에 대한 경험을 쌓는 것은 어렵습니다. 왜냐하면 대량의 리소스에 접근해야 하기 때문입니다.
다음으로 몇 가지 분산 학습 모드를 다루겠습니다.
데이터 병렬성 (Data parallelism)
직관: 데이터를 여러 머신에 분할하고, 모든 머신에서 모델을 학습시킨 다음, 그래디언트(gradients)를 축적합니다.
이것은 가장 일반적인 병렬화 방법이지만 몇 가지 문제가 있습니다.
문제 1: 동기식(sync) vs 비동기식(async) 그래디언트 하강
데이터가 매우 클 때 (예: 1,000억 개 샘플), 전체 학습 세트를 여러 머신(예: 10대 머신, 머신당 100억 개 샘플)으로 분할하더라도 전체 학습 세트를 처리하고 그래디언트를 축적할 수 없습니다. 이는 여전히 실현 불가능합니다.
병렬이 아닌 경우와 마찬가지로, 미니배치(mini-batches)를 사용해야 합니다. 병렬이 아닌 경우와 달리, 병렬 학습에서는 미니배치를 사용 가능한 머신 수만큼 더 분할하고 각 머신이 미니-미니배치(mini-mini-batch)를 처리하게 합니다.
문제는 머신이 미니-미니배치 처리를 마친 후 가중치와 편향을 업데이트하는 방법을 결정해야 할 때 발생합니다.
-
동기식 확률적 경사 하강법 (sync-SGD): 이 접근 방식은 모든 머신이 각자의 미니-미니배치 처리를 마칠 때까지 기다린 다음 그래디언트를 축적합니다. 메인 노드(main node)가 그래디언트를 축적하고 가중치를 업데이트합니다. 모든 보조 노드(assisting nodes)는 메인 노드가 완료되면 새 모델 가중치를 다운로드하고 다음 미니배치 처리를 시작합니다.
- 장점: 더 빨리 수렴합니다.
- 단점: 느린 보조 머신 하나가 전체 시스템을 느리게 만듭니다. 머신이 많을수록 하나가 느릴 가능성이 커집니다. 이 문제를 해결하는 알고리즘들이 있습니다.
-
비동기식 확률적 경사 하강법 (async-SGD): 머신들은 미니-미니배치 처리를 마치는 즉시 메인 노드로 그래디언트를 보냅니다. 메인 노드는 다른 보조 노드를 기다리지 않고 즉시 가중치를 업데이트합니다. 보조 노드들은 업데이트된 버전의 모델을 즉시 다운로드하고 다음 미니-미니배치 작업을 시작합니다.
- 장점: 느린 노드로 인한 시스템 속도 저하가 없습니다.
- 단점: 이론적으로는 그래디언트 부실(gradient staleness) 때문에 수렴하는 데 더 많은 스텝이 필요합니다.
- 실제로는, 그래디언트 업데이트가 희소(sparse)하다면, sync-SGD와 async-SGD 간의 수렴 속도에 거의 차이가 없습니다.
문제 2: 많은 수의 보조 노드 = 큰 배치 크기
만약 100개의 보조 GPU 노드에 접근할 수 있고 각 노드가 10,000개의 샘플로 구성된 미니-미니배치를 처리할 수 있다면, 미니배치 크기는 1백만이 될 것입니다. 더 큰 미니배치는 일반적으로 모델을 더 빨리 학습시킵니다. 왜냐하면 GPU는 샘플 수에 그다지 민감하지 않기 때문입니다 (즉, 샘플 수를 2배로 늘려도 계산 시간이 2배가 되지는 않습니다).
이론상 일반적인 경험 법칙은 미니배치 크기를 2배로 늘리면 학습률도 약 2배로 늘려야 한다는 것입니다. 하지만, 실제로는:
- 학습률을 너무 많이 높이면 수렴이 불안정해질 수 있습니다.
- 배치 크기를 특정 지점 이상으로 늘리면 효율이 체감(diminishing returns)됩니다.
- 학습률과 에포크(epoch) 수를 일정하게 유지하면서 배치 크기를 늘리면 정확도가 낮아질 수 있습니다.
- 배치 크기와 학습률 하이퍼파라미터 선택은 과학이라기보다는 예술입니다. Weights and biases에는 학습률 대 배치 크기의 직관에 대해 설명하는 훌륭한 비디오가 있습니다.
많은 수의 GPU에 접근할 수 있다는 이유만으로 미니배치 크기를 간과하거나 거대한 배치 크기를 사용하는 함정에 빠지지 마십시오.
모델 병렬성 (Model parallelism)
모델 병렬성에서는, 모델의 서로 다른 구성 요소가 서로 다른 머신에서 학습됩니다. 모델 병렬성과 데이터 병렬성은 상호 배타적이지 않습니다. 둘 다 동시에 수행될 수 있지만, 이러한 구성을 설정하려면 상당한 엔지니어링 노력이 필요합니다.
아래 그림은 하이브리드 데이터 및 모델 병렬성 구성을 보여줍니다. 이 구성에서 머신 2와 4는 첫 번째 단계(머신 1과 3)가 완료될 때까지 기다립니다. 유휴(Idle) 머신 시간은 나쁩니다. 파이프라인 병렬성은 이러한 유휴 시간을 줄이는 데 사용될 수 있습니다.
파이프라인 병렬성 (Pipeline parallelism)
이것에는 여러 변형이 있지만, 기본 아이디어는 모델을 여러 머신에서 실행되는 직렬 구성 요소로 분할한 다음 미니-미니배치의 입력을 시차를 두고(stagger) 공급하는 것입니다. 아래 그림이 이 구성을 보여줍니다. 파이프라인 병렬성이 대기 시간을 완전히 제거하지는 않지만, 줄여줍니다.
Auto ML
Auto-ML은 ML 엔지니어와 대학원생들이 하던 일부 반복적이거나 기계적인 작업을 해결하기 위해 더 많은 컴퓨팅 파워를 사용한다는 포괄적인 아이디어입니다.
소프트 Auto ML: 하이퍼파라미터 튜닝
- 하이퍼파라미터 튜닝은 주어진 모델에 대해 탐색 공간 내에서 최적의 하이퍼파라미터 세트를 찾는 것을 의미합니다.
- 2022년 기준, 이것은 Auto-ML의 가장 인기 있는 형태입니다.
- 주어진 모델과 데이터셋에 대해 하이퍼파라미터를 잘 튜닝하는 것은 성능에 극적인 영향을 미칠 수 있습니다.
- 잘 튜닝된 파라미터를 가진 약한 모델이 더 강한 모델보다 성능이 뛰어날 수 있다는 것도 보여졌습니다.
- 중요성에도 불구하고, 많은 기업이 여전히 하이퍼파라미터 튜닝에 대한 엄격하고 체계적인 접근 방식을 무시하고 수동의, 직관 기반 방법을 사용합니다.
- 많은 인기 있는 ML 라이브러리에는 자동 하이퍼파라미터 튜닝을 위한 내장 유틸리티가 있거나 서드파티 유틸리티가 있습니다. 예를 들어 sklearn에는 auto-sklearn, TensorFlow에는 Keras Tune, Ray Tune이 있습니다.
- 자동 하이퍼파라미터 튜닝의 인기 있는 방법으로는 랜덤 서치(random search), 그리드 서치(grid search), 베이지안 최적화(bayesian optimisation)가 있습니다.
- 모델 성능에 더 큰 영향을 미치는 하이퍼파라미터는 더 신중하게 튜닝해야 합니다.
- 하이퍼파라미터를 튜닝하는 데 테스트 분할(test split)을 절대 사용하지 마십시오. 검증 세트(validation set)를 기반으로 파라미터를 선택하고, 최종 성능을 평가할 때만 테스트 세트를 사용하십시오.
- 하이퍼파라미터 튜닝에 대한 심층 가이드는 Auto ML: Methods, Systems, Challenges 책의 1장에서 찾을 수 있습니다.
하드 Auto ML: 아키텍처 탐색 및 학습된 옵티마이저
- 아키텍처 탐색(Architecture search)과 학습된 옵티마이저(learned optimizers)는 두 가지 독립적인 AutoML 작업입니다 (이론적으로는 함께 사용될 수 있음). 2022년 기준, 이들은 아직 "연구" 단계에 있으며 프로덕션 환경의 기업에서 널리 사용되지는 않습니다.
- 두 작업 중 하나의 초기 계산 비용(up-front computation cost)이 너무 높아서 전 세계적으로 소수의 기업만이 이 분야의 연구를 추구할 여력이 있습니다.
- 일반 ML 엔지니어로서 AutoML의 진행 상황을 인지하는 것이 중요합니다. 왜냐하면:
-
아키텍처 탐색 연구에서 나온 아키텍처는 많은 실제 작업에 바로(off-the-shelf) 사용할 수 있을 만큼 충분히 일반적일 수 있으며, 여러분의 문제에 적용할 수 있습니다.
- 아키텍처 탐색 연구는 더 높은 성능을 보이거나, 학습 및/또는 추론이 더 빠른 아키텍처 등을 생산해냈습니다. 예를 들어, Google AutoML 팀의 EfficientNets는 10배의 효율성으로 SOTA 정확도를 능가합니다.
- 학습된 옵티마이저 연구에서 나온 옵티마이저는 추가적인 사전 학습 없이(즉, 일종의 전이 학습(transfer learning) 형태가 될 수 있음) 다양한 작업에 적용할 수 있을 만큼 충분히 일반적일 수 있습니다. 이는 학습을 더 빠르고 안정적으로 만들 수 있습니다.
- 이러한 연구 분야의 결과물은 기존 아키텍처와 옵티마이저로는 이전에 불가능했던 많은 실제 문제를 해결하는 것을 가능하게 만들 수 있습니다.
-
아키텍처 탐색 연구에서 나온 아키텍처는 많은 실제 작업에 바로(off-the-shelf) 사용할 수 있을 만큼 충분히 일반적일 수 있으며, 여러분의 문제에 적용할 수 있습니다.
아키텍처 탐색 (일명 Neural Architecture Search, NAS)
직관: 알고리즘을 사용하여 신경망의 아키텍처 파라미터(예: 레이어 크기, 스킵 레이어(skip layer) 추가 여부)를 체계적으로 변경하여 문제에 대한 최적의 아키텍처를 찾습니다.
NAS 문제에는 세 가지 구성 요소가 있습니다.
- 탐색 공간(A search space): 알고리즘이 변경할 수 있는 아키텍처 구성 요소(building blocks), 각각의 제약 조건, 그리고 이를 결합하는 규칙. 구성 요소 세트는 기본 아키텍처(예: CNN, 트랜스포머, FFNN, RNN 등)에 따라 다릅니다.
- 성능 추정 전략(A performance estimation strategy): 탐색 공간이 클 때, 아키텍처의 성능을 평가하기 위해 각 아키텍처 후보를 수렴할 때까지 학습시키는 것은 엄청나게 비용이 많이 들 수 있습니다. NAS 알고리즘이 결정을 내릴 수 있도록 저렴하게 성능을 추정하는 전략이 필요합니다.
- 탐색 전략(A search strategy): 탐색 공간을 탐색하는 전략입니다. 일반적인 접근 방식은 강화 학습(성능을 향상시키는 선택에 대해 알고리즘에 보상)과 진화 알고리즘(아키텍처에 돌연변이 추가 → 가장 성능이 좋은 것 선택 → 그것들에 돌연변이 추가)입니다.
학습된 옵티마이저 (Learned optimizers)
경사 하강법 기반 학습 작업은 그래디언트 업데이트가 주어졌을 때 모델의 가중치를 업데이트하는 방법을 지정하는 옵티마이저를 내부적으로(under the hood) 사용합니다. 인기 있는 옵티마이저로는 Adam, Momentum, SGD가 있습니다.
이러한 옵티마이저들은 함수와 일부 옵티마이저 하이퍼파라미터(학습률 등)의 형태를 띱니다. 함수 자체는 사람에 의해 정의되었습니다; 만약 그 함수를 NN으로 대체하고 하이퍼파라미터를 학습할 수 있다면 어떨까요?
이것이 바로 학습된 옵티마이저 연구 분야가 하려는 일입니다. 여러 학습 작업을 "학습 데이터"로 사용하여 NN을 메타-학습(meta-train)시켜, 여러 학습 작업과 여러 NN 아키텍처에 "바로(off-the-shelf)" 사용할 수 있는 일반화 가능한 신경망 기반 옵티마이저 함수를 만들려고 합니다.
- 이 작업을 수행하는 계산 비용은 막대합니다. 메타-학습 작업을 위한 단일 샘플은
<NIST 데이터셋, 5개 합성곱 레이어를 가진 CNN, 배치 크기 32, 10K 스텝>과 같을 수 있습니다. - 일부 옵티마이저는 스스로를 더 학습시킬 수 있는 속성을 가지고 있습니다.
- Google의 "더 효과적인 학습된 옵티마이저 학습시키기, 그리고 그것들을 사용하여 스스로를 학습시키기(Training more effective learned optimizers, and using them to train themselves)" 논문에 대한 이 비디오는 이 직관을 매우 잘 설명합니다. (직관 부분만 보려면 15분까지 시청하세요.)
모델 오프라인 평가
이 섹션에서는 배포할 최상의 모델을 선택하기 위해 모델을 평가하는 방법을 다룹니다. 이 섹션은 두 가지 주제를 다룹니다.
- 베이스라인: 비교할 대상이 필요합니다.
- 전반적인 ML 지표를 넘어서는 평가 방법.
참고:
- ML 기반 지표 외에도, 비즈니스 팀과 협력하여 회사의 비즈니스와 더 관련 있는 지표를 개발할 수 있습니다.
- 이상적으로는 개발 환경과 프로덕션 환경에서의 평가 방법이 동일하기를 원합니다.
베이스라인 (Baselines)
평가 지표 자체는 큰 의미가 없습니다. 비교 대상인 베이스라인을 아는 것이 필수적입니다. 사용하는 베이스라인은 유스케이스에 따라 달라집니다. 그러나 이 섹션에서는 일반적으로 사용되고 유스케이스 전반에 걸쳐 일반화될 수 있는 몇 가지를 다룹니다.
무작위 베이스라인 (Random baseline)
특정 무작위 분포를 따르는 예측을 생성하는 베이스라인 모델을 생성합니다. 균등 분포(uniform distribution)일 수도 있고, 작업의 레이블 분포를 따를 수도 있습니다.
간단한 휴리스틱 (Simple heuristic)
간단한 휴리스틱을 사용하여 예측하는 베이스라인 모델을 생성하고 그 성능을 측정합니다. 휴리스틱의 예로는: 가장 인기 있는 것 예측, 투표 수로 추천하기가 있습니다.
제로 룰 베이스라인 (Zero rule baseline)
항상 가장 흔한 클래스를 예측하는 베이스라인 모델. 만약 이 베이스라인이 70%의 정확도를 낸다면, 여러분의 모델은 추가된 복잡성을 정당화하기 위해 이보다 훨씬 더 나은 성능을 보여야 합니다.
인간 베이스라인 (Human baseline)
인간의 성능/정확도를 베이스라인으로 사용합니다. 모델이 인간에 비해 작업을 얼마나 더 잘/못 하는지 아는 것은 종종 유용합니다.
기존 솔루션 베이스라인 (Existing solution baseline)
if/else 문으로 가득 찬 알고리즘, 이전 모델 또는 벤더가 제공한 모델이 이미 있다면, 그것을 베이스라인으로 사용하십시오. 모델을 선택하기 위해 반드시 베이스라인보다 더 나은 성능을 낼 필요는 없습니다. 때때로 모델의 정확도가 약간 낮더라도 훨씬 저렴하다면, 추진할 가치가 있습니다.
전반적인 ML 지표를 넘어서는 평가 방법
사람들은 F1이나 정확도와 같은 전반적인 성능 지표에 집착하는 경향이 있습니다. 그러나 모델 평가 및 선택에는 전반적인 ML 지표보다 훨씬 더 많은 것이 있습니다. 프로덕션 모델의 경우, 모델이 견고하고(robust), 공정하며(fair), 보정(calibrated)되어 있고, 전반적으로 말이 되는지(makes sense) 확인해야 합니다.
섭동 테스트(perturbation tests)를 통한 견고성 평가
- 이상적으로는 프로덕션 데이터와 동일한 분포 및 노이즈 수준을 가진 데이터로 모델을 학습시켜야 합니다. 그러나 이것이 항상 가능한 것은 아닙니다.
- 노이즈가 있는 프로덕션 데이터와 분포 변화 때문에, 노이즈 없는 데이터에서 측정된 최고의 모델이 노이즈가 있는 프로덕션 데이터에 대해 반드시 최고의 모델은 아닙니다.
- 모델이 노이즈가 있는 데이터에 대해 얼마나 잘 작동할지 파악하려면, 테스트 세트의 샘플에 약간의 노이즈(섭동, perturbations)를 추가하고 그것이 모델 성능에 극적인 영향을 미치는지 확인하십시오.
- 모델이 노이즈에 더 민감할수록 유지 관리가 더 어려워지고 적대적 공격(adversarial attacks)에 더 취약해집니다.
- 모델이 노이즈에 너무 민감하다면, 학습 중에 이를 줄이는 방법에 대한 준지도 학습 및 데이터 증강 노트를 확인하십시오.
불변성 테스트(invariance tests)를 통한 공정성 평가
- 입력의 특정 변경 사항이 예측의 변경으로 이어져서는 안 됩니다. 예를 들어, 샘플의 인종이나 성별 변경이 누군가의 신용 가치(credit worthiness)에 영향을 미쳐서는 안 됩니다.
- 불변성 테스트 중에, ML 엔지니어는 테스트 샘플의 민감한 정보를 변경하고 예측이 바뀌는지 확인합니다.
- 이를 근본적으로 제거하는 데 도움이 되도록, 민감한 정보 피처를 모델 학습에서 완전히 제외해야 합니다.
방향성 기대 테스트(directional expectation tests)를 통한 건전성 확인
- 데이터의 특정 변경 사항은 예측 가능한 방식으로 예측을 변경해야 합니다. 예를 들어, 주택 면적이 증가하면 부동산의 예측 가치도 증가해야 합니다.
- 방향성 기대 테스트는 출력이 예상된 방식으로 변경되도록 해야 하는 피처를 변경하고 모델이 예상대로 작동하는지 평가합니다.
- 방향성 변경이 출력을 반대 방향으로 작동하게 만든다면, 모델이 잘못된 것을 학습하고 있을 수 있습니다.
보정(calibration) 평가
- 만약 모델이 100개의 이미지에 대해 "이것이 고양이일 확률 60%"라고 레이블을 붙인다면, 그 100개 이미지 중 60개는 실제로 고양이여야 합니다. 만약 그렇다면, 우리는 그 모델이 "잘 보정되었다(well calibrated)"고 말합니다. 반대로, 100개 중 90개의 이미지가 고양이로 판명된다면, 그 모델은 보정되지 않은(uncalibrated) 것입니다.
- 보정은 0에서 1 사이의 모델 출력을 확률로 자신 있게 다룰 수 있는 핵심입니다. 모델이 보정되면, 출력 확률이 실제 확률과 일치하므로, 기댓값과 같은 추가적인 확률 기반 계산을 자신 있게 수행할 수 있습니다.
- 보정은 또한 더 나은 모델 모듈성(modularity)을 가능하게 합니다. 모델 모듈성이란 한 모델의 출력을 다운스트림 모델의 피처로 사용하는 것입니다. 만약 업스트림 모델이 변경되었는데 그 모델이 보정되지 않았다면, 모든 다운스트림 모델을 재학습해야 할 수도 있습니다.
- 모델 보정은 종종 ML 엔지니어들에 의해 간과됩니다.
- 이 유튜브 비디오는 보정이 무엇인지 간단하고 직관적으로 설명합니다.
- 회귀 모델도 보정 테스트가 필요합니다.
- 모델 보정은 종종 모델 이후의 후처리(post-processing) 레이어로 수행됩니다. 이를 위한 몇 가지 리소스는 다음과 같습니다.
-
sklearn.calibration.CalibratedClassifierCV의 Platt Scaling - Temperature scaling을 이용한 신경망 보정
- Google의 보정에 관한 블로그 포스트
-
신뢰도 측정(confidence measurement)을 통한 건전성 확인
- 일부 유스케이스의 경우, 모델이 무언가에 대해 확신이 없다면 사용자가 신뢰를 잃을 수 있으므로 추천을 보여주지 않는 것이 나을 수 있습니다.
- 신뢰도 측정은 다음을 식별하려고 시도합니다.
- 유스케이스에 대한 확실성(certainty)을 어떻게 측정합니까?
- 예측에 대해 확신할 수 있는 임계값(threshold)은 얼마입니까?
- 임계값 미만의 예측으로 무엇을 합니까? (예: 폐기, 사람 개입(loop in humans), 사용자에게 추가 정보 요청)
- 신뢰도 측정은 샘플별로 이루어집니다. 이는 평균적인 측정이 아닙니다.
슬라이스 기반(slice-based) 평가 테스트를 통한 성능 및 공정성 평가
슬라이싱(Slicing)은 데이터를 중요한 슬라이스(하위 집합)로 분리하고 각 슬라이스에 대해 모델이 어떻게 수행되는지 평가하는 것을 의미합니다.
전역 집계(global aggregation) 지표에 초점을 맞추고 슬라이스 기반 성능을 무시하는 것은 문제가 있습니다.
- 어떤 문제의 경우, 모델이 모든 슬라이스에 대해 동일하게 작동해야 합니다. 만약 다르게 작동한다면 모델이 편향되고(biased) 불공정할(unfair) 수 있습니다.
- 예를 들어, 모델 A의 전체 정확도는 96%이지만,
여성슬라이스를남성슬라이스와 별도로 평가할 때 정확도가 더 나쁘다는 것을 발견할 수 있습니다. 모델 B는 전체적으로 95%로 약간 성능이 낮지만남성과여성슬라이스에 대해 동일한 정확도를 가집니다. 모델 B가 아마도 더 바람직할 것입니다. - 동일하게 수행되어야 하는 다른 슬라이스에서 모델이 다르게 수행된다는 것을 발견하는 것은 전체 성능을 향상시킬 기회를 발견했음을 의미합니다.
- 예를 들어, 모델 A의 전체 정확도는 96%이지만,
- 어떤 문제의 경우, 특정 슬라이스에 대해 모델이 더 잘 수행되기를 기대합니다. 만약 슬라이스들이 동일하게 수행된다면, 그것은 문제입니다.
- 예를 들어, 이탈 예측(churn prediction)에서,
유료 고객슬라이스가무료(freemiam) 고객슬라이스보다 더 중요합니다. 이 경우유료 고객슬라이스의 성능이 더 좋기를 기대할 것입니다.
- 예를 들어, 이탈 예측(churn prediction)에서,
중요한 슬라이스가 무엇인지 정의하는 것은 과학이라기보다는 예술에 가깝습니다. 몇 가지 일반적인 접근 방식은 다음과 같습니다.
- 휴리스틱 기반: 도메인 지식에 기반하여 데이터를 슬라이스합니다. (예: 성별, 모바일 vs 웹 트래픽, 인종별)
- 오류 분석(Error analysis): 잘못 분류된 예제들을 수동으로 검토하고 그들 사이의 패턴을 찾습니다. 특정 그룹이 지속적으로 잘못 분류된다면 슬라이스를 발견할 수 있습니다.
-
슬라이스 파인더(Slice finder): 슬라이스 찾기를 체계화하기 위한 일부 연구가 있었습니다. 보통 후보 슬라이스를 자동 생성한 다음 나쁜 후보를 가지치기(pruning)합니다. 예:
- Slice Finder: Automated Data Slicing for Model Validation
- Subgroup Discovery Algorithms: A Survey and Empirical Evaluation
중요한 슬라이스를 찾으면, 슬라이스 기반 평가 테스트를 수행할 수 있도록 각 슬라이스에 대해 충분하고 정확하게 레이블이 지정된 데이터가 필요합니다.
7 - 모델 배포 및 예측 서비스
이제 ML 모델이 선택, 학습, 평가되었으므로 프로덕션에 배포할 차례입니다. 이 책의 다른 모든 단계와 마찬가지로, 이것은 반복적인 과정이며 이전 단계로 돌아가 일부를 다시 생각해야 할 수도 있습니다. 특히 이 단계에서는 모델이 예측을 서빙하고 계산하는 방식이 모델 설계 방식, 필요한 인프라, 그리고 사용자가 경험하는 동작에 영향을 미치기 때문에 이러한 반복적 특성이 특히 적용됩니다.
이 장에서는 다음 내용을 다룹니다.
- ML 프로덕션 배포에 관한 몇 가지 일반적인 오해.
- 4가지 주요 예측 서빙 모드 설명: 1. 배치 예측, 2. 배치 피처만 사용하는 온라인 예측, 3. 배치 피처와 스트리밍 피처를 모두 사용하는 온라인 예측 (일명 스트리밍 예측), 4. 1과 2의 하이브리드.
- 배치 예측 VS 온라인 예측을 언제 사용해야 하는지에 대한 직관 제공.
- 배치 학습 데이터 파이프라인과 스트리밍 예측 파이프라인을 통합할 때의 과제.
- ML 모델의 추론 속도를 높이기 위한 모델 압축(model compression) 기술.
- 모델을 클라우드 VS 엣지에 배포할 때의 고려 사항.
시작하기 전 몇 가지 입문 참고 사항:
- 프로덕션(Production)은 팀마다 매우 다른 것을 의미합니다. 어떤 팀에게 "프로덕션"은 일부 데이터 분석을 수행하기 위해 노트북에서 모델을 사용하는 것입니다. 다른 팀에게 "프로덕션"은 하루에 수백만 명의 사용자를 위한 예측을 생성하기 위해 모델을 계속 실행하는 것입니다. 이 장은 후자에 더 가까운 유스케이스에 초점을 맞춥니다.
- "모델을 프로덕션에 배포하는 것"의 어려운 부분 중 일부는 다음과 같습니다.
- 밀리초(millisecond) 수준의 지연 시간과 99%의 가동 시간(uptime)으로 많은 수의 사용자에게 모델을 사용 가능하게 만들기.
- 프로덕션에서 모델이 실패하는 시점을 인지하고 적절한 사람에게 자동으로 알리기.
- 프로덕션 버그를 수정하기 위해 원활하게(seamlessly) 업데이트를 배포하기.
- 어떤 회사에서는 모델을 만드는 엔지니어가 배포하고 운영하는 엔지니어와 동일합니다. 다른 회사에서는 모델을 만드는 엔지니어가 모델을 내보내고(export) 배포를 위해 다른 팀에 전달합니다.
- Chip(저자)은 첫 번째 방식 (당신이 만들었으면, 당신이 배포한다)이 더 낫다고 믿습니다.
- 프로덕션에 배포하려면 "모델 내보내기" (일명 직렬화(serialization))가 필요합니다. 모델에는 내보낼 수 있는 두 가지 부분, 즉 모델 정의(model definition)와 모델 파라미터(model parameters)가 있습니다. 일반적으로 두 아티팩트(artifacts)는 함께 내보내집니다.
- TF 및 PyTorch의 내보내기 함수 예:
tf.keras.Model.save및torch.onnx.export()
- TF 및 PyTorch의 내보내기 함수 예:
ML 배포에 관한 오해
오해 1: 한 번에 하나 또는 두 개의 ML 모델만 배포한다
이는 사실이 아닙니다. 중대형 기업은 프로덕션 환경에서 동시에(concurrently) 아주 많은 ML 모델을 실행합니다. 애플리케이션 기능당 적어도 하나의 모델을 찾는 것이 일반적입니다. 예: 승차 수요 예측, 드라이버 가용성 예측, 도착 시간 추정, 동적 가격 책정 등.
오해 2: 아무것도 하지 않으면 모델 성능은 동일하게 유지된다
소프트웨어 시스템처럼 ML 시스템도 "소프트웨어 노후화(software rot)"를 겪습니다. 또한, ML 시스템은 시간이 지남에 따라 데이터 분포 변화를 겪으며, 이러한 변화는 성능을 저해할 것입니다. 이것이 ML 모델이 학습 직후에 가장 좋은 성능을 내는 경향이 있는 이유입니다.
오해 3: 모델을 그렇게 자주 업데이트할 필요는 없다
위에서 언급한 성능 저하(performance decay)와 관련하여, 우리는 가능한 한 자주 모델을 업데이트하기를 원합니다. "한 달에 한 번" 또는 "분기에 한 번"의 주기가 일반적입니다. 그러나 필요한 경우 X분마다 업데이트를 수행할 수 있는 회사도 있습니다.
오해 4: 대부분의 ML 엔지니어는 규모(Scale)에 대해 걱정할 필요가 없다
이는 사실이 아닙니다. 대부분의 ML 엔지니어는 중대형 규모의 회사에 고용되어 있습니다. 이 정도 규모의 회사가 초당 수백 건의 추론을 생성하거나 수백만 명의 사용자를 처리해야 하는 모델을 보유하는 것은 상당히 일반적입니다.
예측 서빙의 네 가지 모드
참고: 이 주제에 대한 용어는 커뮤니티 내에서 여전히 모호합니다(murky). 유사한 개념을 지칭하는 다른 용어를 찾을 수도 있습니다.
프로덕션에서 예측을 서빙하는 세 가지 주요 모드와 하나의 하이브리드 모드가 있습니다.
- 배치 예측(Batch prediction): 배치 피처만 사용하며, 예약되거나 트리거되는 작업을 실행하여 예측을 대량 생산하고 데이터베이스에 저장합니다. 추론 시점에는 DB에서 예측을 조회(looked up)합니다.
- 장점:
- 미리 계산된 예측은 매우 빠르게 검색되어 사용자에게 제공될 수 있습니다.
- GPU 벡터화(vectorisation)를 활용하기 위해 여러 추론을 일괄 처리(batching up)하는 것이 자연스러운 단계입니다. 추론 배칭은 높은 예측 처리량(throughput)으로 이어집니다.
- 추론이 필요한 데이터를 분할하여 여러 병렬 노드로 전송함으로써 예측 처리량을 더욱 높일 수 있습니다.
- 단점:
- 해당 예측을 사용할 만큼 자주 앱을 사용하지 않는 사용자를 위해 예측을 생성할 수 있습니다.
- 예측에 포함하기 위해 "방금" 사용 가능해진 데이터를 고려할 수 없습니다. 예측은 마지막 피처 계산 작업이 실행되었을 때의 세상 상태를 반영합니다.
- 사용 시기:
- 예측의 빠른 검색이 필요하고 최신이 아닐 수도 있는 피처를 사용해도 괜찮을 때. 즉, "배치 피처만 사용하는 온라인 예측 (모드 2)"이 충분히 빠르지 않거나 저렴하지 않을 때입니다. 이 모드가 실행 가능하려면 모든 쿼리(예: 모든 사용자)에 대한 예측을 계산하고 저장하는 것이 수용 가능해야 합니다.
- 이 모드의 또 다른 유스케이스는 모집단에 대한 전반적인 집계 통계를 파악하기 위해 모든 가능한 쿼리에 대한 예측이 필요할 때입니다. 예를 들어, 다음 달에 이탈할 가능성이 있는 상위 10%의 고객에게 연락하고 싶을 때입니다.
- 장점:
- 배치 피처만 사용하는 온라인 예측(Online prediction that uses only batch features): 추론 계산 자체는 즉석에서(on the fly) 그리고 요청 시(on demand) 수행되지만, 필요한 모든 피처는 미리 계산되었으며 스트리밍 피처의 실시간 계산이 필요하지 않습니다.
- 장점:
- 실제로 예측이 필요한 사용자를 위해서만 예측을 생성합니다.
- 단점:
- 피처에 "방금" 사용 가능해진 데이터를 고려할 수 없습니다.
- 추론 시 GPU 벡터화를 사용하기 위해 추론 요청을 일괄 처리하는 것은 배칭을 위한 시간 윈도우(time windows)를 처리해야 하므로 약간 더 어렵습니다.
- 예측이 사용자에게 제공되기까지 더 느릴 것입니다.
- 사용 시기: 전체 고객 기반에 대한 예측을 얻는 컴퓨팅 노력을 낭비할 여유가 없고 검색 속도를 희생할 의향이 있을 때. 또한 미리 계산된 피처를 사용해도 괜찮아야 합니다.
- 장점:
- 배치 피처와 스트리밍 피처를 모두 사용하는 온라인 예측 (일명 스트리밍 예측): 추론 계산은 즉석에서 그리고 요청 시 수행됩니다. 모델을 위한 일부 피처는 미리 계산되지만 일부는 "최신(fresh)"이어야 하므로 스트리밍 피처를 사용하여 즉석에서 계산되어야 합니다. 이 모드가 작동하려면 시스템에 거의 실시간(near real-time) 전송 및 데이터 스트림 계산이 필요합니다. 아래 다이어그램 참조.
- 장점:
- 실제로 예측이 필요한 사용자를 위해서만 예측을 생성합니다.
- 피처 계산을 위해 "방금" 사용 가능해진 데이터를 고려할 수 있습니다.
- 단점:
- 추론 시 GPU 벡터화를 사용하기 위해 추론 요청을 일괄 처리하는 것은 배칭을 위한 시간 윈도우를 처리해야 하므로 약간 더 어렵습니다.
- 스트리밍 피처가 계산된 다음 추론될 때까지 기다려야 하므로, 예측은 "배치 피처만 사용하는 온라인 예측"보다 훨씬 더 느릴 것입니다.
- 사용 시기: 방금 생성된 데이터를 예측에 반영하는 것이 매우 중요할 때. 거래 및 로그인 사기 탐지가 일반적인 예입니다.
- 장점:
- 모드 1과 2의 하이브리드 (배치 및 배치 피처를 사용한 온라인): 인기 있는 쿼리(예: 자주 로그인하는 사용자)에 대해서는 예측을 미리 계산하고, 덜 인기 있는 쿼리에 대해서는 모드 2를 사용하여 온라인 예측을 생성합니다.
- 장점:
- 빈번하지 않은 쿼리에 대한 추론을 계산하고 저장하는 자원 낭비 문제가 해결됩니다.
- 단점:
- "빈번한 쿼리"가 무엇을 의미하는지 파악해야 합니다.
- 배치 시스템과 온라인 시스템을 모두 처리해야 하므로 복잡성이 증가합니다.
- 이 모드는 여전히 피처에 "방금" 사용 가능해진 데이터를 고려할 수 없습니다.
- 사용 시기: 일반적인 쿼리(모드 1처럼)에 대해 매우 빠른 예측 검색을 제공해야 하지만, 모든 쿼리에 대한 예측을 미리 계산할 여유가 없을 때. 더 빠른 예측 검색의 이점이 증가된 복잡성보다 커야 합니다.
- 장점:
- "온라인 예측"과 "배치 예측"이라는 용어는 혼란스러울 수 있습니다. 이 문맥에서 이 용어들은 예측이 언제 이루어지는지를 나타냅니다. 두 방식 모두 GPU 벡터화 최적화를 활용하기 위해 여러 샘플에 대한 예측을 동시에 수행(즉, 추론 요청을 배치로 그룹화)할 수 있습니다. 또한 두 방식 모두 한 번에 하나의 샘플을 처리할 수도 있습니다.
스트리밍 예측 → 배치 학습 파이프라인과 스트리밍 서빙 파이프라인 통합하기
이 섹션은 주로 서빙 모드 3: "배치 피처와 스트리밍 피처를 모두 사용하는 온라인 예측"과 관련이 있습니다.
학습용 피처를 구축하는 데 사용되는 배치 데이터 파이프라인과, 추론 시점에 즉석에서 피처를 계산하기 위한 별도의 데이터 파이프라인을 가진 회사를 찾는 것은 드문 일이 아닙니다.
두 개의 서로 다른 파이프라인을 갖는 것은 한 파이프라인의 변경 사항이 다른 파이프라인에 전파되지 않아 피처 드리프트(feature drift)를 유발하기 때문에 ML에서 버그의 일반적인 원인이 됩니다.
배치 파이프라인과 스트리밍 파이프라인을 통합하기 위한 인프라 구축은 매우 인기 있는 주제가 되었습니다. 기업들은 일반적으로 피처 스토어(feature stores)를 사용하거나 Apache Flink와 같은 스트림 컴퓨팅 기술을 기반으로 자체 개발(in-house) 도구를 구축하여 문제를 해결하려고 합니다.
모델 압축을 통한 더 빠른 추론
필요한 경우 추론 지연 시간을 줄일 수 있는 3가지 방법이 있습니다.
- 모델 압축(Model compression): 모델을 더 작게 만듭니다. 이 섹션의 초점입니다.
- 추론 최적화(Inference Optimization): 추론 지연 시간을 줄이기 위해 계산 및 컴파일 파라미터를 조정합니다. 이에 대한 자세한 내용은 아래의 "모델 최적화" 섹션에서 다룹니다.
- 더 빠른 하드웨어(Faster hardware): 더 좋은 하드웨어를 구매하거나 가지고 있는 하드웨어를 더 빠르게 실행시킵니다.
원래 모델 압축은 모델을 더 작게 만들어 엣지 디바이스(edge devices)에 탑재할 수 있도록 돕기 위해 개발되었습니다. 그러나 모델을 더 작게 만들면 일반적으로 더 빨리 실행되므로, 이제 압축은 엣지가 아닌 시나리오에서도 추론 속도를 높이는 데 사용되고 있습니다.
압축은 또한 공정성(fairness) 영역에도 파급 효과(ripple effects)를 미친다는 점에 유의하십시오. 자세한 정보는 11장을 참조하십시오.
모델 압축에는 4가지 일반적인 기술이 있습니다.
- 저계수 분해 (Low-rank factorization)
- 지식 증류 (Knowledge distillation)
- 가지치기 (Pruning)
- 양자화 (Quantization)
저계수 분해 (Low-rank factorization)
- 직관: 고차원 텐서(tensor)를 더 낮은 차원의 텐서로 대체합니다.
- 예: CNN을 위한 컴팩트 컨볼루셔널 필터(compact convolutional filters).
- 단점: 저계수 방법은 특정 유형의 모델에 한정되는 경향이 있으며 기본 아키텍처에 대한 깊은 이해를 필요로 합니다. 이 때문에 아직 광범위한 유스케이스에서 사용되지 않고 있습니다.
지식 증류 (Knowledge Distillation)
- 직관: 작은 모델(학생)이 더 큰 모델 또는 모델 앙상블(교사)을 모방하도록 학습됩니다. 배포하는 것은 이 더 작은 모델입니다.
- 예: DistilBERT(학생)는 BERT 모델(교사)의 크기를 40% 줄이면서 언어 이해 능력의 97%를 유지하고 60% 더 빠릅니다.
- 장점: 교사와 학생 간의 아키텍처 차이에 관계없이 작동합니다. 예를 들어, 랜덤 포레스트 학생과 트랜스포머 NN 교사를 가질 수 있습니다.
- 단점: 사용 가능한 교사 모델이 있어야 합니다.
가지치기 (Pruning)
-
직관: 결정 트리의 가지치기(불필요하거나 중복되는 트리 가지 제거)에서 영감을 받았습니다. 신경망의 맥락에서, 가지치기는 2가지 형태를 띱니다.
- 전체 노드(node)를 제거하여 파라미터 수를 줄입니다. 이 형태는 덜 일반적입니다.
- 더 일반적인 방법: 예측에 거의 기여하지 않는 파라미터를 찾아 0으로 설정합니다. 아키텍처는 동일하게 유지되지만 0이 아닌 파라미터의 수를 극적으로 줄일 수 있습니다. 희소(Sparse) 모델은 더 적은 저장 공간을 필요로 하고 더 높은 계산 성능을 갖는 경향이 있습니다. 실험에 따르면 전체 정확도를 유지하면서 크기를 최대 90%까지 줄일 수 있습니다.
- 장점: NN의 아키텍처에 관계없이 작동합니다.
-
단점: 가지치기는 모델에 편향(bias)을 도입할 수 있습니다. 자세한 내용은 11장에서 다룹니다.
- #todo : 올바른 섹션 링크하기
- 참고: 가지치기는 또한 어떤 노드가 중요한지 결정하고, 모델을 재설계(re-architecting)하며, 재학습하는 아키텍처 탐색 도구로도 사용되어 왔습니다. 그러나 일부 연구에서는 크고 희소한 모델이 재설계되고 재학습된 모델보다 성능이 우수함을 보여주었습니다.
양자화 (Quantization)
-
직관: 파라미터를 저장하기 위해 32비트 정밀도 부동 소수점(floats)을 사용하는 대신, 더 적은 비트를 사용합니다.
- 파라미터당 비트 수가 적다는 것은 모델의 메모리 점유 공간(footprint)이 더 작아짐을 의미합니다.
- 메모리가 적다는 것은 (학습 중 양자화를 사용한다면) 원할 경우 더 큰 모델을 학습시킬 수 있음을 의미합니다.
- 메모리가 적다는 것은 또한 배치 크기를 늘려 학습 계산 속도를 높일 수 있음(학습 중 양자화를 사용한다면)을 의미합니다.
- 부동 소수점의 정밀도가 낮다는 것은 원하는 정밀도를 달성하는 데 필요한 계산이 적다는 것을 의미하며, 이는 학습과 추론 속도를 모두 높여줍니다.
-
유형:
- 목표 정밀도별 유형:
- 16비트 (일명 "저정밀도(low-precision)")
- 8비트 (일명 정수 양자화(integer quantization) 또는 "고정 소수점(fixed-point)")
- 고정 소수점 추론은 엣지에서의 추론을 위한 업계 표준이 되었습니다.
- Google의 TensorFlow Lite, Facebook의 PyTorch Mobile, NVIDIA의 TensorRT는 모두 몇 줄의 코드로 학습 후 고정 소수점(post-training fixed-point) 양자화를 제공합니다.
- 1비트 (일명 이진 가중치 네트워크(binary weight networks))
- 양자화 적용 시점별 유형:
- 학습 중 양자화(Quantization during training): 처음부터 저정밀도로 학습합니다.
- 학습 후 양자화(Quantization post-training): 완전 정밀도(full precision)로 학습한 다음, 추론을 위해서만 학습된 모델을 양자화합니다.
- 목표 정밀도별 유형:
-
장점:
- 모든 형태의 양자화는 기존 라이브러리를 사용하여 수행하기 간단하며 여러 모델 유형에 잘 일반화됩니다.
- 저정밀도 학습은 매우 인기를 얻었으며 학습 하드웨어(예: NVIDIA GPU 및 Google TPU)가 이에 대한 직접적인 하드웨어 지원을 제공하기 시작했습니다.
- 2022년 기준 고정 소수점 학습은 아직 매우 대중적이진 않지만 유망한 결과를 보여주었습니다.
-
단점:
- 정밀도가 낮다는 것은 표현될 수 있는 숫자가 적다는 것을 의미합니다. 이는 정밀도 반올림 오류(precision rounding errors) 및 숫자 경계 오버플로우(number boundary overflow)의 위험을 도입합니다. 작은 반올림 오류가 성능의 극적인 변화로 이어질 수 있습니다.
- 효율적인 반올림 및 스케일링은 간단하지 않습니다(non-trivial). 하지만 다행히도 주요 프레임워크에는 이것이 내장되어 있습니다.
- 정밀도가 낮다는 것은 표현될 수 있는 숫자가 적다는 것을 의미합니다. 이는 정밀도 반올림 오류(precision rounding errors) 및 숫자 경계 오버플로우(number boundary overflow)의 위험을 도입합니다. 작은 반올림 오류가 성능의 극적인 변화로 이어질 수 있습니다.
더 많은 압축 리소스
- “The Top 168 Model Compression Open Source Projects”
- “Survey of Model Compression and Acceleration for Deep Neural Networks" Cheng et al, 2020.
클라우드와 엣지의 ML
추론 계산의 주요 부분을 어디에서 수행할지 결정해야 합니다. 계산은 클라우드 또는 엣지에서 일어날 수 있습니다.
클라우드 (The cloud)
장점
- 모델을 배포하는 가장 쉬운 방법입니다. 모델과 모델이 실행되는 하드웨어의 호환성에 대해 거의 걱정할 필요가 없습니다.
단점
- ML 추론 클라우드 비용은 상당할 수 있습니다. 중소기업의 비용은 연간 5만 달러에서 2백만 달러 사이가 될 수 있습니다. 엣지로 더 많은 계산을 옮길수록, 비용을 더 적게 지불합니다.
- 클라우드는 추론에 네트워크 지연 시간(network latency)을 도입합니다. 많은 경우 네트워크 지연 시간은 추론 지연 시간보다 더 큰 병목 현상이 될 수 있습니다.
- 해당 기능은 고객의 인터넷 접속에 의존하게 됩니다.
엣지 (The edge)
브라우저, 휴대폰, 노트북, 스마트워치, 자동차, 보안 카메라, 로봇, 임베디드 디바이스, FPGA(필드 프로그래머블 게이트 어레이), ASIC(주문형 반도체).
그 인기로 인해, 기업들은 다양한 ML 유스케이스에 최적화된 엣지 디바이스를 개발하기 위해 경쟁하고 있습니다. Google, Apple, Tesla를 포함한 기존 기업들은 모두 자체 ML 최적화 칩을 만들 계획을 발표했습니다.
장점
- 인터넷 없이 작동합니다.
- 유스케이스에 엄격한 "인터넷 사용 불가" 정책이 있는 경우 유일한 방법일 수 있습니다.
- 네트워크 지연 시간이 없기 때문에 전반적인 지연 시간이 줄어들 수 있습니다.
- 민감한 데이터를 다루고 있어 네트워크로 전송하고 싶지 않은 경우(예: 지문, FaceID) 유용합니다.
- 엣지 계산은 GDPR과 같은 개인정보 보호 규정을 준수하기 더 쉽게 만듭니다.
단점
- 모델이 대상 하드웨어에서 실행되도록 호환되지 않거나 효율적으로 실행되지 않을 수 있습니다.
- 하드웨어 제조업체와 프레임워크 제작자는 새로운 하드웨어 또는 기존 하드웨어의 새로운 프레임워크에 대한 지원을 추가하는 데 일반적으로 오랜 시간이 걸립니다. 이는 매우 어려운 작업이기 때문입니다.
- 모델을 다른 대상 하드웨어에서 실행되도록 컴파일(compilation)하는 것은 많은 수동 노력이 필요한 어렵고 시간이 많이 걸리는 작업입니다.
- 다양한 하드웨어 아키텍처에 맞게 모델을 최적화하는 것은 매우 어렵습니다. 응용 ML 엔지니어라면 일반적으로 이 작업을 수행할 필요가 없습니다. 이는 보통 ML 프레임워크 개발자가 수행합니다. 연구자라면 이 작업을 수행해야 할 수도 있습니다.
- 엣지 디바이스는 모델을 실행할 수 있을 만큼 충분히 강력하고, 충분한 메모리와 배터리를 갖추고 있어야 합니다. 이 모든 것이 솔루션에 제어하기 어려운 변수를 추가합니다.
엣지 디바이스를 위한 모델 컴파일 및 최적화
컴파일은 ML 모델을 대상 하드웨어에서 실행될 수 있는 아티팩트로 변환하는 과정입니다. 최적화는 대상 하드웨어의 속성을 활용하여 모델이나 아티팩트를 조정하고 더 빨리 실행되도록 만드는 과정입니다.
컴파일
응용 ML 엔지니어로서, 여러분은 일반적으로 이미 생성된 모델 컴파일러를 사용할 것입니다.
일부 컴파일러는 중간 표현(Intermediate Representations, IRs)이라는 개념에 의존합니다. IR은 프레임워크 제작자와 하드웨어 제조업체가 컴파일러 생성을 용이하게 하고 하드웨어 지원 문제를 완화하기 위해 모두 채택한 표준화된 중간 형식입니다.
최적화
모델이 대상 아키텍처에서 실행된다는 사실이 효율적으로 그렇게 할 수 있음을 의미하지는 않습니다.
응용 ML 엔지니어로서, 여러분은 일반적으로 컴파일러 또는 하드웨어 최적화에 의존할 것이며, 하드웨어 작업을 직접 최적화하지는 않을 것입니다.
ML 프레임워크 개발자와 하드웨어 제조업체는 ML과 하드웨어 아키텍처 모두에 대해 아는 전문 최적화 엔지니어를 고용하는 데 막대한 돈을 씁니다.
최적화 엔지니어가 아니더라도 코드를 최적화할 수는 있다는 점에 유의하십시오.
수동 모델 최적화
최적화 엔지니어는 모델을 더 빨리 실행시킬 방법을 찾기 위해 휴리스틱(heuristics)을 사용합니다. 수동으로 만든 휴리스틱은 최적이 아니며 기본 ML 프레임워크나 하드웨어 아키텍처가 변경될 때 유연하지 못하다는 문제가 있습니다.
모델 최적화에 ML 사용
휴리스틱에 의존하는 대신 ML을 사용하여 최적의 연산 순서 탐색을 좁힙니다. 이는 최적에 더 가깝고 ML 프레임워크 및 하드웨어 변경에 유연하게 적응할 수 있다는 장점이 있습니다.
ML 기반 컴파일러의 결과는 인상적입니다. 응용 ML 엔지니어로서 여러분은 하나의 하드웨어 백엔드에 대해 모델을 한 번 최적화한 다음, 동일한 하드웨어 유형의 여러 디바이스에서 실행합니다. ML 기반 옵티마이저는 실행하는 데 몇 시간, 때로는 며칠이 걸릴 수 있다는 점을 고려하십시오.
브라우저에서의 ML
원칙적으로, 모델을 브라우저에서 효율적으로 실행할 수 있게 한다면, 더 이상 대상 하드웨어, 컴파일, 최적화에 대해 걱정할 필요가 없습니다.
사람들은 보통 브라우저에 대해 이야기할 때 자바스크립트(Javascript)를 생각합니다. 모델을 자바스크립트 브라우저 실행 가능 아티팩트로 변환하는 라이브러리가 있습니다. 그러나 JS는 근본적으로 ML에 적합하지 않으므로 사용을 권장하지 않습니다.
유망한 대안은 WebAssembly(WASM)로 컴파일되는 모델입니다. WASM은 JS보다 훨씬 빠르며 2021년 기준 브라우저의 93%가 이를 지원했습니다.
WASM은 iOS 또는 Android 앱을 통해 네이티브 하드웨어에서 모델을 실행하는 것에 비하면 여전히 느립니다.
This content originally appeared on DEV Community and was authored by Jonas Kim
Jonas Kim | Sciencx (2025-11-08T14:54:16+00:00) ‘머신러닝 시스템 설계’ (Chip Huyen) 요약 – 파트 2. Retrieved from https://www.scien.cx/2025/11/08/%eb%a8%b8%ec%8b%a0%eb%9f%ac%eb%8b%9d-%ec%8b%9c%ec%8a%a4%ed%85%9c-%ec%84%a4%ea%b3%84-chip-huyen-%ec%9a%94%ec%95%bd-%ed%8c%8c%ed%8a%b8-2/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.




