12월 21, 2025
45 분 읽기

주니어 머신러닝 엔지니어 면접 질문

interview
career-advice
job-search
entry-level
주니어 머신러닝 엔지니어 면접 질문
Milad Bonakdar

Milad Bonakdar

작성자

주니어 ML 엔지니어 면접을 위해 Python, 모델 평가, 데이터 누수, 배포, 모니터링, MLOps 기초 질문과 답변 포인트를 준비하세요.


주니어 머신러닝 엔지니어 면접 질문

주니어 머신러닝 엔지니어 면접에서는 안정적인 Python 코드 작성, 모델 학습과 평가, 데이터 누수 방지, 배포 준비, 릴리스 이후 예측 모니터링을 설명할 수 있어야 합니다. 좋은 답변은 알고리즘뿐 아니라 데이터 가정, 지표 선택, 운영상의 트레이드오프까지 보여줍니다.

이 가이드는 엔트리 레벨 ML 엔지니어링 면접에서 자주 나오는 Python, 기본 ML 알고리즘, 검증, 불균형 데이터, 모델 서빙, Docker, 모니터링, CI/CD 기초 질문을 연습하도록 돕습니다.

Python & 프로그래밍 (5개 질문)

1. 메모리에 맞지 않는 대용량 데이터 세트를 어떻게 처리합니까?

답변: 사용 가능한 RAM보다 큰 데이터를 처리하는 몇 가지 기술이 있습니다.

  • 배치 처리: 데이터를 덩어리로 처리
  • 제너레이터: 필요에 따라 데이터 생성
  • Dask/Ray: 분산 컴퓨팅 프레임워크
  • 데이터베이스 쿼리: 필요한 데이터만 로드
  • 메모리 매핑 파일: 디스크를 메모리처럼 액세스
  • 데이터 스트리밍: 데이터가 도착하는 대로 처리
import pandas as pd
import numpy as np

# 나쁨: 전체 데이터 세트를 메모리에 로드
# df = pd.read_csv('large_file.csv')  # 충돌 가능

# 좋음: 덩어리로 읽기
chunk_size = 10000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
    # 각 덩어리 처리
    processed = chunk[chunk['value'] > 0]
    # 결과 저장 또는 집계
    processed.to_csv('output.csv', mode='a', header=False)

# 제너레이터 사용
def data_generator(filename, batch_size=32):
    while True:
        batch = []
        with open(filename, 'r') as f:
            for line in f:
                batch.append(process_line(line))
                if len(batch) == batch_size:
                    yield np.array(batch)
                    batch = []

# 분산 컴퓨팅을 위한 Dask
import dask.dataframe as dd
ddf = dd.read_csv('large_file.csv')
result = ddf.groupby('category').mean().compute()

희귀도: 매우 흔함 난이도: 중간


2. Python 데코레이터에 대해 설명하고 ML 사용 사례를 제시하십시오.

답변: 데코레이터는 코드를 변경하지 않고 함수를 수정하거나 향상시킵니다.

  • ML의 사용 사례:
    • 함수 실행 시간 측정
    • 예측 로깅
    • 결과 캐싱
    • 입력 유효성 검사
import time
import functools

# 시간 측정 데코레이터
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.2f} seconds")
        return result
    return wrapper

# 로깅 데코레이터
def log_predictions(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        predictions = func(*args, **kwargs)
        print(f"Made {len(predictions)} predictions")
        print(f"Prediction distribution: {np.bincount(predictions)}")
        return predictions
    return wrapper

# 사용법
@timer
@log_predictions
def predict_batch(model, X):
    return model.predict(X)

# 캐싱 데코레이터 (메모이제이션)
def cache_results(func):
    cache = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@cache_results
def expensive_feature_engineering(data_id):
    # 비용이 많이 드는 계산
    return processed_features

희귀도: 흔함 난이도: 중간


3. @staticmethod@classmethod의 차이점은 무엇입니까?

답변: 둘 다 인스턴스가 필요 없는 메서드를 정의합니다.

  • @staticmethod: 클래스 또는 인스턴스에 액세스 불가
  • @classmethod: 클래스를 첫 번째 인수로 받음
class MLModel:
    model_type = "classifier"
    
    def __init__(self, name):
        self.name = name
    
    # 일반 메서드 - 인스턴스 필요
    def predict(self, X):
        return self.model.predict(X)
    
    # 정적 메서드 - 유틸리티 함수
    @staticmethod
    def preprocess_data(X):
        # self 또는 cls에 액세스 불가
        return (X - X.mean()) / X.std()
    
    # 클래스 메서드 - 팩토리 패턴
    @classmethod
    def create_default(cls):
        # cls에 액세스 가능
        return cls(name=f"default_{cls.model_type}")
    
    @classmethod
    def from_config(cls, config):
        return cls(name=config['name'])

# 사용법
# 정적 메서드 - 인스턴스 불필요
processed = MLModel.preprocess_data(X_train)

# 클래스 메서드 - 인스턴스 생성
model = MLModel.create_default()
model2 = MLModel.from_config({'name': 'my_model'})

희귀도: 중간 난이도: 중간


4. ML 파이프라인에서 예외를 어떻게 처리합니까?

답변: 적절한 오류 처리는 파이프라인 실패를 방지하고 디버깅에 도움이 됩니다.

import logging
from typing import Optional

# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ModelTrainingError(Exception):
    """모델 훈련 실패에 대한 사용자 정의 예외"""
    pass

def train_model(X, y, model_type='random_forest'):
    try:
        logger.info(f"Starting training with {model_type}")
        
        # 입력 유효성 검사
        if X.shape[0] != y.shape[0]:
            raise ValueError("X와 y는 샘플 수가 같아야 합니다.")
        
        if X.shape[0] < 100:
            raise ModelTrainingError("훈련 데이터가 부족합니다.")
        
        # 모델 훈련
        if model_type == 'random_forest':
            from sklearn.ensemble import RandomForestClassifier
            model = RandomForestClassifier()
        else:
            raise ValueError(f"Unknown model type: {model_type}")
        
        model.fit(X, y)
        logger.info("Training completed successfully")
        return model
        
    except ValueError as e:
        logger.error(f"Validation error: {e}")
        raise
    except ModelTrainingError as e:
        logger.error(f"Training error: {e}")
        # 더 간단한 모델로 대체 가능
        return train_fallback_model(X, y)
    except Exception as e:
        logger.error(f"Unexpected error: {e}")
        raise
    finally:
        logger.info("Training attempt finished")

# 리소스 관리를 위한 컨텍스트 관리자
class ModelLoader:
    def __init__(self, model_path):
        self.model_path = model_path
        self.model = None
    
    def __enter__(self):
        logger.info(f"Loading model from {self.model_path}")
        self.model = load_model(self.model_path)
        return self.model
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        logger.info("Cleaning up resources")
        if self.model:
            del self.model
        return False  # 예외를 억제하지 않음

# 사용법
with ModelLoader('model.pkl') as model:
    predictions = model.predict(X_test)

희귀도: 흔함 난이도: 중간


5. Python 제너레이터란 무엇이며 ML에서 왜 유용합니까?

답변: 제너레이터는 값을 한 번에 하나씩 생성하여 메모리를 절약합니다.

  • 장점:
    • 메모리 효율적
    • 지연 평가
    • 무한 시퀀스
  • ML 사용 사례:
    • 데이터 로딩
    • 배치 처리
    • 데이터 증강
import numpy as np

# 배치 처리를 위한 제너레이터
def batch_generator(X, y, batch_size=32):
    n_samples = len(X)
    indices = np.arange(n_samples)
    np.random.shuffle(indices)
    
    for start_idx in range(0, n_samples, batch_size):
        end_idx = min(start_idx + batch_size, n_samples)
        batch_indices = indices[start_idx:end_idx]
        yield X[batch_indices], y[batch_indices]

# 훈련에서의 사용법
for epoch in range(10):
    for X_batch, y_batch in batch_generator(X_train, y_train):
        model.train_on_batch(X_batch, y_batch)

# 데이터 증강 제너레이터
def augment_images(images, labels):
    for img, label in zip(images, labels):
        # 원본
        yield img, label
        # 뒤집기
        yield np.fliplr(img), label
        # 회전
        yield np.rot90(img), label

# 훈련을 위한 무한 제너레이터
def infinite_batch_generator(X, y, batch_size=32):
    while True:
        indices = np.random.choice(len(X), batch_size)
        yield X[indices], y[indices]

# steps_per_epoch와 함께 사용
gen = infinite_batch_generator(X_train, y_train)
# model.fit(gen, steps_per_epoch=100, epochs=10)

희귀도: 흔함 난이도: 중간


ML 알고리즘 & 이론 (5개 질문)

6. 배깅과 부스팅의 차이점을 설명하십시오.

답변: 둘 다 앙상블 방법이지만 다르게 작동합니다.

  • 배깅 (Bootstrap Aggregating):
    • 랜덤 부분 집합에 대한 병렬 훈련
    • 분산 감소
    • 예: 랜덤 포레스트
  • 부스팅:
    • 순차적 훈련, 각 모델은 이전 오류 수정
    • 편향 감소
    • 예: AdaBoost, Gradient Boosting, XGBoost
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score

# 데이터 생성
X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

# 배깅 - 랜덤 포레스트
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf_scores = cross_val_score(rf, X, y, cv=5)
print(f"Random Forest CV: {rf_scores.mean():.3f} (+/- {rf_scores.std():.3f})")

# 부스팅 - Gradient Boosting
gb = GradientBoostingClassifier(n_estimators=100, random_state=42)
gb_scores = cross_val_score(gb, X, y, cv=5)
print(f"Gradient Boosting CV: {gb_scores.mean():.3f} (+/- {gb_scores.std():.3f})")

# XGBoost (고급 부스팅)
import xgboost as xgb
xgb_model = xgb.XGBClassifier(n_estimators=100, random_state=42)
xgb_scores = cross_val_score(xgb_model, X, y, cv=5)
print(f"XGBoost CV: {xgb_scores.mean():.3f} (+/- {xgb_scores.std():.3f})")

희귀도: 매우 흔함 난이도: 중간


7. 불균형 데이터 세트를 어떻게 처리합니까?

답변: 불균형 데이터는 모델이 다수 클래스 쪽으로 편향될 수 있습니다.

  • 기술:
    • 재샘플링: SMOTE, 언더샘플링
    • 클래스 가중치: 오분류에 페널티 부여
    • 앙상블 방법: Balanced Random Forest
    • 평가: 정확도가 아닌 F1, 정밀도, 재현율 사용
    • 임계값 조정: 결정 임계값 최적화
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler
from collections import Counter

# 불균형 데이터 세트 생성
X, y = make_classification(
    n_samples=1000, n_features=20,
    weights=[0.95, 0.05],  # 95% 클래스 0, 5% 클래스 1
    random_state=42
)

print(f"Original distribution: {Counter(y)}")

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 1. 클래스 가중치
model_weighted = RandomForestClassifier(class_weight='balanced', random_state=42)
model_weighted.fit(X_train, y_train)
print("\nWith class weights:")
print(classification_report(y_test, model_weighted.predict(X_test)))

# 2. SMOTE (소수 클래스 오버샘플링)
smote = SMOTE(random_state=42)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"After SMOTE: {Counter(y_train_smote)}")

model_smote = RandomForestClassifier(random_state=42)
model_smote.fit(X_train_smote, y_train_smote)
print("\nWith SMOTE:")
print(classification_report(y_test, model_smote.predict(X_test)))

# 3. 임계값 조정
y_proba = model_weighted.predict_proba(X_test)[:, 1]
threshold = 0.3  # 낮은 임계값은 소수 클래스에 유리
y_pred_adjusted = (y_proba >= threshold).astype(int)
print("\nWith adjusted threshold:")
print(classification_report(y_test, y_pred_adjusted))

희귀도: 매우 흔함 난이도: 중간


8. 교차 검증이란 무엇이며 왜 중요합니까?

답변: 교차 검증은 단일 훈련-테스트 분할보다 더 안정적으로 모델 성능을 평가합니다.

  • 유형:
    • K-Fold: k개의 폴드로 분할
    • Stratified K-Fold: 클래스 분포 유지
    • Time Series Split: 시간 순서 존중
  • 장점:
    • 더 강력한 성능 추정
    • 훈련 및 유효성 검사에 모든 데이터 사용
    • 과적합 감지
from sklearn.model_selection import (
    cross_val_score, KFold, StratifiedKFold,
    TimeSeriesSplit, cross_validate
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# 데이터 로드
data = load_iris()
X, y = data.data, data.target

model = RandomForestClassifier(random_state=42)

# 표준 K-Fold
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold)
print(f"K-Fold scores: {scores}")
print(f"Mean: {scores.mean():.3f} (+/- {scores.std():.3f})")

# Stratified K-Fold (클래스 분포 유지)
stratified_kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
stratified_scores = cross_val_score(model, X, y, cv=stratified_kfold)
print(f"\nStratified K-Fold: {stratified_scores.mean():.3f}")

# Time Series Split (시간 데이터용)
tscv = TimeSeriesSplit(n_splits=5)
ts_scores = cross_val_score(model, X, y, cv=tscv)
print(f"Time Series CV: {ts_scores.mean():.3f}")

# 다중 메트릭
cv_results = cross_validate(
    model, X, y, cv=5,
    scoring=['accuracy', 'precision_macro', 'recall_macro', 'f1_macro'],
    return_train_score=True
)

print(f"\nAccuracy: {cv_results['test_accuracy'].mean():.3f}")
print(f"Precision: {cv_results['test_precision_macro'].mean():.3f}")
print(f"Recall: {cv_results['test_recall_macro'].mean():.3f}")
print(f"F1: {cv_results['test_f1_macro'].mean():.3f}")

희귀도: 매우 흔함 난이도: 쉬움


9. 정밀도, 재현율 및 F1-점수를 설명하십시오.

답변: 모델 성능을 평가하기 위한 분류 메트릭:

  • 정밀도: 예측된 양성 중 얼마나 정확한가
    • 공식: TP / (TP + FP)
    • 사용 시기: 거짓 양성이 비용이 많이 들 때
  • 재현율: 실제 양성 중 얼마나 많이 찾았는가
    • 공식: TP / (TP + FN)
    • 사용 시기: 거짓 음성이 비용이 많이 들 때
  • F1-점수: 정밀도와 재현율의 조화 평균
    • 공식: 2 × (정밀도 × 재현율) / (정밀도 + 재현율)
    • 사용 시기: 정밀도와 재현율 사이의 균형이 필요할 때
from sklearn.metrics import (
    precision_score, recall_score, f1_score,
    classification_report, confusion_matrix
)
import numpy as np

# 예제 예측
y_true = np.array([0, 1, 1, 0, 1, 1, 0, 1, 0, 0])
y_pred = np.array([0, 1, 0, 0, 1, 1, 0, 1, 1, 0])

# 메트릭 계산
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)

print(f"Precision: {precision:.3f}")  # 0.800
print(f"Recall: {recall:.3f}")        # 0.800
print(f"F1-Score: {f1:.3f}")          # 0.800

# 혼동 행렬
cm = confusion_matrix(y_true, y_pred)
print(f"\nConfusion Matrix:\n{cm}")
# [[4 1]
#  [1 4]]

# 분류 보고서 (모든 메트릭)
print("\nClassification Report:")
print(classification_report(y_true, y_pred))

# 상충 관계 예제
from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt

# 확률 예측 가져오기
y_proba = model.predict_proba(X_test)[:, 1]

# 다양한 임계값에서 정밀도-재현율 계산
precisions, recalls, thresholds = precision_recall_curve(y_test, y_proba)

# 최적의 임계값 찾기 (F1 최대화)
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-10)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx]
print(f"Optimal threshold: {optimal_threshold:.3f}")

희귀도: 매우 흔함 난이도: 쉬움


10. 정규화란 무엇이며 언제 사용합니까?

답변: 정규화는 모델 복잡성에 페널티를 부여하여 과적합을 방지합니다.

  • 유형:
    • L1 (Lasso): 계수의 절대값 추가
    • L2 (Ridge): 계수의 제곱 추가
    • Elastic Net: L1과 L2 결합
  • 사용 시기:
    • 높은 분산 (과적합)
    • 많은 특징
    • 다중 공선성
from sklearn.linear_model import Ridge, Lasso, ElasticNet, LinearRegression
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# 많은 특징이 있는 데이터 생성
X, y = make_regression(
    n_samples=100, n_features=50,
    n_informative=10, noise=10, random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

# 정규화 없음
lr = LinearRegression()
lr.fit(X_train, y_train)
lr_score = r2_score(y_test, lr.predict(X_test))
print(f"Linear Regression R²: {lr_score:.3f}")

# L2 정규화 (Ridge)
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, y_train)
ridge_score = r2_score(y_test, ridge.predict(X_test))
print(f"Ridge R²: {ridge_score:.3f}")

# L1 정규화 (Lasso)
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
lasso_score = r2_score(y_test, lasso.predict(X_test))
print(f"Lasso R²: {lasso_score:.3f}")
print(f"Lasso non-zero coefficients: {np.sum(lasso.coef_ != 0)}")

# Elastic Net (L1 + L2)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic.fit(X_train, y_train)
elastic_score = r2_score(y_test, elastic.predict(X_test))
print(f"Elastic Net R²: {elastic_score:.3f}")

# alpha에 대한 하이퍼파라미터 튜닝
from sklearn.model_selection import GridSearchCV

param_grid = {'alpha': [0.001, 0.01, 0.1, 1.0, 10.0]}
grid = GridSearchCV(Ridge(), param_grid, cv=5)
grid.fit(X_train, y_train)
print(f"\nBest alpha: {grid.best_params_['alpha']}")
print(f"Best CV score: {grid.best_score_:.3f}")

희귀도: 매우 흔함 난이도: 중간


모델 훈련 및 배포 (5개 질문)

11. 프로덕션 환경에서 모델을 저장하고 로드하는 방법은 무엇입니까?

답변: 모델 직렬화는 배포 및 재사용을 가능하게 합니다.

import joblib
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# 모델 훈련
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier()
model.fit(X, y)

# 방법 1: Joblib (scikit-learn에 권장)
joblib.dump(model, 'model.joblib')
loaded_model = joblib.load('model.joblib')

# 방법 2: Pickle
with open('model.pkl', 'wb') as f:
    pickle.dump(model, f)

with open('model.pkl', 'rb') as f:
    loaded_model = pickle.load(f)

# TensorFlow/Keras의 경우
import tensorflow as tf

# 전체 모델 저장
# keras_model.save('model.h5')
# loaded_model = tf.keras.models.load_model('model.h5')

# 가중치만 저장
# keras_model.save_weights('model_weights.h5')
# new_model.load_weights('model_weights.h5')

# PyTorch의 경우
import torch

# 모델 상태 사전 저장
# torch.save(model.state_dict(), 'model.pth')
# model.load_state_dict(torch.load('model.pth'))

# 전체 모델 저장
# torch.save(model, 'model_complete.pth')
# model = torch.load('model_complete.pth')

# 모델 버전 관리
import datetime

model_version = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
model_path = f'models/model_{model_version}.joblib'
joblib.dump(model, model_path)
print(f"Model saved to {model_path}")

희귀도: 매우 흔함 난이도: 쉬움


12. 모델 서비스를 위한 REST API를 만드는 방법은 무엇입니까?

답변: REST API는 애플리케이션에서 모델에 액세스할 수 있도록 합니다.

from flask import Flask, request, jsonify
import joblib
import numpy as np

app = Flask(__name__)

# 시작 시 모델 로드
model = joblib.load('model.joblib')

@app.route('/predict', methods=['POST'])
def predict():
    try:
        # 요청에서 데이터 가져오기
        data = request.get_json()
        features = np.array(data['features']).reshape(1, -1)
        
        # 예측 수행
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        # 응답 반환
        return jsonify({
            'prediction': int(prediction[0]),
            'probability': probability[0].tolist(),
            'status': 'success'
        })
    
    except Exception as e:
        return jsonify({
            'error': str(e),
            'status': 'error'
        }), 400

@app.route('/health', methods=['GET'])
def health():
    return jsonify({'status': 'healthy'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

# FastAPI 대안 (최신, 더 빠름)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class PredictionRequest(BaseModel):
    features: list

class PredictionResponse(BaseModel):
    prediction: int
    probability: list
    status: str

@app.post("/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
    try:
        features = np.array(request.features).reshape(1, -1)
        prediction = model.predict(features)
        probability = model.predict_proba(features)
        
        return PredictionResponse(
            prediction=int(prediction[0]),
            probability=probability[0].tolist(),
            status="success"
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))

# 사용법:
# curl -X POST "http://localhost:5000/predict" \
#      -H "Content-Type: application/json" \
#      -d '{"features": [5.1, 3.5, 1.4, 0.2]}'

희귀도: 매우 흔함 난이도: 중간


13. Docker란 무엇이며 ML 배포에 유용한 이유는 무엇입니까?

답변: Docker 컨테이너는 모든 종속성과 함께 애플리케이션을 패키징합니다.

  • 장점:
    • 재현성
    • 환경 전반의 일관성
    • 쉬운 배포
    • 격리
# ML 모델용 Dockerfile
FROM python:3.9-slim

WORKDIR /app

# 요구 사항 복사
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 모델 및 코드 복사
COPY model.joblib .
COPY app.py .

# 포트 노출
EXPOSE 5000

# 애플리케이션 실행
CMD ["python", "app.py"]
# Docker 이미지 빌드
docker build -t ml-model:v1 .

# 컨테이너 실행
docker run -p 5000:5000 ml-model:v1

# 다중 컨테이너 설정을 위한 Docker Compose
# docker-compose.yml
version: '3.8'
services:
  model:
    build: .
    ports:
      - "5000:5000"
    environment:
      - MODEL_PATH=/app/model.joblib
    volumes:
      - ./models:/app/models

희귀도: 흔함 난이도: 중간


14. 프로덕션 환경에서 모델 성능을 어떻게 모니터링합니까?

답변: 모니터링은 모델 성능 저하를 감지하고 안정성을 보장합니다.

  • 모니터링 대상:
    • 예측 메트릭: 정확도, 대기 시간
    • 데이터 드리프트: 입력 분포 변경
    • 모델 드리프트: 성능 저하
    • 시스템 메트릭: CPU, 메모리, 오류
import logging
from datetime import datetime
import numpy as np

class ModelMonitor:
    def __init__(self, model):
        self.model = model
        self.predictions = []
        self.actuals = []
        self.latencies = []
        self.input_stats = []
        
        # 로깅 설정
        logging.basicConfig(
            filename='model_monitor.log',
            level=logging.INFO,
            format='%(asctime)s - %(message)s'
        )
    
    def predict(self, X):
        import time
        
        # 입력 통계 추적
        self.input_stats.append({
            'mean': X.mean(),
            'std': X.std(),
            'min': X.min(),
            'max': X.max()
        })
        
        # 대기 시간 측정
        start = time.time()
        prediction = self.model.predict(X)
        latency = time.time() - start
        
        self.predictions.append(prediction)
        self.latencies.append(latency)
        
        # 예측 로깅
        logging.info(f"Prediction: {prediction}, Latency: {latency:.3f}s")
        
        # 대기 시간이 너무 높으면 경고
        if latency > 1.0:
            logging.warning(f"High latency detected: {latency:.3f}s")
        
        return prediction
    
    def log_actual(self, y_true):
        self.actuals.append(y_true)
        
        # 데이터가 충분하면 정확도 계산
        if len(self.actuals) >= 100:
            accuracy = np.mean(
                np.array(self.predictions[-100:]) == np.array(self.actuals[-100:])
            )
            logging.info(f"Rolling accuracy (last 100): {accuracy:.3f}")
            
            if accuracy < 0.8:
                logging.error(f"Model performance degraded: {accuracy:.3f}")
    
    def check_data_drift(self, reference_stats):
        if not self.input_stats:
            return
        
        current_stats = self.input_stats[-1]
        
        # 간단한 드리프트 감지 (평균 비교)
        mean_diff = abs(current_stats['mean'] - reference_stats['mean'])
        if mean_diff > 2 * reference_stats['std']:
            logging.warning(f"Data drift detected: mean diff = {mean_diff:.3f}")

# 사용법
monitor = ModelMonitor(model)
prediction = monitor.predict(X_new)
# 나중에 실제 레이블을 사용할 수 있는 경우
monitor.log_actual(y_true)

희귀도: 흔함 난이도: 중간


15. 머신러닝을 위한 CI/CD란 무엇입니까?

답변: CI/CD는 ML 모델의 테스트 및 배포를 자동화합니다.

  • Continuous Integration:
    • 자동화된 테스트
    • 코드 품질 검사
    • 모델 유효성 검사
  • Continuous Deployment:
    • 자동화된 배포
    • 롤백 기능
    • A/B 테스팅
# .github/workflows/ml-pipeline.yml
name: ML Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Set up
Newsletter subscription

실제로 효과가 있는 주간 커리어 팁

최신 인사이트를 받은 편지함으로 직접 받아보세요

지원을 멈추세요. 채용되기 시작하세요.

전 세계 구직자들이 신뢰하는 AI 기반 최적화로 이력서를 면접 자석으로 변환하세요.

무료로 시작하기

이 게시물 공유

이력서 작성 시간을 90% 단축하세요

평균적인 구직자는 이력서 서식을 작성하는 데 3시간 이상을 소비합니다. 우리 AI는 15분 이내에 완성하여 지원 단계에 12배 더 빠르게 도달할 수 있습니다.