감정 실험 — 설계 확정 + 데이터 전체셋
감정 실험의 변형(modality × context × prosody) 전체셋을 마감하고, 응답 task의 paradigm·평가축까지 확정. log-15에서 톤 매핑·TTS 96/112를 만들어 두었고, 본 글은 그 위에서 설계를 닫는 단계.
변형 정의 (3축)
대화의 전달 매체(modality), 상황 설명(context) 포함 여부, 그리고 task용 입력 컨텍스트 크기 — 세 축으로 변형을 나눈다.
| 축 | 값 |
|---|---|
| modality | text / image / audio |
| context (text·image만) | bare(상황 제외) / ctx(상황 포함) |
| prosody (audio만) | neutral / emotion |
| task 컨텍스트 | classify(전체 대화) / respond(turns[:-1]) |
비용 비대칭이 설계를 결정했다. text·image는 생성 비용 0이라 ctx와 task 두 축 모두 두 버전을 다 만들고 실험은 bare부터 시작. audio는 ctx축은 만들지 않고(상황은 프롬프트 텍스트로 공통 제공), task 축은 만들어야 한다 — paradigm A를 살리려면 turns[:-1] 음성이 필요하기 때문(아래 §Paradigm 참조).
prosody 축(neutral/emotion)은 audio 안에서 운율 정보가 분류·응답에 영향을 주는지 보려고 유지. neutral은 “다음 대화를 자연스럽게 읽어줘”, emotion은 log-15 톤 매핑 테이블의 감정 톤 지시.
파일 컨벤션 (task별 서브폴더)
data/exp_c/artifacts/
classify/ ← 전체 대화 (분류 task)
{id}-text.txt : 대화만
{id}-text-ctx.txt : [상황] + 대화
{id}-image.png : 대화만 PNG
{id}-image-ctx.png : [상황] + 대화 PNG
{id}-neutral.wav : 대화만 평이 TTS
{id}-emotion.wav : 대화만 감정 톤 TTS
respond/ ← turns[:-1] (응답 생성 task)
{id}-text.txt
{id}-text-ctx.txt
{id}-image.png
{id}-image-ctx.png
{id}-neutral.wav (※ 신규 TTS 콜 필요)
{id}-emotion.wav (※ 동상)
같은 파일명을 task 폴더로만 분리. 세 generator 모두 sample_pool.json을 읽고 두 task에 대해 각각 생성, 기존 파일은 skip. 변형 추가는 image/text는 VARIANTS dict에 한 줄, audio는 _generate_tts의 분기 추가.
데이터 현황 (4-27 시점)
| 카테고리 | 샘플 | classify (전체 대화) | respond (turns[:-1]) |
|---|---|---|---|
| 기쁨 | 10 | text·ctx·image·image-ctx·neutral·emotion 6×10 ✓ | text·ctx·image·image-ctx 4×10 ✓ / wav 0 ❌ |
| 슬픔 | 10 | 동상 ✓ | 동상 |
| 분노 | 10 | 동상 ✓ | 동상 |
| 중립 | 10 | 동상 ✓ | 동상 |
| 정 | 8 | 동상 ✓ | 동상 |
| 한 | 8 | 동상 ✓ | 동상 |
| 합 | 56 | 336/336 ✓ | 224/336 (text/image 224 ✓, audio 112 미생성) |
audio respond 112개는 다음 세션에서 TTS 콜로 생성 예정.
Paradigm: turns[:-1] 입력 + 마지막 발화 생성 (A)
응답 생성 task에서 두 paradigm을 두고 흔들렸다.
| Paradigm | 입력 | 모델 출력 | 사람 응답 reference | 추가 비용 |
|---|---|---|---|---|
| A: turns[:-1] 입력 | 마지막 발화 제외 | 마지막 발화 생성 | turns[-1] = 데이터셋 사람 응답 | audio respond 신규 TTS 56×2=112콜 |
| B: 전체 turns 입력 | N턴 전체 | N+1번째 발화 생성 | 없음(N+1 발화는 데이터셋에 없음) | 0 |
처음에는 비용을 우선해 B로 결정했다. 그러나 A의 큰 장점 — 사람의 turn[-1]을 LaaJ에 후보로 추가할 수 있음 — 이 연구의 핵심을 살린다는 판단으로 뒤집었다. 모델 응답들끼리만 비교하면 “어떤 모달리티 입력이 더 좋은 응답을 만드는가”는 답할 수 있지만, “한국어 정서 응답에서 모델이 사람을 이기는가/사람과 비슷한가” 라는 흥미로운 질문이 빠진다. paradigm A는 그 anchor를 무료로 제공한다.
비용 측면: 마지막 발화까지 포함된 음성을 새로 만드는 게 아니라(=B에 필요), 오히려 마지막 발화를 빼고 TTS해야 한다(=A 입력). 56샘플 × neutral/emotion 2종 = 112 wav, ≈2일 콜 분량. text·image의 turns[:-1] 변형은 비용 0이라 이미 만들어 둠.
→ A로 결정. audio respond TTS는 다음 세션에서 진행.
Tasks
분류 (classify)
6지선다(기쁨/슬픔/분노/중립/정/한)를 골라내는 단답 task. 출력은 text only.
- 평가: 정답률(accuracy) — sample_pool의
category필드와 비교 - 4 variants 사용:
text-bare/image-bare/audio_neutral/audio_emotion - Kanana 콜 예산: 4콜/샘플 × 56샘플 = 224콜 (≈4일 분량)
응답 생성 (respond)
artifacts/respond/ 입력(turns[:-1])을 받아 데이터셋의 마지막 발화 자리(turn[-1])에 들어갈 응답을 자유 텍스트로 생성. 출력은 text + audio.
- 입력: turns[:-1] (modality별로 text·image·audio 형태)
- 모델 출력: text 응답 + audio 응답 (HCX는 text only)
- 평가: LLM-as-a-judge pairwise — 후보군에 사람 응답(turn[-1])도 포함해서 모델 vs 사람 vs 모델 간 비교
- 4 variants 사용
- HCX의 audio 차원은 결측 처리
분리 vs 통합
분류와 응답 생성을 한 콜에 묶어 출력할 수도 있지만(콜 절반), 평가가 섞여서 깔끔하지 않다. 분리해서 각각의 정답률과 응답 품질을 독립적으로 측정한다.
모델 라인업
| 모델 | 파라미터 | 한국어 특화 | adapter | audio in | audio out |
|---|---|---|---|---|---|
| Kanana-1.5-o | 11.6B | ✅ | kanana_client.py (REST) |
✅ | ✅ |
| HCX-SEED-Omni | 8B | ✅ | experiments/exp_c/adapters/hcx.py |
✅ | ✗ (디코더 미공개) |
| Qwen2.5-Omni | 7B | ❌ | .../qwen.py |
✅ | ✅ |
| MiniCPM-o 2.6 | 8B | ❌ | .../minicpm.py |
✅ | ✅ |
비교 모델은 모두 4×RTX3090 local에서 돌고, exp_a 어댑터를 상속해 INSTRUCTION만 감정 task용으로 교체. parallel_runner로 4-GPU 분산 실행 패턴은 exp_a와 동일하게 가져갈 예정.
다음
- 분류 풀 배치 — 13~17샘플/일, 4일 안에 56샘플 완주
- audio respond TTS 112개 — paradigm A 입력 (turns[:-1] 음성), 2일 분량 콜
- 응답 생성 runner —
RESPOND_INSTRUCTION가다듬기, audio output 받기, 사람 응답 후보 결과 JSON에 같이 저장 - LaaJ 프롬프트 설계 — pairwise 비교 기준(공감 정도, 자연스러움, 한국어 운율 적합성 등). 후보군: Kanana / HCX / Qwen / MiniCPM / 사람
- 비교 모델 어댑터 확장 — exp_c용 Kanana 어댑터 추가, parallel_runner 패턴 도입
분류 결과부터 모이는 대로 patterns 보면서 RQ1·RQ3에 대한 예비 신호를 본다.