Skip to content

Commit 69f7d22

Browse files
committed
chore: Notion 동기화 2026-07-14 19:00
1 parent e4b2e54 commit 69f7d22

2 files changed

Lines changed: 143 additions & 18 deletions

File tree

blog/.notion-sync.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
},
2323
"399e15b4-0359-80bc-a362-f310d7425d86": {
2424
"file": "blog/2026-07-10-07-faster-whisper-한국어-음성인식-최적화-경험-공유.md",
25-
"last_edited": "2026-07-14T07:41:00.000Z",
26-
"content_hash": "be65a485cd90f9dcb4b8759c9a6d6456e6532f26e89276d516992ed7178459cd",
25+
"last_edited": "2026-07-14T09:59:00.000Z",
26+
"content_hash": "bfcd2261edf52597eddfa93f052dce8fafda35a76cffe291fc43156541439fbf",
2727
"order": 4,
2828
"parent_id": null
2929
},

blog/2026-07-10-07-faster-whisper-한국어-음성인식-최적화-경험-공유.md

Lines changed: 141 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,155 @@ last_update:
1212

1313
---
1414

15-
16-
음성인식 최적화 경험은 해당 PoC 참고하여 재기재
15+
faster-whisper-large-v3 로 음성 인식 작업을 진행하였다.
1716

1817
<!--truncate-->
1918

20-
[https://doc.scenemaker.solbox.com/docs/poc/audio-bench/1](https://doc.scenemaker.solbox.com/docs/poc/audio-bench/1)
19+
최적화를 진행하였으며, 구체적 과정은
20+
21+
1. transcribe 실행. segment 결과 추출
22+
2. for 루프 검사, 결과물 필터하여 모델 최적화
23+
24+
라고 할 수 있다.
25+
26+
27+
---
28+
29+
### 1. transcribe
30+
31+
```python
32+
# poc-stt-bench/ib/audio/whisper/whisper_stt.py
33+
# Line 97
34+
35+
def _do_transcribe(audio_np: np.ndarray, language: str) -> tuple[list, float]:
36+
"""Call faster-whisper transcribe. Returns segments (list) + mean avg_logprob.
37+
38+
When segments are empty, logprob = -inf (automatic loss in dual comparison).
39+
"""
40+
segments_gen, _info = _model.transcribe(
41+
audio_np,
42+
language=language,
43+
beam_size=5,
44+
no_speech_threshold=0.6,
45+
log_prob_threshold=-1.0,
46+
compression_ratio_threshold=2.4,
47+
condition_on_previous_text=False,
48+
repetition_penalty=1.2,
49+
no_repeat_ngram_size=3,
50+
)
51+
segs = list(segments_gen)
52+
if not segs:
53+
return segs, float("-inf")
54+
return segs, sum(s.avg_logprob for s in segs) / len(segs)
55+
```
56+
57+
- 해당 파라미터 값 (beam_size, log_prob_threshold 등) 은, 패키지 함수 시그니처에 있는 것과 동일함을 확인
58+
59+
60+
### 2. 결과물 필터
61+
62+
2-1)
63+
64+
2-2)
65+
66+
2-3)
67+
68+
2-4)
69+
70+
2-5)
71+
72+
```python
73+
MIN_LOGPROB = -1.0 # drop if avg_logprob < this. catch-all for hallucinations
74+
MIN_SEG_S = 0.2 # drop segments shorter than this. preserves short interjections like "네"/"그렇죠"
75+
76+
# When a short speech is detected as a non-main language, run both ko and lid and compare logprob.
77+
# Avoids 1-2s segments in Korean content being misclassified as ja/zh.
78+
MAIN_LANG = "ko"
79+
SHORT_SEG_S = 3.0 # below this duration and LID != MAIN_LANG -> dual transcribe
80+
MIN_DUAL_LOGPROB = -0.6 # if both dual sides fall below this -> drop (hallucination/noise)
81+
LID_TRUST_PROB = 0.5 # LID prob below this + non-main lang -> force MAIN_LANG (LID itself untrustworthy)
82+
KO_MIN_HANGUL_RATIO = 0.3 # if Hangul ratio of ko transcription is below this -> drop. Cuts Whisper outputting kana/hanja hallucinations in ko mode
83+
84+
# poc-stt-bench/ib/audio/whisper/whisper_stt.py
85+
86+
87+
# 게이트1
88+
# Line 148
89+
# ── 1) VAD (raw)
90+
speech_ranges = vad.detect(raw_np, sr=TARGET_SR)
91+
log.info(f"audio {total_sec:.1f}s → VAD {len(speech_ranges)} speech segments")
92+
93+
# ── 2) Per-speech LID(raw) + ASR(denoised)
94+
all_segments: list[dict] = []
95+
for start_s, end_s in speech_ranges:
96+
s_idx = int(start_s * TARGET_SR)
97+
e_idx = int(end_s * TARGET_SR)
2198

22-
**4. 시스템 설계 + 환각 처리 > 4.1 Whisper 측**
99+
# Line 170
100+
# A low prob like 0.23 means ko/de/ja/zh are all comparable = the model doesn't know -> ko is the natural assumption
23101

24-
내용을 기재하면 됨.
102+
# 게이트 3- LID_TRUST_PROB
103+
if lang_code != MAIN_LANG and prob < LID_TRUST_PROB:
104+
log.info(f" LID {lang_code}={prob:.2f} < {LID_TRUST_PROB}{MAIN_LANG} 강제")
105+
lang_code = MAIN_LANG
106+
107+
# 게이트 4 — dual transcribe + MIN_DUAL_LOGPROB (-0.6)
25108

26-
- poc/poc-stt-bench 내부 코드 확인
27-
- 레퍼런스 글 확인 및 간단 정리
109+
# 2c) transcribe — dual compare (ko vs lid) when short speech + non-main lang
110+
chunk_dur = end_s - start_s
111+
if chunk_dur < SHORT_SEG_S and lang_code != MAIN_LANG:
112+
segs_main, lp_main = _do_transcribe(chunk_den, MAIN_LANG)
113+
segs_lid, lp_lid = _do_transcribe(chunk_den, lang_code)
114+
if max(lp_main, lp_lid) < MIN_DUAL_LOGPROB:
115+
log.info(f" dual [{_hms(start_s)}~{_hms(end_s)}|{chunk_dur:.1f}s] "
116+
f"lp({MAIN_LANG})={lp_main:.2f}, lp({lang_code})={lp_lid:.2f} → 양쪽 약함, drop")
117+
continue
118+
if lp_main >= lp_lid:
119+
segments_out, chosen_lang = segs_main, MAIN_LANG
120+
log.info(f" dual [{_hms(start_s)}~{_hms(end_s)}|{chunk_dur:.1f}s] "
121+
f"lp({MAIN_LANG})={lp_main:.2f} ≥ lp({lang_code})={lp_lid:.2f}{MAIN_LANG}")
122+
else:
123+
segments_out, chosen_lang = segs_lid, lang_code
124+
log.info(f" dual [{_hms(start_s)}~{_hms(end_s)}|{chunk_dur:.1f}s] "
125+
f"lp({lang_code})={lp_lid:.2f} > lp({MAIN_LANG})={lp_main:.2f}{lang_code}")
126+
else:
127+
segments_out, _ = _do_transcribe(chunk_den, lang_code)
128+
chosen_lang = lang_code
129+
130+
# Line 197
131+
for seg in segments_out:
132+
text = (seg.text or "").strip()
133+
if not text:
134+
continue
135+
abs_start = float(seg.start) + start_s
136+
abs_end = float(seg.end) + start_s
137+
dur = abs_end - abs_start
138+
# 게이트 2 — MIN_LOGPROB (-1.0)
139+
if seg.avg_logprob < MIN_LOGPROB:
140+
log.info(f" drop [{_hms(abs_start)}~{_hms(abs_end)}|{chosen_lang}] lp={seg.avg_logprob:.2f} | {text[:30]}")
141+
continue
142+
if dur < MIN_SEG_S:
143+
log.info(f" drop [{_hms(abs_start)}~{_hms(abs_end)}|{chosen_lang}] dur={dur:.2f}s | {text[:30]}")
144+
continue
145+
# 게이트 5 — 한글 char 비율 게이트 (30%)
146+
if chosen_lang == MAIN_LANG:
147+
ratio = _hangul_ratio(text)
148+
if ratio < KO_MIN_HANGUL_RATIO:
149+
log.info(f" drop [{_hms(abs_start)}~{_hms(abs_end)}|{chosen_lang}] hangul={ratio:.0%} | {text[:30]}")
150+
continue
151+
# 통과한 것만 채택
152+
all_segments.append({
153+
"start": abs_start,
154+
"end": abs_end,
155+
"text": text,
156+
"speaker": None,
157+
"language": chosen_lang,
158+
})
159+
160+
161+
```
28162

29-
| 최적화 | 무엇 | 효과 |
30-
| --- | --- | --- |
31-
| **Strategy 2** | VAD/LID=raw, STT=denoise | 감지 정확도 + 전사 품질 둘 다 |
32-
| **언어 티어링** | Whisper 못하는 언어 버림 (nl/zh/vi 오판 차단) | 환각 제거 |
33-
| **LID 신뢰도 게이트** | 저신뢰 비-한국어 → 한국어 강제 | 한국어 특화 |
34-
| **logprob 필터** | 확신 낮은 세그먼트 drop | 환각 제거 |
35-
| **VAD 튜닝** | 단음절 버림, 발화 단위 분할 | 경계 오류 방지 |
36-
| **LLM 후처리 교정** | Qwen 1차 교정 (물고지→물고기) | 오탈자·동음이의 |
37163

38-
### 마무리
39164

40165
---
41166

0 commit comments

Comments
 (0)