在前幾篇的文章中我們了解SeamlessM4T的轉譯流程,基本上是將語音或文本先轉成目標語言文本後,再送入T2U(Text-to-Unit)模型,最後將語音單元送入Vocoder,進而達成輸出語音聲波訊號的目的。然而早在2022年MetaAI就已提出Direct S2ST的翻譯系統(文獻),其中提到訓練模型有兩種路徑,一種是直接將語音轉成單元(Unit),中間並沒有經過轉成文本的階段;另一種路徑還是經過轉呈文本作為第一種路徑的輔助,以提升轉譯正確率。這讓筆者對於如何將語音轉成單元很好奇,因為這對於沒有文字系統的語言非常有用的,沒有文字系統的語言多半也是弱勢語言,若能藉由轉成單元訓練AI,或許是一個保存語言的可行方法。
在SeamlessM4T的開源程式碼中,其實還是有保留S2U(Speech-to-Unit)的功能,使用模型UnitExtractor的predict函式即可達成。
我們來研究一下該程式碼(程式碼參考這裡):
class UnitExtractor(nn.Module):
"""Unit Extractor which converts raw audio into units."""
# 初始化一個unit extractor
def __init__(
self,
model_name_or_card: Union[str, AssetCard],
kmeans_uri: str,
device: Device,
dtype: DataType = torch.float32,
):
super().__init__()
self.wav2vec2_model: Wav2Vec2Model = Translator.load_model_for_inference(
load_wav2vec2_model, model_name_or_card, device, dtype
)
self.model = Wav2Vec2LayerOutputModel(self.wav2vec2_model)
self.device = device
self.decode_audio = AudioDecoder(dtype=torch.float32, device=device)
self.collate = Collater(pad_idx=2, pad_to_multiple=2)
self.kmeans_model = KmeansModel(kmeans_uri, device)
# unit extractor的其中一個函式predict
@torch.inference_mode()
def predict(
self,
audio: Union[str, Tensor],
out_layer_idx: int,
sample_rate: int = 16000,
) -> Tensor:
if isinstance(audio, str):
with Path(audio).open("rb") as fb:
block = MemoryBlock(fb.read())
# 對語音檔做解碼
decoded_audio = self.decode_audio(block)
else:
decoded_audio = {
"waveform": audio,
"sample_rate": sample_rate,
"format": -1,
}
src = self.collate(decoded_audio)["waveform"]
x = src["seqs"]
x = x.view(1, -1)
x = F.layer_norm(x, x.shape)
batch = SequenceBatch(seqs=x, seq_lens=src["seq_lens"])
# 取得解碼後的語音序列特徵
features = self.model(batch, out_layer_idx).squeeze(0)
# 用k-means分群演算法取得單元
units = self.kmeans_model(features)
return units
# unit extractor的其中一個函式resynthesize_audio
# 其實就是用vocoder來對單元合成語音
@staticmethod
def resynthesize_audio(
units: Tensor,
src_lang: str,
device: Device,
vocoder_name: str = "vocoder_36langs",
) -> Tensor:
def reduce_list(lst):
return [key for key, _ in groupby(lst)]
reduced_units = reduce_list(units.cpu().tolist())
vocoder: Vocoder = Translator.load_model_for_inference(
load_vocoder_model, vocoder_name, device, torch.float32
)
wav = vocoder(reduced_units, src_lang, spkr=-1, dur_prediction=True)
return wav
MetaAI開發出來的S2UT模型適用於沒有文字的語言,因為當中的S2U的功能不須經過文本的轉換,直接將語音轉成單元,直接將單元送入Vocoder以生成語音輸出。透過程式碼的觀察,在S2U的過程是找出語音輸入解碼後,找出其特徵,用k-means分群演算法分出Unit,而k-means演算法屬於非監督式學習,所以語音轉成單元模型不需要事先標註答案,自行透過特徵進行分群。不過正確來說S2UT屬於非監督式學習中的自監督式學習。自督導式學習是在正式開始模型訓練前,需要大量沒有標註的資料讓機器進行預訓練,用來培養它的基本能力。當機器有了基本能力後,只需要提供少量任務的標註資料,再經過微調就能進行預測。然而單元到底是什麼呢? 下一篇我們來介紹。