无人知晓的SOTA文本转语音和零样本语音克隆模型...
快速链接
- Spaces DEMO: https://huggingface.co/spaces/srinivasbilla/llasa-3b-tts
- 模型: https://huggingface.co/HKUST-Audio/Llasa-3B
- Github: https://github.com/zhenye234/LLaSA_training
大家好,最近玩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)