# AnimaSync V3 — 결정 사항 & 실행 계획 요약

> 최종 업데이트: 2026-04-10
> 이 문서는 V3 방향 논의에서 결정된 사항, 아키텍처 설계, 데이터 전략, 구현 로드맵을 정리한 것입니다.

---

## 1. 프로젝트 배경

### V2 현재 상태

- 오디오 → 52 ARKit 블렌드쉐이프 (30fps) 실시간 립싱크
- 5개 감정 (happy, sad, angry, surprised, neutral), 외부에서 수동 설정 (`setEmotion()`)
- 모델: 5,409,480 params / 5.2MB INT8 ONNX
- 배포: WASM (브라우저 on-device)

### 시니어 요청

> "텍스트에서 감정을 감지하는 LLM을 만들고, 그 감지된 감정과 VAD를 사용하여 매우 다양한 표정을 생성해라."

### V3 핵심 변화

- 5개 고정 감정 → **16개 감정 레이블 + 3차원 VAD 연속 공간**
- 수동 감정 설정 → **텍스트 기반 자동 감정 감지** (MicroALBERT)
- 단일 모델 → **2-모델 시스템** (감정 감지 + 립싱크)

---

## 2. 결정 사항 요약

### 2.1 감정 표현 방식: Option C (Hybrid) 채택

FiLM layer의 emotion 입력을 기존 5-dim one-hot → **19-dim hybrid vector**로 변경:

- **16-dim label probabilities** (softmax): 어떤 감정 계열인지
- **3-dim VAD** (tanh, 각 [-1, 1]): 그 감정의 연속적 뉘앙스

```
"정말 고마워요"      → gratitude(0.95) + VAD(0.70, 0.40, 0.30) → 차분한 감사 미소
"진짜 너무 고마워!!" → gratitude(0.92) + VAD(0.75, 0.80, 0.35) → 흥분된 감사 표정
                                              ↑ 같은 label, 다른 arousal → 다른 얼굴
```

**다른 옵션을 배제한 이유:**

| Option | 배제 이유 |
|---|---|
| A (16 labels only) | 16개 고정 표정만 가능, 시니어의 "다양한 표정" 요구에 부족 |
| B (VAD only) | "gratitude 표정 보여줘" 같은 직관적 제어 불가, QA/디버그 어려움 |
| **C (Hybrid)** | **label로 감정 가족 제어 + VAD로 뉘앙스 조절 = 최대 다양성** |

### 2.2 VAD란?

VAD (Valence-Arousal-Dominance) — 감정을 3개 연속 축으로 표현하는 심리학 모델 (Russell & Mehrabian, 1977):

- **Valence**: 긍정 ↔ 부정 (기쁨은 +, 슬픔은 -)
- **Arousal**: 흥분 ↔ 차분 (분노/놀람은 높음, 슬픔/평온은 낮음)
- **Dominance**: 통제감 ↔ 무력감 (분노는 높음, 공포는 낮음)

```
                Arousal (각성도)
                    ↑ +1.0
                    │
         분노 😡    │    놀람 😲
        (-0.5,+0.6) │   (+0.4,+0.7)
                    │
  ──────────────────┼──────────────────→ Valence (감정가)
  -1.0              │              +1.0
                    │
         슬픔 😢    │    만족 😊
        (-0.6,-0.3) │   (+0.9,-0.2)
                    │
                    ↓ -1.0

  ◆ Dominance (지배력) = Z축
    D+ (높음): 분노, 흥분, 만족
    D- (낮음): 공포, 슬픔, 놀람
```

### 2.3 적용 단위: 문장 단위 감정 감지

- 입력 텍스트를 **문장 단위로 분리** → 각 문장마다 MicroALBERT 추론
- 문장별 서로 다른 19-dim emotion vector 생성
- 문장 경계에서 **10-frame smoothstep crossfade** (333ms) 로 자연스러운 전환
- 한 문장 내에서는 emotion 고정 (broadcast)

```
"정말 고마워요. 근데 좀 서운했어요."
  문장1: "정말 고마워요"     → gratitude + VAD(0.7, 0.4, 0.3)  │ frames 0-45
  문장2: "근데 좀 서운했어요" → sadness + VAD(-0.4, 0.1, -0.3)  │ frames 45-120
                             crossfade ↕ frames 40-50
```

- TTS에서 문장별 timing 메타데이터를 받아서 정렬
- MicroALBERT는 문장당 ~15ms → 3문장이어도 ~45ms, latency 부담 없음

### 2.4 초점: Case A (챗봇/TTS 아바타)

V3-alpha는 **텍스트 → TTS → 오디오 → 아바타** 시나리오에 집중:

- 텍스트가 오디오 재생 전에 준비됨 → 감정 감지 latency 문제 없음
- 챗봇 응답의 90%+ 커버 (대부분 1-3문장, 감정 전환 자연스러움)

**Live mic 스트리밍은 범위 제외** (V3-beta):
- 텍스트 기반 감정 감지는 STT latency (~500ms) 때문에 real-time에 부적합
- 대신 audio→VAD head 작은 모델 (~0.5MB) 추가하면 해결
- **립싱크 모델 재학습 필요 없음** — FiLM의 19-dim interface가 source-agnostic이라 동일 모델 재사용

### 2.5 크기 예산

| 컴포넌트 | 크기 |
|---|---|
| MicroALBERT 감정 감지 모델 | ~6.0 MB |
| 립싱크 모델 V3 (FiLM 수정) | ~5.2 MB |
| **합계** | **~11.2 MB** |
| 예산 | 20 MB |
| **여유** | **~8.8 MB (44%)** |

---

## 3. 시스템 아키텍처

### 3.1 2-모델 파이프라인

```
┌──────────────────────────────────────────────────────────────┐
│ Model 1: 감정 감지 모델 (MicroALBERT)                       │
│                                                              │
│  입력: 텍스트 (한/중/영/일)                                  │
│  출력: 16-dim label softmax + 3-dim VAD → concat 19-dim      │
│  크기: ~6.0MB (INT8 ONNX + SentencePiece 토크나이저)         │
│  추론: ~15ms per sentence                                    │
└──────────────────────┬───────────────────────────────────────┘
                       │ 19-dim emotion vector
                       │ (문장 단위, 프레임에 broadcast)
                       ▼
┌──────────────────────┴───────────────────────────────────────┐
│ Model 2: 립싱크 모델 (AnimaSync V3)                          │
│                                                              │
│  입력: 오디오 feature (T, 141) + emotion vector (T, 19)      │
│  출력: 52개 ARKit 블렌드쉐이프 (T, 52) @ 30fps              │
│  크기: ~5.2MB (INT8 ONNX)                                    │
│                                                              │
│  audio + emotion → FiLM 컨디셔닝 → LSTM/Transformer → 출력   │
└──────────────────────────────────────────────────────────────┘
```

### 3.2 립싱크 모델 변경 범위

**Backbone은 전혀 변경하지 않음.** FiLM layer만 수정:

```
현재 V2:  emotion (5)  → Linear(5, 64)         → gamma/beta
V3:       emotion (19) → MLP(19 → 64 → 64)     → gamma/beta
                           추가 파라미터: +5,312 (전체의 0.10%)
```

V2 모델 구조 (변경 없는 부분):

```
Input (T, 141) audio features
    │
    ▼
Input Projection: Linear(141→320) + LayerNorm + GELU
    │
    ▼
CausalMultiScaleConv: 4 dilated Conv1d (d=1,2,4,8) → concat → Linear(1280→320)
    │                  receptive field: 16 frames (0.53s)
    ▼
FiLM Layer ◆ 여기만 수정 ◆
    │        emotion (19) → embed → gamma/beta → features * (1+γ) + β
    ▼
LSTM: 2 layers, hidden=320, unidirectional
    │
    ▼
Causal Transformer: 2 layers, 8 heads, ff_dim=896, lookahead=3
    │
    ▼
Output Head: Linear(320→160) → GELU → Linear(160→52) → Sigmoid
    │
    ▼
Output (T, 52) blendshapes ∈ [0, 1]
```

**파라미터 상세 (5,409,480 total):**

| 컴포넌트 | 파라미터 수 | 비율 |
|---|---|---|
| Input Projection | 46,080 | 0.85% |
| CausalMultiScaleConv | 1,643,200 | 30.38% |
| **FiLM Layer** | **41,984 → 47,296** | **0.78% → 0.87%** |
| LSTM (2 layers) | 1,643,520 | 30.38% |
| Transformer (2 layers) | 1,973,632 | 36.49% |
| Output Head | 60,424 | 1.12% |

### 3.3 141-dim 오디오 Feature 구성 (변경 없음)

| 채널 | Feature | 차원 |
|---|---|---|
| 0-39 | MFCC | 40 |
| 40-79 | Delta MFCC | 40 |
| 80-119 | Delta² MFCC | 40 |
| 120 | Pitch (f0/500, normalized) | 1 |
| 121 | Voiced flag (0/1) | 1 |
| 122 | Energy (RMS) | 1 |
| 123 | Energy delta | 1 |
| 124 | Spectral centroid (/8000) | 1 |
| 125 | Spectral bandwidth (/4000) | 1 |
| 126 | Spectral rolloff (/8000) | 1 |
| 127 | Spectral flatness | 1 |
| 128 | Zero-crossing rate | 1 |
| 129-140 | Chroma (12 pitch classes) | 12 |
| **Total** | | **141** |

추출 파라미터: 16kHz sample rate, N_FFT=512, hop=533 (~30fps), N_MELS=80, N_MFCC=40, Hann window.

### 3.4 WASM 런타임 구조 (변경 최소)

```
┌─────────────────────────────────────────────────────┐
│ Rust WASM (lipsync-wasm-se/v2/)                     │
│                                                     │
│  features/mod.rs  — 141-dim feature extraction      │
│  postprocess/     — OneEuro filter, blink, fade     │
│  lib.rs           — LipSyncWasmV2 entry point       │
│  shared/vrm_convert.rs — ARKit 52 → VRM 18         │
└────────────────────┬────────────────────────────────┘
                     │ ONNX bytes (decrypted)
                     ▼
┌────────────────────┴────────────────────────────────┐
│ JavaScript Wrapper (lipsync-wasm-wrapper.js)        │
│                                                     │
│  onnxruntime-web (wasm backend)                     │
│  Emotion source → 19-dim vector 주입                │
│  Stream state: lstm_h, lstm_c, conv_ctx 관리        │
└─────────────────────────────────────────────────────┘
```

**JS wrapper 수정 사항:**
- emotion 차원: 5 → 19
- 문장 분리 + 문장별 MicroALBERT 추론 + crossfade 로직 추가
- emotion source를 외부에서 주입 가능한 인터페이스로 설계 (source-agnostic)

---

## 4. 16개 감정 분류 체계

### 4.1 구조

```
Base (5개, MEAD real capture):
  NEUTRAL ──── (sub-emotion 없음)
  JOY ─────┬── laughter (웃음)
           ├── excitement (흥분)
           ├── agreement (동의)
           └── gratitude (감사)
  SADNESS ─┬── crying (울음)
           ├── sulk (삐침)
           ├── apology (사과)
           └── struggle (고민)
  ANGER ───┬── refusal (거절)
  SURPRISE ┬── fluster (당황)
           └── shy (수줍음)
```

### 4.2 각 감정별 표정 & VAD 좌표

| # | Label | Parent | 주요 블렌드쉐이프 | 표정 | Default VAD (V, A, D) |
|---|---|---|---|---|---|
| 0 | neutral | — | baseline | 무표정 | (0.00, 0.00, 0.00) |
| 1 | joy | — | mouthSmile + cheekSquint + eyeSquint | 기본 미소 | (0.76, 0.48, 0.35) |
| 2 | laughter | joy | mouthSmile HIGH + jawOpen + eyeSquint | 입 벌리고 크게 웃음 | (0.82, 0.65, 0.30) |
| 3 | excitement | joy | mouthSmile + eyeWide + browInnerUp | 눈 크게 뜨고 신난 표정 | (0.62, 0.75, 0.46) |
| 4 | agreement | joy | mouthSmile slight + nod gesture | 가볍게 끄덕이며 미소 | (0.40, 0.20, 0.35) |
| 5 | gratitude | joy | mouthSmile + eyeBlink slight + browInnerUp | 눈 살짝 감으며 고마운 미소 | (0.70, 0.20, 0.10) |
| 6 | sadness | — | mouthFrown + browInnerUp + eyeSquint | 기본 슬픈 표정 | (-0.63, -0.27, -0.33) |
| 7 | crying | sadness | browInnerUp HIGH + mouthFrown + mouthPucker | 눈썹 올리고 우는 얼굴 | (-0.75, -0.10, -0.50) |
| 8 | sulk | sadness | mouthFrown + mouthPucker + mouthLeft(비대칭) | 입 삐쭉 토라진 표정 | (-0.30, -0.20, -0.20) |
| 9 | apology | sadness | browInnerUp + mouthFrown slight + eyeLookDown | 눈 내리깔고 미안한 표정 | (-0.30, -0.20, -0.60) |
| 10 | struggle | sadness | browInnerUp + browDown(충돌) + mouthPress | 찌푸리며 고민하는 얼굴 | (-0.50, 0.10, -0.40) |
| 11 | anger | — | browDown + mouthFrown + noseSneer + jawForward | 기본 화난 표정 | (-0.51, 0.59, 0.25) |
| 12 | refusal | anger | mouthPress + browDown + mouthFrown | 입 꾹 다물고 단호한 표정 | (-0.40, 0.50, 0.50) |
| 13 | surprise | — | eyeWide + browUp + jawOpen + mouthOpen | 기본 놀란 표정 | (0.40, 0.67, -0.07) |
| 14 | fluster | surprise | eyeWide + browInnerUp + rapid blink | 눈 커지고 당황한 표정 | (0.10, 0.70, -0.40) |
| 15 | shy | surprise | mouthSmile slight + eyeLookDown + mouthPress | 눈 내리깔고 수줍은 표정 | (0.30, 0.20, -0.30) |

---

## 5. 데이터셋 전략

### 5.1 핵심 철학

> **"합성 데이터로 감정 공간을 채우는 게 아니라, 모델이 VAD 감정 공간 자체를 이해하게 만드는 것"**

- Synthetic은 "답"이 아니라 "힌트"
- Real data가 ground truth, synthetic은 빈 영역의 방향성만 제시
- Consistency loss가 모델이 VAD 공간을 부드럽게 내재화하도록 유도

### 5.2 데이터 구성

#### Real 데이터 (Primary, high loss weight)

**MEAD 데이터셋** = 60명 배우, 5 base emotion × 3 intensity level, 영상 + 오디오

- 영상 프레임에서 **MediaPipe로 52 ARKit 블렌드쉐이프 추출** → 오디오 141-dim feature와 시간축 정렬
- 결과물: `(audio_features, blendshapes, label, VAD)` pair가 진짜 사람 얼굴에서 뽑아낸 ground truth
- intensity level 재라벨링: level_1 → A=0.3, level_2 → A=0.55, level_3 → A=0.8

#### Synthetic 데이터 (Auxiliary, low loss weight)

MEAD에 없는 **11개 sub-emotion** 데이터를 다음과 같이 생성:

1. 각 sub-emotion에 맞는 **텍스트 스크립트** 준비 (예: "정말 고마워요", "죄송합니다" 등)
2. **TTS로 오디오 생성** → 기존 feature extractor로 141-dim feature 추출
3. 블렌드쉐이프 타겟은 **parent emotion의 MEAD 패턴을 base로, VAD 방향으로 modulation**
   - 예: gratitude = joy base blendshapes + α · (VAD_gratitude - VAD_joy) 방향 shift
   - apology = sadness base + VAD delta
4. `(synthetic_audio, synthetic_blendshapes, label, VAD)` 튜플로 학습 데이터에 추가

**Loss weight를 낮게** 줘서 real > synthetic 신뢰 순위 유지. Synthetic은 답이 아니라 힌트.

#### 추가 데이터 (Optional)

- RAVDESS, CREMA-D 등 공개 감정 영상에서 MediaPipe로 블렌드쉐이프 추출 → MEAD 커버리지 보강

### 5.3 Loss Function 설계

```python
L_total = L_supervised + L_consistency + L_disentangle

# 1. Supervised loss (blendshape reconstruction)
L_supervised = w_real * MSE(pred, target)_real     # w_real = 3.0
             + w_syn  * MSE(pred, target)_synthetic # w_syn  = 1.0

# 2. VAD consistency loss (연속적인 emotion space 학습 유도)
#    입력 VAD가 조금만 변하면 출력 표정도 조금만 변하도록
L_consistency = MSE(model(audio, vad), model(audio, vad + small_delta))

# 3. Label disentanglement loss (label identity가 VAD에 흡수되는 collapse 방지)
#    같은 VAD라도 label이 다르면 얼굴도 달라야 함
L_disentangle = -distance(model(audio, label_A, vad), model(audio, label_B, vad))
```

기존 V2의 3-Way 채널 가중치도 유지:

| 채널 그룹 | 채널 수 | Loss 가중치 | 역할 |
|---|---|---|---|
| LIPSYNC_ONLY | 14 | 5.0 | 순수 입모양 — 감정 영향 없음 |
| EXPRESSION_ONLY | 22 | 1.0 | 순수 표정 — 오디오 영향 없음 |
| SHARED | 16 | 2.0 | 입모양 + 표정 혼합 |

### 5.4 학습 Curriculum

| Stage | 내용 | 목적 |
|---|---|---|
| A | Label 고정 + VAD 고정 (utterance 단위) | Hybrid conditioner가 작동하는지 검증 |
| B | Label 고정 + Arousal만 variation | 강도 변화에 따른 표정 변화 학습 |
| C | Label 고정 + Full VAD variation | 전체 VAD 공간 학습 |
| D | Synthetic sub-emotion 데이터 추가 | 11개 sub-emotion 커버리지 확장 |

---

## 6. 감정 감지 모델 (MicroALBERT)

### 6.1 왜 필요한가

립싱크 모델은 19-dim emotion vector만 주면 동작하지만, 실제 서비스에서 매 utterance마다 수동으로 감정 태깅하는 건 비현실적. 텍스트 기반 자동 감정 감지가 필요.

### 6.2 왜 생성형 LLM이 아닌가

| | 생성형 LLM | MicroALBERT |
|---|---|---|
| 작업 | 텍스트 생성 | 텍스트 분류/회귀 |
| 최소 크기 | ~500MB | ~6MB |
| 추론 속도 | ~100-300ms | ~15ms |
| On-device | 매우 어려움 | 용이 |

텍스트 → 3개 숫자는 **분류/회귀 문제**. 생성형 LLM은 과잉.

### 6.3 아키텍처

```
MicroALBERT:
  Tokenizer: SentencePiece, 32K vocab (KR/CN/EN/JP 균형)
  Embedding: Factorized — 32,000 × 128 + 128 × 384 (66% 절약)
  Transformer: 6 layers, 모두 가중치 공유 (83% 절약)
  Hidden dim: 384
  FFN dim: 768
  Attention heads: 6
  Max sequence: 512 tokens (multi-turn 대화 맥락 지원)
  Total params: ~5,500,000
  INT8 ONNX: ~5.5MB + 0.5MB tokenizer = ~6.0MB
```

ALBERT 핵심 기술:
1. **Factorized Embedding**: vocab → 128-dim → 384-dim (vs. vocab → 384-dim 직접). 임베딩 12.3MB → 4.1MB
2. **Cross-Layer Weight Sharing**: 6개 레이어가 1세트 가중치 공유. 깊이는 6, 파라미터 비용은 ~1 레이어

### 6.4 Input/Output 명세

```
Input:
  text: str (UTF-8, 한/중/영/일)
  → tokenize → input_ids: int32[1, 512], attention_mask: int32[1, 512]

Output:
  cls_probs: float32[16]   # softmax, sums to 1.0
  vad:       float32[3]    # tanh-bounded, [-1, 1]
  → concat → emotion_vector: float32[19]
  → broadcast to (1, T, 19) for lipsync FiLM
```

### 6.5 학습 파이프라인

**Phase 1: XLM-R Teacher Fine-tuning**

- XLM-R-base (280M params, 100 languages) 를 감정 데이터로 fine-tune
- Multi-task: 16-class classification + 3-dim VAD regression

학습 데이터:

| 데이터셋 | 언어 | 규모 | 타입 |
|---|---|---|---|
| EmoBank | EN | 10K | VAD 직접 라벨 |
| GoEmotions | EN | 58K | 27 감정 → VAD 매핑 |
| AI Hub 감정 | KR | 40-50K | 7 감정 → VAD 매핑 |
| KOTE | KR | 50K | 다중 감정 → VAD 매핑 |
| NLPCC | CN | 35K | 8 감정 → VAD 매핑 |
| WRIME | JP | 30K | 8 감정 → VAD 매핑 |
| **Total** | **4 languages** | **~170K+** | |

**Phase 2: Knowledge Distillation**

- XLM-R teacher → MicroALBERT student
- Soft label + attention transfer
- 추가: teacher로 500K-1M unlabeled 문장을 pseudo-labeling → 증강 학습

**Phase 3: ONNX 변환 & INT8 양자화**

- Opset 14, INT8 dynamic quantization (QUInt8)
- 최종 크기: ~6.0MB

### 6.6 품질 기대치

50x 압축 (280M → 5.5M) 이지만, task가 감정 분류라서 난이도가 낮음:

| 측면 | Teacher (XLM-R) | MicroALBERT | 아바타 영향 |
|---|---|---|---|
| 감정 family 분류 (joy/sad/anger) | ~95% | ~90-93% | 거의 없음 |
| Sub-emotion 분류 (gratitude vs excitement) | ~85% | ~75-80% | 미미함 (같은 family) |
| VAD 회귀 (correlation) | ~0.82 | ~0.65-0.75 | 미미함 (뉘앙스 차이) |

5.5M이 부족하면 hidden 384→512로 올려서 10M (~10MB)까지 확장 가능, 20MB 예산 내.

### 6.7 독립성

**립싱크 모델은 MicroALBERT와 완전히 독립적:**
- MicroALBERT 개발이 지연되어도 립싱크 모델은 수동 label+VAD 입력으로 동작
- 나중에 다른 emotion source (audio-driven, UI slider, 게임 상태 등) 로 교체해도 립싱크 모델 재학습 불필요
- FiLM의 19-dim interface가 source-agnostic

---

## 7. 실행 로드맵

### Phase 1: 감정 감지 모델 개발

| Step | 작업 | 의존성 |
|---|---|---|
| 1-1 | 학습 데이터 수집 & 전처리 (~170K 다국어 문장, VAD 라벨링) | 없음 |
| 1-2 | XLM-R Teacher fine-tuning (280M, 다국어 감정 데이터) | 1-1 완료 |
| 1-3 | MicroALBERT Student 증류 + SentencePiece 학습 | 1-2 완료 |
| 1-4 | ONNX 변환 + INT8 양자화 + 다국어 검증 | 1-3 완료 |

### Phase 2: 립싱크 모델 V3 수정

| Step | 작업 | 의존성 |
|---|---|---|
| 2-1 | 학습 데이터 준비 (MEAD VAD 재라벨링, TTS 합성, 블렌드쉐이프 생성) | 1-1 (텍스트 코퍼스 공유) |
| 2-2 | FiLM layer 수정 (5→19 dim) + Curriculum 학습 (Stage A→D) | 2-1 완료 |
| 2-3 | ONNX 변환 + INT8 양자화 | 2-2 완료 |

### Phase 3: 통합 & 배포

| Step | 작업 | 의존성 |
|---|---|---|
| 3-1 | JS wrapper: 문장 분리 + MicroALBERT + 립싱크 파이프라인 | 1-4, 2-3 완료 |
| 3-2 | WASM 통합 + 번들 크기 검증 (<20MB) | 3-1 완료 |
| 3-3 | 데모 + 시각적 검증 + 시니어 리뷰 | 3-2 완료 |

### 병렬화 가능 구간

```
Phase 1-1 ──→ 1-2 ──→ 1-3 ──→ 1-4 ─────────────┐
                                                  ├──→ Phase 3
Phase 2-1 ──→ 2-2 ──→ 2-3 ─────────────────────┘
  ↑
  └── 1-1 완료 시 텍스트 코퍼스 공유하여 병렬 시작 가능
```

---

## 8. 디버깅 & 검증 계획

### Test 1: Label 테스트

같은 오디오, 같은 VAD, 다른 label → **다른 표정 family가 나오는지**

### Test 2: VAD 연속성 테스트

같은 오디오, 같은 label, arousal을 0.2 → 0.4 → 0.6 → 0.8 점진 변화 → **표정 강도가 부드럽게 변하는지**

### Test 3: 문장 전환 테스트

멀티 문장 입력 (gratitude → sadness) → **경계에서 자연스러운 crossfade가 되는지**

### Test 4: 립싱크 보존 테스트

V2와 V3 동일 오디오 비교 → **입 움직임(LIPSYNC_ONLY 채널)이 유지되는지, 감정이 입 채널에 bleeding 안 하는지**

### Test 5: Conditioning 민감도 테스트

VAD를 미세하게 변경 (±0.05) → **gamma/beta 또는 출력이 실제로 변하는지** (label이 VAD를 무시하고 dominate하는 버그 탐지)

---

## 9. 향후 확장 가능성

| 확장 | 필요한 작업 | 립싱크 재학습 |
|---|---|---|
| Live mic 스트리밍 (Case B) | Audio→VAD head 추가 (~0.5MB) | **불필요** |
| Webcam 표정 → 아바타 | Webcam→19-dim 매핑 | **불필요** |
| UI slider 수동 조작 | 프론트엔드 UI만 | **불필요** |
| 게임 상태 → 감정 | 게임 이벤트→19-dim 매핑 | **불필요** |
| 감정 수 확장 (16 → 24) | FiLM input dim 변경 | **필요** |
| 아바타별 calibration | per-channel gains/deltas | **불필요** |

FiLM의 19-dim interface가 **source-agnostic**이라 감정 소스만 교체하면 립싱크 모델은 그대로 재사용.

---

## 10. 핵심 수치 요약

| 항목 | 값 |
|---|---|
| 립싱크 모델 파라미터 | 5,409,480 (5.2MB INT8) |
| 감정 감지 모델 파라미터 | ~5,500,000 (6.0MB INT8) |
| 총 시스템 크기 | ~11.2MB / 20MB 예산 |
| 감정 레이블 수 | 16 (5 base + 11 sub) |
| VAD 차원 | 3 (V, A, D, 각 [-1, 1]) |
| FiLM 입력 차원 | 19 (16 + 3) |
| 오디오 Feature 차원 | 141 |
| 블렌드쉐이프 출력 차원 | 52 ARKit |
| 프레임 레이트 | 30fps |
| 감정 감지 추론 속도 | ~35-40ms per utterance (multi-turn context 512 tokens) |
| 립싱크 추론 속도 | ~15ms per 5-frame chunk |
| 토크나이저 vocab | 32K SentencePiece |
| 지원 언어 | KR, CN, EN, JP |
| 학습 데이터 (감정 감지) | ~170K 다국어 문장 |
| 학습 데이터 (립싱크) | MEAD 5,001 clips + synthetic |
