Gradio全解20——Streaming:流式傳輸的多模態應用(1)——Mistral-7B實現流式傳輸音頻:魔力8號球
- 前言
- 本篇摘要
- 20. Streaming:流式傳輸的多模態應用
- 20.1 Mistral-7B實現流式傳輸音頻:魔力8號球
- 20.1.1 工作原理
- 20.1.2 Inference API:在服務器上運行推理
- 1. text_to_image:文生圖任務
- 2. chat_completion:生成響應
- 20.1.3 Spaces ZeroGPU:動態GPU分配方案
- 1. ZeroGPU Spaces使用與托管指南
- 2. 其它技術性說明
- 3. ZeroGPU快速入門
- 20.1.4 Gradio用戶界面
- 20.1.5 基于Whisper模型轉錄語音與基于Mathstral-7B生成回答
- 20.1.6 基于Parler-TTS的語音合成與流式傳輸
- 參考文獻:
前言
本系列文章主要介紹WEB界面工具Gradio。Gradio是Hugging Face發布的簡易WebUI開發框架,它基于FastAPI和svelte,可以使用機器學習模型、python函數或API開發多功能界面,并可部署人工智能模型,是當前熱門的非常易于展示機器學習大語言模型LLM及擴散模型DM的WebUI框架。
本系列文章分為六部分:Gradio介紹、HuggingFace資源與工具庫、Gradio基礎功能實戰、LangChain詳解、Gradio與大模型融合實戰及Agent代理實戰、Gradio高級功能實戰。第一部分Gradio介紹,方便讀者對Gradio整體把握,包括三章內容:第一章先介紹Gradio的概念,包括詳細技術架構、歷史、應用場景、與其他框架Gradio/NiceGui/StreamLit/Dash/PyWebIO的區別,然后詳細講述Gradio的安裝與運行,安裝包括Linux/Win/Mac三類系統安裝,運行包括普通方式和熱重載方式;第二章介紹Gradio的4種部署方式,包括本地部署launch()、huggingface托管、FastAPI掛載和Gradio-Lite瀏覽器集成;第三章介紹Gradio的三種客戶端(Client),包括python客戶端、javascript客戶端和curl客戶端。第二部分講述著名網站Hugging Face的各類資源和工具庫,因為Gradio演示中經常用到Hugging Face的models,還有某些場景需要部署在Spaces,以及經常用到的transformers及datasets庫,包括兩章內容:第四章詳解三類資源models/datasets/spaces的使用,第五章實戰六類工具庫transformers/diffusers/datasets/PEFT/accelerate/optimum實戰。第三部分實戰Gradio基礎功能,進入本系列文章的核心,包括四章內容:第六章講解Gradio庫的模塊架構和環境變量,第七章講解Gradio高級抽象界面類Interface,第八章講解Gradio底層區塊類Blocks,第九章講解補充特性Additional Features。第四部分講述LangChain,包括四章內容:第十章講述LangChain基礎知識詳述,內容有優勢分析、學習資料、架構及LCEL,第十一章講述LangChain組件Chat models,第十二章講述組件Tools/Toolkits,第十三章講述其它五類主要組件:Text splitters/Document loaders/Embedding models/Vector stores/Retrievers。第五部分是Gradio與大模型融合及Agent代理實戰,包括四章內容:第十四章講解融合大模型的多模態聊天機器人組件Chatbot,第十五章講解使用transformers.agents構建Gradio,第十六章講述使用LangChain Agents構建Gradio及Gradio Tools,第十七章講述使用LangGraph構建Gradio。第六部分講述Gradio其它高級功能,包括三章內容:第十八章講述從Gradio App創建Discord Bot/Slack Bot/Website Widget,第十九章講述數據科學與繪圖Data Science And Plots,第二十章講述流式傳輸Streaming。
本系列文章講解細致,涵蓋Gradio及相關框架的大部分組件和功能,代碼均可運行并附有大量運行截圖,方便讀者理解并應用到開發中,Gradio一定會成為每個技術人員實現各種奇思妙想的最稱手工具。
本系列文章目錄如下:
- 《Gradio全解1——Gradio簡介》
- 《Gradio全解1——Gradio的安裝與運行》
- 《Gradio全解2——Gradio的3+1種部署方式實踐》
- 《Gradio全解2——瀏覽器集成Gradio-Lite》
- 《Gradio全解3——Gradio Client:python客戶端》
- 《Gradio全解3——Gradio Client:javascript客戶端》
- 《Gradio全解3——Gradio Client:curl客戶端》
- 《Gradio全解4——剖析Hugging Face:詳解三類資源models/datasets/spaces》
- 《Gradio全解5——剖析Hugging Face:實戰六類工具庫transformers/diffusers/datasets/PEFT/accelerate/optimum》
- 《Gradio全解6——Gradio庫的模塊架構和環境變量》
- 《Gradio全解7——Interface:高級抽象界面類(上)》
- 《Gradio全解7——Interface:高級抽象界面類(下)》
- 《Gradio全解8——Blocks:底層區塊類(上)》
- 《Gradio全解8——Blocks:底層區塊類(下)》
- 《Gradio全解9——Additional Features:補充特性(上)》
- 《Gradio全解9——Additional Features:補充特性(下)》
- 《Gradio全解10——LangChain基礎知識詳述》
- 《Gradio全解11——LangChain組件Chat models詳解》
- 《Gradio全解12——LangChain組件Tools/Toolkits詳解》
- 《Gradio全解13——LangChain其它五類組件詳解》
- 《Gradio全解14——Chatbot:融合大模型的多模態聊天機器人》
- 《Gradio全解15——使用transformers.agents構建Gradio》
- 《Gradio全解16——使用LangChain Agents構建Gradio及Gradio Tools》
- 《Gradio全解17——使用LangGraph構建Gradio》
- 《Gradio全解18——從Gradio App創建Discord Bot/Slack Bot/Website Widget》
- 《Gradio全解19——Data Science And Plots:數據科學與繪圖》
- 《Gradio全解20——Streaming:流式傳輸的多模態應用》
本章目錄如下:
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(1)——Mistral-7B實現流式傳輸音頻:魔力8號球》;
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(2)——基于Mini-Omni模型構建對話式聊天機器人》;
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(3)——實時語音識別技術(ASR)》;
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(4)——基于Groq的帶自動語音檢測功能的多模態Gradio應用》;
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(5)——基于YOLO與WebRTC的攝像頭實時目標檢測》;
- 《Gradio全解20——Streaming:流式傳輸的多模態應用(6)——RT-DETR模型構建視頻流目標檢測系統》。
本篇摘要
本章講述流式傳輸的應用,包括音頻、圖像和視頻格式的流式傳輸。
20. Streaming:流式傳輸的多模態應用
本章講述流式傳輸的多模態應用,包括音頻、圖像和視頻格式的流式傳輸。音頻應用包括流式傳輸音頻、構建音頻對話式聊天機器人、實時語音識別技術和自動語音檢測功能;圖像應用包括基于WebRTC的攝像頭實時目標檢測;視頻應用包括構建視頻流目標檢測系統。
20.1 Mistral-7B實現流式傳輸音頻:魔力8號球
在本指南中,我們將構建一個新穎的AI應用:會說話的魔力8號球🎱(Magic 8 Ball),以展示Gradio的音頻流式輸出功能。Magic 8 Ball是一種玩具,在你搖晃它后,它會回答任何問題,而我們的應用不僅能給出回答,還會用語音播報答案!這篇博客不會涵蓋所有實現細節,但代碼已在Hugging Face Spaces上開源:gradio/magic-8-ball。
20.1.1 工作原理
本例會說話的魔力8號球的工作原理:和經典的Magic 8 Ball一樣,用戶需要口頭提問,然后等待回應。在后臺,我們將使用Whisper進行語音轉錄,再通過大語言模型(LLM)生成Magic 8 Ball風格的答案,最后用Parler TTS將回答朗讀出來。
在講述邏輯實現之前,需要先學習一下要用到的知識點:Inference API和ZeroGPU,已掌握這兩個知識點的讀者可直接跳過。
20.1.2 Inference API:在服務器上運行推理
推理是使用訓練好的模型對新數據進行預測的過程,由于該過程可能計算密集,在專用或外部服務上運行是一個值得考慮的選擇,關于Hugging Face 的推理請參閱: Run Inference on servers。huggingface_hub庫提供了統一接口,可對托管在Hugging Face Hub上的模型使用多種服務來運行推理,比如:
- HF Inference API:一種無服務器解決方案,可以免費在Hugging Face的基礎設施上運行模型推理。該服務是快速入門、測試不同模型和原型化AI產品的便捷途徑;
- 第三方供應商:由外部供應商provider(如Together、Sambanova等)提供的各類無服務器解決方案。這些供應商按用量付費模式(pay-as-you-go)提供生產就緒的API,這是以免維護、可擴展的解決方案將AI集成至產品中的最快方式。在支持的供應商和任務章節列出了具體供應商信息: Supported providers and tasks;
- 推理終端節點:一個可將模型輕松部署至生產環境的產品,推理由Hugging Face在用戶選擇的云供應商提供的專用全托管基礎設施上運行。
這些服務均可通過InferenceClient對象調用,該對象替代了舊版InferenceApi客戶端,新增了對特定任務和第三方供應商的支持,從舊版客戶端遷移至新客戶端的方法請參閱Legacy InferenceAPI client。
InferenceClient是一個通過HTTP調用與官方API交互的Python客戶端。如果用戶希望直接使用常用工具(如curl、Postman等)發起HTTP請求,請參閱Inference API或Inference Endpoints文檔頁面。對于網頁開發,官方已發布JS Client。如果用戶從事游戲開發,可以關注官方的 C# project。
1. text_to_image:文生圖任務
讓我們從一個文生圖任務開始入門:
from huggingface_hub import InferenceClient# Example with an external provider (e.g. replicate)
replicate_client = InferenceClient(provider="replicate",api_key="my_replicate_api_key",
)
replicate_image = replicate_client.text_to_image("A flying car crossing a futuristic cityscape.",model="black-forest-labs/FLUX.1-schnell",
)
replicate_image.save("flying_car.png")
在上述示例中,我們使用第三方服務提供商Replicate初始化了一個 InferenceClient。當使用第三方提供商時,必須指定要使用的模型,該模型ID 必須是Hugging Face Hub上的模型標識符,而非第三方提供商自身的模型ID。在本例中,我們通過文本提示生成了一張圖像,返回值為PIL.Image對象,可保存為文件,更多細節請參閱文檔:text_to_image()。
2. chat_completion:生成響應
接下來讓我們看一個使用chat_completion() API的示例,該任務利用大語言模型根據消息列表生成響應:
from huggingface_hub import InferenceClientmessages = [{"role": "user","content": "What is the capital of France?",}
]
client = InferenceClient(provider="together",model="meta-llama/Meta-Llama-3-8B-Instruct",api_key="my_together_api_key",
)
client.chat_completion(messages, max_tokens=100)
輸出為:
ChatCompletionOutput(choices=[ChatCompletionOutputComplete(finish_reason="eos_token",index=0,message=ChatCompletionOutputMessage(role="assistant", content="The capital of France is Paris.", name=None, tool_calls=None),logprobs=None,)],created=1719907176,id="",model="meta-llama/Meta-Llama-3-8B-Instruct",object="text_completion",system_fingerprint="2.0.4-sha-f426a33",usage=ChatCompletionOutputUsage(completion_tokens=8, prompt_tokens=17, total_tokens=25),
)
在上述示例中,我們創建客戶端時使用了第三方服務提供商(Together AI)并指定了所需模型(“meta-llama/Meta-Llama-3-8B-Instruct”)。隨后我們提供了待補全的消息列表(此處為單個問題),并向API傳遞了額外參數(max_token=100)。
輸出結果為遵循OpenAI規范的ChatCompletionOutput對象,生成內容可通過output.choices[0].message.content獲取。更多細節請參閱chat_completion()文檔。該API設計簡潔,但并非所有參數和選項都會向終端用戶開放或說明,如需了解各任務支持的全部參數,請查閱Inference Providers - API Reference。
20.1.3 Spaces ZeroGPU:動態GPU分配方案
ZeroGPU是Hugging Face Spaces平臺上專為AI模型和演示優化的GPU共用基礎設施,采用動態分配機制實現NVIDIA A100顯卡的按需調用與釋放,其主要特性包括:
- 免費GPU資源:為Spaces用戶提供零成本GPU算力支持;
- 多GPU并發:支持Spaces上的單個應用同時調用多塊顯卡進行運算。
與傳統單GPU分配模式相比,ZeroGPU的高效系統通過資源利用率最大化和能效比最優化,有效降低了開發者、研究機構及企業部署AI模型的技術門檻。更多信息請參閱:Spaces ZeroGPU: Dynamic GPU Allocation for Spaces。
1. ZeroGPU Spaces使用與托管指南
使用ZeroGPU,就要用到ZeroGPU Spaces。對于使用現有ZeroGPU Spaces:
- 所有用戶均可免費使用(可查看精選Space列表:ZeroGPU Spaces);
- PRO用戶在使用任何ZeroGPU Spaces時,享有5倍的每日使用配額和GPU隊列的最高優先級。
用戶也可托管自有ZeroGPU Spaces,但個人和企業需訂閱不同版本:
- 個人賬戶:需訂閱PRO版,在新建Gradio SDK Space時可選擇ZeroGPU硬件配置,最多創建10個ZeroGPU Space;
- 企業用戶:需訂閱Enterprise Hub,即可為全體成員啟用ZeroGPU Spaces功能,最多創建50個ZeroGPU Space。
2. 其它技術性說明
技術規格:
- GPU類型:NVIDIA A100;
- 可用顯存:每個工作負載為40GB
兼容性說明:ZeroGPU Spaces設計為兼容大多數基于PyTorch的GPU Spaces,雖然對Hugging Face高級庫(如transformers和diffusers)的兼容性更優,但用戶需注意以下事項:
- 目前ZeroGPU Spaces僅兼容Gradio SDK;
- 與標準GPU Spaces相比,ZeroGPU Spaces的兼容性可能受限;
- 某些場景下可能出現意外問題。
支持版本:
- Gradio:4+;
- PyTorch:2.0.1、2.1.2、2.2.2、2.4.0(注:由于PyTorch漏洞,不支持2.3.x版本);
- Python:3.10.13
3. ZeroGPU快速入門
用戶的Space中使用ZeroGPU需遵循以下步驟:
- 確保在Space設置中已選擇ZeroGPU硬件;
- 導入spaces模塊;
- 使用@spaces.GPU裝飾器標記依賴GPU的函數。
此裝飾機制使得Space能在函數調用時申請GPU資源,并在執行完成后自動釋放。示例如下:
import spaces
from diffusers import DiffusionPipelinepipe = DiffusionPipeline.from_pretrained(...)
pipe.to('cuda')
@spaces.GPU
def generate(prompt):return pipe(prompt).images
gr.Interface(fn=generate,inputs=gr.Text(),outputs=gr.Gallery(),
).launch()
注:@spaces.GPU裝飾器在非ZeroGPU環境中將不產生任何作用,以確保不同配置下的兼容性。另外,@spaces.GPU裝飾器還可設置時長管理。若函數預計超過默認的60秒GPU運行時長,可指定自定義時長:
@spaces.GPU(duration=120)
def generate(prompt):return pipe(prompt).images
該設置將函數最大運行時限定為120秒,但為快速執行的函數指定更短時長,可提升Space訪客的隊列優先級。
通過ZeroGPU,開發者能創建更高效、可擴展的Space,在最大化GPU利用率的同時實現成本優化。
20.1.4 Gradio用戶界面
首先,我們定義UI界面,并為所有Python邏輯預留占位符。
import gradio as grwith gr.Blocks() as block:gr.HTML(f"""<h1 style='text-align: center;'> Magic 8 Ball 🎱 </h1><h3 style='text-align: center;'> Ask a question and receive wisdom </h3><p style='text-align: center;'> Powered by <a href="https://github.com/huggingface/parler-tts"> Parler-TTS</a>""")with gr.Group():with gr.Row():audio_out = gr.Audio(label="Spoken Answer", streaming=True, autoplay=True)answer = gr.Textbox(label="Answer")state = gr.State()with gr.Row():audio_in = gr.Audio(label="Speak your question", sources="microphone", type="filepath")audio_in.stop_recording(generate_response, audio_in, [state, answer, audio_out])\.then(fn=read_response, inputs=state, outputs=[answer, audio_out])block.launch()
我們將音頻輸出組件、文本框組件和音頻輸入組件分別放置在不同行中。為了實現服務器端的音頻流式傳輸,我們會在輸出音頻組件中設置streaming=True。同時,我們還會啟用autoplay=True,以便音頻在準備就緒時自動播放。此外,我們將利用音頻輸入組件的stop_recording事件,在用戶停止麥克風錄音時觸發應用邏輯。
我們將邏輯分為兩部分:
- generate_response:負責接收錄音音頻,進行語音轉錄,并通過大語言模型生成回答。生成的回答會存儲在gr.State變量中,并傳遞給下一步的read_response函數。
- read_response:負責將文本回答轉換為語音音頻。
這樣拆分的原因是,由于生成回答的部分可以通過Hugging Face的Inference API完成,無需占用GPU配額,因此我們避免將這部分邏輯放在GPU函數中,以節省資源。而只有read_response需要GPU資源,它將運行在Hugging Face的ZeroGPU上,該服務有基于時長的配額限制,因此去掉generate_response可以降低費用。
20.1.5 基于Whisper模型轉錄語音與基于Mathstral-7B生成回答
如上所述,我們將使用Hugging Face的Inference API來轉錄音頻,并通過LLM 生成回答。邏輯實現時,首先實例化客戶端,調用automatic_speech_recognition方法來轉錄音頻,該方法會自動使用 Hugging Face推理服務器上的Whisper模型。接著將問題輸入LLM模型Mistral-7B-Instruct生成回答,我們通過系統格式消息(system message)讓LLM模擬Magic 8 Ball的風格進行回復。
generate_response函數還會向輸出文本框和音頻組件發送空更新(即返回 None)。這樣做的目的是:顯示 Gradio 的進度指示器(progress tracker),讓用戶感知處理狀態;延遲顯示答案,直到音頻生成完成,確保文字和語音同步呈現。代碼如下:
from huggingface_hub import InferenceClientclient = InferenceClient(token=os.getenv("HF_TOKEN"))def generate_response(audio):gr.Info("Transcribing Audio", duration=5)question = client.automatic_speech_recognition(audio).textmessages = [{"role": "system", "content": ("You are a magic 8 ball.""Someone will present to you a situation or question and your job ""is to answer with a cryptic adage or proverb such as ""'curiosity killed the cat' or 'The early bird gets the worm'.""Keep your answers short and do not include the phrase 'Magic 8 Ball' in your response. If the question does not make sense or is off-topic, say 'Foolish questions get foolish answers.'""For example, 'Magic 8 Ball, should I get a dog?', 'A dog is ready for you but are you ready for the dog?'")},{"role": "user", "content": f"Magic 8 Ball please answer this question - {question}"}]response = client.chat_completion(messages, max_tokens=64, seed=random.randint(1, 5000),model="mistralai/Mistral-7B-Instruct-v0.3")response = response.choices[0].message.content.replace("Magic 8 Ball", "").replace(":", "")return response, None, None
20.1.6 基于Parler-TTS的語音合成與流式傳輸
現在我們有了文本響應,我們將使用Parler TTS將其朗讀出來。read_response 函數是一個Python生成器,生成器將在音頻準備好時逐塊產生下一段音頻。函數中,我們將使用Mini v0.1進行特征提取,但使用Jenny微調版本進行語音生成,以確保語音在多次生成中保持一致。
Parler-TTS Mini v0.1是一個輕量級文本轉語音(TTS)模型,它經過10.5千小時的音頻數據訓練,可以生成高質量、自然的聲音,并通過簡單的文本提示控制聲音特征(例如性別、背景噪音、語速、音調和混響等),它是Parler-TTS項目的首個發布模型,該項目旨在為社區提供TTS訓練資源和數據集預處理代碼。Jenny微調版本是Parler-TTS Mini v0.1的微調版本,基于Jenny(愛爾蘭口音)30小時單人高品質數據集進行訓練,適合用于訓練TTS模型。使用方法與 Parler-TTS v0.1大致相同,只需在語音描述中指定關鍵字“Jenny”即可。
使用Transformers進行流式音頻傳輸需要一個自定義的Streamer類,可以在這里查看實現細節:magic-8-ball/streamer.py。此外,我們會將輸出轉換為字節流,以便從后端更快地流式傳輸。
from streamer import ParlerTTSStreamer
from transformers import AutoTokenizer, AutoFeatureExtractor, set_seed
import numpy as np
import spaces
import torch
from threading import Threaddevice = "cuda:0" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
torch_dtype = torch.float16 if device != "cpu" else torch.float32
repo_id = "parler-tts/parler_tts_mini_v0.1"
jenny_repo_id = "ylacombe/parler-tts-mini-jenny-30H"model = ParlerTTSForConditionalGeneration.from_pretrained(jenny_repo_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True
).to(device)
tokenizer = AutoTokenizer.from_pretrained(repo_id)
feature_extractor = AutoFeatureExtractor.from_pretrained(repo_id)
sampling_rate = model.audio_encoder.config.sampling_rate
frame_rate = model.audio_encoder.config.frame_rate@spaces.GPU
def read_response(answer):play_steps_in_s = 2.0play_steps = int(frame_rate * play_steps_in_s)description = "Jenny speaks at an average pace with a calm delivery in a very confined sounding environment with clear audio quality."description_tokens = tokenizer(description, return_tensors="pt").to(device)streamer = ParlerTTSStreamer(model, device=device, play_steps=play_steps)prompt = tokenizer(answer, return_tensors="pt").to(device)generation_kwargs = dict(input_ids=description_tokens.input_ids,prompt_input_ids=prompt.input_ids,streamer=streamer,do_sample=True,temperature=1.0,min_new_tokens=10,)set_seed(42)thread = Thread(target=model.generate, kwargs=generation_kwargs)thread.start()for new_audio in streamer:print(f"Sample of length: {round(new_audio.shape[0] / sampling_rate, 2)} seconds")yield answer, numpy_to_mp3(new_audio, sampling_rate=sampling_rate)
運行界面如下:
參考文獻:
- Streaming AI Generated Audio
- Run Inference on servers
- Spaces ZeroGPU: Dynamic GPU Allocation for Spaces