음성인식

[Faster Whisper] 학습 없이 성능 향상이 되는 Hotwords란 뭘까?

곧미남 2025. 7. 17. 18:17

Faster Whisper 모델을 활용하면서 브랜드 고유 명사와 같은 삼성, 엘지, 투싼 등 이런 단어들은 전사 정확도가 낮은 것을 볼 수 있었다.

 

그래서, 모델을 개선하기 위한 방법은 음성 데이터를 수집하여 파인 튜닝을 해야한다. 하지만 매번 이런 고유 명사를 위해서 파인 튜닝하는 것은 비용 효율적이지 않다.

 

그래서 고안된 기능이 Hotwords이다.

 

오늘은 Faster Whisper 모델의 Hotwords 기능에 대해서 알아보자!


 

 Faster Whisper 모델의 코드를 보면 hotwords라는 변수를 가지고 있다.

 설명을 보면 모델에 어구에 대한 힌트를 제공한다고 되어 있다.

         hotwords:
            Hotwords/hint phrases to provide the model with. Has no effect if prefix is not None.

 

그런데, 더 상세히 코드를 보면, get_prompt라는 함수에 입력되어 사용이 된다.

 

 

get_prompt 함수를 살펴보면,

    def get_prompt(
        self,
        tokenizer: Tokenizer,
        previous_tokens: List[int],
        without_timestamps: bool = False,
        prefix: Optional[str] = None,
        hotwords: Optional[str] = None,
    ) -> List[int]:
        prompt = []

        if previous_tokens or (hotwords and not prefix):
            prompt.append(tokenizer.sot_prev)
            if hotwords and not prefix:
                hotwords_tokens = tokenizer.encode(" " + hotwords.strip())
                if len(hotwords_tokens) >= self.max_length // 2:
                    hotwords_tokens = hotwords_tokens[: self.max_length // 2 - 1]
                prompt.extend(hotwords_tokens)
            if previous_tokens:
                prompt.extend(previous_tokens[-(self.max_length // 2 - 1) :])

        prompt.extend(tokenizer.sot_sequence)

        if without_timestamps:
            prompt.append(tokenizer.no_timestamps)

        if prefix:
            prefix_tokens = tokenizer.encode(" " + prefix.strip())
            if len(prefix_tokens) >= self.max_length // 2:
                prefix_tokens = prefix_tokens[: self.max_length // 2 - 1]
            if not without_timestamps:
                prompt.append(tokenizer.timestamp_begin)
            prompt.extend(prefix_tokens)

        return prompt

 

핫워드가 “후보군 중에 해당 구문이 있으면 골라내는” 식으로 후처리되는 게 아니라, 디코더의 조건부 확률 분포 자체를 바꿔서 빔 서치가 그 문구를 더 높은 점수로 고려하도록 만드는 방식입니다.

if hotwords and not prefix:
    prompt.append(tokenizer.sot_prev)
    hotwords_tokens = tokenizer.encode(" " + hotwords.strip())
    hotwords_tokens = hotwords_tokens[: self.max_length // 2 - 1]
    prompt.extend(hotwords_tokens)
    …

1. 디코더 프롬프트에 hotwords 삽입하기

앞서 보신 get_prompt 함수에서, hotwords를 이렇게 디코더 입력 맨 앞에 토큰 시퀀스로 넣습니다:

  • 이 순간 “고유 명사”가 토큰 ID로 변환되어 디코더의 첫 컨텍스트로 주입됩니다.

2. 조건부 확률이 바뀐다

Transformer 디코더는 매 시점 t마다 조건부 확률을 계산합니다.

  • hotwords 토큰들이 prompt에 포함되면, 이들이 디코더의 쿼리·키·값 계산에 영향을 주어 "고유 명사"가 나올 확률이 상대적으로 올라갑니다.

3. 빔 서치 속 동작

빔 서치(beam search)는 이 수정된 확률 분포를 바탕으로 아래 과정을 반복합니다:

  1. 첫 토큰 선택
    • <|startoftranscript|> 다음에 나올 토큰 후보 중, hotwords prompt와 결합했을 때 점수가 높은 것부터 선택
  2. 후속 토큰 확장
    • 각 빔(beam)이 이전까지 선택된 시퀀스와 hotwords prompt, 그리고 audio context를 함께 고려
  3. 종료 조건
    • EOS 토큰 또는 max length 도달까지 탐색

결국 hotwords prompt가 후보 시퀀스 전체의 우선순위를 바꿔 주는 셈이라,
“후보 중에 hotword가 있으면 그걸 고른다”기보다는 “핫워드가 나오도록 확률을 조정한 상태에서빔 서치를 돌린다"고 이해하시면 됩니다.


 

4. 요약

  • 후보 점수 조정: hotwords가 prompt로 들어가면 디코더의 log-prob가 높아짐
  • 빔 서치 시 반영: 바뀐 확률 분포를 그대로 쓰는 빔 서치가 hotword 시퀀스를 더 잘 선택
  • 후처리 아님: 후에 후보군을 필터링하거나 재정렬하는 게 아니라, 생성 확률 자체를 bias하는 방식입니다.
 

위 원칙만 지키면 hotwords 가 디코더 컨텍스트를 침범해 버리는 상황 없이, 안정적으로 원하는 키워드가 우선적으로 디코딩되도록 활용할 수 있습니다.

 

 

읽어 주셔서 감사합니다.

반응형