🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Qwen3-TTS-Tokenizer-12Hz 분석 - 슬라이드 1/7
A

AI Generated

2026. 2. 2. · 6 Views

Qwen3-TTS-Tokenizer-12Hz 분석

음성 합성 모델의 핵심인 Tokenizer가 어떻게 텍스트와 음성을 숫자로 변환하는지 살펴봅니다. 12Hz 프레임 레이트의 의미와 실제 코드 구조를 초급자도 이해할 수 있도록 친절하게 설명합니다.


목차

  1. 음성을_숫자로_변환하기
  2. Tokenizer의_역할
  3. modeling_qwen3_tts_tokenizer_v2_분석
  4. 12Hz_프레임_레이트의_의미
  5. Encode와_Decode_프로세스
  6. 실전_Tokenizer_직접_사용하기

1. 음성을 숫자로 변환하기

김개발 씨는 최근 회사에서 AI 음성 합성 프로젝트에 투입되었습니다. "음성을 어떻게 컴퓨터가 이해하는 형태로 바꾸죠?" 선배 박시니어 씨에게 물었더니, "그게 바로 Tokenizer의 첫 번째 역할이야"라는 답이 돌아왔습니다.

음성의 디지털 변환은 연속적인 아날로그 신호를 컴퓨터가 처리할 수 있는 이산적인 숫자 시퀀스로 바꾸는 과정입니다. 마치 아날로그 시계의 시침이 부드럽게 움직이는 것을 디지털 시계가 1초 단위로 끊어서 표시하는 것과 같습니다.

TTS 모델에서는 텍스트와 음성 모두 이런 숫자 변환 과정을 거칩니다.

다음 코드를 살펴봅시다.

import numpy as np

# 연속적인 음성 신호를 이산적인 토큰으로 변환
def audio_to_tokens(audio_waveform, sample_rate=24000, frame_rate=12):
    # 프레임당 샘플 수 계산
    samples_per_frame = sample_rate // frame_rate  # 24000 / 12 = 2000

    # 총 프레임 수 계산
    num_frames = len(audio_waveform) // samples_per_frame

    # 각 프레임을 토큰 ID로 변환 (실제로는 양자화 과정)
    tokens = []
    for i in range(num_frames):
        frame = audio_waveform[i * samples_per_frame:(i + 1) * samples_per_frame]
        token_id = quantize_frame(frame)  # 프레임을 코드북에서 가장 가까운 토큰으로
        tokens.append(token_id)

    return tokens

김개발 씨는 입사 6개월 차 주니어 개발자입니다. AI 팀에 합류한 첫날, 그는 TTS라는 낯선 세계에 발을 들였습니다.

Text-to-Speech, 텍스트를 음성으로 바꾸는 기술입니다. 하지만 그 첫 번째 관문에서 벽에 부딪혔습니다.

"음성이 어떻게 숫자가 되는 거죠?" 김개발 씨의 질문에 박시니어 씨가 빙긋 웃었습니다. "그거 알면 TTS의 절반은 이해한 거야." 우리가 듣는 소리는 공기의 진동입니다.

이 진동은 연속적인 파형으로, 마치 바다의 물결처럼 끊임없이 이어집니다. 하지만 컴퓨터는 연속적인 것을 다루지 못합니다.

오직 0과 1, 그리고 그 조합인 숫자만 이해할 수 있습니다. 그래서 샘플링이라는 과정이 필요합니다.

연속적인 파형을 일정한 간격으로 잘라서 각 순간의 값을 기록하는 것입니다. 마치 초고속 카메라가 움직이는 물체를 빠르게 연속 촬영하는 것과 같습니다.

일반적인 오디오는 초당 24,000번 또는 44,100번 샘플링합니다. 이것을 샘플 레이트라고 부릅니다.

24kHz라면 1초에 24,000개의 숫자가 생기는 셈입니다. 상당히 많은 양이지요.

하지만 TTS 모델에서는 여기서 한 단계 더 나아갑니다. 24,000개의 숫자를 그대로 다루면 너무 복잡하니까, 여러 샘플을 묶어서 하나의 토큰으로 표현합니다.

이것이 바로 12Hz 프레임 레이트의 핵심입니다. 12Hz라는 것은 1초에 12개의 토큰을 만든다는 의미입니다.

24,000개의 샘플을 12개의 토큰으로 압축하는 것이지요. 각 토큰은 약 2,000개의 샘플 정보를 담고 있습니다.

위의 코드를 보면 이 과정이 명확해집니다. sample_rate를 frame_rate로 나누면 프레임당 샘플 수가 나옵니다.

2,000개의 샘플이 하나의 프레임이 되고, 이 프레임이 양자화를 거쳐 하나의 토큰 ID로 변환됩니다. 양자화는 무한한 가능성을 유한한 선택지로 줄이는 과정입니다.

마치 무한한 색상을 256가지 팔레트로 표현하는 것과 같습니다. Qwen3-TTS-Tokenizer는 이런 방식으로 음성을 효율적으로 압축합니다.

박시니어 씨가 정리해주었습니다. "결국 TTS 모델이 다루는 것은 텍스트 토큰과 음성 토큰이야.

둘 다 숫자 시퀀스지. 모델은 텍스트 숫자를 받아서 음성 숫자를 출력하는 거야." 김개발 씨는 고개를 끄덕였습니다.

복잡해 보이던 음성 합성의 첫 번째 퍼즐 조각이 맞춰지는 느낌이었습니다.

실전 팁

💡 - 샘플 레이트와 프레임 레이트를 혼동하지 마세요. 샘플 레이트는 원본 오디오, 프레임 레이트는 토큰 생성 속도입니다.

  • 12Hz는 상당히 낮은 프레임 레이트지만, 양자화된 토큰이 풍부한 정보를 담고 있어 고품질 음성 재현이 가능합니다.

2. Tokenizer의 역할

"그런데 Tokenizer가 정확히 뭘 하는 건가요?" 김개발 씨가 다시 물었습니다. 박시니어 씨는 화이트보드에 그림을 그리기 시작했습니다.

"Tokenizer는 번역가야. 사람의 언어를 기계의 언어로 바꾸는 번역가."

Tokenizer는 입력 데이터를 모델이 이해할 수 있는 토큰 시퀀스로 변환하고, 모델의 출력을 다시 원래 형태로 복원하는 양방향 변환기입니다. TTS Tokenizer는 특별히 텍스트와 음성 두 가지 모달리티를 모두 처리해야 하는 독특한 특성을 가집니다.

입구와 출구를 모두 관리하는 공항의 출입국 심사관과 같습니다.

다음 코드를 살펴봅시다.

class TTSTokenizer:
    """TTS 모델을 위한 통합 토크나이저"""

    def __init__(self, text_vocab_size=151936, audio_vocab_size=65536):
        self.text_vocab_size = text_vocab_size    # 텍스트 토큰 개수
        self.audio_vocab_size = audio_vocab_size  # 오디오 토큰 개수

        # 특수 토큰 정의
        self.bos_token_id = 151643  # 시작 토큰
        self.eos_token_id = 151645  # 종료 토큰
        self.audio_bos_token_id = 151668  # 오디오 시작
        self.audio_eos_token_id = 151669  # 오디오 종료

    def encode_text(self, text):
        """텍스트를 토큰 ID 시퀀스로 변환"""
        # 실제로는 BPE 등의 알고리즘 사용
        return self._tokenize(text)

    def decode_audio(self, audio_tokens):
        """오디오 토큰을 파형으로 변환"""
        return self._dequantize(audio_tokens)

박시니어 씨의 비유가 김개발 씨의 머릿속에 그림을 그렸습니다. Tokenizer는 번역가입니다.

하지만 보통 번역가와는 조금 다릅니다. 일반적인 NLP에서 Tokenizer는 텍스트만 다룹니다.

"안녕하세요"라는 문장을 [1523, 4521, 892]와 같은 숫자 배열로 바꾸는 것이지요. 하지만 TTS Tokenizer는 더 복잡한 임무를 수행합니다.

입력 측면에서는 텍스트를 토큰으로 변환합니다. "오늘 날씨가 좋습니다"라는 문장이 들어오면, 이를 모델이 이해할 수 있는 숫자 시퀀스로 바꿉니다.

여기까지는 일반 Tokenizer와 같습니다. 출력 측면에서는 오디오 토큰을 실제 음성 파형으로 변환합니다.

모델이 생성한 숫자들을 우리 귀로 들을 수 있는 소리로 바꾸는 것입니다. 이 과정을 디코딩 또는 디양자화라고 부릅니다.

위의 코드에서 두 가지 주요 메서드를 볼 수 있습니다. encode_text는 텍스트를 토큰으로 바꾸고, decode_audio는 오디오 토큰을 파형으로 바꿉니다.

마치 공항에서 출국 심사(encode)와 입국 심사(decode)를 담당하는 것과 같습니다. 주목할 점은 **어휘 크기(vocab_size)**입니다.

텍스트 토큰은 151,936개, 오디오 토큰은 65,536개의 어휘를 가집니다. 이 숫자들은 각각의 코드북 크기를 나타냅니다.

특수 토큰도 중요합니다. bos_token_id는 시퀀스의 시작을, eos_token_id는 끝을 표시합니다.

오디오에도 별도의 시작/종료 토큰이 있어서 모델이 텍스트 영역과 오디오 영역을 구분할 수 있습니다. "이해됐어요!

Tokenizer가 입력과 출력 양쪽의 관문을 지키는 거군요." 김개발 씨가 말했습니다. 박시니어 씨가 고개를 끄덕였습니다.

"맞아. 그리고 이 관문을 얼마나 잘 설계하느냐가 모델 성능에 큰 영향을 미쳐.

Qwen3-TTS가 12Hz를 선택한 것도 다 이유가 있는 거야."

실전 팁

💡 - encode와 decode는 서로 역함수 관계입니다. encode로 변환한 것은 decode로 복원할 수 있어야 합니다.

  • 특수 토큰(BOS, EOS)은 시퀀스 경계를 명확히 하여 모델이 생성을 언제 시작하고 끝내야 하는지 알려줍니다.

3. modeling qwen3 tts tokenizer v2 분석

김개발 씨는 드디어 실제 코드를 열어보았습니다. modeling_qwen3_tts_tokenizer_v2.py 파일입니다.

"와, 생각보다 구조가 깔끔하네요." 박시니어 씨가 옆에서 코드를 함께 살펴보았습니다.

Qwen3TTSTokenizerV2 클래스는 Hugging Face의 PreTrainedModel을 상속받아 구현된 TTS 전용 토크나이저입니다. 핵심은 QwenCodec이라는 오디오 코덱을 내장하고 있어서 텍스트 처리와 오디오 양자화/역양자화를 하나의 클래스에서 모두 처리한다는 점입니다.

모델과 긴밀하게 통합되어 end-to-end 학습이 가능한 구조입니다.

다음 코드를 살펴봅시다.

class Qwen3TTSTokenizerV2(PreTrainedModel):
    """Qwen3 TTS를 위한 통합 토크나이저 모델"""

    config_class = Qwen3TTSTokenizerV2Config

    def __init__(self, config: Qwen3TTSTokenizerV2Config):
        super().__init__(config)

        # 핵심: QwenCodec - 오디오 인코딩/디코딩 담당
        self.codec_model = QwenCodec(config)

        # 설정값 저장
        self.audio_vocab_size = config.audio_vocab_size  # 65536
        self.audio_bos_token_id = config.audio_bos_token_id
        self.audio_eos_token_id = config.audio_eos_token_id

    @torch.no_grad()
    def encode(self, audio_waveform, sample_rate=24000):
        """오디오를 토큰으로 인코딩"""
        return self.codec_model.encode(audio_waveform, sample_rate)

    @torch.no_grad()
    def decode(self, audio_codes):
        """토큰을 오디오로 디코딩"""
        return self.codec_model.decode(audio_codes)

김개발 씨는 코드를 천천히 훑어보았습니다. 첫 번째로 눈에 띄는 것은 클래스 상속 구조였습니다.

PreTrainedModel을 상속받았다는 것은 이 Tokenizer가 단순한 유틸리티 클래스가 아니라 하나의 학습 가능한 모델이라는 의미입니다. Hugging Face 생태계와 완벽하게 호환되어 from_pretrained로 쉽게 로드할 수 있고, GPU 가속도 자연스럽게 적용됩니다.

"일반 Tokenizer랑 다르네요?" 김개발 씨가 물었습니다. "맞아.

보통 텍스트 Tokenizer는 규칙 기반이야. 하지만 오디오 Tokenizer는 신경망이 필요해." 박시니어 씨가 설명했습니다.

핵심은 QwenCodec입니다. 이것이 실제로 오디오를 토큰으로, 토큰을 오디오로 변환하는 일을 담당합니다.

Neural Audio Codec이라고 불리는 기술로, 학습된 신경망을 통해 오디오를 효율적으로 압축하고 복원합니다. config_class라는 속성도 보입니다.

이것은 모델의 설정을 담당하는 클래스를 지정합니다. audio_vocab_size가 65536이라는 것은 오디오 토큰이 0부터 65535까지 총 65,536가지의 값을 가질 수 있다는 의미입니다.

@torch.no_grad() 데코레이터도 중요합니다. encode와 decode 메서드에 붙어 있는데, 이는 추론 시에 그래디언트 계산을 하지 않겠다는 의미입니다.

학습이 아닌 순수 추론 모드로 동작하여 메모리와 연산을 절약합니다. encode 메서드는 오디오 파형과 샘플 레이트를 받아서 토큰 시퀀스를 반환합니다.

decode 메서드는 그 반대로 토큰을 받아서 오디오 파형을 생성합니다. "구조가 정말 깔끔하네요.

복잡한 로직은 다 QwenCodec 안에 숨어있고요." 김개발 씨가 감탄했습니다. 박시니어 씨가 미소 지었습니다.

"좋은 추상화의 예시야. 사용자는 encode, decode만 알면 되고, 내부의 복잡한 양자화 과정은 신경 쓸 필요가 없어."

실전 팁

💡 - PreTrainedModel을 상속받으면 save_pretrained, from_pretrained, to(device) 등의 메서드를 자동으로 사용할 수 있습니다.

  • torch.no_grad()는 추론 시 필수입니다. 메모리 사용량을 크게 줄여줍니다.

4. 12Hz 프레임 레이트의 의미

"근데 왜 하필 12Hz인가요? 다른 모델들은 50Hz나 75Hz도 쓰던데요." 김개발 씨의 질문에 박시니어 씨가 의미심장하게 웃었습니다.

"그게 바로 Qwen3-TTS의 핵심 설계 철학이야."

12Hz 프레임 레이트는 1초 분량의 오디오를 단 12개의 토큰으로 표현한다는 의미입니다. 이는 다른 TTS 모델들이 사용하는 50Hz, 75Hz에 비해 현저히 낮은 수치입니다.

토큰 수가 적으면 시퀀스가 짧아지고, 짧은 시퀀스는 Transformer 모델이 더 효율적으로 처리할 수 있습니다. 마치 책을 요약해서 핵심만 전달하는 것과 같습니다.

다음 코드를 살펴봅시다.

class FrameRateComparison:
    """프레임 레이트별 토큰 수 비교"""

    def calculate_tokens(self, audio_duration_sec, frame_rate):
        """오디오 길이에 따른 토큰 수 계산"""
        return int(audio_duration_sec * frame_rate)

    def compare_models(self, duration=10):  # 10초 오디오 기준
        comparisons = {
            'Qwen3-TTS (12Hz)': self.calculate_tokens(duration, 12),   # 120 토큰
            'EnCodec (75Hz)': self.calculate_tokens(duration, 75),      # 750 토큰
            'SoundStream (50Hz)': self.calculate_tokens(duration, 50),  # 500 토큰
        }
        return comparisons

# 결과: 10초 오디오
# Qwen3-TTS: 120 토큰 (가장 짧음!)
# EnCodec: 750 토큰
# SoundStream: 500 토큰

# Transformer attention 복잡도: O(n²)
# 토큰 수가 1/6이면 → attention 연산량은 1/36로 감소!

김개발 씨는 숫자를 계산해보았습니다. 10초짜리 음성을 토큰으로 바꾼다고 가정해봅시다.

12Hz라면 10초 곱하기 12, 즉 120개의 토큰이 생깁니다. 반면 75Hz인 EnCodec을 쓰면 750개의 토큰이 됩니다.

무려 6배 이상 차이가 납니다. "그게 왜 중요하죠?" 김개발 씨가 물었습니다.

박시니어 씨가 화이트보드에 수식을 적었습니다. "Transformer의 attention 연산 복잡도가 O(n제곱)이거든." 시퀀스 길이가 n이면, attention 연산은 n의 제곱에 비례합니다.

120개의 토큰은 14,400번의 연산이 필요하고, 750개의 토큰은 562,500번의 연산이 필요합니다. 약 40배 차이입니다.

이것이 바로 Qwen3-TTS가 12Hz를 선택한 이유입니다. 더 적은 토큰으로 동일한 정보를 표현할 수 있다면, 모델은 더 긴 음성을 더 빠르게 처리할 수 있습니다.

하지만 여기서 의문이 생깁니다. 토큰 수를 줄이면 정보가 손실되지 않을까요?

답은 코드북의 품질에 있습니다. Qwen3-TTS는 65,536개의 오디오 토큰을 사용합니다.

각 토큰이 담을 수 있는 정보량이 충분히 크기 때문에, 낮은 프레임 레이트에서도 고품질 음성을 재현할 수 있습니다. 마치 고화질 사진을 적은 수의 큰 타일로 표현하는 것과 같습니다.

타일 수는 적지만, 각 타일이 충분히 정교하다면 전체 그림은 여전히 선명합니다. "아, 그래서 vocab_size가 65536인 거군요!" 김개발 씨가 연결고리를 찾았습니다.

박시니어 씨가 고개를 끄덕였습니다. "맞아.

프레임 레이트를 낮추는 대신 각 토큰의 표현력을 높인 거야. 트레이드오프를 현명하게 선택한 설계지." 실제로 Qwen3-TTS는 이 설계 덕분에 더 긴 음성을 실시간에 가깝게 생성할 수 있습니다.

스트리밍 TTS 서비스에서 이 효율성은 결정적인 장점이 됩니다.

실전 팁

💡 - 프레임 레이트와 음질은 트레이드오프 관계입니다. 낮은 프레임 레이트는 코드북 품질로 보완해야 합니다.

  • 실시간 TTS 서비스를 구축할 때는 토큰 시퀀스 길이가 레이턴시에 직접적인 영향을 미칩니다.

5. Encode와 Decode 프로세스

이론은 충분했습니다. 김개발 씨는 실제로 encode와 decode가 어떻게 동작하는지 코드 레벨에서 파악하고 싶었습니다.

"직접 데이터가 흘러가는 과정을 따라가 볼까요?"

Encode 프로세스는 오디오 파형을 리샘플링하고, 프레임 단위로 분할한 뒤, 각 프레임을 코드북에서 가장 가까운 토큰으로 양자화하는 과정입니다. Decode 프로세스는 그 역순으로, 토큰 ID들을 코드북 벡터로 변환하고, 이를 신경망을 통해 연속적인 오디오 파형으로 복원합니다.

다음 코드를 살펴봅시다.

class EncodeDecode:
    """Encode/Decode 프로세스 상세 분석"""

    def encode_process(self, audio_waveform, sample_rate):
        # 1단계: 리샘플링 (입력 샘플레이트 → 24kHz로 통일)
        if sample_rate != 24000:
            audio_waveform = self.resample(audio_waveform, sample_rate, 24000)

        # 2단계: 정규화 (-1 ~ 1 범위로)
        audio_normalized = audio_waveform / (np.abs(audio_waveform).max() + 1e-8)

        # 3단계: 인코더 신경망 통과 (프레임 추출)
        latent_features = self.encoder_network(audio_normalized)

        # 4단계: 양자화 (연속값 → 이산 토큰 ID)
        token_ids = self.quantize(latent_features)  # 코드북 매칭

        return token_ids

    def decode_process(self, token_ids):
        # 1단계: 역양자화 (토큰 ID → 코드북 벡터)
        latent_features = self.dequantize(token_ids)

        # 2단계: 디코더 신경망 통과 (파형 생성)
        audio_waveform = self.decoder_network(latent_features)

        return audio_waveform  # 24kHz 오디오

김개발 씨는 데이터의 여정을 따라가기로 했습니다. 먼저 Encode 프로세스입니다.

사용자가 녹음한 음성이 입력으로 들어옵니다. 이 음성은 다양한 샘플 레이트를 가질 수 있습니다.

휴대폰 녹음은 보통 44.1kHz, 웹 녹음은 48kHz인 경우가 많습니다. 첫 번째 단계는 리샘플링입니다.

모든 입력을 24kHz로 통일합니다. 이렇게 해야 모델이 일관된 형식의 데이터를 처리할 수 있습니다.

두 번째 단계는 정규화입니다. 오디오의 최대값을 1로 맞춥니다.

볼륨이 작은 녹음이든 큰 녹음이든 동일한 스케일로 처리됩니다. 세 번째 단계가 핵심입니다.

인코더 신경망이 오디오 파형을 고차원 특성 벡터로 변환합니다. 원본 파형의 중요한 정보를 압축해서 추출하는 과정입니다.

네 번째 단계는 양자화입니다. 연속적인 특성 벡터를 이산적인 토큰 ID로 바꿉니다.

65,536개의 코드북 중에서 가장 가까운 것을 찾아 그 인덱스를 반환합니다. 이제 Decode 프로세스를 살펴봅시다.

방향이 반대입니다. 토큰 ID가 입력으로 들어옵니다.

예를 들어 [15234, 8921, 45012, ...] 같은 숫자 배열입니다. 첫 번째 단계는 역양자화입니다.

각 토큰 ID에 해당하는 코드북 벡터를 찾습니다. 숫자 15234는 코드북의 15234번째 벡터를 의미합니다.

두 번째 단계에서 디코더 신경망이 이 벡터들을 연속적인 오디오 파형으로 복원합니다. 신경망은 벡터 사이의 빈 공간을 부드럽게 채우며 자연스러운 소리를 만들어냅니다.

"결국 양자화와 역양자화가 핵심이네요." 김개발 씨가 정리했습니다. "맞아.

얼마나 적은 토큰으로 얼마나 원본에 가깝게 복원하느냐, 그게 코덱의 품질을 결정해." 박시니어 씨가 덧붙였습니다.

실전 팁

💡 - 리샘플링 시 안티앨리어싱 필터를 사용해야 품질 저하를 방지할 수 있습니다.

  • 양자화 과정에서 정보 손실이 발생하므로, 코드북 크기와 학습 품질이 최종 음질에 결정적입니다.

6. 실전 Tokenizer 직접 사용하기

"이론은 충분해요. 이제 직접 코드를 실행해볼까요?" 박시니어 씨가 노트북을 열었습니다.

김개발 씨도 옆에 앉아 화면을 주시했습니다. 실제로 Qwen3-TTS-Tokenizer를 사용하는 방법을 배울 시간입니다.

Qwen3-TTS-Tokenizer는 Hugging Face를 통해 쉽게 로드할 수 있습니다. from_pretrained 메서드로 모델을 불러오고, encode로 오디오를 토큰화하고, decode로 토큰을 다시 오디오로 변환합니다.

GPU 가속이 지원되어 대용량 오디오도 빠르게 처리할 수 있습니다.

다음 코드를 살펴봅시다.

import torch
import torchaudio
from transformers import AutoModel

# 1. Tokenizer 모델 로드
tokenizer = AutoModel.from_pretrained(
    "Qwen/Qwen3-TTS-Tokenizer",
    trust_remote_code=True  # 커스텀 코드 신뢰 필요
)
tokenizer = tokenizer.to("cuda")  # GPU 사용

# 2. 오디오 파일 로드
waveform, sample_rate = torchaudio.load("input_audio.wav")
waveform = waveform.to("cuda")

# 3. Encode: 오디오 → 토큰
with torch.no_grad():
    audio_tokens = tokenizer.encode(waveform, sample_rate)
print(f"토큰 수: {audio_tokens.shape[-1]}")  # 12Hz 기준 1초당 12개

# 4. Decode: 토큰 → 오디오
with torch.no_grad():
    reconstructed = tokenizer.decode(audio_tokens)

# 5. 복원된 오디오 저장
torchaudio.save("output_audio.wav", reconstructed.cpu(), 24000)

김개발 씨는 드디어 실제 코드를 실행해볼 차례가 되었습니다. 긴장되면서도 설렙니다.

첫 번째 단계는 모델 로드입니다. Hugging Face의 AutoModel.from_pretrained를 사용합니다.

여기서 주의할 점이 있습니다. trust_remote_code=True 옵션이 필수입니다.

"왜 이 옵션이 필요하죠?" 김개발 씨가 물었습니다. "Qwen3-TTS-Tokenizer는 커스텀 모델 코드를 사용하거든.

이 옵션이 없으면 보안상의 이유로 로드가 거부돼." 박시니어 씨가 설명했습니다. 모델을 GPU로 옮기는 것도 잊지 마세요.

to("cuda")를 호출하면 GPU 메모리에 모델이 올라가고, 이후의 연산이 GPU에서 실행됩니다. 두 번째 단계는 오디오 로드입니다.

torchaudio.load 함수를 사용하면 WAV, MP3 등 다양한 포맷의 오디오를 불러올 수 있습니다. 반환값은 파형 텐서와 샘플 레이트입니다.

세 번째 단계가 Encode입니다. tokenizer.encode에 파형과 샘플 레이트를 전달합니다.

torch.no_grad() 컨텍스트 안에서 실행해야 메모리를 절약할 수 있습니다. 결과로 반환되는 audio_tokens의 길이를 확인해보면, 12Hz 프레임 레이트를 직접 체감할 수 있습니다.

10초짜리 오디오라면 약 120개의 토큰이 생성됩니다. 네 번째 단계는 Decode입니다.

생성된 토큰을 다시 오디오로 변환합니다. 이 과정을 통해 압축과 복원이 얼마나 잘 되는지 확인할 수 있습니다.

마지막으로 복원된 오디오를 저장합니다. torchaudio.save로 WAV 파일을 만듭니다.

샘플 레이트는 항상 24000을 사용합니다. Tokenizer의 출력이 24kHz로 고정되어 있기 때문입니다.

김개발 씨가 코드를 실행하고 결과물을 재생해보았습니다. "와, 거의 원본이랑 똑같네요!" 박시니어 씨가 미소 지었습니다.

"12Hz로 그 정도 품질이 나오는 게 대단한 거야. 이제 이 Tokenizer를 TTS 파이프라인에 연결하면 텍스트에서 음성까지 완전한 흐름이 완성되는 거지." 김개발 씨는 고개를 끄덕였습니다.

복잡해 보이던 TTS의 세계가 조금씩 이해되기 시작했습니다. 오늘 배운 12Hz Tokenizer의 원리는 앞으로 AI 음성 합성을 다루는 데 든든한 기초가 될 것입니다.

실전 팁

💡 - 처음 모델을 로드할 때 수 GB의 다운로드가 필요합니다. 인터넷 환경을 확인하세요.

  • 메모리 부족 오류가 발생하면 배치 크기를 줄이거나 CPU에서 실행해보세요.
  • 실제 TTS 파이프라인에서는 이 Tokenizer가 Text-to-Token 모델의 출력을 받아 최종 음성을 생성합니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#TTS#Tokenizer#Qwen3#SpeechSynthesis#AudioProcessing#AI,Speech

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스