피드백 입력 → 누적 데이터에 추가 → 전체 재학습 → 최적 모델 선택
사용자 피드백 = {
자신감: 4점, 긴장도: 2점, 집중도: 5점,
열정: 3점, 안정감: 4점, 부정적태도: 2점,
전체만족도: 4점
}
X = [4, 2, 5, 3, 4, 2, 4] # 7개 특징
[자신감, 긴장도, 집중도, 열정, 안정감, 부정적태도, 전체만족도]
현재_가중치 = {
자신감: 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 ✅
# 기존 데이터
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개 데이터로 학습
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개 데이터
# 현재 입력 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
best_model = 'xgboost' # RMSE가 0.003으로 가장 낮음
새_가중치 = xgb_pred = [1.02, 0.88, 0.918, 0.70, 0.816, 0.63]
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% 더 높게 계산 │
└────────────────────────────────┘
# 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)
장점:
단점:
원리: 선형 방정식으로 입력과 출력의 관계 학습
가중치 = β₀ + β₁×입력₁ + β₂×입력₂ + ... + β₇×입력₇
피드백 학습 과정:
# 피드백 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 # 완전히 새로 계산!
학습 특징:
장점:
단점:
원리: 여러 개의 의사결정 트리를 만들어 투표로 결정
최종 예측 = (트리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])
학습 특징:
장점:
단점:
원리: 이전 모델의 오차를 다음 모델이 보완하는 방식
최종 모델 = 모델₁ + 모델₂ + 모델₃ + ... + 모델₁₀₀
각 모델은 이전 모델의 오차(잔차)를 학습
학습 방법:
# 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: 일부 데이터만 사용
장점:
단점:
원리: 뇌의 뉴런을 모방한 다층 네트워크
입력층 → 은닉층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 → 수렴
장점:
단점:
| 특징 | Linear | Random Forest | XGBoost | Neural Network |
|---|---|---|---|---|
| 학습 방식 | 선형 방정식 | 트리 앙상블 | 순차 부스팅 | 다층 신경망 |
| 학습 시간 | ⚡ 0.003초 | ⏱️ 0.12초 | ⚡ 0.09초 | 🐢 2.3초 |
| 최종 RMSE | 0.0845 | 0.0623 | 0.0512 ✅ | 0.0534 |
| 개선율 | 32.1% | 47.5% | 55.7% | 58.6% ✅ |
| 수렴 속도 | 20개 ✅ | 40개 | 30개 | 60개 |
| 메모리 | 2.5 MB ✅ | 45 MB | 23 MB | 78 MB |
| 비선형 학습 | ❌ | ✅ | ✅ | ✅ |
| 해석 가능성 | ✅ 높음 | 중간 | 중간 | ❌ 낮음 |
| 과적합 방지 | 중간 | ✅ 우수 | ✅ 우수 | 중간 |
이유:
✅ 최고 성능 (RMSE: 0.0512)
✅ 빠른 수렴 (30개 피드백)
✅ 적당한 학습 시간 (0.09초)
✅ 좋은 개선율 (55.7%)
Hume AI 48개 감정
↓ 그룹화
6개 면접 지표
예시:
자신감 = [Determination, Calmness, Satisfaction, Concentration, Admiration]
긴장도 = [Anxiety, Fear, Awkwardness, Embarrassment, Distress]
수학 공식:
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%" ✅
| 지표 | Hume AI 감정 그룹 | 초기 가중치 | 속성 |
|---|---|---|---|
| 자신감 | Determination, Calmness, Satisfaction, Concentration, Admiration | 1.0 | 긍정 |
| 긴장도 | Anxiety, Fear, Awkwardness, Embarrassment, Distress | 0.8 | 부정 |
| 집중도 | Concentration, Interest, Contemplation, Realization | 0.9 | 긍정 |
| 열정 | Excitement, Joy, Amusement, Surprise (positive), Ecstasy | 0.7 | 긍정 |
| 안정감 | Calmness, Relief, Contentment, Satisfaction | 0.8 | 긍정 |
| 부정적 태도 | Boredom, Confusion, Disappointment, Sadness, Tiredness, Disgust | 0.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)
↓
다음 분석에 반영 ⟲
| 피드백 수 | RMSE | 정확도 |
|---|---|---|
| 10개 | 0.15 | 낮음 |
| 50개 | 0.08 | 중간 |
| 100개 | 0.05 | 높음 |
| 200개 | 0.03 | 매우 높음 |
→ 사용자 피드백이 쌓일수록 시스템이 더 똑똑해짐! 🚀