音频课程文档
创建语音助手
并获得增强的文档体验
开始使用
创建语音助手
在本节中,我们将把我们已经有实践经验的三个模型组合在一起,构建一个名为 Marvin 🤖 的端到端语音助手。 就像亚马逊的 Alexa 或苹果的 Siri 一样,Marvin 是一个虚拟语音助手,它会对特定的“唤醒词”做出响应,然后监听口头查询,最后用口头答案做出回应。
我们可以将语音助手管道分解为四个阶段,每个阶段都需要一个独立的模型

1. 唤醒词检测
语音助手不断监听通过设备麦克风输入的音频,但只有在说出特定的“唤醒词”或“触发词”时才会启动。
唤醒词检测任务由一个小型设备端音频分类模型处理,该模型比语音识别模型小巧得多,通常只有数百万个参数,而语音识别模型则有数亿个参数。 因此,它可以持续在您的设备上运行而不会耗尽电池电量。 只有当检测到唤醒词时,才会启动更大的语音识别模型,之后又会再次关闭。
2. 语音转录
管道中的下一个阶段是将口头查询转录为文本。 实际上,由于音频文件本质上很大,因此将音频文件从本地设备传输到云端速度很慢,因此更有效的方法是直接在设备上使用自动语音识别 (ASR) 模型来转录它们,而不是使用云端模型。 设备端模型可能比云端托管的模型更小,因此精度较低,但更快的推理速度使其值得,因为我们可以近乎实时地运行语音识别,当我们说话时,我们的口头音频话语会被转录。
我们现在对语音识别过程非常熟悉,因此这应该是一件轻而易举的事!
3. 语言模型查询
既然我们知道了用户问了什么,我们就需要生成一个回应! 此任务的最佳候选模型是大型语言模型 (LLM),因为它们能够有效地理解文本查询的语义并生成合适的响应。
由于我们的文本查询很小(只有几个文本标记),而语言模型很大(数十亿个参数),因此运行 LLM 推理的最有效方法是将我们的文本查询从我们的设备发送到云端运行的 LLM,生成文本响应,然后将响应返回到设备。
4. 合成语音
最后,我们将使用文本到语音 (TTS) 模型将文本响应合成为口语。 这在设备上完成,但您也可以在云端运行 TTS 模型,生成音频输出并将其传输回设备。
同样,我们现在已经做过好几次了,所以这个过程会非常熟悉!
唤醒词检测
语音助手管道中的第一阶段是检测是否说出了唤醒词,我们需要为这项任务找到一个合适的预训练模型! 您会从有关音频分类预训练模型的章节中记得,Speech Commands 是一个口语数据集,旨在评估音频分类模型在 15 多个简单命令词(如 "up"
、"down"
、"yes"
和 "no"
)以及 "silence"
标签(用于分类非语音)上的性能。 花一点时间在 Hub 上的数据集查看器上听取样本,并重新熟悉 Speech Commands 数据集:数据集查看器。
我们可以采用在 Speech Commands 数据集上预训练的音频分类模型,并选择这些简单的命令词之一作为我们选择的唤醒词。 在 15 多个可能的命令词中,如果模型以最高概率预测我们选择的唤醒词,我们可以相当肯定地说出了唤醒词。
让我们前往 Hugging Face Hub 并点击“模型”选项卡:https://huggingface.co/models
这将调出 Hugging Face Hub 上的所有模型,按过去 30 天的下载量排序

您会注意到左侧,我们有一系列选项卡,我们可以选择这些选项卡来按任务、库、数据集等过滤模型。向下滚动并从音频任务列表中选择任务“音频分类”

我们现在看到了 Hub 上 500 多个音频分类模型的子集。 为了进一步优化此选择,我们可以按数据集过滤模型。 单击“数据集”选项卡,然后在搜索框中键入“speech_commands”。 当您开始键入时,您会看到 speech_commands
的选项出现在搜索选项卡下方。 您可以单击此按钮将所有音频分类模型过滤为在 Speech Commands 数据集上微调的模型

太棒了! 我们看到我们有六个预训练模型可用于此特定数据集和任务(尽管如果您在稍后日期阅读,可能会添加新模型!)。 您会认出这些模型中的第一个是我们在单元 4 示例中使用的 音频频谱图 Transformer 检查点。 我们将再次使用此检查点来执行唤醒词检测任务。
让我们继续使用 pipeline
类加载检查点
from transformers import pipeline
import torch
device = "cuda:0" if torch.cuda.is_available() else "cpu"
classifier = pipeline(
"audio-classification", model="MIT/ast-finetuned-speech-commands-v2", device=device
)
我们可以通过检查模型配置中的 id2label
属性来检查模型训练时使用的标签
classifier.model.config.id2label
好的! 我们看到该模型在 35 个类别标签上进行了训练,包括我们上面描述的一些简单命令词,以及一些特定对象,如 "bed"
、"house"
和 "cat"
。 我们看到这些类别标签中有一个名称:id 27 对应于标签 “marvin”
classifier.model.config.id2label[27]
'marvin'
完美! 我们可以使用这个名称作为我们语音助手的唤醒词,类似于亚马逊的 Alexa 使用“Alexa”,或苹果的 Siri 使用“Hey Siri”。 在所有可能的标签中,如果模型以最高的类别概率预测 "marvin"
,我们可以相当肯定地说出了我们选择的唤醒词。
现在我们需要定义一个函数,该函数持续监听我们设备的麦克风输入,并将音频持续传递给分类模型以进行推理。 为此,我们将使用 🤗 Transformers 附带的一个方便的助手函数,名为 ffmpeg_microphone_live
。
此函数将指定长度 chunk_length_s
的小音频块转发到模型进行分类。 为了确保我们在音频块之间获得平滑的边界,我们使用步幅 chunk_length_s / 6
在我们的音频上运行一个滑动窗口。 为了在我们开始推理之前不必等待整个第一个块被记录下来,我们还定义了一个最小的临时音频输入长度 stream_chunk_s
,它在达到 chunk_length_s
时间之前被转发到模型。
函数 ffmpeg_microphone_live
返回一个生成器对象,生成一系列音频块,每个音频块都可以传递给分类模型以进行预测。 我们可以将此生成器直接传递给 pipeline
,pipeline
反过来返回一系列输出预测,每个音频输入块对应一个输出预测。 我们可以检查每个音频块的类别标签概率,并在检测到已说出唤醒词时停止我们的唤醒词检测循环。
我们将使用一个非常简单的标准来分类是否说出了我们的唤醒词:如果具有最高概率的类别标签是我们的唤醒词,并且此概率超过阈值 prob_threshold
,我们声明已说出唤醒词。 以这种方式使用概率阈值来门控我们的分类器可确保在音频输入为噪声时不会错误地预测唤醒词,这通常发生在模型非常不确定且所有类别标签概率都很低时。 您可能需要调整此概率阈值,或者探索更复杂的方法,通过基于熵(或不确定性)的指标来确定唤醒词。
from transformers.pipelines.audio_utils import ffmpeg_microphone_live
def launch_fn(
wake_word="marvin",
prob_threshold=0.5,
chunk_length_s=2.0,
stream_chunk_s=0.25,
debug=False,
):
if wake_word not in classifier.model.config.label2id.keys():
raise ValueError(
f"Wake word {wake_word} not in set of valid class labels, pick a wake word in the set {classifier.model.config.label2id.keys()}."
)
sampling_rate = classifier.feature_extractor.sampling_rate
mic = ffmpeg_microphone_live(
sampling_rate=sampling_rate,
chunk_length_s=chunk_length_s,
stream_chunk_s=stream_chunk_s,
)
print("Listening for wake word...")
for prediction in classifier(mic):
prediction = prediction[0]
if debug:
print(prediction)
if prediction["label"] == wake_word:
if prediction["score"] > prob_threshold:
return True
让我们尝试一下这个函数,看看它是如何工作的! 我们将标志 debug=True
设置为打印出每个音频块的预测。 让模型运行几秒钟,看看当没有语音输入时它会做出什么样的预测,然后清晰地说出唤醒词 "marvin"
,并观察 "marvin"
的类别标签预测飙升至接近 1
launch_fn(debug=True)
Listening for wake word... {'score': 0.055326107889413834, 'label': 'one'} {'score': 0.05999856814742088, 'label': 'off'} {'score': 0.1282748430967331, 'label': 'five'} {'score': 0.07310110330581665, 'label': 'follow'} {'score': 0.06634809821844101, 'label': 'follow'} {'score': 0.05992642417550087, 'label': 'tree'} {'score': 0.05992642417550087, 'label': 'tree'} {'score': 0.999913215637207, 'label': 'marvin'}
太棒了! 正如我们所预期的,模型在前几秒钟生成了垃圾预测。 没有语音输入,因此模型做出接近随机的预测,但概率非常低。 一旦我们说出唤醒词,模型就会以接近 1 的概率预测 "marvin"
并终止循环,表明已检测到唤醒词,并且应激活 ASR 系统!
语音转录
再次,我们将使用 Whisper 模型作为我们的语音转录系统。 具体来说,我们将加载 Whisper Base English 检查点,因为它足够小,可以在合理的转录准确率下提供良好的推理速度。 我们将使用一个技巧来获得接近实时的转录,方法是巧妙地处理如何将音频输入转发到模型。 与以前一样,可以随意使用 Hub 上的任何语音识别检查点,包括 Wav2Vec2、MMS ASR 或其他 Whisper 检查点
transcriber = pipeline(
"automatic-speech-recognition", model="openai/whisper-base.en", device=device
)
"openai/whisper-small.en"
。我们现在可以定义一个函数来记录我们的麦克风输入并转录相应的文本。 通过 ffmpeg_microphone_live
助手函数,我们可以控制我们的语音识别模型的“实时”程度。 使用较小的 stream_chunk_s
有利于更实时的语音识别,因为我们将我们的输入音频分成更小的块并即时转录它们。 然而,这是以牺牲较差的准确性为代价的,因为模型可以从中推断的上下文较少。
当我们转录语音时,我们还需要了解用户何时停止说话,以便我们可以终止录音。 为了简单起见,我们将在第一个 chunk_length_s
(默认设置为 5 秒)后终止我们的麦克风录音,但您可以尝试使用 语音活动检测 (VAD) 模型来预测用户何时停止说话。
import sys
def transcribe(chunk_length_s=5.0, stream_chunk_s=1.0):
sampling_rate = transcriber.feature_extractor.sampling_rate
mic = ffmpeg_microphone_live(
sampling_rate=sampling_rate,
chunk_length_s=chunk_length_s,
stream_chunk_s=stream_chunk_s,
)
print("Start speaking...")
for item in transcriber(mic, generate_kwargs={"max_new_tokens": 128}):
sys.stdout.write("\033[K")
print(item["text"], end="\r")
if not item["partial"][0]:
break
return item["text"]
让我们试一试,看看我们进展如何! 一旦麦克风处于活动状态,就开始说话,并观看您的转录以半实时方式出现
transcribe()
Start speaking... Hey, this is a test with the whisper model.
很好! 您可以根据您的语速快慢调整最大音频长度 chunk_length_s
(如果您觉得没有足够的时间说话,则增加它;如果您最后被留下等待,则减少它),以及用于实时因素的 stream_chunk_s
。 只需将这些作为参数传递给 transcribe
函数。
语言模型查询
既然我们已经转录了我们的口头查询,我们想要生成一个有意义的响应。 为此,我们将使用云端托管的 LLM。 具体来说,我们将选择 Hugging Face Hub 上的 LLM,并使用 Inference API 轻松查询模型。
首先,让我们前往 Hugging Face Hub。 为了找到我们的 LLM,我们将使用 🤗 Open LLM Leaderboard,这是一个 Space,它根据 LLM 模型在四个生成任务中的性能对其进行排名。 我们将按“instruct”搜索以过滤掉已进行指令微调的模型,因为这些模型应该更适合我们的查询任务

我们将使用 tiiuae/falcon-7b-instruct 检查点,由 TII 提供,这是一个 7B 参数的仅解码器 LM,在聊天和指令数据集的混合上进行了微调。 您可以使用 Hugging Face Hub 上任何启用了“Hosted inference API”的 LLM,只需注意模型卡右侧的小部件

Inference API 允许我们从本地机器向 Hub 上托管的 LLM 发送 HTTP 请求,并将响应作为 json
文件返回。 我们只需要提供我们的 Hugging Face Hub 令牌(我们直接从我们的 Hugging Face Hub 文件夹检索)和我们希望查询的 LLM 的模型 ID
from huggingface_hub import HfFolder
import requests
def query(text, model_id="tiiuae/falcon-7b-instruct"):
api_url = f"https://api-inference.huggingface.co/models/{model_id}"
headers = {"Authorization": f"Bearer {HfFolder().get_token()}"}
payload = {"inputs": text}
print(f"Querying...: {text}")
response = requests.post(api_url, headers=headers, json=payload)
return response.json()[0]["generated_text"][len(text) + 1 :]
让我们用一个测试输入试一试!
query("What does Hugging Face do?")
'Hugging Face is a company that provides natural language processing and machine learning tools for developers. They'
您会注意到使用 Inference API 进行推理的速度有多快 - 我们只需从本地机器向托管模型发送少量文本标记,因此通信成本非常低。 LLM 托管在 GPU 加速器上,因此推理运行非常快。 最后,生成的响应从模型传输回我们的本地机器,同样具有较低的通信开销。
合成语音
现在我们准备好获得最终的口语输出了! 再次,我们将使用 Microsoft SpeechT5 TTS 模型进行英语 TTS,但您可以使用您选择的任何 TTS 模型。 让我们继续加载处理器和模型
from transformers import SpeechT5Processor, SpeechT5ForTextToSpeech, SpeechT5HifiGan
processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts")
model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts").to(device)
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan").to(device)
还有说话人嵌入
from datasets import load_dataset
embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")
speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)
我们将重用我们在上一章关于语音到语音翻译中定义的 synthesise
函数
def synthesise(text):
inputs = processor(text=text, return_tensors="pt")
speech = model.generate_speech(
inputs["input_ids"].to(device), speaker_embeddings.to(device), vocoder=vocoder
)
return speech.cpu()
让我们快速验证这是否按预期工作
from IPython.display import Audio
audio = synthesise(
"Hugging Face is a company that provides natural language processing and machine learning tools for developers."
)
Audio(audio, rate=16000)
做得好 👍
Marvin 🤖
既然我们已经为语音助手管道的四个阶段中的每一个阶段定义了一个函数,那么剩下要做的就是将它们组合在一起以获得我们的端到端语音助手。 我们将简单地连接这四个阶段,从唤醒词检测 (launch_fn
)、语音转录、查询 LLM,最后是语音合成开始。
launch_fn()
transcription = transcribe()
response = query(transcription)
audio = synthesise(response)
Audio(audio, rate=16000, autoplay=True)
用一些提示语试用一下! 这里有一些示例供您入门
- 世界上最热的国家是哪个?
- Transformer 模型是如何工作的?
- 你会说西班牙语吗?
至此,我们完成了端到端语音助手的构建,它使用了您在本课程中学到的 🤗 音频工具,并在最后加入了一些 LLM 的魔力。 我们可以进行一些扩展来改进语音助手。 首先,音频分类模型对 35 个不同的标签进行分类。 我们可以使用更小、更轻量级的二元分类模型,该模型仅预测是否说出了唤醒词。 其次,我们预先加载所有模型并保持它们在我们的设备上运行。 如果我们想省电,我们只会在需要时加载每个模型,然后在之后卸载它们。 第三,我们的转录功能中缺少语音活动检测模型,转录的时间是固定的,在某些情况下太长,而在另一些情况下太短。
推广到任何事物 🪄
到目前为止,我们已经了解了如何使用我们的语音助手 Marvin 生成语音输出。 最后,我们将演示如何将这些语音输出推广到文本、音频和图像。
我们将使用 Transformers Agents 构建我们的助手。 Transformers Agents 在 🤗 Transformers 和 Diffusers 库之上提供了一个自然语言 API,使用 LLM 和精心制作的提示来解释自然语言输入,并使用一组精选工具来提供多模态输出。
让我们开始实例化一个 Agent。Transformers Agents 有三个可用的 LLM,其中两个是开源的,并且在 Hugging Face Hub 上免费。第三个是 OpenAI 的模型,需要 OpenAI API 密钥。在这个例子中,我们将使用免费的 Bigcode Starcoder 模型,但你也可以尝试其他可用的 LLM。
from transformers import HfAgent
agent = HfAgent(
url_endpoint="https://api-inference.huggingface.co/models/bigcode/starcoder"
)
要使用 Agent,我们只需使用文本提示调用 agent.run
。例如,我们将让它生成一张猫 🐈 的图片(希望看起来比这个表情符号好一点)。
agent.run("Generate an image of a cat")

就这么简单!Agent 解释了我们的提示,并在后台使用了 Stable Diffusion 来生成图像,而我们不必担心加载模型、编写函数或执行代码。
现在我们可以用我们的 Transformers Agent 替换语音助手中的 LLM 查询功能和文本合成步骤,因为 Agent 将为我们处理这两个步骤。
launch_fn() transcription = transcribe() agent.run(transcription)
尝试说出相同的提示 “Generate an image of a cat”,看看系统运行情况。如果你向 Agent 提出一个简单的问答查询,Agent 将以文本答案回复。你可以鼓励它生成多模态输出,要求它返回图像或语音。例如,你可以要求它:“Generate an image of a cat, caption it, and speak the caption”。
虽然 Agent 比我们的第一个迭代版本 Marvin 🤖 助手更灵活,但以这种方式概括语音助手任务可能会导致在标准语音助手查询上的性能下降。为了恢复性能,你可以尝试使用性能更高的 LLM 检查点,例如 OpenAI 的模型,或者定义一组特定于语音助手任务的自定义工具。
< > 在 GitHub 上更新