본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 26. 02:18

내 전사(Transcript) 속의 유령: 왜 내 AI 회의 앱은 계속 'Thank you'라고 말했을까

요약

AI 회의 어시스턴트 개발 중 음성 인식 모델이 박수나 침묵 구간에서 'Thank you'를 반복적으로 생성하는 환각 현상을 분석합니다. 이는 모델이 학습 데이터의 패턴(박수 소리-감사 인사)을 잘못 학습하여 발생하는 문제임을 설명합니다.

핵심 포인트

  • 음성 모델은 비음성 구간(박수, 웃음, 침묵)에서 환각을 일으킬 수 있음
  • Whisper 등 최신 모델은 학습 데이터의 조건부 분포를 따름
  • 박수 소리와 'Thank you' 문구 사이의 강력한 통계적 연관성이 원인
  • 단순히 신뢰도가 낮은 구간을 삭제하는 것만으로는 해결이 어려움

저는 수동적인 AI 회의 어시스턴트를 만들고 있습니다. 이 앱은 녹음, 26개 언어 실시간 전사 (Transcription), 그리고 웹을 통한 실시간 주장 사실 확인 (Fact-check) 기능을 제공합니다. 전사 (Transcript) 데이터는 사용자가 신뢰하는 핵심 인터페이스입니다. 그래서 어느 날 밤 녹음 파일을 열었을 때 이것을 보고 저는 가슴이 철렁 내려앉았습니다.

12:21   Thank you.
12:27   Thank you.
13:16   Thank you.
...

아무도 "Thank you"라고 말하지 않았습니다. 단 한 번도요. 화자는 세대 정치에 대해 한창 열변을 토하고 있었습니다 ("Washington has become a cross between the land of the dead and the Golden Girls" — 제 앱이 완벽하게 포착한 실제 문구입니다). 그리고 이 날카로운 전사 (Transcript) 곳곳에는 수십 개의 유령 같은 Thank you가 흩어져 있었습니다.

저의 첫 반응은 다른 사람들과 마찬가지였습니다: 멍청한 ASR (Automatic Speech Recognition, 자동 음성 인식). 하지만 "멍청함"은 근본 원인이 아니었고, 저에게는 출시해야 할 제품이 있었습니다. 그래서 원인을 찾아 나섰습니다.

패턴

유령 같은 문장들은 무작위가 아니었습니다. 오디오와 대조해 보니 다음과 같은 패턴이 발견되었습니다: 모든 "Thank you"는 박수 소리, 웃음소리, 또는 문장 사이의 침묵 구간에서 발생했습니다. 모델은 단어를 잘못 들은 것이 아니었습니다. 들을 단어가 없을 때 단어를 만들어내고 있었던 것입니다.

그 단서가 이야기의 전부입니다.

음성 모델이 왜 "Thank you"를 환각 (Hallucinate) 하는가

이것은 현대 음성 인식 분야에서 가장 유명한 실패 사례 중 하나이며, 한 번 그 메커니즘을 이해하고 나면 계속 눈에 밟히게 됩니다.

Whisper, qwen3-asr 등과 같은 모델들은 YouTube, 팟캐스트, 강연, 강의 등 방대한 양의 실제 오디오 데이터로 학습됩니다. 이들은 조건부 분포 (Conditional distribution)를 학습합니다: 이 오디오가 주어졌을 때, 가장 가능성 높은 텍스트는 무엇인가? 오디오가 음성일 때 이 모델들은 매우 뛰어난 성능을 발휘합니다.

하지만 이 모델들은 비음성 (Non-speech) 상황에서 어떻게 해야 하는지는 제대로 배우지 못했습니다. 박수, 웃음, 침묵, 음악 등도 학습 데이터에 가득하며, 이들은 인간 전사자 (Transcriber)가 작성한 내용으로 라벨링(Labeling)되어 있습니다. 그렇다면 청중이 박수를 터뜨리는 강연의 마지막 순간에 인간은 무엇이라고 적을까요?

"Thank you."
"Thanks for watching."
"Thank you very much."

따라서 모델은 매우 강력한 연관 관계를 학습합니다: 박수 소리 → "Thank you." 박수 소리를 입력하면 모델은 무시하거나 아무것도 반환하지 않는 대신, 해당 음향 질감(acoustic texture)과 함께 본 적 있는 문구 중 가장 확률이 높은 단일 문구를 자신 있게 내뱉습니다. 이것은 제 코드의 버그가 아닙니다. 모델이 정확히 훈련받은 대로 수행하고 있는 것이며, 단지 아무도 큐레이션(curation)하지 않은 맥락에서 발생했을 뿐입니다.

이것은 "자신 있게 틀리는(confidently wrong)" 실패입니다. 출력 결과가 다른 모든 문장과 똑같이 보이기 때문에 가장 위험한 유형의 실패입니다.

내가 할 수 없는 것 (모두가 가장 먼저 제안하는 해결책)

당연한 본능은 "신뢰도(confidence)가 낮은 세그먼트를 그냥 버려라"입니다. 좋은 아이디어입니다. 하지만 한 가지 문제가 있습니다.

저는 WebSocket 프로토콜을 통해 실시간 ASR (Automatic Speech Recognition, 자동 음성 인식)을 스트리밍하고 있으며, 실제로 네트워크(wire)를 통해 무엇이 돌아오는지 확인해 보았습니다. 최종 전사(transcript) 이벤트는 다음과 같습니다:

{
  "type": "conversation.item.input_audio_transcription.completed",
  "transcript": "Thank you."
...

그게 전부입니다. 신뢰도(confidence)도 없습니다. 로그 확률(logprob)도 없습니다. no_speech_probability도 없습니다. 모델은 저에게 텍스트만 줄 뿐 그 외에는 아무것도 주지 않습니다. 모델이 숫자를 넘겨주지 않기 때문에 임계값(threshold)을 설정할 숫자 자체가 없습니다. 따라서 "신뢰도에 따른 필터링" 방식의 모든 솔루션은 시작하기도 전에 고려 대상에서 제외됩니다. 존재하지 않는 필드를 기준으로 설계를 하기 전에, 사용 중인 와이어 프로토콜(wire protocol)을 파악하는 것이 얼마나 중요한지 알 수 있는 대목입니다.

맞은 것 같지만 틀린 지렛대

두 번째 아이디어는 **음성 활동 감지 (VAD, Voice Activity Detection)**를 강화하는 것입니다. 제 세션 설정은 업스트림(upstream)에 비음성 구간을 얼마나 공격적으로 차단할지 알려줍니다:

"turn_detection": [
    "type": "server_vad",
    "threshold": 0.0,          // <- 최대한 허용적인 설정
...

threshold: 0.0은 "기본적으로 모든 것을 음성으로 취급하라"는 의미입니다. 이 값을 높이면 모델 자체의 VAD가 전사를 시도하기 전에 조용하고 에너지가 낮은 오디오를 거부하게 됩니다. 이는 침묵 구간 (silence-gap) 환각(문장 사이에 나타나는 유령 같은 문장들)을 제거할 수 있습니다.

하지만 여기에 함정이 있습니다. 박수 소리는 큽니다. 웃음소리도 큽니다. 에너지 기반의 VAD(Voice Activity Detection, 음성 활동 감지) 임계값은 박수치는 군중과 말하는 사람을 구분할 수 없습니다. 둘 다 침묵 임계값(silence floor)보다 훨씬 높기 때문입니다. 따라서 임계값을 높이는 것은 조용한 간극(gaps)에는 도움이 되지만, 이 모든 조사를 시작하게 만든 바로 그 스크린샷 사례들에는 아무런 도움이 되지 않습니다.

설상가상으로, 여기에는 가치(values)의 비용이 따릅니다. 제 앱에는 하나의 신성한 규칙이 있습니다. 바로 실제 음성을 절대 놓치지 않는 것입니다. VAD를 너무 높게 설정하면 목소리가 작은 참가자의 음성을 잘라내기(clipping) 시작합니다. 가짜 "Thank you."를 억제하기 위해 실제 문장을 희생하는 것은 나쁜 거래입니다. 따라서 VAD 튜닝은 기껏해야 조심스러운 보완책일 뿐, 결코 근본적인 해결책이 될 수 없습니다.

문제에 실제로 부합하는 해결책

만약 모델이 조작된 문구를 생성하고, 신뢰도(confidence)로 잡아낼 수 없으며, 음량(loudness)으로 차단할 수도 없다면... 이를 잡아낼 수 있는 유일하고 신뢰할 수 있는 지점은 **출력되는 과정(on the way out)**입니다. 텍스트가 생성된 후, 사용자의 기록의 일부가 되기 전 단계 말입니다.

이것이 바로 수년 전 Whisper 커뮤니티가 도달한 결론이며, 매우 상식적이고 지루할 정도로 명확한 방법입니다. 바로 **알려진 환각 문구 필터(known-hallucination phrase filter)**입니다.

그 구조는 다음과 같습니다:

struct TranscriptHallucinationFilter {
    // 엄격하게 선별된, 다중 단어로 구성된 비음성 사전 확률(non-speech priors).
    private let blocklist: Set<String> = [
...

타협할 수 없는 설계 결정 사항들은 다음과 같으며, 각 결정은 시행착오를 통해 얻어진 결과입니다:

타협할 수 없는 설계 결정 사항들은 다음과 같으며, 각 결정은 시행착오를 통해 얻어진 결과입니다:

  1. 정확히 일치해야 하며, 부분 일치는 절대 허용하지 않음 (Exact match, never substring). 독립적인 "Thank you."는 환각 (hallucination)입니다. 하지만 "Thank you for joining us, let's get started"는 실제 사람의 말입니다. 만약 포함 (contains) 여부로 매칭했다면, 저는 진짜 문장들을 삭제하기 시작했을 것입니다. 이는 제가 고치고 있는 버그보다 훨씬 더 심각한 버그입니다. 필터는 정규화된 (normalized) 전체 세그먼트가 차단 목록 (blocklist) 항목과 일치할 때만 작동합니다.

  2. 차단 목록을 엄격하게 시작할 것. 저는 신뢰도가 높은 다어구 (multi-word) 사전 확률 (priors)으로 목록을 구성하며, 단일 단어를 추가하고 싶은 유혹을 뿌리칩니다. "you"나 "okay"는 전형적인 환각이기도 하지만, 사람들이 실제로 단독으로 사용하는 말이기도 합니다. 의심스러울 때는 제외하십시오. 유령 같은 "okay" 하나가 빠져나가는 비용은 저렴하지만, 실제 문장을 삭제하는 비용은 그렇지 않습니다.

  3. 조용히 버리지 말고, 요란하게 버릴 것. 제 코드베이스에는 철칙이 있습니다: 실패는 요란해야 한다 (failures must be loud). 따라서 모든 삭제 작업은 내용이 없는 (content-free) 카운터를 증가시키고 로그를 남깁니다 (매칭된 차단 목록 인덱스만 남기며, 개인정보 보호를 위해 원문 텍스트는 절대 남기지 않습니다). 만약 제 필터가 실제 음성을 잡아먹기 시작한다면, 화난 사용자로부터 발견하는 대신 현장에서 그 비율이 상승하는 것을 즉시 확인할 수 있을 것입니다.

  4. 단 하나의 병목 지점 (chokepoint)에 배치할 것. 전사 (transcript) 이벤트가 저장된 세그먼트가 됨과 동시에 인사이트/사실 확인 (insight/fact-check) 엔진의 입력값이 되는 단 하나의 깔때기가 존재합니다. 필터는 그 함수가 실행되기 전, 즉 두 작업이 일어나기 전의 최상단에 위치합니다. 따라서 유령 같은 "Thank you."는 전사 데이터도, 다운스트림 (downstream) AI도 오염시키지 않습니다. 하나의 가드 (guard)로 전체를 커버합니다.

그리고 오디오 파일 자체는 절대 건드리지 않기 때문에, 녹음본은 신성하게 유지됩니다. 저는 기록에서 조작된 (fabricated) 문장만을 억제하는 것입니다. 만약 제가 틀렸더라도, 원래의 오디오가 바로 그곳에 있어 다시 재생해 볼 수 있습니다.

"문제를 해결했습니다"의 솔직한 버전

이것이 무엇인지에 대해 솔직해지고 싶습니다. 이것은 마법도 아니고, 완벽한 해결책도 아닙니다:

  • 가끔 누군가 실제로 말한 진짜 고립된 "Thank you"를 누락할 수도 있습니다. 회의 전사(Transcript)에서 단 한 번의 "감사합니다"를 놓치는 비용은 거의 0에 가깝습니다. 하지만 수십 개의 가짜 "Thank you"가 발생하는 비용은 실질적입니다. 쉬운 트레이드오프(Trade-off)입니다.
  • 현재는 영어 우선(English-first)입니다. 환각(Hallucinations)은 오디오의 주된 언어로 나타나는 경향이 있으므로, 차단 목록(Blocklist)은 언어별로 확장되어야 합니다. 이는 후속 과제이지, 진행을 막는 장애물은 아닙니다.
  • 장기적인 정답은 음성 부재 확률(No-speech probability)을 반환하는 모델이거나, 단어를 추측하는 대신 "[박수]"라고 태깅하는 음향 이벤트 분류기(Acoustic event classifier)일 수 있습니다. 하지만 그것들은 더 큰 변화를 요구합니다. 이 필터는 제가 지금 바로 출시할 수 있는 고효율(High-leverage), 저위험(Low-risk)의 변화입니다.

제가 계속해서 다시 배우고 있는 교훈은 이것입니다: 모델이 당혹스러운 행동을 할 때, "모델이 멍청하다"는 생각은 조사가 끝나는 지점이 아니라 조사가 시작되는 지점이라는 것입니다. 유령 같은 "Thank you"는 노이즈가 아니었습니다. 그것은 모델이 저에게, 매우 정확하게, 음성이 포함되지 않은 소리를 전달받았으며, 자신이 할 줄 아는 가장 인간적인 행동을 하고 있다는 것을 말해주고 있었던 것입니다.

모델은 감사를 표했습니다.

수동형 AI 회의 어시스턴트인 Faktum을 만들고 있습니다. 만약 여러분도 ASR 환각(Hallucinations)에 맞서 싸운 실전 경험이 있다면, 차단 목록(Blocklist)과 오탐(False-positive) 사이의 트레이드오프를 어떻게 처리했는지 꼭 듣고 싶습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0