본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로AI › 언어 모델 › 응용

WhisperX 음성 인식 품질 개선과 모델 파인튜닝

목차

WhisperX 음성 인식 품질 개선과 모델 파인튜닝

회의 녹음에서 “세프트리악손”을 다른 단어로 받아쓰는 문제와, 맞게 받아쓴 단어의 시작 시간이 틀리는 문제는 원인이 다르다. 전자는 Whisper 계열 음성 인식(ASR), 후자는 강제 정렬(forced alignment)을 먼저 살펴야 한다. 화자가 바뀌었는데 이름표가 그대로라면 화자 분리(diarization) 단계의 문제다.

원본 메모는 initial_prompt, Beam Search, VAD, 화자 수 제한을 조정하고, 그래도 인식이 부족하면 Whisper 모델을 파인튜닝해 CTranslate2로 변환하는 두 경로를 제시했다. 아래에서는 각 설정이 어느 단계에 영향을 주는지 분리해서 정리한다.

flowchart LR
  A[녹음 파일] --> B[VAD: 발화 구간 찾기]
  B --> C[Whisper: 문장 전사]
  C --> D[언어별 정렬 모델: 단어 시각]
  B --> E[화자 분리 모델: 발화자 구간]
  D --> F[단어와 화자 결합]
  E --> F
  F --> G[자막·전사 결과]

이 흐름에서 Whisper 파인튜닝은 C의 전사 모델을 바꾸는 일이다. D의 단어 시각이나 E의 화자 분리 품질이 자동으로 좋아지는 것은 아니다. WhisperX 공식 README도 전사, 정렬, 화자 배정을 별도 단계로 보여준다.

먼저 실패 유형을 기록하기

모델을 바꾸기 전에 짧은 검증용 녹음을 고른다. 조용한 단일 화자, 잡음이 있는 회의, 전문 용어, 숫자·금액, 두 명이 겹쳐 말한 구간을 따로 포함한다. 사람이 확인한 정답 전사와 화자·시간 라벨을 만들고, 동일한 음성 파일에서 한 번에 한 설정만 바꿔 비교한다.

증상먼저 볼 단계확인 방법
의학 용어를 다른 단어로 전사Whisper 디코딩 또는 학습정답 전사와 단어 비교
발화 시작·끝을 놓침VAD잘린 구간을 원음과 대조
단어는 맞는데 자막 시각이 틀림강제 정렬선택한 단어의 시작·끝 시간 확인
A의 말을 B에게 붙임화자 분리와 단어 배정화자 전환 구간 수동 확인

전사 오류만 숫자로 보고 싶다면 WER = (대체 + 삭제 + 삽입) / 정답 단어 수를 쓸 수 있다. 한국어는 띄어쓰기 기준에 따라 WER이 크게 바뀔 수 있으므로 문자 오류율(CER)과 도메인 용어 정확도도 같이 본다. 숫자·고유명사처럼 실제 사용에 중요한 항목은 별도로 세어야 한다. 화자 분리의 평가는 정답 화자 라벨과 대조해 혼동, 누락, 잘못 검출된 시간을 본다. pyannote.metrics는 이 항목을 포함한 DER 계산 방법을 제공한다.

예를 들어 가상의 정답 100어절에서 대체 8개, 삭제 4개, 삽입 3개가 발생했다면 WER은 (8 + 4 + 3) / 100 = 15%다. 설정 변경 뒤 WER이 13%로 내려가더라도 전문 용어 오류가 늘었다면 의료 자막 용도로는 개선이라고 단정하기 어렵다. 같은 정규화 규칙으로 정답과 예측을 비교하고, 화자 분리 오류율(DER)은 전사 오류율과 별도 표에 기록한다. VAD가 짧은 발화를 놓치면 WER과 시간 누락이 동시에 악화될 수 있으므로 해당 구간의 원음을 확인한다.

1. 재학습 없이 전사 옵션 조정

원본 메모는 전문 용어를 initial_prompt로 전달하고, beam_size=5, patience=1.0, temperatures=(0.0, 0.2, 0.4)를 비교하는 예시를 남겼다. initial_prompt는 단어 후보를 알려주는 힌트이며 반드시 그 단어를 출력시키는 사전은 아니다. Beam Search는 온도가 0일 때 적용되는 탐색 설정이고, 온도를 올리는 단계는 디코딩이 실패 기준에 걸렸을 때의 대안이다. 빔을 늘리면 계산량도 늘어나므로 같은 녹음에서 정확도와 처리 시간을 함께 비교한다.

현재 WhisperX load_model 코드는 asr_options를 모델을 불러올 때 받는다. 원본의 model.transcribe(audio, asr_options=...) 형태는 현재 transcribe 인자 목록에 없어서 그대로 옮기지 않았다. 설치한 WhisperX 버전의 API를 확인해 적용한다.

import whisperx

device = "cuda"
audio = whisperx.load_audio("sample.wav")
model = whisperx.load_model(
    "large-v3",
    device,
    compute_type="float16",
    language="ko",
    asr_options={
        "initial_prompt": "세프트리악손, 아세트아미노펜, 심근경색, 바이탈",
        "beam_size": 5,
        "patience": 1.0,
        "temperatures": [0.0, 0.2, 0.4],
    },
)
result = model.transcribe(audio, batch_size=8)
print(result["segments"])

이 코드는 API 형태를 보여주는 예시이며 이 저장소에서 실행한 결과가 아니다. GPU 메모리가 부족하면 배치 크기를 낮추고, CPU 사용 시에는 장치와 compute_type을 해당 환경에 맞춰 바꾼다. 같은 용어가 잘못 인식되는지, 맞지 않는 곳에 프롬프트 단어가 끼어드는지 함께 확인한다. 현재 CLI에는 전문 용어 힌트용 --hotwords도 있으므로 설치 버전에 따라 비교할 수 있다.

VAD 임계값

VAD(Voice Activity Detection)는 발화 구간을 골라 전사 모델에 넘긴다. 문장 시작이 잘리면 Whisper가 그 음절을 복원할 수 없다. 원본 메모는 잡음이 많은 녹음에서 VAD 임계값 조정을 제안했지만 고정된 추천 수치는 남기지 않았다. 현재 WhisperX는 vad_options의 vad_onset, vad_offset을 load_model()에 전달할 수 있다. CLI 인자 설명에서 값과 의미를 확인한다.

model = whisperx.load_model(
    "large-v3", device, compute_type="float16", language="ko",
    vad_options={"vad_onset": 0.50, "vad_offset": 0.36},
)

이 값은 기본값 부근의 비교 출발점이다. 임계값을 낮추면 짧고 작은 발화를 더 잡을 수 있지만 소음을 발화로 잘못 잡을 가능성도 있다. 잘린 음절과 무음 구간의 거짓 전사를 원음으로 대조해 조정한다. VAD를 고쳐도 이미 정확히 잡힌 구간 안의 전문 용어를 학습시키는 효과는 없다.

2. 단어 시각과 화자 분리를 따로 확인

WhisperX는 전사문과 오디오를 언어별 정렬 모델에 넣어 단어별 시각을 붙인다. 한국어도 사용하는 정렬 모델과 데이터에 따라 결과를 검토해야 한다. 숫자·통화 기호처럼 정렬 모델의 문자 집합에 없는 표현은 시각이 빠질 수 있고, 겹쳐 말하는 구간의 정렬·화자 분리는 어렵다고 WhisperX가 제한 사항에 명시한다.

align_model, metadata = whisperx.load_align_model(
    language_code=result["language"], device=device
)
aligned = whisperx.align(result["segments"], align_model, metadata, audio, device)

원본 메모의 “화자 수가 2명인 경우”는 다음처럼 화자 분리 단계에 적용한다. 화자 수를 잘못 고정하면 실제 화자를 합치거나 없는 화자를 만들 수 있으므로, 확실할 때만 제한한다. 화자 분리는 추가 모델과 해당 모델의 접근 동의가 필요할 수 있다. 토큰은 코드에 적지 않고 환경 변수로 읽는다.

import os
from whisperx.diarize import DiarizationPipeline

diarizer = DiarizationPipeline(token=os.environ["HF_TOKEN"], device=device)
speaker_segments = diarizer(audio, min_speakers=2, max_speakers=2)
with_speakers = whisperx.assign_word_speakers(speaker_segments, aligned)

HF_TOKEN은 예시 환경 변수 이름이다. WhisperX README에 따르면 화자 분리 모델의 사용 동의와 접근 토큰이 필요하다. 전사는 맞는데 화자가 틀린 상황에서 Whisper만 파인튜닝하는 것은 오류 원인에 맞지 않는다.

3. Whisper 전사 모델을 파인튜닝할 때

설정을 바꿔도 전문 용어와 발음에서 일관되게 오류가 남고, 정답이 확인된 음성 데이터가 있다면 Whisper 모델 자체의 파인튜닝을 검토한다. 원본 메모는 .wav와 대응하는 .txt 라벨을 모으고, transformers와 peft의 LoRA로 학습한 뒤 CTranslate2 형식으로 변환하는 흐름을 적었다. “몇십~몇백 시간”은 원본의 데이터 수집 방향일 뿐, 필요한 최소 시간이나 성능 보증치는 아니다. 데이터 품질과 발화 조건이 더 중요하다.

flowchart LR
  A[동의받은 음성·정답 전사] --> B[화자·녹음 단위 분리]
  B --> C[Whisper 기준선 평가]
  C --> D[Transformers에서 학습]
  D --> E[검증 세트 WER·CER·용어 평가]
  E --> F[LoRA라면 가중치 병합]
  F --> G[CTranslate2 변환]
  G --> H[WhisperX에 로드해 전체 파이프라인 재평가]
  1. 데이터 준비: 음성 파일과 정확한 전사문을 연결하고, 언어·길이·화자·녹음 장치를 기록한다. 같은 녹음 또는 화자의 잘린 구간이 학습과 평가에 동시에 들어가지 않게 나눈다. 음성에 개인 정보가 있으면 수집 동의, 접근 범위, 보존 기간을 정하고 외부 모델 저장소에 공개하지 않는다.
  2. 기준선 기록: 파인튜닝 전 모델로 WER·CER·전문 용어 오류·처리 시간을 측정한다. 평가 문장을 학습 데이터에 다시 넣지 않는다.
  3. Whisper 학습: Hugging Face의 Whisper 파인튜닝 안내는 오디오 전처리, 라벨, Seq2Seq 학습과 WER 평가 흐름을 보여준다. 원본에서 언급한 LoRA는 선택 사항이며, 적용 모듈과 학습 설정은 모델 구조·PEFT 버전에 맞춰 검증한다.
  4. 변환 전 병합: LoRA 어댑터만 저장했다면 기본 Whisper 가중치와 합쳐 완전한 Transformers 모델 디렉터리를 만든다. 어댑터 디렉터리만 CTranslate2 변환기에 넣지 않는다. PEFT 문서는 merge_and_unload()와 제약을 설명한다.
  5. 변환·재평가: 병합한 모델을 CTranslate2로 변환하고 WhisperX에서 로드한다. 변환·양자화 뒤에도 같은 평가 세트를 다시 돌린다. 정렬 모델과 화자 분리 모델은 별개이므로 타임스탬프와 화자 품질도 확인한다.

원본의 변환 명령은 다음처럼 병합 완료된 모델을 가리킬 때 사용한다. float16은 해당 장치와 배포 환경에서 지원되는지 확인한다. 파일 이름과 변환 옵션은 설치된 faster-whisper의 변환 안내를 기준으로 조정한다.

ct2-transformers-converter \
  --model ./whisper-merged \
  --output_dir ./whisper-ct2 \
  --quantization float16
custom_model = whisperx.load_model("./whisper-ct2", device, compute_type="float16")
custom_result = custom_model.transcribe(audio, batch_size=8)

변환된 모델 디렉터리에 필요한 토크나이저와 전처리 설정이 있는지도 확인한다. 로컬 배포를 위해 관련 파일을 함께 보관하거나 변환기의 --copy_files 옵션을 사용한다. 실제 디렉터리의 파일 이름을 확인하고 지정해야 한다. 학습 과정과 변환 과정을 거쳤다는 사실만으로 품질 향상을 주장하지 않는다. 기준선과 같은 음성에서 전사, 단어 시각, 화자 라벨, 처리 비용을 다시 비교해야 한다.

참고 자료

같은 카테고리의 글