# V3 감정 감지 + 표정 생성 시스템 — 통합 실행 계획서

> 이 문서는 시니어의 새로운 방향 제시에 따라, 기존 V3 계획(16개 감정 레이블)을 재검토하고,
> **텍스트 기반 감정 감지 LLM + 립싱크 모델**로 구성된 2-모델 시스템의 전체 계획을 정리한 것입니다.
> 3가지 접근 방식(Option A/B/C)을 비교하여 시니어가 선택할 수 있도록 구성하였습니다.

---

## 1. 프로젝트 개요

### 1.1 현재 상태 (V2)

AnimaSync V2는 오디오 입력으로부터 52개 ARKit 블렌드셰이프를 30fps로 생성하여 아바타 얼굴을 실시간으로 움직이게 하는 시스템입니다. 현재 V2 모델은 **5개 감정(happy, sad, angry, surprised, neutral)**을 지원하며, 감정은 외부에서 수동으로 설정합니다 (`setEmotion()` API).

- **모델 크기**: 5.2MB (INT8 ONNX)
- **파라미터**: 5,409,480개
- **감정 입력**: 5차원 one-hot 벡터, FiLM 레이어를 통해 주입
- **배포**: WASM (브라우저 on-device)

### 1.2 시니어 요청 사항

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

핵심 요구사항:
1. **텍스트 → 감정 감지 모델**: 입력 텍스트를 분석하여 감정/VAD를 자동으로 판별
2. **다양한 표정**: 고정된 감정 레이블에 국한되지 않는 풍부한 표현
3. **다국어 지원**: 한국어, 중국어, 영어, 일본어 필수 + 기타 언어
4. **On-device 배포**: 전체 시스템 **20MB 이하**
5. **별도 API 비용 없이** 자체 모델로 구동

### 1.3 V3 새로운 방향

기존 V3 계획(16개 감정 레이블 확장)에서 **2-모델 시스템**으로 전환합니다:

```
기존 V3 계획:
  [LLM 태그: {JOY:excitement:30}] → 16차원 one-hot → FiLM → 립싱크 모델 → 블렌드셰이프
  → 16개 고정된 표정만 가능

새로운 방향:
  [텍스트] → 감정 감지 모델 → 감정/VAD 정보 → FiLM → 립싱크 모델 → 블렌드셰이프
  → 다양하고 연속적인 표정 가능
```

---

## 2. 시스템 아키텍처 (2-모델 구조)

### 2.1 전체 파이프라인

```
┌──────────────────────────────────────────────────────────────┐
│ Model 1: 감정 감지 모델 (MicroALBERT)                          │
│                                                                │
│  입력: 텍스트 (한/중/영/일)                                      │
│  출력: 감정 정보 (Option에 따라 다름)                              │
│  크기: ~6.0MB (INT8 ONNX)                                      │
│                                                                │
│  "정말 고마워요" → 감정 분석 → VAD (0.70, 0.40, 0.30)             │
│  "I'm so angry"  → 감정 분석 → VAD (-0.51, 0.59, 0.56)          │
└──────────────────────┬─────────────────────────────────────────┘
                       │ 감정/VAD 벡터
                       ▼
┌──────────────────────┴─────────────────────────────────────────┐
│ Model 2: 립싱크 모델 (AnimaSync V3)                              │
│                                                                  │
│  입력: 오디오 특성 (141차원) + 감정/VAD 벡터                        │
│  출력: 52개 ARKit 블렌드셰이프 (30fps)                             │
│  크기: ~5.2MB (INT8 ONNX)                                        │
│                                                                  │
│  오디오 + VAD → FiLM 컨디셔닝 → LSTM/Transformer → 블렌드셰이프    │
└──────────────────────────────────────────────────────────────────┘

동시에:
  같은 텍스트 → TTS → 오디오 → 립싱크 모델 (입 움직임)
```

### 2.2 사용 시나리오

```
1. 사용자가 텍스트를 입력 (예: 챗봇 응답, 대본)
2. 텍스트 → 감정 감지 모델 → VAD/감정 벡터 추출
3. 텍스트 → TTS → 오디오 생성
4. 오디오 + VAD/감정 벡터 → 립싱크 모델 → 52채널 블렌드셰이프
5. 블렌드셰이프 → 아바타 렌더링 (Three.js)
6. 결과: 아바타가 적절한 감정 표정으로 텍스트를 읽음
```

---

## 3. 3가지 접근 방식 비교 (시니어 선택 필요)

### Option A: 16개 이산 감정 레이블 (기존 V3 계획)

#### 개요

감정을 16개 카테고리 중 하나로 분류합니다. 각 카테고리마다 고정된 표정 패턴을 학습합니다.

```
텍스트 → 감정 감지 모델 → "gratitude" (16개 중 하나)
  → one-hot 벡터 [0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0]
  → FiLM(16, 64) → 립싱크 모델 → 블렌드셰이프
```

#### 16개 감정 체계

```
Base (5개, MEAD 실제 데이터):
  JOY(기쁨), SADNESS(슬픔), ANGER(분노), SURPRISE(놀람), NEUTRAL(중립)

Sub (11개, Base에서 파생):
  JOY 계열:      laughter(웃음), excitement(흥분), agreement(동의), gratitude(감사)
  SADNESS 계열:  crying(울음), sulk(삐침), apology(사과), struggle(고민)
  ANGER 계열:    refusal(거절)
  SURPRISE 계열: fluster(당황), shy(수줍음)
```

#### 감정 감지 모델 출력

```python
# 16-class classification (softmax)
output = model(text)  # → [0.02, 0.01, 0.00, ..., 0.85, ..., 0.01]
predicted_emotion = argmax(output)  # → "gratitude"
```

#### 립싱크 모델 변경

```python
# FiLM 레이어 변경
self.emotion_embed = nn.Linear(16, 64)  # 기존: nn.Linear(5, 64)
# 추가 파라미터: +704개 (0.013%)
```

#### 학습 데이터 (립싱크 모델)

- MEAD 5개 감정: 실제 캡처 데이터 (5,001 클립)
- 11개 Sub 감정: Base + VAD Delta 합성 데이터 (감정당 ~1,000-2,000 클립)
- 강도 3단계: factor 0.40, 0.70, 0.95
- 스타일 모드: 감정당 2-3개 변형

#### 장단점

| 장점 | 단점 |
|------|------|
| 예측 가능 — 각 감정의 표정이 명확 | 16개 표정만 가능, "다양성" 제한적 |
| QA/디버깅 용이 (각 감정 개별 테스트) | 17번째 감정 추가 시 재학습 필요 |
| 분류(classification)는 학습이 쉬움 | 감정 간 보간(interpolation)이 부자연스러움 |
| 데모 용이 ("이건 감사 표정") | 동일 감정은 항상 비슷한 표정 |

---

### Option B: 연속 VAD (Continuous VAD)

#### 개요

감정을 3개 연속 수치(Valence, Arousal, Dominance)로 표현합니다. 고정 레이블 없이, VAD 공간의 모든 점이 고유한 표정을 만듭니다.

```
텍스트 → 감정 감지 모델 → (V=0.70, A=0.40, D=0.30)
  → FiLM(3, 64) → 립싱크 모델 → 블렌드셰이프
```

#### VAD 차원 설명

```
V (Valence, 감정가):   얼마나 긍정적/부정적인가?  (-1 ~ +1)
  -1: 극도로 부정적 (혐오, 분노)
   0: 중립
  +1: 극도로 긍정적 (기쁨, 감사)

A (Arousal, 각성도):   얼마나 흥분/차분한가?      (-1 ~ +1)
  -1: 극도로 차분 (졸림, 무기력)
   0: 보통
  +1: 극도로 흥분 (격앙, 환호)

D (Dominance, 지배력): 얼마나 통제적/무력한가?    (-1 ~ +1)
  -1: 극도로 무력 (공포, 복종)
   0: 보통
  +1: 극도로 통제적 (권위, 확신)
```

#### VAD 공간에서의 감정 분포 (예시)

```
                   A (각성도)
                   ↑
            anger  ●     ● surprise
          (-0.51,  |  (0.40,
           0.59)   |   0.67)
                   |
                   |        ● excitement
                   |      (0.85, 0.88)
         ─────────●neutral──────────→ V (감정가)
                (0,0)
                   |
                   |            ● joy
          sadness  |          (0.76, 0.65)
          (-0.63,  ●
          -0.27)   |
                   |
                   ↓

  (D축은 화면 안쪽/바깥쪽 방향, 2D로 표현 불가)
```

**핵심**: 이 3D 공간의 어떤 점이든 고유한 표정을 생성합니다. 레이블 없이 무한한 다양성.

#### 감정 감지 모델 출력

```python
# 3-value regression
output = model(text)  # → (V=0.70, A=0.40, D=0.30)
# 연속값이므로 같은 "감사"도 매번 미세하게 다른 VAD 출력
```

#### 립싱크 모델 변경

```python
# FiLM 레이어 변경
self.emotion_embed = nn.Sequential(
    nn.Linear(3, 64),
    nn.ReLU(),
    nn.Linear(64, 64),   # 추가 레이어 (연속 매핑을 위한 비선형성)
    nn.ReLU()
)
# gamma/beta 프로젝션은 동일
self.gamma_proj = nn.Linear(64, hidden_dim)  # 320
self.beta_proj = nn.Linear(64, hidden_dim)   # 320
```

#### 학습 데이터 (립싱크 모델)

- MEAD 5개 감정: VAD 좌표로 재라벨링 (실제 캡처 데이터)
- VAD 공간 밀집 샘플링: 300-500개 VAD 포인트에 대해 Base+Delta 합성 데이터 생성
- 강도: VAD 공간 내 neutral로부터의 거리로 자연스럽게 결정 (별도 강도 시스템 불필요)
- (선택) 비디오 데이터셋에서 블렌드셰이프 추출 (RAVDESS, CREMA-D → MediaPipe)

#### 장단점

| 장점 | 단점 |
|------|------|
| 무한 다양성 — VAD 공간 어디든 고유 표정 | QA 어려움 ("V=0.32에서 맞는 표정인가?") |
| 재학습 없이 새 감정 대응 가능 | 회귀(regression)는 분류보다 학습 난이도 높음 |
| 강도 제어 내장 (neutral로부터의 거리) | 일부 VAD 영역에서 이상한 표정 가능성 |
| 부드러운 감정 전환 | 데모 시 직관적이지 않음 ("이건 VAD=(0.7,0.4,0.3)") |
| FiLM 입력 작음 (3차원) | VAD 공간 전체를 커버하는 학습 데이터 필요 |

---

### Option C: 하이브리드 (16 레이블 + VAD) ★ 추천

#### 개요

이산 감정 레이블과 연속 VAD를 **동시에** 사용합니다. 레이블은 "어떤 감정인지", VAD는 "그 감정의 미묘한 뉘앙스"를 결정합니다.

```
텍스트 → 감정 감지 모델 → "gratitude" (0.95 확률) + VAD (0.70, 0.40, 0.30)
  → concat → [0,...,0.95,...,0, 0.70, 0.40, 0.30]  (19차원)
  → FiLM(19, 64) → 립싱크 모델 → 블렌드셰이프
```

#### 핵심 아이디어

같은 감정 레이블이라도 VAD에 따라 **다른 표정**을 만듭니다:

```
"정말 고마워요"     → gratitude + VAD(0.70, 0.40, 0.30) → 차분한 감사 미소
"진짜 너무 고마워!!" → gratitude + VAD(0.75, 0.80, 0.35) → 흥분된 감사 표정
                                         ↑ 높은 각성도
                                         = 같은 감정, 다른 뉘앙스

"미안해..."        → apology + VAD(-0.20, 0.30, -0.40) → 무기력한 사과
"정말 죄송합니다"    → apology + VAD(-0.30, 0.50, -0.20) → 진지한 사과
```

#### 감정 감지 모델 출력

```python
# Multi-task: classification + regression
class EmotionHead(nn.Module):
    def __init__(self, hidden_dim=384):
        self.cls_head = nn.Linear(hidden_dim, 16)   # 16-class 분류
        self.vad_head = nn.Linear(hidden_dim, 3)     # VAD 회귀

    def forward(self, features):
        cls_probs = softmax(self.cls_head(features))  # [0.02, ..., 0.85, ...]
        vad = tanh(self.vad_head(features))           # (0.70, 0.40, 0.30)
        return cls_probs, vad
```

#### 립싱크 모델 변경

```python
# FiLM 레이어: 19차원 입력 (16 감정 확률 + 3 VAD)
self.emotion_embed = nn.Sequential(
    nn.Linear(19, 64),
    nn.ReLU(),
    nn.Linear(64, 64),
    nn.ReLU()
)
# 추가 파라미터: +896 (0.017%)
```

#### 장단점

| 장점 | 단점 |
|------|------|
| 명명된 감정 + 연속적 변형 = 최대 다양성 | 가장 복잡한 학습 구조 |
| "감사 표정 보여줘" 가능 + 매번 미세하게 다름 | Multi-task 학습 필요 |
| 레이블만 또는 VAD만으로 fallback 가능 | 학습 데이터 요구량 가장 많음 |
| 시니어의 "다양한 표정" 요구에 가장 적합 | |

---

### 3가지 옵션 요약 비교

| | A: 16 레이블 | B: 순수 VAD | C: 하이브리드 |
|---|---|---|---|
| **다양성** | 16개 고정 표정 | 무한 (VAD 공간 전체) | 무한 + 명명 가능 |
| **제어** | 목록에서 선택 | 3개 숫자 설정 | 둘 다 가능 |
| **감정 감지 모델 작업** | 분류 (쉬움) | 회귀 (어려움) | Multi-task (중간) |
| **QA/디버그** | 쉬움 | 어려움 | 중간 |
| **데모 용이성** | "이건 감사 표정" | "이건 V=0.7 A=0.4 D=0.3" | "이건 감사인데 좀 더 excited한 버전" |
| **총 크기** | ~9.2MB | ~9.2MB | ~9.4MB |
| **확장성** | 재학습 필요 | 이미 무한 | 이미 무한 |
| **학습 난이도** | 중간 | 중-상 | 상 |

---

## 4. 감정 감지 모델 (MicroALBERT) 상세 설계

### 4.1 왜 "LLM"이 아닌 MicroALBERT인가?

시니어가 "LLM"이라고 언급하셨지만, 이 작업(텍스트 → 3개 숫자)에 생성형 LLM은 **과도한 선택**입니다.

| | 생성형 LLM | MicroALBERT |
|---|---|---|
| 작업 | 텍스트 생성 | 텍스트 분류/회귀 |
| 최소 크기 | ~500MB (0.5B 모델) | ~6MB |
| 추론 속도 | ~100-300ms | ~15ms |
| 다국어 | 가능 | 가능 |
| On-device | 매우 어려움 | 용이 |
| 이 작업에 적합도 | 과잉 | 최적 |

텍스트에서 감정 3개 수치를 뽑아내는 것은 **분류/회귀 문제**이며, 인코더 모델(BERT 계열)이 가장 효율적입니다.

### 4.2 ALBERT 아키텍처의 핵심 기술

MicroALBERT는 Google의 ALBERT(A Lite BERT) 아키텍처의 두 가지 핵심 기술을 사용합니다:

#### 기술 1: 인수분해 임베딩 (Factorized Embedding)

일반 BERT: 어휘 크기 × hidden dim = 거대한 임베딩 테이블
```
일반:    32,000 × 384 = 12,288,000 파라미터 (12.3MB)
인수분해: 32,000 × 128 + 128 × 384 = 4,145,152 파라미터 (4.1MB)  ← 66% 절약
```

어휘를 먼저 작은 차원(128)으로 매핑한 후, hidden 차원(384)으로 확장합니다.

#### 기술 2: 레이어 간 가중치 공유 (Cross-Layer Weight Sharing)

일반 BERT: 6개 레이어 = 6세트의 가중치
```
일반:     6 × 1,182,720 = 7,096,320 파라미터
가중치 공유: 1 × 1,182,720 = 1,182,720 파라미터  ← 83% 절약
```

6개 레이어가 모두 **동일한 가중치를 공유**합니다. 깊이(depth)는 6이지만, 파라미터 비용은 1개 레이어분. 데이터가 같은 레이어를 6번 반복 통과하면서 점진적으로 정보를 정제합니다.

### 4.3 MicroALBERT 구체 스펙

```
┌─────────────────────────────────────────────────────┐
│ MicroALBERT — 감정 감지 모델                           │
│                                                       │
│ Tokenizer:           SentencePiece (32,000 어휘)       │
│                      한/중/영/일 균등 학습               │
│                                                       │
│ Embedding:                                            │
│   Token embedding:   32,000 × 128 (인수분해)            │
│   Projection:        128 → 384                        │
│   Position embedding: 128 × 128                       │
│                                                       │
│ Transformer:                                          │
│   Hidden dim:        384                              │
│   FFN dim:           768 (2× hidden)                  │
│   Layers:            6 (가중치 공유 — 1세트)             │
│   Attention heads:   6 (head_dim = 64)                │
│   Max seq length:    128                              │
│                                                       │
│ Output Head (Option에 따라):                            │
│   A: Linear(384, 16) + softmax                        │
│   B: Linear(384, 3) + tanh                            │
│   C: Linear(384, 16) + Linear(384, 3)                 │
│                                                       │
│ 총 파라미터:    ~5,500,000 (~5.5M)                      │
│ INT8 ONNX:     ~5.5MB                                 │
│ Tokenizer:     ~0.5MB                                 │
│ 배포 크기 합계: ~6.0MB                                  │
└─────────────────────────────────────────────────────┘
```

#### 파라미터 상세 분해

| 구성 요소 | 계산 | 파라미터 수 | INT8 크기 |
|-----------|------|-----------|----------|
| Token embedding | 32,000 × 128 | 4,096,000 | 4.10MB |
| Embedding projection | 128 × 384 | 49,152 | 0.05MB |
| Position embedding | 128 × 128 | 16,384 | 0.02MB |
| Embedding LayerNorm | 2 × 128 | 256 | ~0 |
| Q/K/V projection | 3 × (384 × 384 + 384) | 443,520 | 0.42MB |
| Attention output | 384 × 384 + 384 | 147,840 | 0.14MB |
| FFN up-projection | 384 × 768 + 768 | 295,680 | 0.28MB |
| FFN down-projection | 768 × 384 + 384 | 295,296 | 0.28MB |
| 2 × LayerNorm | 4 × 384 | 1,536 | ~0 |
| Pooler | 384 × 384 + 384 | 147,840 | 0.14MB |
| Output head (Option C) | 384×16 + 384×3 + 19 | 7,315 | ~0 |
| **합계** | | **~5,500,819** | **~5.5MB** |

### 4.4 다국어 지원 원리

32,000 어휘 배분 (SentencePiece 학습 시 균등 코퍼스 사용):

| 언어 | 예상 토큰 수 | 커버리지 |
|------|------------|---------|
| 한국어 | ~7,000-8,000 | 한글 자모/음절 + 자주 쓰는 서브워드 |
| 중국어 | ~7,000-8,000 | 상용 한자 + 고빈도 서브워드 |
| 영어 | ~7,000-8,000 | 서브워드 피스 (BPE) |
| 일본어 | ~6,000-7,000 | 히라가나 + 카타카나 + 상용 한자 (중국어와 공유) |
| 특수문자/공용 | ~2,000-3,000 | 숫자, 문장부호, 특수 토큰 |

**중일 한자 공유**: CJK Unified Ideographs로 중국어 한자와 일본어 한자가 동일 토큰을 공유합니다. 별도 학습 없이도 한자 커버리지가 높아집니다.

### 4.5 학습 파이프라인 (감정 감지 모델)

#### Phase 1: Teacher 모델 학습

```
XLM-RoBERTa-base (280M 파라미터, 100개 언어 지원)
  → 감정/VAD 데이터로 fine-tuning
  → "Teacher" 역할: 높은 정확도의 감정 감지 능력 확보
```

**학습 데이터**:

| 데이터셋 | 언어 | 크기 | 유형 | 변환 |
|---------|------|------|------|------|
| EmoBank | 영어 | 10,062 문장 | VAD 직접 레이블 | 불필요 |
| SemEval 2018 | 영어 | 1,600 트윗 | VAD 직접 레이블 | 불필요 |
| GoEmotions | 영어 | 58,000 문장 | 27개 감정 카테고리 | 카테고리 → VAD 매핑 |
| AI Hub 감정 분류 텍스트 | 한국어 | 40,000-50,000 발화 | 7개 감정 | 카테고리 → VAD 매핑 |
| KOTE | 한국어 | 50,000 텍스트 | 다중 레이블 감정 | 카테고리 → VAD 매핑 |
| NLPCC/Ren-CECps | 중국어 | 35,000 문장 | 8개 감정 | 카테고리 → VAD 매핑 |
| WRIME | 일본어 | 30,000 텍스트 | 8개 감정 (Plutchik) | 카테고리 → VAD 매핑 |
| **합계** | | **~170,000+ 문장** | | |

**카테고리 → VAD 변환 기준** (Russell & Mehrabian, 1977):

| 감정 카테고리 | Valence | Arousal | Dominance |
|-------------|---------|---------|-----------|
| Joy/Happy | 0.85 | 0.65 | 0.70 |
| Anger | -0.51 | 0.59 | 0.56 |
| Fear | -0.64 | 0.60 | -0.43 |
| Sadness | -0.63 | -0.27 | -0.33 |
| Surprise | 0.40 | 0.67 | 0.45 |
| Disgust | -0.60 | 0.35 | 0.11 |
| Neutral | 0.00 | 0.00 | 0.00 |

#### Phase 2: Student 모델 증류 (Distillation)

```
XLM-R Teacher (280M) ──증류──→ MicroALBERT Student (5.5M)
```

증류 방법:
```python
# 손실 함수
L = α × MSE(student_vad, gold_vad)           # 정답 레이블과의 오차
  + (1-α) × MSE(student_vad, teacher_vad)     # Teacher 예측과의 오차
  + β × attention_transfer_loss               # Teacher의 attention 패턴 모방

α = 0.5, β = 0.1
```

추가 학습 데이터 증강:
- Teacher를 사용하여 **50만-100만개** 비라벨 다국어 문장에 VAD 예측값 부여
- 이 소프트 레이블을 Student 학습에 사용
- 결과: 원본 17만개 + 증강 50-100만개 = 대규모 학습 데이터

#### Phase 3: ONNX 변환 및 양자화

```bash
# PyTorch → ONNX
torch.onnx.export(student_model, dummy_input, "emotion_detector.onnx")

# INT8 양자화
quantize_dynamic("emotion_detector.onnx", "emotion_detector_int8.onnx")

# 최종 크기: ~5.5MB (모델) + ~0.5MB (토크나이저) = ~6.0MB
```

#### 예상 성능

| 모델 | 파라미터 | Pearson r (V/A/D 평균) | 대비 Teacher |
|------|---------|---------------------|------------|
| XLM-R Teacher | 280M | ~0.80-0.85 | 100% |
| MicroALBERT Student | 5.5M | ~0.72-0.78 | ~90% |

**VAD 오차 ±0.05 이내**: 아바타 표정에서 육안으로 구분 불가능한 수준.

언어 이해력:
- 단순 긍부정: ★★★★★ (거의 완벽)
- 부정문 처리: ★★★★☆ (85-90% 정확도)
- 이중 부정: ★★★☆☆ (70-80%)
- 문맥 의존적: ★★★☆☆
- 풍자/아이러니: ★★☆☆☆ (근본적 한계)

---

## 5. 립싱크 모델 (V3) 상세 설계

### 5.1 현재 모델 아키텍처 (변경 없음)

```
Audio Features (B, T, 141) + 감정/VAD 벡터 (B, T, dim)
    |                              |
Input Projection (141 → 320)       |
    |                              |
CausalMultiScaleConvBlock (320)    |    ← 4개 dilated conv (d=1,2,4,8)
    |                              |
FiLM Conditioning <────────────────┘    ← 여기만 변경
    |
Unidirectional LSTM (2 layers, hidden=320)
    |
Causal Transformer (2 layers, 8 heads, ff=896)
    |
Output MLP (320 → 160 → 52) + Sigmoid
    |
Output: (B, T, 52) blendshapes
```

### 5.2 파라미터 상세 (현재 V2)

| 구성 요소 | 파라미터 수 | 비율 |
|-----------|-----------|------|
| Input Projection | 46,080 | 0.85% |
| CausalMultiScaleConvBlock | 1,643,200 | 30.38% |
| **FiLM Layer** | **41,984** | **0.78%** |
| LSTM (2-layer) | 1,643,520 | 30.38% |
| Causal Transformer (2 layers) | 1,973,632 | 36.49% |
| Output MLP | 60,372 | 1.12% |
| Output Bias | 52 | 0.00% |
| **합계** | **5,409,480** | **100%** |

**핵심**: FiLM 레이어는 전체의 **0.78%**에 불과합니다. 감정 입력 차원을 변경해도 모델 크기에 실질적 영향이 없습니다.

### 5.3 Option별 FiLM 변경사항

| 설정 | emotion_embed 형태 | FiLM 파라미터 | 전체 모델 변화 |
|------|-------------------|-------------|-------------|
| V2 (현재, 5 감정) | Linear(5, 64) | 41,984 | 기준 |
| Option A (16 레이블) | Linear(16, 64) | 42,688 | +704 (+0.013%) |
| Option B (VAD 3차원) | MLP(3→64→64) | 46,272 | +4,288 (+0.08%) |
| Option C (19차원) | MLP(19→64→64) | 47,296 | +5,312 (+0.10%) |

**어떤 Option을 선택해도 모델 크기는 ~5.2MB INT8로 동일합니다.**

### 5.4 3-Way 채널 분류 시스템 (모든 Option 공통)

감정 표정과 립싱크가 충돌하지 않도록 52개 채널을 3개 영역으로 분류합니다:

| 영역 | 채널 수 | 역할 | 감정 개입 |
|------|---------|------|----------|
| **LIPSYNC_ONLY** | 14 | 핵심 조음 (mouthClose, mouthFunnel 등) | 없음 |
| **EXPRESSION_ONLY** | 22 | 눈, 눈썹, 볼, 코 등 | 전적으로 감정 제어 |
| **SHARED** | 16 | 미소, 찡그림, jawOpen 등 | speech-aware 감쇠 적용 |

### 5.5 학습 파이프라인 (립싱크 모델)

#### 학습 데이터 확보 전략 — 3가지 소스

```
┌─────────────────────────────────────────────────────────────┐
│ 소스 1: MEAD 실제 캡처 데이터                                   │
│                                                               │
│  ~5,001 클립 × 5 감정                                          │
│  오디오: 실제 녹음 음성                                          │
│  블렌드셰이프: 실제 모션캡처                                      │
│  VAD: 감정 레이블에서 매핑                                       │
│  품질: ★★★★★ (ground truth)                                   │
│                                                               │
│  → 각 클립을 VAD 좌표로 재라벨링                                 │
│  예: happy 클립 → VAD (0.76, 0.65, 0.70)                       │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 소스 2: 비디오 데이터셋에서 블렌드셰이프 추출                       │
│                                                               │
│  RAVDESS (8감정), CREMA-D (6감정) 등 감정 비디오 데이터셋          │
│  비디오 → MediaPipe Face Landmarker → 52 블렌드셰이프 추출        │
│  오디오: 비디오에서 추출 (이미 존재)                               │
│  VAD: 감정 레이블에서 매핑                                       │
│  품질: ★★★☆☆ (MediaPipe 추출은 노이즈 있음)                     │
│                                                               │
│  → MEAD에 없는 감정 영역 커버리지 확보                            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 소스 3: 감정 감지 모델 학습 텍스트 → TTS → 합성 데이터              │
│                                                               │
│  감정 감지 모델의 학습 데이터 (~170K 문장 with VAD 레이블)          │
│  → TTS로 오디오 생성                                            │
│  → 기존 립싱크 V2 모델로 base 블렌드셰이프 추론                    │
│  → Base + VAD Delta로 표정 타겟 합성                             │
│  품질: ★★☆☆☆ ~ ★★★☆☆ (합성 데이터)                             │
│                                                               │
│  ★ 감정 감지 모델과 동일한 데이터 소스 공유                        │
│  → 하나의 코퍼스로 두 모델 학습                                   │
└─────────────────────────────────────────────────────────────┘
```

#### 핵심: 데이터 공유 파이프라인

하나의 텍스트 코퍼스가 **두 모델의 학습 데이터**로 동시에 활용됩니다:

```
텍스트 코퍼스 (170K+ 문장, VAD 레이블 포함)
    │
    ├──→ 감정 감지 모델 학습: text → VAD 분류/회귀
    │
    └──→ TTS → 오디오 생성
              │
              ├──→ 립싱크 모델 학습: audio + VAD → 블렌드셰이프
              │    (VAD는 이미 알고 있음, 블렌드셰이프는 Base+Delta로 합성)
              │
              └──→ 동일한 audio를 감정 감지 모델 검증에도 활용 가능
```

#### 학습 단계

```
Phase 0: 데이터 준비
├── MEAD 5,001 클립 → VAD 재라벨링
├── (선택) RAVDESS/CREMA-D 비디오 → MediaPipe 블렌드셰이프 추출
├── VAD 공간 샘플링: anchor 포인트 + 모델이 부족한 영역 추가
└── 합성 블렌드셰이프 타겟 생성 (Base + Delta)

Phase 1: FiLM 레이어만 학습 (backbone 고정)
├── V2 체크포인트에서 초기화 (LSTM/Transformer 가중치 그대로)
├── FiLM 레이어만 새로 초기화 (차원 변경)
├── 모델이 VAD → gamma/beta 매핑 학습
└── ~5-10 에폭, 빠르게 수렴

Phase 2: 전체 fine-tuning
├── 전체 파라미터 학습 (낮은 학습률)
├── 혼합 배치: 실제 데이터 (높은 가중치) + 합성 데이터 (낮은 가중치)
├── ~20-30 에폭
└── 3-Way 채널 가중치 적용 손실 함수

Phase 3: 반복 개선
├── 중간 VAD 포인트에서 출력 확인
├── 부자연스러운 영역 식별 → 해당 영역에 합성 앵커 추가
├── 재학습
└── 예상: 3-5회 반복 후 품질 달성
```

#### 손실 함수

```python
L = w_lip × MSE(lipsync_channels)         # 립싱크 보호 (가중치 5.0)
  + w_exp × MSE(expression_channels)       # 표정 학습 (가중치 1.0)
  + w_sh  × MSE(shared_channels)           # 공유 채널 (가중치 2.0)
  + w_vel × velocity_loss                  # 시간적 매끄러움
  + w_smooth × smoothness_loss             # 프레임 간 가속도 페널티
```

#### "모델이 스스로 보간(interpolation)하는" 접근법

5개 MEAD 앵커에 대해 먼저 학습한 후, 모델이 **VAD 공간의 빈 영역을 자체적으로 보간**하도록 합니다:

```
1단계: 5개 실제 앵커만으로 학습
       → 중간 VAD 포인트에서 출력 시각적 확인

2단계: 보간이 부자연스러운 영역 식별
       → 해당 영역에만 합성 앵커 추가

3단계: 추가된 앵커 포함하여 재학습

4단계: 반복
       → 모델이 "어디서 도움이 필요한지" 알려주는 방식
       → 500개 포인트를 맹목적으로 생성하는 것보다 효율적
```

---

## 6. 사이즈 예산 (20MB 제한)

### 6.1 최종 사이즈 분해

```
┌─────────────────────────────────────────────┐
│ 20MB 예산                                     │
│                                               │
│ ┌─────────────────────────┐                   │
│ │ 감정 감지 모델            │  ~6.0MB          │
│ │ (MicroALBERT, 32K vocab) │                  │
│ │ ├── ONNX 모델: ~5.5MB    │                  │
│ │ └── 토크나이저: ~0.5MB    │                  │
│ └─────────────────────────┘                   │
│                                               │
│ ┌─────────────────────────┐                   │
│ │ 립싱크 모델               │  ~5.2MB          │
│ │ (AnimaSync V3)           │                  │
│ │ └── ONNX 모델: ~5.2MB    │                  │
│ └─────────────────────────┘                   │
│                                               │
│ 합계:      ~11.2MB                             │
│ 잔여 예산:  ~8.8MB (향후 확장 여유)               │
│                                               │
│ ██████████████░░░░░░  56% 사용                 │
└─────────────────────────────────────────────┘
```

### 6.2 예산 잔여분 활용 가능성

~8.8MB 여유분이 있으므로, 필요 시 다음과 같은 확장이 가능합니다:

| 확장 옵션 | 추가 크기 | 설명 |
|----------|----------|------|
| 어휘 확장 (32K → 48K) | +2MB | 더 많은 언어/단어 커버리지 |
| 립싱크 모델 용량 증가 | +2-3MB | hidden_dim 확장으로 표현력 향상 |
| 추가 모델 (음성 감정 인식) | +3-5MB | 향후 음성에서도 감정 감지 |
| 그대로 유지 | 0 | 빠른 로딩, 작은 번들 사이즈 |

---

## 7. 아바타 지원

### 7.1 지원 형식

시스템은 **형식에 구애받지 않는** 출력을 생성합니다:

| 형식 | 지원 | 방식 |
|------|------|------|
| **VRM** (VRoid Studio) | ✅ | ARKit 52 → VRM 18 표현식 변환 (`vrm_convert.rs`) |
| **GLB/glTF** | ✅ | ARKit 52 → morph target 직접 적용 |
| **커스텀 아바타** | ✅ | morph target dictionary 매핑 |

### 7.2 렌더링

Three.js + `@pixiv/three-vrm` 라이브러리 사용. 브라우저에서 실시간 렌더링.

---

## 8. 구현 로드맵

### 8.1 단계별 실행 계획

```
Phase 1: 감정 감지 모델 개발 (Teacher + Student)
├── 1-1: 학습 데이터 수집 및 전처리
│   ├── EmoBank, GoEmotions 다운로드 및 VAD 매핑
│   ├── AI Hub 한국어 감정 데이터 신청/다운로드
│   ├── WRIME 일본어 데이터 다운로드
│   └── NLPCC 중국어 데이터 다운로드
│
├── 1-2: XLM-R Teacher fine-tuning
│   ├── 170K+ 문장으로 학습
│   ├── Multi-task loss (분류 + VAD 회귀)
│   └── 검증셋 평가
│
├── 1-3: MicroALBERT Student 증류
│   ├── 아키텍처 구현 (32K vocab, 6-layer shared)
│   ├── SentencePiece 토크나이저 학습
│   ├── Teacher → Student 증류 학습
│   ├── 비라벨 데이터 소프트 레이블링 (50-100만 문장)
│   └── Student 성능 평가
│
└── 1-4: ONNX 변환 및 검증
    ├── INT8 양자화
    └── 다국어 테스트 (한/중/영/일 각 100문장)

Phase 2: 립싱크 모델 V3 개선
├── 2-1: 학습 데이터 준비
│   ├── MEAD 클립 VAD 재라벨링
│   ├── (선택) 비디오 데이터셋 블렌드셰이프 추출
│   └── Phase 1 텍스트 → TTS → 오디오 생성
│
├── 2-2: FiLM 레이어 수정 및 학습
│   ├── V2 체크포인트 로드
│   ├── FiLM 교체 (5-dim → 3/16/19-dim, Option에 따라)
│   ├── Stage 1: FiLM만 학습 (backbone 고정)
│   ├── Stage 2: 전체 fine-tuning
│   └── 보간 품질 확인 → 앵커 추가 → 반복
│
└── 2-3: ONNX 변환 및 양자화
    ├── INT8 양자화
    └── 기존 WASM 파이프라인에 통합 테스트

Phase 3: 통합 및 배포
├── 3-1: 2-모델 파이프라인 통합
│   ├── JS wrapper에서 감정 감지 → 립싱크 연동
│   ├── setEmotion() API 유지 (수동 오버라이드용)
│   └── 자동 감정 감지 모드 추가
│
├── 3-2: WASM 통합
│   ├── 감정 감지 모델 ONNX Runtime Web 로딩
│   ├── 립싱크 모델과 병렬 추론
│   └── 총 번들 사이즈 확인 (< 20MB)
│
└── 3-3: 데모 및 검증
    ├── 다국어 텍스트 → 아바타 표정 데모
    ├── 감정 다양성 시각적 확인
    └── 시니어 리뷰
```

### 8.2 선행/후행 의존성

```
Phase 1-1 (데이터 수집)
    ↓
Phase 1-2 (Teacher 학습)
    ↓
Phase 1-3 (Student 증류) ──────→ Phase 2-1 (립싱크 데이터 준비)
    ↓                                    ↓
Phase 1-4 (ONNX 변환)           Phase 2-2 (립싱크 모델 학습)
    ↓                                    ↓
    └────────── 합류 ──────────→ Phase 3 (통합 및 배포)
```

**Phase 1-3과 2-1은 병렬 진행 가능**: Student 증류 학습과 립싱크 데이터 준비는 동시에 진행할 수 있습니다. Student의 학습 텍스트를 TTS로 변환하는 것이 이 두 Phase의 연결점입니다.

---

## 9. 시니어 결정 필요 사항

### 9.1 즉시 결정 필요

| # | 결정 사항 | 선택지 | 추천 |
|---|----------|--------|------|
| 1 | **감정 표현 방식** | A: 16 레이블 / B: 순수 VAD / C: 하이브리드 | **C (하이브리드)** |
| 2 | **다국어 범위** | 한중영일만 / + 추가 언어 | 한중영일 (32K vocab으로 충분) |

### 9.2 추후 결정 가능

| # | 결정 사항 | 현재 기본값 |
|---|----------|-----------|
| 3 | 비디오 데이터셋 활용 여부 | 선택 사항 (MEAD + 합성만으로 시작) |
| 4 | TTS 엔진 선택 | ElevenLabs (기존) 또는 무료 대안 |
| 5 | 립싱크 모델 용량 증가 여부 | 현재 크기 유지 (5.2MB) |

---

## 10. 기존 V3 계획과의 관계

### 10.1 유지되는 부분

| 기존 V3 계획 항목 | 새 계획에서의 위치 |
|-----------------|----------------|
| 3-Way 채널 분류 (LIPSYNC/EXPRESSION/SHARED) | ✅ 그대로 유지 |
| Base + VAD Delta 합성 데이터 생성 | ✅ 그대로 유지 (학습 데이터용) |
| FiLM 컨디셔닝 아키텍처 | ✅ 유지 (입력 차원만 변경) |
| V2 체크포인트 초기화 전략 | ✅ 그대로 유지 |
| 손실 함수 (3-Way 가중치) | ✅ 그대로 유지 |
| WASM 배포 파이프라인 | ✅ 그대로 유지 |
| 아바타 캘리브레이션 시스템 | ✅ 독립적, 그대로 유지 |

### 10.2 변경/대체되는 부분

| 기존 V3 계획 항목 | 변경 내용 |
|-----------------|----------|
| LLM 태그 파서 `{JOY:excitement:30}` | → 자동 감정 감지 모델로 대체 (수동 오버라이드는 유지) |
| 16개 고정 감정 분류 (Option B/C 선택 시) | → 연속 VAD 공간으로 확장 |
| 감정 입력을 외부에서 수동 설정 | → 텍스트에서 자동 감지 |
| 스타일 모드 (고정 2-3 변형) | → VAD 연속 변형으로 자연스럽게 대체 (Option B/C) |

### 10.3 새롭게 추가되는 부분

| 항목 | 설명 |
|------|------|
| **감정 감지 모델 (MicroALBERT)** | 텍스트 → VAD/감정 분류. 완전히 새로운 모델. |
| **다국어 토크나이저** | 한중영일 32K SentencePiece. 새로 학습. |
| **증류 파이프라인** | XLM-R → MicroALBERT. 새로운 학습 인프라. |
| **2-모델 통합 JS wrapper** | 감정 감지 + 립싱크 모델 동시 추론 로직. |

---

## 부록 A: VAD (Valence-Arousal-Dominance) 모델 설명

VAD는 심리학에서 감정을 3차원으로 표현하는 차원 모델(dimensional model)입니다.

```
V (Valence, 감정가)
  "이 감정이 얼마나 좋은/나쁜 느낌인가?"
  +1: 매우 긍정적 (기쁨, 사랑, 흥분)
   0: 중립
  -1: 매우 부정적 (분노, 슬픔, 혐오)

A (Arousal, 각성도)
  "이 감정이 얼마나 에너지가 높은/낮은가?"
  +1: 매우 활성화됨 (흥분, 공포, 격노)
   0: 보통
  -1: 매우 비활성화됨 (졸림, 무기력, 평온)

D (Dominance, 지배력)
  "이 감정에서 얼마나 통제력을 느끼는가?"
  +1: 완전한 통제 (권위, 자신감)
   0: 보통
  -1: 완전히 무력 (공포, 복종, 수치)
```

이 3개 수치로 인간이 느끼는 거의 모든 감정을 위치시킬 수 있으며, 이산적인 감정 레이블보다 훨씬 세밀한 표현이 가능합니다.

---

## 부록 B: 용어 정리

| 용어 | 설명 |
|------|------|
| **FiLM** | Feature-wise Linear Modulation. 감정 신호를 features에 곱셈(gamma)과 덧셈(beta)으로 주입하는 기법 |
| **ALBERT** | A Lite BERT. 인수분해 임베딩 + 레이어 공유로 경량화된 BERT 변형 |
| **증류 (Distillation)** | 큰 Teacher 모델의 지식을 작은 Student 모델에 전달하는 학습 기법 |
| **SentencePiece** | 언어에 무관한 서브워드 토크나이저. 한중영일 동시 처리 가능 |
| **VAD** | Valence-Arousal-Dominance. 감정의 3차원 수치 표현 |
| **ARKit 52** | Apple ARKit의 52개 얼굴 블렌드셰이프 표준 |
| **MEAD** | Multi-view Emotional Audio-visual Dataset. 5개 감정의 실제 캡처 데이터 |
| **Base + Delta** | 부모 감정의 실제 블렌드셰이프에 VAD 차이만큼 수정하여 파생 표정을 만드는 방식 |
| **XLM-R** | XLM-RoBERTa. 100개 언어를 지원하는 다국어 Transformer 모델 |
| **INT8 양자화** | 32비트 부동소수점 가중치를 8비트 정수로 변환하여 4배 크기 절감 |
| **ONNX** | Open Neural Network Exchange. 모델 호환성을 위한 표준 포맷 |
| **WASM** | WebAssembly. 브라우저에서 네이티브 수준 성능으로 실행되는 바이너리 포맷 |
