无人知晓的SOTA文本转语音和零样本语音克隆模型...

社区文章 发布于 2025年1月20日

快速链接

大家好,最近玩Llasa (https://huggingface.co/HKUST-Audio/Llasa-3B) 玩得很开心。它是一个开源的Llama3 3B微调模型,可作为文本转语音模型使用。它不仅可以进行令人难以置信的逼真文本转语音,还可以仅用几秒钟的采样音频克隆任何声音。

它太棒了,我不得不注册Huggingface Pro,获得零GPU访问权限,并写一篇博客向社区展示它。虽然作者指出他们的论文即将发表,但这并没有阻止我研究和弄清楚如何使用这个模型。

语音克隆

这是一个Llama 3.2 3B微调/持续预训练模型,用于使模型在不改变模型架构的情况下生成语音token。唯一的添加是音频分词器 xcodec2

在我滔滔不绝地谈论我发现的所有酷炫功能之前。我创建了一个空间供大家试用 这里,这里有一些我制作的真实语音克隆样本(这些不是真实人物,我使用了来自Elevenlabs语音的样本音频)

亚历克斯

参考 请在下面的评论区告诉我。这里是COD档案,我们明天见。保重。 克隆 大家好,Alex又回来啦,带来另一个视频。今天我们将学习如何使用最先进的文本转语音模型克隆语音。激动人心,对吧?让我们直接开始吧。

阿米莉亚

参考 大家好!我是阿米莉亚,一个超高质量的英语配音员。我爱阅读。说真的,我就是个书呆子。还在等什么?让我来阅读吧! 克隆 你只需要一个5到10秒的简短清晰的音频样本。然后模型就能生成高质量的语音样本,模仿原始声音的音调、语调和说话风格,甚至口音。

罗素

参考 光有好的思想是不够的,关键在于善用它。 克隆 该模型在 160,000 250,000 小时的Xcodec2音频分词数据上进行了训练,Xcodec2能以每秒50个token的高效率将音频转换为token。

多种说话风格

耳语

给定的样本音频非常重要。它决定了接下来所有音频的音质。因此,输入耳语,输出耳语。

情感

困惑 我不知道该说什么。会是晴天吗?还是雨天?天气完全无法预测。我太困惑了。

愤怒 我不知道该说什么。会是晴天吗?还是雨天?天气完全无法预测。我只是很恼火。

大笑 我不知道该说什么。会是晴天吗?还是雨天?天气完全无法预测。这其实很有趣。

擎天柱?这是一个模型很难处理的例子。它无法真正捕捉彼得·卡伦(Peter Cullen)为擎天柱配音的方式。

8B?

作者有一个8B模型空间,目前是空的,考虑到3B模型对大多数语音来说已经非常好,看看8B模型有多好会很有趣。Lora微调是否有效?我们能否合并和混合语音?有太多可以调整的地方了,我等不及官方论文的发布了。

希望你喜欢我的第一篇博客文章/随笔。

P.S. 我喜欢它本质上就是一个伪装的Llama模型。

正如我之前提到的,唯一的添加是xcodec2音频分词器模型,其他一切都只是通过正确的提示模板和分词进行的llama 3推理。请参阅我的ZERO Space的app.py文件以获取hf transformers中的推理代码。但既然它只是一个llama 3模型,我们就可以使用更优化的推理库,比如vllm,像这样:

注意,我将仓库克隆到我的配置文件中,因为它们是受限的,这样更容易运行演示...

from transformers import pipeline, AutoTokenizer
import torch
import soundfile as sf
from xcodec2.modeling_xcodec2 import XCodec2Model
from IPython import display
import torchaudio
from vllm import LLM, SamplingParams

llm = LLM(model="srinivasbilla/llasa-3b", gpu_memory_utilization=0.5, max_model_len=4096)
tokenizer = AutoTokenizer.from_pretrained('srinivasbilla/llasa-3b')

model_path = "srinivasbilla/xcodec2"
 
Codec_model = XCodec2Model.from_pretrained(model_path)
Codec_model.eval().cuda()

whisper_turbo_pipe = pipeline("automatic-speech-recognition", model="openai/whisper-large-v3-turbo", device='cuda')

sampling_params = SamplingParams(temperature=0.8, top_p=1, max_tokens=2048, stop=['<|SPEECH_GENERATION_END|>'], stop_token_ids=[128261])


def ids_to_speech_tokens(speech_ids):
 
    speech_tokens_str = []
    for speech_id in speech_ids:
        speech_tokens_str.append(f"<|s_{speech_id}|>")
    return speech_tokens_str

def extract_speech_ids(speech_tokens_str):
    return [int(x.replace('s_', '')) for x in speech_tokens_str[2:-2].split('|><|')]


def text_to_speech(sample_audio_path, target_text, sampling_params, prompt_text=None):
    waveform, sample_rate = torchaudio.load(sample_audio_path)

    # Check if the audio is stereo (i.e., has more than one channel)
    if waveform.size(0) > 1:
        # Convert stereo to mono by averaging the channels
        waveform_mono = torch.mean(waveform, dim=0, keepdim=True)
    else:
        # If already mono, just use the original waveform
        waveform_mono = waveform

    waveform_16k = torchaudio.transforms.Resample(orig_freq=sample_rate, new_freq=16000)(waveform_mono)
    torchaudio.save('/local_disk0/input.wav', waveform_16k, 16000)

    # only 16khz speech support!
    prompt_wav, sr = sf.read("/local_disk0/input.wav") # English prompt
    prompt_wav = torch.from_numpy(prompt_wav).float().unsqueeze(0)

    if prompt_text is None:
        prompt_text = whisper_turbo_pipe('/local_disk0/input.wav')['text'].strip()
        print(prompt_text)

    input_text = prompt_text + ' ' + target_text

    #TTS start!
    with torch.no_grad():
        # Encode the prompt wav
        vq_code_prompt = Codec_model.encode_code(input_waveform=prompt_wav)

        vq_code_prompt = vq_code_prompt[0,0,:]
        # Convert int 12345 to token <|s_12345|>
        speech_ids_prefix = ids_to_speech_tokens(vq_code_prompt)

        formatted_text = f"<|TEXT_UNDERSTANDING_START|>{input_text}<|TEXT_UNDERSTANDING_END|>"

        # Tokenize the text and the speech prefix
        chat = [
            {"role": "user", "content": "Convert the text to speech:" + formatted_text},
            {"role": "assistant", "content": "<|SPEECH_GENERATION_START|>" + ''.join(speech_ids_prefix)}
        ]

        input_ids = tokenizer.apply_chat_template(
            chat, 
            tokenize=False, 
            continue_final_message=True
        )

        outputs = llm.generate([input_ids], sampling_params)

        generated_text = outputs[0].outputs[0].text

        speech_tokens = extract_speech_ids(generated_text)
        speech_tokens = torch.tensor(speech_tokens).cuda().unsqueeze(0).unsqueeze(0)
        gen_wav = Codec_model.decode_code(speech_tokens)
    
    return gen_wav[0, 0, :].cpu().numpy()

然后进行推理

target_text = """The model was trained on a 160 *thousand* hours of audio tokenized by X codec 2. Which converts audio to tokens at a very efficient 50 tokens per second."""
audio_out, text_out = text_to_speech(
    "./sample_voices/voice_preview_neal.mp3",
    target_text,
    sampling_params=sampling_params,
    prompt_text="it is not enough to have a good mind the main thing is to use it well",
)
display.Audio(audio_out, rate=16000)

社区

天哪,这个模型太棒了,谢谢你写这篇博客并提供演示,这简直是我见过的最好的TTS,比我见过的任何其他模型好10倍

8b仓库空空如也,数据集也空空如也……嗯,离SOTA还有点距离……老实说,glm4voice效果更好——但这无疑是一个“还不错”的PoC

GitHub仓库为空/无论文

·
文章作者

是的,作者说8b将在月底发布,论文不确定。我确实没听说过glm4voice。我会去查一下。

此评论已被隐藏

我真的想运行这个,但是我很难让vllm和xcodec2在同一个环境中运行。有人能帮我找出哪些版本可以一起工作吗?

·

只需将vllm限制为1个GPU,其余的在另一个GPU上运行……或者使用-gmu

你能解释一下你是如何实现这些情感的吗?是参考语音带有情感,还是在文本提示中?

·

你搞清楚了吗?我的情感有时会根据内容变化,但我一直无法成功地将其引导到特定的情感。

如果它“只是llama”,那么大概意味着你可以使用现有的控制向量实现来引导模型。这是否能让你独立于输入样本来控制韵律和情感?

·
文章作者

这很有趣,我不知道,我从来没有尝试过向量控制实现。你能举个例子说明如何操作吗?

这个目录从何而来?

"./sample_voices/voice_preview_neal.mp3",

?

我试了一些中文,效果似乎比英文差很多。

这个GPU要求是什么?
此外,语音输出长度或提示长度是否有限制?

·
文章作者

大概10GB,300个字符左右是最佳点。你可以分块处理文本。

创建者发布了Llasa 8b。你会探索它吗?

·
文章作者

是的!谢谢你告诉我

昨天的GitHub更新太棒了!

但我遇到一个问题。你Huggingface spaces上生成的语音非常自然,而且与给定的参考音频非常接近。
但是当我安装GitHub版本时,它有点不同,语速有点快,也不太尊重给定的参考音频,听起来有点机械化,而且需要3-4次生成才能得到一个完美的音频(不是之前说的问题,而是音频会变形为非语言声音)。
你在Huggingface spaces中做了什么自定义配置吗?
我的配置是这样的
max_length=2048,
top_p=1,
temperature=0.8

嗨!感谢您的发现 :) 训练这个模型进行法语TTS会有多难?只需要10万小时的音频数据集吗?还是需要其他(可能是完整的)代码?我很想知道,因为Llama3.2是多语言的,这与这个模型有什么关系?

你好!你设置了什么提示才能让模型生成情感?

·
文章作者

不是通过提示。原始音频中包含情感上下文,模型只是简单地复制了它。

注册登录 发表评论