Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions app/services/va_fusion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from math import exp, sqrt
from math import exp, sqrt, log
from typing import Dict, Tuple, Optional

# Emotion anchors for Valence (V) and Arousal (A)
Expand Down Expand Up @@ -199,6 +199,33 @@ def apply_zero_prob_mask(
return out


def compute_entropy(probs: Dict[str, float]) -> float:
"""μ •κ·œν™”λœ μ—”νŠΈλ‘œν”Ό 계산 (0~1).

감정 뢄포가 κ· μΌν• μˆ˜λ‘(λͺ¨λ“  감정이 λΉ„μŠ·ν•œ ν™•λ₯ ) 1에 가깝고,
νŠΉμ • 감정에 μ§‘μ€‘λ μˆ˜λ‘ 0에 κ°€κΉμŠ΅λ‹ˆλ‹€.

Args:
probs: 감정별 ν™•λ₯  λ”•μ…”λ„ˆλ¦¬ (합이 1일 ν•„μš” μ—†μŒ)

Returns:
μ •κ·œν™”λœ μ—”νŠΈλ‘œν”Ό κ°’ (0~1)
"""
eps = 1e-10
# μ •κ·œν™”
total = sum(max(0.0, p) for p in probs.values())
if total <= 0:
return 1.0 # λͺ¨λ“  값이 0이면 μ΅œλŒ€ μ—”νŠΈλ‘œν”Ό(균일)둜 κ°„μ£Ό

normalized = {k: max(0.0, v) / total for k, v in probs.items()}

# μ—”νŠΈλ‘œν”Ό 계산
h = -sum(p * log(p + eps) for p in normalized.values() if p > 0)
max_h = log(len(probs)) if len(probs) > 0 else 1.0 # κ· λ“± 뢄포일 λ•Œ μ΅œλŒ€ μ—”νŠΈλ‘œν”Ό

return h / max_h if max_h > 0 else 0.0


def fuse_VA(audio_probs: Dict[str, float], text_score: float, text_magnitude: float) -> Dict[str, object]:
"""Fuse audio (emotion probabilities) and text (score,magnitude) into composite VA.

Expand Down Expand Up @@ -231,8 +258,8 @@ def fuse_VA(audio_probs: Dict[str, float], text_score: float, text_magnitude: fl
"happy": pos * mag,
"sad": neg * mag,
"neutral": max(0.0, neutral_base),
"angry": neg * mag * 0.8,
"fear": neg * mag * 0.7,
"angry": neg * mag, # λΆ€μ • 감정 동일 κ°€μ€‘μΉ˜
"fear": neg * mag, # λΆ€μ • 감정 동일 κ°€μ€‘μΉ˜
"surprise": pos * mag * 0.8,
}
# 긍정 ν…μŠ€νŠΈ( v_text > 0 )일 λ•Œ happy 동적 가쀑(증가) + surprise 경감, 이후 μž¬μ •κ·œν™”
Expand Down Expand Up @@ -297,7 +324,26 @@ def fuse_VA(audio_probs: Dict[str, float], text_score: float, text_magnitude: fl
neutral_factor = max(0.3, neutral_base_factor - extra_down)
else:
neutral_factor = neutral_base_factor
composite_score["neutral"] = composite_score.get("neutral", 0.0) * neutral_factor * 0.7

# 좩돌 감지: v_audio와 v_text λΆ€ν˜Έκ°€ λ‹€λ₯΄λ©΄ 감정 상쇄 λ°œμƒ
# 이 경우 neutral이 κ³ΌλŒ€ ν‰κ°€λ˜λ―€λ‘œ μΆ”κ°€ μ–΅μ œ
is_conflict = (v_audio * v_text) < 0
if is_conflict:
conflict_factor = 0.1 # 좩돌 μ‹œ neutral 0.1배둜 κ°•ν•˜κ²Œ μ–΅μ œ
else:
conflict_factor = 1.0

# μ—”νŠΈλ‘œν”Ό 기반 μ–΅μ œ: 감정 뢄포가 κ· μΌν• μˆ˜λ‘(μ—”νŠΈλ‘œν”Ό λ†’μŒ) neutral μΆ”κ°€ μ–΅μ œ
entropy = compute_entropy(composite_score)
if entropy > 0.8:
entropy_factor = 0.3 # 높은 μ—”νŠΈλ‘œν”Ό μ‹œ 0.3λ°°
elif entropy > 0.6:
entropy_factor = 0.6 # 쀑간 μ—”νŠΈλ‘œν”Ό μ‹œ 0.6λ°°
else:
entropy_factor = 1.0

# μ΅œμ’… neutral μ–΅μ œ: κΈ°μ‘΄ + 좩돌 + μ—”νŠΈλ‘œν”Ό
composite_score["neutral"] = composite_score.get("neutral", 0.0) * neutral_factor * 0.7 * conflict_factor * entropy_factor
composite_score["surprise"] = composite_score.get("surprise", 0.0) * 0.9

per_emotion_bps = _normalize_to_bps(composite_score)
Expand Down
4 changes: 2 additions & 2 deletions app/voice_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ async def _process_stt_and_nlp_background(self, file_content: bytes, filename: s
db = SessionLocal()
try:
logger.log_step("(비동기 μž‘μ—…) STT μž‘μ—… μ‹œμž‘", category="async")
deadline = time.monotonic() + 20.0
deadline = time.monotonic() + 30.0

# 1. STT 처리 (μŠ€λ ˆλ“œ ν’€μ—μ„œ μ‹€ν–‰ν•˜μ—¬ μ‹€μ œ 병렬 처리 κ°€λŠ₯)
file_obj_for_stt = BytesIO(file_content)
Expand All @@ -322,7 +322,7 @@ def __init__(self, content, filename):
try:
stt_result = await asyncio.wait_for(stt_coro, timeout=remaining)
except asyncio.TimeoutError:
print(f"STT νƒ€μž„μ•„μ›ƒ: voice_id={voice_id} after 20s")
print(f"STT νƒ€μž„μ•„μ›ƒ: voice_id={voice_id} after 30s")
logger.log_step("stt νƒ€μž„μ•„μ›ƒ", category="async")
mark_text_done(db, voice_id)
try_aggregate(db, voice_id)
Expand Down