透過 SpeechT5 實現語音合成、識別等多種功能
我們很高興地宣佈,SpeechT5 現已在 🤗 Transformers 中可用,這是一個開源庫,提供了易於使用的最先進機器學習模型的實現。
SpeechT5 最初在微軟亞洲研究院的論文 SpeechT5: Unified-Modal Encoder-Decoder Pre-Training for Spoken Language Processing 中被描述。該論文作者釋出的官方檢查點可在 Hugging Face Hub 上找到。
如果你想直接上手,這裡有一些 Spaces 上的演示
引言
SpeechT5 不是一種,不是兩種,而是將三種語音模型集於一個架構中。
它可以做
- 語音到文字 (speech-to-text) 用於自動語音識別或說話人識別,
- 文字到語音 (text-to-speech) 用於合成音訊,以及
- 語音到語音 (speech-to-speech) 用於在不同聲音之間轉換或執行語音增強。
SpeechT5 背後的主要思想是在文字到語音、語音到文字、文字到文字和語音到語音的混合資料上預訓練一個單一模型。這樣,模型可以同時從文字和語音中學習。這種預訓練方法的結果是一個模型,它擁有一個由文字和語音共享的 統一空間 的隱藏表示。
SpeechT5 的核心是一個常規的 Transformer 編碼器-解碼器 模型。就像任何其他 Transformer 一樣,編碼器-解碼器網路使用隱藏表示來建模序列到序列的轉換。這個 Transformer 主幹對於所有 SpeechT5 任務都是相同的。
為了讓同一個 Transformer 能夠同時處理文字和語音資料,添加了所謂的 前置網路 (pre-nets) 和 後置網路 (post-nets)。前置網路的任務是將輸入的文字或語音轉換為 Transformer 使用的隱藏表示。後置網路接收 Transformer 的輸出,並將其再次轉換為文字或語音。
下圖展示了 SpeechT5 的架構(引自原始論文)。

在預訓練期間,所有的前置網路和後置網路都同時使用。預訓練之後,整個編碼器-解碼器主幹在一個單一任務上進行微調。這樣一個微調後的模型只使用特定於該任務的前置網路和後置網路。例如,要使用 SpeechT5 進行文字到語音轉換,你需要為文字輸入換上文字編碼器前置網路,為語音輸出換上語音解碼器前置和後置網路。
注意:儘管微調後的模型最初使用來自共享預訓練模型的同一組權重,但最終版本最終都大不相同。例如,你不能拿一個微調過的 ASR 模型,換掉前置網路和後置網路來得到一個能工作的 TTS 模型。SpeechT5 很靈活,但沒 *那麼* 靈活。
文字到語音
SpeechT5 是我們新增到 🤗 Transformers 中的 第一個文字到語音模型,我們計劃在不久的將來新增更多的 TTS 模型。
對於 TTS 任務,模型使用以下前置網路和後置網路
文字編碼器前置網路。 一個文字嵌入層,將文字 token 對映到編碼器期望的隱藏表示。類似於 NLP 模型如 BERT 中發生的情況。
語音解碼器前置網路。 它將一個對數梅爾頻譜圖作為輸入,並使用一系列線性層將頻譜圖壓縮成隱藏表示。這個設計取自 Tacotron 2 TTS 模型。
語音解碼器後置網路。 它預測一個殘差來新增到輸出頻譜圖上,並用於改善結果,也來自 Tacotron 2。
微調模型的架構如下所示。

這裡有一個完整的示例,展示如何使用 SpeechT5 文字到語音模型來合成語音。你也可以在這個互動式的 Colab notebook中跟著操作。
SpeechT5 尚未在最新版本的 Transformers 中提供,因此你需要從 GitHub 安裝它。同時安裝額外的依賴 sentencepiece,然後重啟你的執行時。
pip install git+https://github.com/huggingface/transformers.git
pip install sentencepiece
首先,我們從 Hub 載入微調模型,以及用於 tokenization 和特徵提取的處理器物件。我們將使用的類是 SpeechT5ForTextToSpeech
。
from transformers import SpeechT5Processor, SpeechT5ForTextToSpeech
processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts")
model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts")
接下來,對輸入文字進行分詞。
inputs = processor(text="Don't count the days, make the days count.", return_tensors="pt")
SpeechT5 TTS 模型不限於為單個說話人建立語音。相反,它使用所謂的 說話人嵌入 (speaker embeddings),這些嵌入捕獲了特定說話人的聲音特徵。我們將從 Hub 上的一個數據集中載入這樣一個說話人嵌入。
from datasets import load_dataset
embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")
import torch
speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)
說話人嵌入是一個形狀為 (1, 512) 的張量。這個特定的說話人嵌入描述了一個女性的聲音。這些嵌入是使用這個指令碼從 CMU ARCTIC 資料集中獲得的,但任何 X-Vector 嵌入都應該有效。
現在,我們可以告訴模型根據輸入的 token 和說話人嵌入來生成語音。
spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings)
這將輸出一個形狀為 (140, 80) 的張量,包含一個對數梅爾頻譜圖。第一個維度是序列長度,它可能在每次執行時都不同,因為語音解碼器前置網路總是對輸入序列應用 dropout。這為生成的語音增加了一些隨機變化。
為了將預測的對數梅爾頻譜圖轉換為實際的語音波形,我們需要一個 聲碼器 (vocoder)。理論上,你可以使用任何適用於 80-bin 梅爾頻譜圖的聲碼器,但為了方便,我們在 Transformers 中提供了一個基於 HiFi-GAN 的聲碼器。這個聲碼器的權重,以及微調 TTS 模型的權重,都由 SpeechT5 的原作者友情提供。
載入聲碼器就像載入任何其他 🤗 Transformers 模型一樣簡單。
from transformers import SpeechT5HifiGan
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")
要從頻譜圖生成音訊,請執行以下操作
with torch.no_grad():
speech = vocoder(spectrogram)
我們還提供了一個快捷方式,這樣你就不需要製作頻譜圖的中間步驟。當你將聲碼器物件傳遞給 generate_speech
時,它會直接輸出語音波形。
speech = model.generate_speech(inputs["input_ids"], speaker_embeddings, vocoder=vocoder)
最後,將語音波形儲存到檔案中。SpeechT5 使用的取樣率始終是 16 kHz。
import soundfile as sf
sf.write("tts_example.wav", speech.numpy(), samplerate=16000)
輸出的聲音聽起來是這樣的(下載音訊)
TTS 模型就介紹到這裡!讓聲音聽起來好的關鍵是使用正確的說話人嵌入。
你可以在 Spaces 上玩一個互動式演示。
💡 有興趣學習如何在自己的資料集或語言上 微調 SpeechT5 TTS 嗎?請檢視這個 Colab notebook,其中有詳細的流程演練。
用於聲音轉換的語音到語音
從概念上講,使用 SpeechT5 進行語音到語音建模與文字到語音是相同的。只需將文字編碼器前置網路換成語音編碼器前置網路即可。模型的其餘部分保持不變。

語音編碼器前置網路 與 wav2vec 2.0 的特徵編碼模組相同。它由卷積層組成,將輸入波形下采樣為音訊幀表示序列。
作為語音到語音任務的一個例子,SpeechT5 的作者提供了一個用於聲音轉換的微調檢查點。要使用它,首先從 Hub 載入模型。請注意,現在的模型類是 SpeechT5ForSpeechToSpeech
。
from transformers import SpeechT5Processor, SpeechT5ForSpeechToSpeech
processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_vc")
model = SpeechT5ForSpeechToSpeech.from_pretrained("microsoft/speecht5_vc")
我們將需要一些語音音訊作為輸入。為了這個例子,我們將從 Hub 上的一個小語音資料集中載入音訊。你也可以載入自己的語音波形,只要它們是單聲道且使用 16 kHz 的取樣率。我們這裡使用的資料集樣本已經是這種格式了。
from datasets import load_dataset
dataset = load_dataset("hf-internal-testing/librispeech_asr_demo", "clean", split="validation")
dataset = dataset.sort("id")
example = dataset[40]
接下來,對音訊進行預處理,使其符合模型期望的格式。
sampling_rate = dataset.features["audio"].sampling_rate
inputs = processor(audio=example["audio"]["array"], sampling_rate=sampling_rate, return_tensors="pt")
與 TTS 模型一樣,我們需要說話人嵌入。它們描述了目標聲音聽起來的樣子。
import torch
embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")
speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)
我們還需要載入聲碼器,將生成的頻譜圖轉換為音訊波形。讓我們使用與 TTS 模型相同的聲碼器。
from transformers import SpeechT5HifiGan
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")
現在我們可以透過呼叫模型的 generate_speech
方法來執行語音轉換。
speech = model.generate_speech(inputs["input_values"], speaker_embeddings, vocoder=vocoder)
import soundfile as sf
sf.write("speech_converted.wav", speech.numpy(), samplerate=16000)
更換成不同的聲音就像載入一個新的說話人嵌入一樣簡單。你甚至可以從你自己的聲音中製作一個嵌入!
原始輸入 (下載)
轉換後的聲音 (下載)
請注意,此示例中轉換後的音訊在句子結束前就中斷了。這可能是由於兩個句子之間的停頓,導致 SpeechT5(錯誤地)預測已到達序列的末尾。用另一個例子試試,你會發現轉換通常是正確的,但有時會過早停止。
你可以在這裡玩一個互動式演示。🔥
用於自動語音識別的語音到文字
ASR 模型使用以下前置網路和後置網路
語音編碼器前置網路。 這與語音到語音模型使用的前置網路相同,由 wav2vec 2.0 的 CNN 特徵編碼器層組成。
文字解碼器前置網路。 類似於 TTS 模型使用的編碼器前置網路,它使用嵌入層將文字 token 對映到隱藏表示。(在預訓練期間,這些嵌入在文字編碼器和解碼器前置網路之間是共享的。)
文字解碼器後置網路。 這是所有網路中最簡單的,由一個單一的線性層組成,該層將隱藏表示投影到詞彙表上的機率。
微調模型的架構如下所示。

如果你之前嘗試過任何其他 🤗 Transformers 語音識別模型,你會發現 SpeechT5 同樣易於使用。最快入門的方法是使用 pipeline。
from transformers import pipeline
generator = pipeline(task="automatic-speech-recognition", model="microsoft/speecht5_asr")
作為語音音訊,我們將使用與上一節中相同的輸入,但任何音訊檔案都適用,因為 pipeline 會自動將音訊轉換為正確的格式。
from datasets import load_dataset
dataset = load_dataset("hf-internal-testing/librispeech_asr_demo", "clean", split="validation")
dataset = dataset.sort("id")
example = dataset[40]
現在我們可以要求 pipeline 處理語音並生成文字轉錄。
transcription = generator(example["audio"]["array"])
列印轉錄結果
a man said to the universe sir i exist
聽起來完全正確!SpeechT5 使用的 tokenizer 非常基礎,並且在字元級別上工作。因此,ASR 模型不會輸出任何標點符號或大寫字母。
當然,也可以直接使用模型類。首先,載入微調模型和處理器物件。現在的類是 SpeechT5ForSpeechToText
。
from transformers import SpeechT5Processor, SpeechT5ForSpeechToText
processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_asr")
model = SpeechT5ForSpeechToText.from_pretrained("microsoft/speecht5_asr")
預處理語音輸入
sampling_rate = dataset.features["audio"].sampling_rate
inputs = processor(audio=example["audio"]["array"], sampling_rate=sampling_rate, return_tensors="pt")
最後,告訴模型從語音輸入生成文字 token,然後使用處理器的解碼函式將這些 token 轉換為實際文字。
predicted_ids = model.generate(**inputs, max_length=100)
transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)
體驗語音到文字任務的互動式演示。
結論
SpeechT5 是一個有趣的模型,因為——與大多數其他模型不同——它允許你用相同的架構執行多個任務。只有前置網路和後置網路會改變。透過在這些組合任務上預訓練模型,它在微調後能夠更好地完成每個單獨的任務。
我們只包含了語音識別 (ASR)、語音合成 (TTS) 和聲音轉換任務的檢查點,但論文還提到該模型已成功用於語音翻譯、語音增強和說話人識別。它非常多才多藝!