수달이네 기술 블로그

7. Whisper Finetuning 1 (Feature Extractor & Tokenizer) 본문

프로젝트

7. Whisper Finetuning 1 (Feature Extractor & Tokenizer)

슬픈 수달이 2026. 4. 18. 20:02

https://velog.io/@mino0121/NLP-OpenAI-Whisper-Fine-tuning-for-Korean-ASR-with-HuggingFace-Transformers

  • 위 블로그 글을 참고함

오디오 소스 리샘플링

16kHz의 sampling rate로 resampling

  • Whisper모델은 기본적으로 16kHz의 샘플링레이트로 된 오디오를 받는다.
    • 잘못된 샘플링 레이트로 들어올 때, 재생속도, 음높이 등이 왜곡되어 제대로 인식하지 못한다.
  • 따라서 같은 샘플로 만들기 위해 밀도를 변환한다.
    • 보간(interpolation): 샘플링 레이트를 줄일 때, 단순히 버리면 음질이 깨진다. 오히려 늘릴 때는 그 사이 값을 어떻게 넣어야 할지 정해야한다. 따라서 다양한 방법을 이용하는데.
      • 선형보간(Linear) - 두 값 사이의 중간값을 넣음.
      • 다항식 보간(Polynomial) - 주변 여러 점을 고려해서 곡선으로 추정(부드러운 신호에 적합)
      • Sinc 보간 - sinc함수(sin(x)/x)를 이용해 추정(오디오 신호는 사인파의 합으로 이루어져 적합)

Librosa

Python오디오 분석 라이브러리

  • Whisper 파인튜닝 파이프라인에서 오디오 파일을 로드하고 리샘플링하는 역할을 한다(Sinc보간)
# 1. 리샘플링 - datasets가 담당
dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))

Feature Extractor

다른 데이터와 달리 whisper에선 pre-processing과 Feature Extractor가 하나로 적용 된다.

  • Pre-processing: raw데이터를 모델에 넣기 적합한 형태로 변환
  • Feature extractor: 전처리된 데이터에서 의미있는 특징을 추출하는 것.

Whisper Feature Extractor

첫 번째, 위에서 받은 16kHz의 샘플링레이트를 가진 오디오를 받아 padding및 truncating을 진행한다.

만약 30초의 배치 길이를 가지고 있을 경우.

  • padding: 배치의 크기가 30초보다 적을 경우 [침묵]데이터를 넣어 30초로 늘려줌.
  • truncating: 배치의 크기가 30초보다 클 경우 데이터를 잘라 30초로 줄여줌.

위처럼 길이를 맞추었을 때. 보통의 오디오 모델은 sequence가 패딩된 위치와 self-attention이 어느부분을 무시해야하는지에 대한 attention mask를 필요하다.

  • 그러나 whisper는 스스로 침묵 부분을 무시하도록 추론하게 학습되었다.

두 번째, 위에서 전처리된 데이터를 가지고 log-Mel spectrogram으로 변환해준다.

Spectrogram: 주파수 신호를 시각적으로 나타낸 신호

진폭 ↑
      /\    /\  /\
──── /──\──/──\/──\시간 →
    /    \/
  • 원래 주파수 신호는 위와 같이 오디오를 파동으로 표현한다.
    • 음높이가 언제 나오는 지에 대한 정보를 확인하기 힘들다.
주파수 ↑
고음  |  ░░░▓▓░░░░░
중음  |  ▓▓▓▓▓▓░░░
저음  |  ▓▓░░░▓▓▓▓
      └──────────── 시간 →
  • 그것을 위와 같이 시간과 주파수, 강도를 2D로 한눈에 표현한 것이다.

Mel: 선형적인 주파수 Hz는 사람이 인식하기에 힘들다

  • 사람은 저주파수 차이는 잘 구분하나, 고주파수 차이는 잘 구분하지 못한다.(마치 log와 같음)
    • 해당 청각 특성을 반영한 주파수 스케일이 Mel이다.
선형:  100  200  300  400  500 ... 10000 Hz
Mel:   100  190  270  340  400 ...  2840 Mel
       ↑저주파 세밀하게    고주파 압축↑
  • 저주파는 세밀하게 표현하고, 고주파는 압축하여 표현한다.
  • 여기에 추가적으로 Log값을 취해 사람 귀에 더 맞게 변환한다.

전체적으로 오디오 파형을 푸리에 변환하여 주파수별로 강도를 추출한 후 Log-Mel을 적용해 나타낸것이다.

위의 두가지 기능을 Whisper Feature Extractor가 하는 일이다.

from transformers import WhisperFeatureExtractor
# 파인튜닝을 진행하고자 하는 모델의 feature extractor를 로드
feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-base")
  • whisper모델의 featureExtractor를 불러온다.

Tokenizer

from transformers import WhisperTokenizer
# 파인튜닝을 진행하고자 하는 모델의 tokenizer를 로드
tokenizer = WhisperTokenizer.from_pretrained("openai/whisper-base", language="Korean", task="transcribe")
  • 토크나이저는 데이터를 토큰 단위로 나누고 모델이 이해할 수 있는 숫자열로 변환한다.

Whisper Tokenizer는 Special token을 부여한다

  • 문장의 시작<|startoftranscript|>, 언어<|ko|>, 태스크<|transcribe|>, 설정값(타임스탬프 없음)<|notimestamps|>, 문장의 끝<|endoftext|>
input_str = "저는 서울중앙지검 지능범죄수사팀 최인호 검사입니다."
labels = tokenizer(input_str).input_ids
decoded_with_special = tokenizer.decode(labels, skip_special_tokens=False)
decoded_str = tokenizer.decode(labels, skip_special_tokens=True)

print(f"Input:{input_str}")
print(f"Decoded w/ special:{decoded_with_special}")
print(f"Decoded w/out special:{decoded_str}")
print(f"Are equal:{input_str == decoded_str}")

# Input: 저는 서울중앙지검 지능범죄수사팀 최인호 검사입니다.
# Decoded w/ special: <|startoftranscript|><|ko|><|transcribe|><|notimestamps|>저는 서울중앙지검 지능범죄수사팀 최인호 검사입니다.<|endoftext|>
# Decoded w/out special: 저는 서울중앙지검 지능범죄수사팀 최인호 검사입니다.
# Are equal: True
  • 이후 문장을 토큰으로 분리하고
  • 해당 토큰들을 ID로 변환한다.

통합

Feature Extractor와 Tokenizer를 WhisperProcessor클래스로 통합해준다.

from transformers import WhisperProcessor

processor = WhisperProcessor.from_pretrained("openai/whisper-base", language="Korean", task="transcribe")
  • 번잡한 과정없이 훈련과정에서 processor하나의 객체로 통합하여 간결하게 만든다.