머신러닝

이석영·2025년 11월 11일

🤖 머신러닝 서버 작동 원리

1️⃣ ML 서버 학습 방법

📊 온라인 학습 (Online Learning)

피드백 입력 → 누적 데이터에 추가 → 전체 재학습 → 최적 모델 선택

🔄 피드백 학습 전체 과정

Step 1: 피드백 도착

사용자 피드백 = {
  자신감: 4, 긴장도: 2, 집중도: 5,
  열정: 3, 안정감: 4, 부정적태도: 2,
  전체만족도: 4}

Step 2: 입력 특징 생성 (X)

X = [4, 2, 5, 3, 4, 2, 4]  # 7개 특징
    [자신감, 긴장도, 집중도, 열정, 안정감, 부정적태도, 전체만족도]

Step 3: 타겟 가중치 계산 (y)

현재_가중치 = {
  자신감: 1.0, 긴장도: 0.8, 집중도: 0.9,
  열정: 0.7, 안정감: 0.8, 부정적태도: 0.7
}

# 평점에 따라 타겟 계산
for 지표 in 6개_지표:
    if 평점 >= 4:
        타겟 = 현재값 × 1.02  # 2% 증가
    elif 평점 == 3:
        타겟 = 현재값  # 유지
    else:  # 평점 <= 2
        if 긍정지표:
            타겟 = 현재값 × 1.1  # 10% 증가 (더 강조 필요)
        else:
            타겟 = 현재값 × 0.9  # 10% 감소 (덜 중요)
    
    타겟 = clip(타겟, 0.5, 2.0)  # 범위 제한

# 결과
y = [1.02, 0.88, 0.918, 0.7, 0.816, 0.63]

계산 예시:

자신감 4점 → 1.0 × 1.02 = 1.02 ✅
긴장도 2점 (부정) → 0.8 × 0.9 = 0.72 (0.5~2.0 범위 내)
집중도 5점 → 0.9 × 1.02 = 0.918 ✅

Step 4: 누적 데이터에 추가

# 기존 데이터
X_history = [[3,3,4,3,3,3,3], [4,2,4,4,4,2,4], ...]  # n개
y_history = [[0.95,0.82,0.88,...], [0.98,0.78,0.90,...], ...]  # n개

# 새 데이터 추가
X_history.append([4,2,5,3,4,2,4])
y_history.append([1.02,0.88,0.918,0.7,0.816,0.63])

# 이제 n+1개 데이터로 학습

Step 5: 4개 모델 모두 학습 (앙상블)

models = {
    'linear': LinearRegression(),
    'random_forest': RandomForest(),
    'xgboost': XGBoost(),
    'neural_network': NeuralNetwork()
}

# 모든 모델을 누적 데이터로 학습
for name, model in models.items():
    model.train(X_history, y_history)  # n+1개 데이터

Step 6: 각 모델의 예측 및 오차 계산

# 현재 입력 X = [4,2,5,3,4,2,4]에 대해 예측
linear_pred = [1.01, 0.86, 0.92, 0.71, 0.82, 0.64]
rf_pred = [1.03, 0.87, 0.91, 0.70, 0.81, 0.63]
xgb_pred = [1.02, 0.88, 0.918, 0.70, 0.816, 0.63]  # 가장 정확!
nn_pred = [1.00, 0.89, 0.93, 0.69, 0.82, 0.62]

# 타겟: [1.02, 0.88, 0.918, 0.7, 0.816, 0.63]

# RMSE 계산
RMSE(linear) =(Σ(pred - target)²/6) = 0.015
RMSE(rf) = 0.012
RMSE(xgb) = 0.003  ✅ 가장 낮음!
RMSE(nn) = 0.018

Step 7: 최적 모델 선택

best_model = 'xgboost'  # RMSE가 0.003으로 가장 낮음
새_가중치 = xgb_pred = [1.02, 0.88, 0.918, 0.70, 0.816, 0.63]

Step 8: 가중치 저장 및 반환

weights.json에 저장:
{
  "confidence": 1.02,
  "nervousness": 0.88,
  "engagement": 0.918,
  "enthusiasm": 0.70,
  "composure": 0.816,
  "negativity": 0.63,
  "totalFeedbacks": 1,
  "mlModel": "xgboost"
}

→ 다음 감정 분석부터 이 가중치 사용! 🎯

📊 학습 과정 시각화

┌──────────────────────────────────────────────────────────────┐
│ 피드백 입력                                                    │
│ 자신감:4, 긴장도:2, 집중도:5, 열정:3, 안정감:4, 부정적:2, 만족:4  │
└────────────────────────┬─────────────────────────────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 입력 특징 생성 (X)               │
        │ [4, 2, 5, 3, 4, 2, 4]          │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 타겟 가중치 계산 (y)             │
        │ 평점 기반으로 증감 결정           │
        │ [1.02, 0.88, 0.918, ...]       │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 누적 데이터에 추가                │
        │ n개 → n+1개 데이터              │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 4개 모델 병렬 학습 🔄            │
        ├────────────────────────────────┤
        │ Linear       → RMSE: 0.015     │
        │ RandomForest → RMSE: 0.012     │
        │ XGBoost      → RMSE: 0.003 ✅  │
        │ NeuralNet    → RMSE: 0.018     │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 최적 모델 선택                   │
        │ XGBoost (RMSE 최소)            │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 새 가중치 저장 (weights.json)    │
        │ confidence: 1.0 → 1.02         │
        │ nervousness: 0.8 → 0.88        │
        │ engagement: 0.9 → 0.918        │
        │ ...                            │
        └────────────────┬───────────────┘
                         ↓
        ┌────────────────────────────────┐
        │ 다음 감정 분석에 적용 🎯          │
        │ 자신감 점수 2% 더 높게 계산       │
        └────────────────────────────────┘

🔑 핵심 포인트

  1. 타겟 계산이 핵심: 평점에 따라 "이 정도로 가중치를 조정해야 함"을 계산
  2. 누적 학습: 모든 과거 피드백을 기억하고 함께 학습
  3. 4개 모델 경쟁: 매번 가장 정확한 모델 선택
  4. 즉시 반영: 저장 즉시 다음 감정 분석부터 적용

⚡ 학습 방식: 즉시 학습 vs 배치 학습

💡 현재 시스템: 즉시 전체 재학습

# app.py의 실제 동작
X_history = []  # 메모리 버퍼
y_history = []

def update_weights_ensemble():
    # 1. 새 피드백을 버퍼에 추가
    X_history.append(새_피드백)
    y_history.append(새_타겟)
    
    # 2. 즉시 전체 데이터로 재학습 (매번!)
    for model in models:
        model.train(X_history, y_history)  # 전체 1~n개
    
    # 3. 최적 모델 선택 및 반환

📊 학습 시점 비교

학습 방식설명현재 시스템
즉시 학습피드백 1개 올 때마다 바로 학습✅ 사용 중
배치 학습10개, 20개 모은 후 학습❌ 미사용
주기적 학습매일 밤 12시에 일괄 학습❌ 미사용

🔄 학습 과정 예시

피드백 1개 → 버퍼[1개] → 4개 모델 학습 (1개 데이터) → 저장
피드백 2개 → 버퍼[2개] → 4개 모델 학습 (2개 데이터) → 저장
피드백 3개 → 버퍼[3개] → 4개 모델 학습 (3개 데이터) → 저장
...
피드백 100개 → 버퍼[100개] → 4개 모델 학습 (100개 데이터) → 저장

매번 전체 데이터로 재학습! (Incremental Batch Learning)

⚠️ 장단점

장점:

  • ✅ 최신 피드백 즉시 반영
  • ✅ 항상 최적의 모델 유지
  • ✅ 사용자 피드백에 빠르게 대응

단점:

  • ⚠️ 데이터 많아지면 느려짐 (100개 이상)
  • ⚠️ 매번 전체 재학습으로 CPU 사용량 증가
  • ⚠️ 메모리에 모든 데이터 유지

🤖 모델별 피드백 학습 방법

1️⃣ Linear Regression (Ridge)

원리: 선형 방정식으로 입력과 출력의 관계 학습

가중치 = β₀ + β₁×입력₁ + β₂×입력₂ + ... + β₇×입력₇

피드백 학습 과정:

# 피드백 1개 도착
X_history = [[4, 2, 5, 3, 4, 2, 4]]  # 1개
y_history = [[1.02, 0.88, 0.918, 0.7, 0.816, 0.63]]

# 1. 데이터 정규화
X_scaled = (X - mean) / std

# 2. Ridge 회귀로 계수(β) 계산 (한 번에!)
# Loss = Σ(실제값 - 예측값)² + α×Σ(β²)
β = (XᵀX + αI)⁻¹Xᵀy  # 행렬 연산으로 최적해 계산

# 계수 예시:
# β = [0.12, -0.15, 0.08, 0.03, 0.09, -0.11, 0.07]
# → 긴장도(β₂=-0.15)가 증가하면 가중치 감소
# → 집중도(β₃=0.08)가 증가하면 가중치 증가

# 피드백 2개 도착
X_history = [[4,2,5,...], [3,4,3,...]]  # 2개
y_history = [[1.02,0.88,...], [0.98,0.85,...]]

# 3. 전체 2개 데이터로 다시 계산
β_new = (XᵀX + αI)⁻¹Xᵀy  # 완전히 새로 계산!

학습 특징:

  • 🔢 전체 재계산: 매번 모든 데이터로 β를 처음부터 다시 계산
  • 매우 빠름: 행렬 연산 한 번으로 끝 (0.003초)
  • 📈 점진적 개선: 데이터 많아질수록 β가 안정화
  • 🎯 단순 평균: 모든 피드백의 평균적 패턴 학습

장점:

  • ⚡ 가장 빠름 (0.001~0.003초)
  • 📊 해석 가능 (각 입력의 영향도 확인 가능)
  • 💾 메모리 효율적
  • 🔄 재학습 부담 없음

단점:

  • 📉 비선형 관계 학습 불가
  • 🎯 복잡한 패턴 포착 어려움
  • ⚠️ 이상치에 민감

2️⃣ Random Forest

원리: 여러 개의 의사결정 트리를 만들어 투표로 결정

최종 예측 = (트리1 예측 + 트리2 예측 + ... + 트리100 예측) / 100

피드백 학습 과정:

# 피드백 1개 도착
X_history = [[4, 2, 5, 3, 4, 2, 4]]  # 1개
y_history = [[1.02, 0.88, 0.918, ...]]

# 1. 100개의 트리를 처음부터 다시 생성!
trees = []
for i in range(100):
    # 2. Bootstrap 샘플링 (중복 허용 랜덤 추출)
    indices = random.choice([0], size=1)  # 1개 데이터에서 샘플링
    X_sample = X_history[indices]
    y_sample = y_history[indices]
    
    # 3. 트리 학습
    tree = DecisionTree(max_depth=10)
    tree.fit(X_sample, y_sample)
    # 트리 예시:
    # if 집중도 > 4:
    #     가중치 = 1.0
    # else:
    #     가중치 = 0.9
    trees.append(tree)

# 피드백 10개 도착
X_history = [[4,2,5,...], [3,4,3,...], ..., [5,2,4,...]]  # 10개
y_history = [[1.02,...], [0.98,...], ..., [1.05,...]]

# 4. 100개 트리 완전히 새로 생성!
trees = []
for i in range(100):
    # Bootstrap: 10개 중 랜덤하게 10개 뽑기 (중복 가능)
    indices = random.choice([0,1,2,...,9], size=10)
    X_sample = X_history[indices]  # 예: [2,5,5,1,8,3,...]
    y_sample = y_history[indices]
    
    tree = DecisionTree(max_depth=10)
    tree.fit(X_sample, y_sample)
    # 트리가 복잡해짐:
    # if 자신감 > 3:
    #     if 집중도 > 4:
    #         if 만족도 > 4:
    #             가중치 = 1.15
    #         else:
    #             가중치 = 1.05
    #     else:
    #         가중치 = 0.95
    # else:
    #     가중치 = 0.85
    trees.append(tree)

# 5. 예측 시 100개 트리의 투표
예측 = mean([tree.predict([4,2,5,...]) for tree in trees])

학습 특징:

  • 🔄 완전 재생성: 매번 100개 트리를 처음부터 새로 생성
  • 🎲 랜덤 샘플링: 각 트리가 다른 데이터 조합 학습
  • 📊 다양성: 100개 트리가 서로 다른 패턴 포착
  • 🌳 깊이 증가: 데이터 많아질수록 트리 복잡도 증가

장점:

  • 🌳 비선형 관계 학습 가능
  • 🛡️ 과적합 저항성 우수 (앙상블 효과)
  • 📊 특징 중요도 제공
  • 🎯 복잡한 패턴 포착 가능

단점:

  • 💾 메모리 많이 사용 (트리 100개)
  • ⏱️ 학습 시간 중간 (0.1~0.2초)
  • 🔄 재학습 부담 중간

3️⃣ XGBoost (Extreme Gradient Boosting)

원리: 이전 모델의 오차를 다음 모델이 보완하는 방식

최종 모델 = 모델₁ + 모델₂ + 모델₃ + ... + 모델₁₀₀
각 모델은 이전 모델의 오차(잔차)를 학습

학습 방법:

# 1. 초기 예측값 (평균)
예측 = mean(y)

# 2. 순차적으로 트리 추가 (Boosting)
for i in range(100):
    # 3. 이전 예측의 오차 계산
    오차 = 실제값 - 예측
    
    # 4. 오차를 줄이는 트리 학습
    tree[i] = DecisionTree()
    tree[i].fit(X, 오차)
    
    # 5. 예측값 업데이트
    예측 += learning_rate × tree[i].predict(X)
    
    # 예시:
    # 1회: 예측=1.0, 실제=1.2, 오차=0.2 → 트리가 +0.02 학습
    # 2회: 예측=1.02, 실제=1.2, 오차=0.18 → 트리가 +0.018 학습
    # ...점점 정확해짐

# 6. 정규화로 과적합 방지
# - max_depth: 트리 깊이 제한
# - subsample: 일부 데이터만 사용

장점:

  • 🏆 최고 성능 (RMSE 가장 낮음)
  • ⚡ 빠른 학습 (0.05~0.1초)
  • 🎯 복잡한 패턴 학습 우수

단점:

  • ⚙️ 하이퍼파라미터 튜닝 필요
  • 📚 이해하기 복잡

4️⃣ Neural Network (Deep Learning)

원리: 뇌의 뉴런을 모방한 다층 네트워크

입력층 → 은닉층1 → 은닉층2 → 은닉층3 → 출력층
  7개  →  64개   →  32개   →  16개   →  6개

학습 방법:

# 1. 네트워크 구조
input (7) → Dense(64, ReLU) → Dropout(0.2) 
          → Dense(32, ReLU) → Dropout(0.2)
          → Dense(16, ReLU) → Dropout(0.2)
          → Dense(6, Linear)

# 2. 순전파 (Forward)
layer1 = ReLU(W1 × input + b1)
layer2 = ReLU(W2 × layer1 + b2)
layer3 = ReLU(W3 × layer2 + b3)
output = W4 × layer3 + b4

# 3. 오차 계산
loss = MSE(output, 실제값) = (output - y)²

# 4. 역전파 (Backpropagation)
# 오차를 역방향으로 전파하며 가중치 업데이트
∂loss/∂W4 → ∂loss/∂W3 → ∂loss/∂W2 → ∂loss/∂W1

# 5. 경사하강법으로 가중치 업데이트
W = W - learning_rate × gradient

# 6. 100번 반복 (Epoch)
for epoch in range(100):
    for batch in data:
        forward → loss → backward → update weights

학습 단계별 예시:

초기: W = random
Epoch 1: loss = 0.150 → W 약간 조정
Epoch 10: loss = 0.080 → W 더 조정
Epoch 50: loss = 0.030 → 거의 최적
Epoch 100: loss = 0.025 → 수렴

장점:

  • 🧠 복잡한 비선형 패턴 학습 최고
  • 📈 가장 높은 개선율 (58.6%)
  • 🔧 확장성 우수 (층 추가 가능)

단점:

  • ⏱️ 학습 가장 느림 (1~3초)
  • 📊 많은 데이터 필요
  • 🔍 블랙박스 (해석 어려움)

📊 4개 모델 한눈에 비교

특징LinearRandom ForestXGBoostNeural Network
학습 방식선형 방정식트리 앙상블순차 부스팅다층 신경망
학습 시간⚡ 0.003초⏱️ 0.12초⚡ 0.09초🐢 2.3초
최종 RMSE0.08450.06230.05120.0534
개선율32.1%47.5%55.7%58.6%
수렴 속도20개40개30개60개
메모리2.5 MB45 MB23 MB78 MB
비선형 학습
해석 가능성✅ 높음중간중간❌ 낮음
과적합 방지중간✅ 우수✅ 우수중간

🏆 추천 모델: XGBoost

이유:
✅ 최고 성능 (RMSE: 0.0512)
✅ 빠른 수렴 (30개 피드백)
✅ 적당한 학습 시간 (0.09초)
✅ 좋은 개선율 (55.7%)

💾 누적 학습

  • 처음: 데이터 1개로 학습
  • 두 번째: 데이터 2개로 학습
  • n번째: 데이터 n개로 학습
  • → 피드백이 쌓일수록 정확도 향상

2️⃣ 감정 재가공 방법

🎭 Hume AI 원시 감정 → 면접 지표 변환

Step 1: 감정 그룹화

Hume AI 48개 감정
   ↓ 그룹화
6개 면접 지표

예시:
자신감 = [Determination, Calmness, Satisfaction, Concentration, Admiration]
긴장도 = [Anxiety, Fear, Awkwardness, Embarrassment, Distress]

Step 2: 점수 계산 공식

수학 공식:

Score(지표) = min(1.0, (Σ(emotion_i × weight × 2.5)) / n) × 100

여기서:
  emotion_i = Hume AI 감정 점수 (0~1 범위)
  weight = ML 학습된 가중치 (0.5~2.0)
  2.5 = 스케일링 배율
  n = 해당 그룹 내 감정 개수
  min(1.0, x) = 1.0 초과 시 1.0으로 제한
  × 100 = 퍼센트 변환

단계별 계산:

// 1. 각 감정에 가중치 적용
for (감정 in 그룹) {
    contribution = Hume점수 × 학습된_가중치 × 2.5
    totalScore += contribution
    emotionCount++
}

// 2. 평균 계산
rawScore = totalScore / emotionCount

// 3. 1.0 초과 방지
finalScore = Math.min(rawScore, 1.0)

// 4. 퍼센트 변환
퍼센트 = finalScore × 100  // 최종 결과: 0~100%

📊 실제 계산 예시: "자신감" 지표

1단계: Hume AI 원시 데이터

자신감 그룹 = [Determination, Calmness, Satisfaction, Concentration, Admiration]

Hume AI 감정 점수 (0~1):
  Determination: 0.15
  Calmness: 0.12
  Satisfaction: 0.18
  Concentration: 0.10
  Admiration: 0.08

2단계: 가중치 적용 (각 감정마다)

학습된 가중치 = 1.2 (ML이 학습한 값)
배율 = 2.5

contribution_1 = 0.15 × 1.2 × 2.5 = 0.45
contribution_2 = 0.12 × 1.2 × 2.5 = 0.36
contribution_3 = 0.18 × 1.2 × 2.5 = 0.54
contribution_4 = 0.10 × 1.2 × 2.5 = 0.30
contribution_5 = 0.08 × 1.2 × 2.5 = 0.24

totalScore = 0.45 + 0.36 + 0.54 + 0.30 + 0.24 = 1.89

3단계: 평균 계산

emotionCount = 5
rawScore = 1.89 / 5 = 0.378

4단계: 정규화

finalScore = min(0.378, 1.0) = 0.378

5단계: 퍼센트 변환

최종 점수 = 0.378 × 100 = 37.8%

결과:

→ 화면 표시: "자신감: 38%" ✅

📋 6개 감정 지표별 계산 공식

지표Hume AI 감정 그룹초기 가중치속성
자신감Determination, Calmness, Satisfaction, Concentration, Admiration1.0긍정
긴장도Anxiety, Fear, Awkwardness, Embarrassment, Distress0.8부정
집중도Concentration, Interest, Contemplation, Realization0.9긍정
열정Excitement, Joy, Amusement, Surprise (positive), Ecstasy0.7긍정
안정감Calmness, Relief, Contentment, Satisfaction0.8긍정
부정적 태도Boredom, Confusion, Disappointment, Sadness, Tiredness, Disgust0.7부정

모든 지표가 동일한 공식 사용:

Score(지표) = min(1.0, (Σ(emotion_i × weight × 2.5)) / n) × 100

예시:

집중도 = [(Concentration × 0.9 × 2.5) + (Interest × 0.9 × 2.5) + 
          (Contemplation × 0.9 × 2.5) + (Realization × 0.9 × 2.5)] / 4 × 100

🔄 동적 가중치 적용 예시

초기: weight = 1.0 (기본값)
   ↓
피드백: "자신감이 너무 낮게 나와요" (평점 2)ML 학습: weight = 1.15로 상승
   ↓
다음 분석: 자신감 점수가 15% 더 높게 계산됨

계산 비교:

[학습 전] weight = 1.0
  자신감 = 0.15 × 1.0 × 2.5 = 0.375 → 37.5%

[학습 후] weight = 1.15
  자신감 = 0.15 × 1.15 × 2.5 = 0.431 → 43.1%
  
→ 5.6% 상승! 🚀

📈 전체 흐름도

┌─────────────┐
│  Hume AI    │ → 얼굴/음성 → 48개 감정 (0~1 점수)
└─────────────┘
       ↓
┌─────────────────────────┐
│  감정 재가공 (Node.js)    │
│  - 6개 그룹으로 묶기      │
│  - 학습된 가중치 적용     │
│  - 점수 정규화           │
└─────────────────────────┘
       ↓
   면접 지표 6개
   (자신감, 긴장도, 집중도, 열정, 안정감, 부정적 태도)
       ↓
   사용자에게 표시
       ↓
   피드백 입력 (1~5점)
       ↓
┌─────────────────────────┐
│  ML 서버 (Python)        │
│  - 4개 모델 학습         │
│  - 최적 모델 선택        │
│  - 새 가중치 계산        │
└─────────────────────────┘
       ↓
   가중치 업데이트 (weights.json)
       ↓
   다음 분석에 반영 ⟲

🎯 핵심 포인트

  1. 온라인 학습: 피드백마다 누적 데이터로 재학습
  2. 앙상블: 4개 모델 중 가장 정확한 것 선택
  3. 동적 가중치: 피드백 평점에 따라 가중치 자동 조정
  4. 감정 그룹화: 48개 → 6개로 면접 맥락에 맞게 변환
  5. 점진적 개선: 피드백이 쌓일수록 정확도 향상

📊 성능 비교 (예시)

피드백 수RMSE정확도
10개0.15낮음
50개0.08중간
100개0.05높음
200개0.03매우 높음

→ 사용자 피드백이 쌓일수록 시스템이 더 똑똑해짐! 🚀

profile
컴퓨터정보공학부 졸업생입니다. 안녕하세요

0개의 댓글