音訊課程文件

建立語音助手

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

建立語音助手

在本節中,我們將整合三個我們已經有動手經驗的模型來構建一個端到端的語音助手,名為 Marvin 🤖。與亞馬遜的 Alexa 或蘋果的 Siri 類似,Marvin 是一個虛擬語音助手,它響應特定的“喚醒詞”,然後監聽語音查詢,最後用語音回答。

我們可以將語音助手流程分解為四個階段,每個階段都需要一個獨立的模型。

1. 喚醒詞檢測

語音助手持續監聽裝置麥克風的音訊輸入,但只有當特定的“喚醒詞”或“觸發詞”被說出時,它們才會啟動。

喚醒詞檢測任務由一個小型裝置端音訊分類模型處理,該模型比語音識別模型小得多、輕得多,通常只有幾百萬個引數,而語音識別模型則有幾億個引數。因此,它可以在您的裝置上持續執行而不會耗盡電池。只有當檢測到喚醒詞時,才會啟動較大的語音識別模型,之後再將其關閉。

2. 語音轉錄

流程的下一個階段是將語音查詢轉錄為文字。在實踐中,由於音訊檔案體積較大,將音訊檔案從本地裝置傳輸到雲端速度很慢,因此更有效的方法是直接在裝置上使用自動語音識別 (ASR) 模型進行轉錄,而不是使用雲端模型。裝置端模型可能比雲端託管的模型小,因此準確性較低,但更快的推理速度使其物有所值,因為我們可以近乎即時地執行語音識別,我們的語音話語在說話時就被轉錄。

我們現在對語音識別過程非常熟悉,所以這應該很簡單!

3. 語言模型查詢

既然我們知道了使用者問了什麼,我們現在需要生成一個回應!此任務的最佳候選模型是大型語言模型 (LLM),因為它們能夠有效地理解文字查詢的語義並生成合適的回應。

由於我們的文字查詢很小(只有幾個文字標記),而語言模型很大(幾十億個引數),執行 LLM 推理最有效的方式是將我們的文字查詢從裝置傳送到雲端執行的 LLM,生成文本回應,然後將回應返回到裝置。

4. 合成語音

最後,我們將使用文字到語音 (TTS) 模型將文本回應合成為語音。這在裝置上完成,但您也可以在雲端執行 TTS 模型,生成音訊輸出並將其傳輸回裝置。

我們已經做過幾次了,所以這個過程會非常熟悉!

以下部分需要使用麥克風錄製語音輸入。由於 Google Colab 機器不相容麥克風,建議您在本地執行此部分,無論是使用 CPU 還是 GPU(如果您有本地訪問許可權)。檢查點大小已選擇為足夠小,以便在 CPU 上足夠快地執行,因此即使沒有 GPU,您仍然可以獲得良好的效能。

喚醒詞檢測

語音助手流程的第一個階段是檢測喚醒詞是否被說出,我們需要為此任務找到一個合適的預訓練模型!您會記得在音訊分類預訓練模型部分中,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,或“Hey Siri”用於蘋果的 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
)
如果您使用的是 GPU,可以增加檢查點大小以使用Whisper Small English檢查點,這將提供更好的轉錄準確性,並且仍在所需的延遲閾值內。只需將模型 ID 替換為:"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,並使用推理 API 輕鬆查詢模型。

首先,讓我們前往 Hugging Face Hub。為了找到我們的 LLM,我們將使用🤗 Open LLM Leaderboard,這是一個根據四項生成任務的效能對 LLM 模型進行排名的 Space。我們將透過“instruct”進行搜尋,以過濾掉經過指令微調的模型,因為這些模型應該更適合我們的查詢任務。

我們將使用tiiuae/falcon-7b-instruct檢查點,這是一個由TII開發的 7B 引數僅解碼器語言模型,經過混合聊天和指令資料集的微調。您可以使用 Hugging Face Hub 上任何已啟用“託管推理 API”的 LLM,只需檢視模型卡右側的小部件即可。

推理 API 允許我們將 HTTP 請求從本地機器傳送到 Hub 上託管的 LLM,並以 `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'

你會注意到使用推理 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 和精心設計的提示來解釋自然語言輸入,並使用一組精選工具來提供多模態輸出。

我們來例項化一個代理。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.run` 並傳入我們的文字提示。舉例來說,我們將讓它生成一張貓的影像 🐈(希望它看起來比這個表情符號好一點)。

agent.run("Generate an image of a cat")
請注意,首次呼叫此函式將觸發模型權重的下載,這可能需要一些時間,具體取決於您的 Hub 下載速度。

就這麼簡單!代理解釋了我們的提示,並在幕後使用穩定擴散來生成影像,而我們無需擔心載入模型、編寫函式或執行程式碼。

現在我們可以用 Transformer Agent 替換語音助手中的 LLM 查詢函式和文字合成步驟,因為 Agent 將為我們完成這兩項步驟。

launch_fn()
transcription = transcribe()
agent.run(transcription)

嘗試說出相同的提示“生成一張貓的圖片”,看看系統如何響應。如果您向 Agent 提出一個簡單的問答查詢,Agent 將以文字形式回答。您可以透過要求它返回圖片或語音來鼓勵它生成多模態輸出。例如,您可以要求它:“生成一張貓的圖片,新增標題,並朗讀標題。”

雖然 Agent 比我們第一個迭代的 Marvin 🤖 助手更靈活,但以這種方式概括語音助手任務可能會導致在標準語音助手查詢上的效能下降。為了恢復效能,您可以嘗試使用效能更好的 LLM 檢查點,例如 OpenAI 的檢查點,或者定義一組特定於語音助手任務的自定義工具

< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.