우리는 복사본을 복제하고 있었다
요약
제품 워크스루를 위한 자연스러운 스코틀랜드 억양의 로컬 TTS 엔진을 찾는 과정과 기술적 한계를 다룹니다. Piper, Kokoro-82M, F5-TTS 등 다양한 모델을 테스트하며 억양, 자연스러움, 로컬 실행 가능성 사이의 트레이드오프를 분석합니다.
핵심 포인트
- 로컬 실행 가능한 TTS 엔진의 억양 및 운율(prosody) 조절 한계
- Piper 엔진의 숫자 읽기 오류 및 문장 단위 운율 부족 문제
- Kokoro-82M의 자연스러움과 캐릭터성 사이의 불균형
- F5-TTS를 이용한 목소리 복제 시 발생하는 캐릭터성 상실 문제
제품 워크스루 (Product walkthrough)에는 내레이터가 필요했습니다. 사람이 아니라, 화면 녹화 영상 위로 스크립트를 읽어주는 합성된 목소리 (synthesised voice) 말입니다. 서류상으로는 충분히 간단해 보였습니다.
우리는 그 목소리로부터 동시에 네 가지를 원했고, 결과적으로 이 네 가지를 모두 완벽하게 만족시킬 수는 없다는 사실을 알게 되었습니다.
- 스코틀랜드 (Scottish) — 제품 뒤에는 실제 이름이 있기 때문에, 무미건조한 미 대서양 중부 (mid-Atlantic) 억양의 목소리가 이를 읽는 것은 첫 문장부터 잘못된 것이 될 것이었습니다.
- 자연스러움 (Natural) — 목록을 읽는 로봇 같지 않아야 합니다.
- 로컬 (Local) — 무료이며 오프라인이어야 합니다. 렌더링당 클라우드 비용이 발생하지 않고, 타인의 서버가 계속 가동되는지에 의존하지 않아야 합니다.
- 획득 가능성 (Obtainable) — 오늘 당장 이 기기에서 실제로 실행할 수 있는 것이어야 합니다.
우리가 시도한 거의 모든 옵션은 이 중 세 가지는 충족했지만, 네 번째는 조용히 무너졌습니다. 이 전체 탐색 과정은 사실 각 후보가 우리를 어떤 구석으로 몰아넣는지, 그리고 결국 그 문제가 어떻게 구석이 없는 상태가 되었는지에 관한 것이었습니다.
Piper, 그리고 잘못 나온 숫자
우리는 작고 빠르며 완전히 로컬에서 작동하는 텍스트 음성 변환 (text-to-speech, TTS) 엔진인 Piper로 시작했으며, en_GB-alba-medium이라는 목소리를 사용했습니다. Alba가 핵심이었습니다. 진짜 스코틀랜드 화자였기 때문입니다. 로컬, 무료, 오프라인, 정확한 억양. 즉시 네 가지 중 세 가지를 충족했습니다.
두 가지 문제가 있었습니다. 첫 번째는 거의 웃음이 나올 정도였습니다. Piper가 묶음 숫자를 잘못 읽는 것이었습니다. 222,000과 같은 숫자가
두 번째 문제는 진정한 장벽이었습니다. 개별 단어는 괜찮았지만, 문장 전체는 로봇 같았습니다. 문장 전반에 걸친 억양의 고저, 즉 운율 (prosody)이 평평했는데, 이는 Piper에서 조절할 수 있는 노브 (knob)가 아니었습니다. 문장 수준의 운율 (prosody)은 해당 엔진의 기술적 한계 (hard ceiling)였습니다. 이를 극복한다는 것은 Piper를 떠나야 한다는 것을 의미했고, 거기서부터 난관들이 시작되었습니다.
다섯 명의 후보, 다섯 가지의 서로 다른 실패 방식
우리는 대안들을 검토했습니다. 모든 대안이 거절되었는데, 흥미로운 점은 두 가지가 거절된 이유가 서로 같지 않았다는 것입니다.
Kokoro-82M, 또 다른 로컬 엔진, 목소리 bf_emma. Piper보다 더 명확하고 진정으로 더 자연스러웠지만, "alba 목소리에 비해 정말 밋밋하고 부자연스럽다"는 평가를 받았습니다. 자연스러움에서는 승리했지만 캐릭터성에서 패배했습니다. 작동은 했고 작동하는 코드를 삭제할 이유가 없었기에, 우리는 스위치 뒤에 연결된 상태로 유지했습니다.
F5-TTS, alba 복제. 영리한 아이디어는 Piper보다 훨씬 나은 운율 (prosody)을 가진 엔진을 가져와서 그 안에 alba 목소리를 복제하는 것이었습니다. alba의 악센트가 F5의 흐름을 타는, 두 장점을 모두 취하는 방식이었습니다. 결과는 즉각적으로 "처참했다. alba를 잃어버렸고 더 영어처럼 들린다"는 것이었습니다. 이미 생성된 목소리인 합성 참조 (synthetic reference)를 복제하는 것은 우리가 유지하려 했던 바로 그 캐릭터성을 뭉개버렸습니다. 그 단계까지 가는 것조차 CPU 상에 격리된 Python 환경을 구축해야 했는데, 기계의 GPU 스택이 해당 도구에 비해 너무 최신이었기 때문입니다.
흐름 (flow)에서는 승리했지만, 악센트에서 패배했으며, 실제 설정 시간이 많이 소요되었습니다. 거절되었습니다.
클라우드, ElevenLabs. 진정으로 스코틀랜드식 발음이면서 동시에 자연스러울 수 있는 유일한 경로였습니다. 두 개의 무료 스코틀랜드 여성 목소리는 적절하지 않았고, 더 나은 티어를 위해 비용을 지불하는 것은 고려 대상이 아니었습니다. 폐기된 것이 아니라 보류되었습니다. 다만 그 트레이드오프 (trade-off)는 들리는 것보다 부드러웠을 것이라는 점은 주목할 만합니다. 빌드 타임 (build time)에만 클라우드를 사용하며, 스크립트를 한 번 합성하면 배포되는 비디오는 로컬 파일이기 때문입니다. 하지만 보류된 것은 보류된 것입니다.
RVC 음성 변환 (voice conversion). 기술적으로 정확한 로컬 경로: 표현력이 풍부한 음성의 출력을 alba의 음색 (timbre)으로 변환하는 것입니다. 연구 수준의 노력이 필요하며, 다른 모든 작업과 마찬가지로 환경적 마찰이 존재하고, 결과물이 탁하게 (muddy) 나올 실질적인 위험이 있습니다. 현 단계에서는 거절되었습니다.
CereProc의 "Heather / The Scottish Voice." 이것은 뼈아팠습니다. 왜냐하면 이것이 이상적이었기 때문입니다: 일반적인 로컬 시스템 음성으로 설치되는 실제 고품질의 스코틀랜드 음성입니다. 우리가 원했던 모든 것을 갖추었지만, 단 하나 얻을 수 없었습니다. 판매 상점은 문을 닫았고, 무료 경로는 우리가 가지고 있지 않은 스코틀랜드 공공 부문, 교육 또는 NHS 이메일 주소가 필요합니다. "실제로 얻을 수 있는가"라는 축을 제외하고는 모든 축에서 승리했습니다. 아니요.
다섯 명의 후보가 있었고, 그들 사이에서 네 가지 요구 사항을 모두 충족할 수 있었습니다. 다만 네 가지를 동시에, 같은 목소리로 충족할 수는 없었습니다.
| 음성 | 스코틀랜드 (Scottish) | 자연스러움 (Natural) | 로컬 (Local) | 획득 가능성 (Obtainable) |
|---|---|---|---|---|
Piper (en_GB-alba-medium) | ✅ | ❌ | ✅ | ✅ |
| ... | ||||
| ✅ 충족 · ❌ 미달 · ➖ 미확인 (확인할 만큼 충분히 실행해 보지 못함) |
우리가 받아들인 한계 (The ceiling we accepted)
그래서 우리는 추격을 멈추고 한계치를 받아들였습니다. 우리가 처음 출시한 음성은 다시 alba였지만, 전체 스크립트에 대해 구절 처리 (phrasing pass)를 거쳤습니다: 긴 문장은 짧은 호흡 단위의 문장으로 나누고, 숫자는 목소리가 자연스럽게 느려지는 문장 끝에 배치하며, 브랜드 이름은 들리는 대로 표기했습니다. 속도를 늦추고 여유를 주기 위해 몇 가지 운율 (prosody) 설정을 조정했습니다. 솔직한 평가는 "로봇보다는 한 단계 위"였습니다. 참을 만했습니다. 그렇게 출시되었습니다.
만약 이야기가 거기서 끝났다면, '적당함(good-enough)'을 받아들이는 것에 대한 멋진 작은 이야기가 되었을 것입니다. 하지만 이야기는 거기서 끝나지 않았습니다. 우리가 아직 알지 못했던 한 가지 사실 때문이었습니다.
이미 우리 것이었던 코퍼스 (The corpus that was already ours)
우리는 Edinburgh DataShare에서 Alba speech corpus를 발견했습니다. CC BY 4.0 라이선스로, 출처만 밝힌다면 자유롭게 사용, 미세 조정 (Fine-tune), 심지어 상업적 이용도 가능합니다. 그리고 이것이 중요한 이유가 여기 있습니다. 이것은 Piper의 alba 음성의 실제 소스 코퍼스 (source corpus)입니다. 우리가 복제본을 거듭하며 보존하려 애썼던 그 화자가 바로 그곳에 가공되지 않은 스튜디오 녹음본 형태로 놓여 있었습니다. 약 4시간 분량, 4,613개의 매칭된 오디오-텍스트 쌍, 48kHz, 반무향실 (hemi-anechoic room)에서 녹음된 깨끗한 상태였습니다.
이것은 문제 전체를 재정의합니다. 지금까지의 질문은 "어떤 목소리를 선택할 것인가"였습니다. 이제 질문은 "더 나은 엔진 내부에서 이 특정 화자를 재현할 수 있는가"로 바뀌었습니다. 그녀의 합성된 메아리를 복제하는 것이 아니라, 그녀의 실제 목소리로 학습 (Train)하는 것입니다. 질문이 달라졌고, 그에 따라 더 나은 답도 달라졌습니다.
가장 저렴한 것부터, 그리고 모든 것을 설명해 준 실패
우리는 가장 저렴한 것부터 단계적으로 진행했습니다. 비싼 것을 시도하기 전에 무료인 것을 먼저 시도하는 방식입니다.
- 제로샷 (Zero-shot) — 표현력이 풍부한 엔진 (XTTS-v2)에 alba의 실제 인간 음성 클립을 건네주고 흉내 내라고 요청합니다.
- 미세 조정 (Fine-tune) — 만약 그것이 실패한다면, 해당 코퍼스로 엔진을 실제로 미세 조정합니다.
- 클라우드 (Cloud) — 그것마저 실패한다면, 클라우드를 사용합니다.
예상대로 1단계는 실패했습니다. 하지만 그 실패가 오히려 유용한 부분이 되었습니다. 실제 인간의 참조 데이터가 있음에도 불구하고, 목소리가 "스코틀랜드 억양에서 크게 벗어났습니다." 그 이유는 다음과 같으며, 이는 이번 탐색 전체의 구조적인 교훈입니다. 제로샷 복제 (Zero-shot cloning)는 음색 (timbre)은 복사하지만, 운율 (prosody)은 엔진 자체의 베이스 모델로부터 재생성하는데, 그 베이스 모델은 영어가 지배적입니다. 참조 클립이 아무리 좋거나 길어도 억양은 깎여 나가게 됩니다. 이 단 하나의 사실이 F5의 참사 또한 사후적으로 설명해 주었습니다.
복제 (Cloning)로는 결코 스코틀랜드 억양을 유지할 수 없었습니다. 오직 학습 (Training)만이 가능했습니다.
2단계로 넘어갑니다.
환경, 벽 하나하나를 마주하며
소비자용 GPU가 장착된 Windows 머신에서 신경망 음성 모델 (neural voice model)을 미세 조정하는 것은 작은 벽들의 연속이며, 각각의 벽은 특정한 문제 원인을 찾아낼 때까지 당신을 완전히 멈춰 세웁니다. 순서는 다음과 같습니다:
기계의 백신 프로그램이 SSL을 가로채면서, 어떤 새로운 환경에서도 패키지 설치 프로그램 (package installer)이 작동하지 않았습니다. 무엇이라도 설치하기 위해서는 백신 프로그램 자체의 인증서(certificate)를 가리키도록 설정해야 했습니다. 학습 라이브러리 (training library)는 특정 의존성 (dependency)의 최신 버전을 요구했지만, 그 의존성의 최신 버전에는 라이브러리가 여전히 호출하고 있는 함수가 삭제되어 있었기에, 우리는 문제가 발생하기 전의 하위 버전으로 고정 (pinned)했습니다. 명백한 PyTorch 빌드 (build)는 Windows에서 설치하기 까다로운 추가 미디어 구성 요소를 요구했습니다. 그래서 우리는 대신 이전의 일치하는 빌드를 고정했으며, 설치 프로그램이 이미 잘못된 빌드가 충족되었다고 판단하여 건너뛰는 바람에 그것조차 강제로 설치해야 했습니다. Windows에서는 학습 루프 (training loop)를 표준적인 if __name__ == "__main__" 주문으로 보호해야 합니다. 그렇지 않으면 병렬 데이터 로딩 워커 (parallel data-loading workers)들이 각각 전체 학습 스크립트를 다시 실행하여 잠긴 파일 (locked file)에서 충돌을 일으킵니다.
GPU는 8GB VRAM을 가진 RTX 5060이지만, 모니터도 구동하고 있었기에 실제로 사용 가능한 메모리는 약 5GB뿐이었고, 이로 인해 가능한 가장 작은 배치 크기 (batch size)를 사용할 수밖에 없었습니다. 그리고 메모리를 절약하기 위한 자연스러운 선택이었던 반정밀도 학습 (half-precision training)은 첫 단계부터 loss = nan을 발생시켰습니다. 반정밀도가 모델의 순전파 (forward pass) 과정 내에서 오버플로 (overflow)를 일으켰고, 배치 크기가 1일 때의 전정밀도 (full precision) 학습만이 깨끗하게 진행되었습니다. 우리가 맞추고 있던 참조 레시피 (reference recipe)는 정확히 이 이유 때문에 반정밀도를 조용히 생략하고 있었습니다. 이런 문제는 항상 일이 벌어진 후에야 알려줍니다.
이 모든 문제들은 해결될 때까지 완전한 정지 상태를 의미했습니다. 그 중 어느 것도 흥미로운 문제는 아니었습니다. 그 모든 것들이 우리와 흥미로운 문제 사이를 가로막고 있었습니다.
마지막 단계는 학습이 아니라 설정에 있었다
그다음은 진짜 한계였습니다. 학습은 스텝당 약 8.7초가 소요되었고, 전체 실행에는 15~18시간이 걸릴 예정이었습니다. 불가능한 일이었습니다. 하지만 반드시 끝까지 마칠 필요는 없었습니다. 적응 (adaptation)은 점차 평탄해졌고, 체크포인트 (checkpoint) 1,000 근처에서 목소리는 분명하게 변했습니다: "훨씬 더 나아졌고, 훨씬 더 명확해졌다." 억양 (accent)은 유지되었습니다. 그것이 핵심 질문이었습니다. 학습이 복제 (cloning)가 할 수 없었던 억양을 회복할 수 있는가 하는 문제였고, 답은 '예'였습니다.
두 가지 결점이 남았습니다. 아주 작은 더듬거림(stutter)과 각 줄의 첫 단어가 약간 높게 들리는 음조 문제였습니다. 둘 다 추가 학습은 필요하지 않았습니다. 그것들은 추론 (inference) 설정의 문제였습니다. 텍스트를 한꺼번에 생성하는 대신 문장 단위로 합성(synthesise)하고, 샘플링 온도 (sampling temperature)를 약간 조정하면 되었습니다. 더듬거림은 사라졌습니다. 그 과정에서 우리는 우리가 가지고 있던 믿음 하나를 바로잡아야 했습니다. 모델이 생성하는 동안 데스크톱을 사용하는 것이 출력을 손상시킨다는 믿음 말입니다. 그렇지 않았습니다. 모델의 출력은 결정론적 (deterministic)입니다. 재생 중에 GPU가 무엇을 하고 있는지는 저장된 파일과 아무런 관련이 없습니다. 우리는 환경 문제 때문이라고 환경을 탓해 왔지만, 사실은 설정 문제였습니다. 진짜 환경적인 장벽들을 한참 동안 찾아 헤맨 뒤에 마주한 이 실수는 저지르기 쉬운 실수였으며, 시사하는 바가 컸습니다.
해결된 두 가지 미결 사항
목소리는 출시되었습니다. 실제 화자를 기반으로 학습되어 오프라인에서 실행되는, 로컬에서 미세 조정 (fine-tuned)된 스코틀랜드식 내레이터입니다. 하지만 "작동한다"와 "완성되었다"는 서로 다른 주장이며, 사후에 발견된 두 가지 작은 사실이 이를 증명합니다.
첫째, 약어 (acronyms)들이 뭉개져서 나왔습니다. 모델이 약어를 하나의 단어로 발음하려고 시도했기 때문입니다. 해결책은 스크립트에 음성 기호(phonetically)로 철자를 적는 것이었습니다. 예를 들어, 'emrc'라는 약어는 "aitch em ar see"가 되었고, 웹 주소는 "guhv dot you kay"가 되었습니다. 마지막 사례에는 기억해 둘 만한 디테일이 있습니다. 우리는 'gov'가 아니라 'guhv'라고 적었는데, 'gov'라고 적으면 목소리가 긴 'o' 발음을 내기 때문입니다. 우리는 실제 사람이 사용하는 짧고 평평한 모음 'a'를 원했습니다. 화면상의 텍스트는 전혀 건드리지 않았고, 오직 목소리가 읽는 부분만 수정했습니다.
둘째, 완성된 영상의 소리가 작게 들렸습니다. 추측하는 대신 우리는 측정했습니다. 오디오는 -26.6 LUFS였는데, 보통 말하는 콘텐츠는 -16 LUFS 근처에 위치합니다. 이는 주관적인 판단이 아니었습니다. 합성기 (synthesiser)의 원시 출력(raw output) 자체가 그냥 작았고, 파이프라인(pipeline)의 그 어떤 단계에서도 이를 보정하지 않았던 것입니다. 정규화 (normalisation) 과정을 한 번 거쳐 다시 렌더링하고 재측정하자 -16.2가 나왔습니다. 목표치에 도달했습니다.
여전히 남아있는 문제
재생(regen) 스크립트의 기본 엔진은 의도적으로 미세 조정된(fine-tuned) 목소리가 아닌 Piper를 가리키고 있습니다. 학습 환경이 누락되었다고 해서, 폴백(fallback) 수단으로 단순 엔진이 존재하는 상황에서 재생 프로세스가 완전히 실패하게 둘 수는 없기 때문입니다. 품질이 좋은 목소리는 명시적인 플래그(flag)를 설정함으로써 배포됩니다. 이 기본값을 바꿀지 여부는 실시간으로 논의 중인 미결정 사항이며, 단순한 실수나 간과가 아니라 여전히 열려 있는 문제입니다.
세부 사항을 걷어내고 요약하자면 이렇습니다. 네 가지 제약 조건이 동시에 충족되지 않는 상황에서의 갈망. 각각의 실질적인 이유로 실패하며 나타난 정직한 막다른 길들의 행렬. "목소리를 선택하라"는 과제를 "화자를 복제하라"로 바꿔버린 단 하나의 정보, 즉 적절한 라이선스를 가진 코퍼스(corpus). 지치고 지루하며 필수적인, 환경적 장벽들의 시련. 그리고 마지막에 마주한 결함들은 어려운 부분인 학습(training)에 있었던 것이 아니었습니다. 그것은 우리가 더 이상 의심하지 않았던 쉬운 부분, 즉 설정(settings)에 있었습니다.
원문은 thekilted.dev/cloning-a-copy에서 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기