
Python实现动图生成:轻松创建自定义表情包
在我最近发布关于如何构建自己的 RAG 并在本地运行它的帖子之后。今天,我们更进一步,不仅实现了大型语言模型的对话能力,还增加了听力和口语能力。这个想法很简单:我们将创建一个语音助手,让人想起标志性钢铁侠电影中的贾维斯或星期五,它可以在你的计算机上离线运行。
由于这是一个入门教程,我将用 Python 实现它,并使其足够简单,适合初学者。最后,我将提供一些关于如何扩展应用程序的指导。
语音助手是一种通过语音识别和自然语言处理技术,能够理解和响应用户语音指令的软件程序。它们通常集成在智能手机、平板电脑、电脑、智能音箱等设备中,使用户能够通过语音与设备进行交互,执行各种任务,如设置提醒、播放音乐、获取天气信息、搜索互联网、发送消息等。
首先,设置一个虚拟 Python 环境。我们有多种选择,包括 pyenv、virtualenv、poetry 和其他具有类似用途的库。就我个人而言,由于个人喜好,我将在本教程中使用 Poetry。
以下是需要安装的几个关键库:
这里最关键的组件是大型语言模型 (LLM) 后端,我们将使用 Ollama。Ollama 被广泛认为是一种流行的离线运行和服务 LLM 的工具。基本上,你只需下载 Ollama 应用程序,提取喜欢的模型,然后运行它即可。
好的,如果一切设置完毕,让我们继续下一步。以下是我们应用程序的总体架构,它基本上包含 3 个主要组件:
工作流程很简单:录制语音、转录为文本、使用 LLM 生成响应,并使用 Bark 发出响应的声音。
实施首先要基于 Bark 制作 TextToSpeechService
,结合从文本合成语音的方法以及无缝处理较长文本输入的方法,如下所示:
import nltk
import torch
import warnings
import numpy as np
from transformers import AutoProcessor, BarkModel
warnings.filterwarnings(
"ignore",
message="torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.",
)
class TextToSpeechService:
def __init__(self, device: str = "cuda" if torch.cuda.is_available() else "cpu"):
"""
Initializes the TextToSpeechService class.
Args:
device (str, optional): The device to be used for the model, either "cuda" if a GPU is available or "cpu".
Defaults to "cuda" if available, otherwise "cpu".
"""
self.device = device
self.processor = AutoProcessor.from_pretrained("suno/bark-small")
self.model = BarkModel.from_pretrained("suno/bark-small")
self.model.to(self.device)
def synthesize(self, text: str, voice_preset: str = "v2/en_speaker_1"):
"""
Synthesizes audio from the given text using the specified voice preset.
Args:
text (str): The input text to be synthesized.
voice_preset (str, optional): The voice preset to be used for the synthesis. Defaults to "v2/en_speaker_1".
Returns:
tuple: A tuple containing the sample rate and the generated audio array.
"""
inputs = self.processor(text, voice_preset=voice_preset, return_tensors="pt")
inputs = {k: v.to(self.device) for k, v in inputs.items()}
with torch.no_grad():
audio_array = self.model.generate(**inputs, pad_token_id=10000)
audio_array = audio_array.cpu().numpy().squeeze()
sample_rate = self.model.generation_config.sample_rate
return sample_rate, audio_array
def long_form_synthesize(self, text: str, voice_preset: str = "v2/en_speaker_1"):
"""
Synthesizes audio from the given long-form text using the specified voice preset.
Args:
text (str): The input text to be synthesized.
voice_preset (str, optional): The voice preset to be used for the synthesis. Defaults to "v2/en_speaker_1".
Returns:
tuple: A tuple containing the sample rate and the generated audio array.
"""
pieces = []
sentences = nltk.sent_tokenize(text)
silence = np.zeros(int(0.25 * self.model.generation_config.sample_rate))
for sent in sentences:
sample_rate, audio_array = self.synthesize(sent, voice_preset)
pieces += [audio_array, silence.copy()]
return self.model.generation_config.sample_rate, np.concatenate(pieces)
__init__
):该类采用可选的设备参数,该参数指定要用于模型的设备(如果有 GPU,则为 cuda,否则为 cpu)。它从 suno/bark-small
预训练模型加载 Bark 模型和相应的处理器。您也可以通过为模型加载器指定 suno/bark
来使用大型版本。synthesize
):此方法采用文本输入和 voice_preset
参数,该参数指定要用于合成的语音。它使用处理器准备输入文本和语音预设,然后使用 model.generate()
方法生成音频数组。生成的音频数组将转换为 NumPy 数组,并随音频数组一起返回采样率。nltk.sent_tokenize
函数将输入文本标记为句子。对于每个句子,它都会调用 synthesize
方法来生成音频数组。然后,它将生成的音频数组连接起来,并在每个句子之间添加短暂的静音(0.25 秒)。现在我们已经设置了 TextToSpeechService
,我们需要为大型语言模型 (LLM) 服务准备 Ollama 服务器。为此,我们需要遵循以下步骤:
ollama pull llama2
。ollama serve
。完成这些步骤后,你的应用程序将能够使用 Ollama 服务器和 Llama-2 模型来生成对用户输入的响应。
接下来,我们将转到主要应用程序逻辑。首先,我们需要初始化以下组件:
base.en
) 转录用户输入。import time
import threading
import numpy as np
import whisper
import sounddevice as sd
from queue import Queue
from rich.console import Console
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
from langchain_community.llms import Ollama
from tts import TextToSpeechService
console = Console()
stt = whisper.load_model("base.en")
tts = TextToSpeechService()
template = """
You are a helpful and friendly AI assistant. You are polite, respectful, and aim to provide concise responses of less
than 20 words.
The conversation transcript is as follows:
{history}
And here is the user's follow-up: {input}
Your response:
"""
PROMPT = PromptTemplate(input_variables=["history", "input"], template=template)
chain = ConversationChain(
prompt=PROMPT,
verbose=False,
memory=ConversationBufferMemory(ai_prefix="Assistant:"),
llm=Ollama(),
)
现在,让我们定义必要的函数:
record_audio
:此函数在单独的线程中运行,使用 sounddevice.RawInputStream
从用户的麦克风捕获音频数据。每当有新的音频数据可用时,就会调用回调函数,并将数据放入 data_queue
中以供进一步处理。transcribe
:此函数利用 Whisper 实例将 data_queue
中的音频数据转录为文本。get_llm_response
:此函数将当前对话上下文提供给 Llama-2 语言模型(通过 Langchain的 ConversationalChain
)并检索生成的文本响应。play_audio
:此函数获取 Bark 文本转语音引擎生成的音频波形,并使用声音播放库(例如 sounddevice)将其播放给用户。def record_audio(stop_event, data_queue):
"""
Captures audio data from the user's microphone and adds it to a queue for further processing.
Args:
stop_event (threading.Event): An event that, when set, signals the function to stop recording.
data_queue (queue.Queue): A queue to which the recorded audio data will be added.
Returns:
None
"""
def callback(indata, frames, time, status):
if status:
console.print(status)
data_queue.put(bytes(indata))
with sd.RawInputStream(
samplerate=16000, dtype="int16", channels=1, callback=callback
):
while not stop_event.is_set():
time.sleep(0.1)
def transcribe(audio_np: np.ndarray) -> str:
"""
Transcribes the given audio data using the Whisper speech recognition model.
Args:
audio_np (numpy.ndarray): The audio data to be transcribed.
Returns:
str: The transcribed text.
"""
result = stt.transcribe(audio_np, fp16=False) # Set fp16=True if using a GPU
text = result["text"].strip()
return text
def get_llm_response(text: str) -> str:
"""
Generates a response to the given text using the Llama-2 language model.
Args:
text (str): The input text to be processed.
Returns:
str: The generated response.
"""
response = chain.predict(input=text)
if response.startswith("Assistant:"):
response = response[len("Assistant:") :].strip()
return response
def play_audio(sample_rate, audio_array):
"""
Plays the given audio data using the sounddevice library.
Args:
sample_rate (int): The sample rate of the audio data.
audio_array (numpy.ndarray): The audio data to be played.
Returns:
None
"""
sd.play(audio_array, sample_rate)
sd.wait()
然后,我们定义主应用程序循环。主应用程序循环引导用户完成对话交互,如下所示:
record_audio
函数来捕获用户的音频输入。transcribe
函数转录音频数据。get_llm_response
函数,该函数使用 Llama-2 语言模型生成响应。play_audio
函数播放给用户。if __name__ == "__main__":
console.print("[cyan]Assistant started! Press Ctrl+C to exit.")
try:
while True:
console.input(
"Press Enter to start recording, then press Enter again to stop."
)
data_queue = Queue() # type: ignore[var-annotated]
stop_event = threading.Event()
recording_thread = threading.Thread(
target=record_audio,
args=(stop_event, data_queue),
)
recording_thread.start()
input()
stop_event.set()
recording_thread.join()
audio_data = b"".join(list(data_queue.queue))
audio_np = (
np.frombuffer(audio_data, dtype=np.int16).astype(np.float32) / 32768.0
)
if audio_np.size > 0:
with console.status("Transcribing...", spinner="earth"):
text = transcribe(audio_np)
console.print(f"[yellow]You: {text}")
with console.status("Generating response...", spinner="earth"):
response = get_llm_response(text)
sample_rate, audio_array = tts.long_form_synthesize(response)
console.print(f"[cyan]Assistant: {response}")
play_audio(sample_rate, audio_array)
else:
console.print(
"[red]No audio recorded. Please ensure your microphone is working."
)
except KeyboardInterrupt:
console.print("\n[red]Exiting...")
console.print("[blue]Session ended.")
一旦所有东西都放在一起,我们就可以运行应用程序,由于 Bark 模型很大,即使是较小版本,应用程序在我的 MacBook 上运行也相当慢。因此,我稍微加快了视频的速度。对于那些拥有支持 CUDA 的计算机的人来说,它可能会运行得更快。以下是我们应用程序的主要功能:
对于那些旨在将此应用程序提升到生产就绪状态的人,建议进行以下增强:
最后,我们完成了简单的语音助手应用程序。语音识别、语言建模和文本转语音技术的结合展示了我们如何构建听起来很难但实际上可以在你的计算机上运行的东西。
先声智能的语音识别可以实时并准确地将用户朗读内容转换成对应的文字信息,让机器能够“听懂”人类语言。能够通过实时反馈识别出用户读的单词,并在后续评测中不停修复识别错误。服务主要是基于人工智能技术的语音交互解决方案。它利用语音识别技术将人类语音转化为文本或命令,从而实现人机交互的目的。
BM文本转换语音服务是一项 API 云服务,使您能够在现有应用程序或 Watsonx Assistant 中将书面文本转换为各种语言和语音的自然音频。使用用户的母语与他们互动。
Azure 说话人识别是一项语音服务功能,用于根据说话人的独特语音特征进行准确的验证和识别。这项服务可以帮助用户确定谁正在说话,无论是在一对一的交互中还是在包含多个说话人的场景下。
实时语音识别是一种先进的技术,能够在语音被说出的同时迅速将其转换为文字。它基于Deep Peak2端到端的建模方法,通过超过10万小时的训练数据和多采样率、多场景的声学模型,实现高精度的语音识别能力。特别是针对近场中文普通话,其识别准确率可达到98%。该技术不仅支持普通话及带轻微口音的中文,还支持英文的识别。通过使用大规模数据集训练的语言模型,它可以智能纠错并根据语音内容理解与停顿情况自动添加适当的标点符号,如句号、感叹号、问号等。实时语音识别服务可以通过WebSocket API或Android、iOS、Linux SDK等多种调用方式进行接入,适应多种操作系统和设备。
原文链接:http://www.bimant.com/blog/build-your-own-voice-assistant/