Open In ColabGitHub 上执行或查看/下载此 notebook

SpeechBrain 简介

SpeechBrain 是一个基于 PyTorch开源一体化语音工具包。它旨在让语音技术的研究和开发变得更容易。

动机

有许多语音和音频处理任务具有重要的实践和科学价值。

过去,主流方法是为每种不同的任务开发一个不同的工具包。然而,学习多个工具包耗时,可能需要了解不同的编程语言,并且强迫你熟悉不同的代码风格和标准(例如,数据读取器)。

如今,大多数这些任务都可以用相同的深度学习技术实现。因此,我们特意设计了 SpeechBrain,使其原生支持多种语音处理任务。我们认为这可能会大大简化语音开发者的工作。此外,我们认为将不同的语音技术结合到单个端到端完全可微分的系统中,将对未来语音技术的发展至关重要。

我们尽最大努力设计了一个易于使用的工具包:

  • 易于使用

  • 易于定制

  • 灵活

  • 模块化

  • 文档齐全

支持的技术

因此,你可以使用 speechbrain 将语音转换为文本,使用说话人验证进行身份验证,提高语音信号的质量,组合来自多个麦克风的信息,以及许多其他事情。

更具体地说,SpeechBrain 目前支持许多会话 AI 技术,包括

  • 语音识别

  • 说话人识别

  • 语音分离

  • 语音增强

  • 文本转语音

  • 声码

  • 口语理解

  • 语音到语音翻译

  • 语音翻译

  • 情感分类

  • 语言识别

  • 语音活动检测 (VAD)

  • 声音分类

  • 自监督学习

  • 可解释性

  • 语音生成

  • 度量学习

  • 对齐

  • 说话人分割

  • 语言建模

  • 响应生成

  • 字素到音素

对于所有这些任务,我们都提供了在流行数据集上的 recipes,这些 recipes 实现了有竞争力的或最先进的性能

SpeechBrain 是一个持续进行的项目(仍处于 beta 版本),我们正在构建一个大型社区来进一步扩展当前的功能。

安装

安装 SpeechBrain 主要有两种方式

  • 本地安装:如果你想修改工具包或从头开始训练一个完整的语音处理系统,建议采用此方式。

  • 通过 PyPI 安装:如果你只想在你的项目中使用 SpeechBrain 的一些核心功能,建议采用此方式。

本地安装 (Git 克隆)

%%capture
# Installing SpeechBrain via pip
BRANCH = 'develop'
!python -m pip install git+https://github.com/speechbrain/speechbrain.git@$BRANCH

# Clone SpeechBrain repository
!git clone https://github.com/speechbrain/speechbrain/
%cd /content/speechbrain/templates/speech_recognition/

安装完成后,你应该可以通过 python 导入 speechbrain 项目

import speechbrain as sb

运行实验

要使用 SpeechBrain 运行实验,典型的语法是

python train.py hparams.yaml

所有超参数都在 yaml 文件中汇总,而主训练脚本是 train.py

例如,让我们运行 SpeechBrain 提供的最小示例之一

%cd /content/speechbrain/tests/integration/ASR_CTC/
!python example_asr_ctc_experiment.py hyperparams.yaml

在此示例中,我们使用存储在 samples 文件夹中的微小数据集训练了一个基于 CTC 的语音识别器。正如你所见,训练损失非常小,这表明模型实现正确。然而,验证损失很高。这是因为,正如预期的那样,数据集太小,不足以让网络泛化。

有关最小示例的更详细说明,请参阅“最小示例分步讲解”教程。

所有实验结果都存储在 yaml 文件中定义的 output_folder 中。在这里,你可以找到检查点、训练好的模型、性能汇总文件以及日志。

通过这种方式,你可以将你的性能与我们实现的性能进行比较,并且可以访问所有预训练模型。

使用 YAML 指定超参数

机器学习系统通常需要指定多个超参数。在 SpeechBrain 中,我们使用 YAML 来完成。YAML 允许我们以一种优雅、灵活和透明的方式指定超参数。

例如,我们来看看这个 yaml 片段

dropout: 0.8
compute_features: !new:speechbrain.lobes.features.MFCC
    n_mels: 40
    left_frames: 5
    right_frames: 5

model: !new:speechbrain.lobes.models.CRDNN.CRDNN
   input_shape: [null, null, 440]
   activation: !name:torch.nn.LeakyReLU []
   dropout:  !ref <dropout>
   cnn_blocks: 2
   cnn_channels: (32, 16)
   cnn_kernelsize: (3, 3)
   time_pooling: True
   rnn_layers: 2
   rnn_neurons: 512
   rnn_bidirectional: True
   dnn_blocks: 2
   dnn_neurons: 1024

正如你所见,这不仅仅是一个简单的超参数列表。对于每个参数,我们指定将要使用它的类(或函数)。这使得代码更透明更容易调试

YAML 文件包含加载类时初始化所需的所有信息。在 SpeechBrain 中,我们使用一个名为 load_hyperpyyaml 的特殊函数来加载它,该函数为我们初始化所有声明的类。这使得代码极其可读紧凑

我们的 hyperpyyaml 是标准 YAML 的扩展。要了解所有支持的功能,请参阅此处的 YAML 教程

请注意,所有超参数都可以通过命令行覆盖。例如,要更改 dropout 系数

python experiment.py params.yaml --dropout=0.5

实验文件

实验文件(例如示例中的 example_asr_ctc_experiment.py)通过组合 yaml 文件中声明的函数或类来训练模型。此脚本定义了数据处理管线,并定义了从输入信号到最终成本函数的所有计算。所有设计都旨在**易于定制**。

数据规范

用户应该准备一个数据规范文件(CSV 或 JSON 格式),其中报告了所有要处理的数据和标签。

例如,在之前运行的最小示例中,数据规范文件是这样的

ID, duration, wav, wav_format, wav_opts, spk_id, spk_id_format, spk_id_opts, ali, ali_format, ali_opts, phn, phn_format, phn_opts,char,char_format,char_opts
spk1_snt5,2.6,$data_folder/spk1_snt5.wav, wav, ,spk1,string, ,$data_folder/spk1_snt5.pkl,pkl, ,s ah n vcl d ey ih z dh ax vcl b eh s cl t cl p aa r dx ax v dh ax w iy cl,string, ,s u n d a y i s t h e b e s t p a r t o f t h e w e e k,string,
spk2_snt5,1.98,$data_folder/spk2_snt5.wav, wav, ,spk2,string, ,$data_folder/spk2_snt5.pkl,pkl, ,vcl jh ah m cl p dh ax f eh n s ae n hh er iy ah cl p dh ax vcl b ae ng cl,string, ,k e n p a I r s l a c k f u l l f l a v o r,string,

你可用 CSV 读取器打开此文件以便更好地查看。每一行都包含一个示例,以及对应的 wav 信号和标签的路径。

另外,用户可用 JSON 格式指定数据

{
    "spk1_snt5": {
        "wav": "{data_root}/spk1_snt5.wav",
        "length": 2.6,
        "spk_id": "spk1",
        "ali": "{data_root}/spk1_snt5.pkl",
        "phn": "s ah n vcl d ey ih z dh ax vcl b eh s cl t cl p aa r dx ax v dh ax w iy cl",
        "char": "s u n d a y i s t h e b e s t p a r t o f t h e w e e k"
    },
    "spk2_snt5": {
        "wav": "{data_root}/spk2_snt5.wav",
        "length": 1.98,
        "spk_id": "spk2",
        "ali": "{data_root}/spk2_snt5.pkl",
        "phn": "vcl jh ah m cl p dh ax f eh n s ae n hh er iy ah cl p dh ax vcl b ae ng cl",
        "char": "k e n p a i r s l a c k f u l l f l a v o r"
    }
}

JSON 不如 CSV 紧凑,但更灵活。对于许多应用来说,使用 CSV 文件就足够了。然而,对于更复杂的任务(例如,说话人分割,说话人分割 + 识别),人们可能会利用 JSON 提供的层次结构。

所有数据集的格式都不同。一般来说,用户必须编写一个数据准备脚本,解析目标数据集并创建数据规范文件。然而,对于所有提供的 recipes,我们都发布了相应的数据准备库。

数据处理管线

感谢我们的动态数据集,数据读取管线可以直接在实验文件中完全定制。例如,在最小示例中,你可以定义以下直观函数来读取音频文件

    # 2. Define audio pipeline:
    @sb.utils.data_pipeline.takes("wav")
    @sb.utils.data_pipeline.provides("sig")
    def audio_pipeline(wav):
        sig = sb.dataio.dataio.read_audio(wav)
        return sig

该函数以 wav 路径作为输入,并返回使用指定读取器读取的信号。在变量 batch.sig(见 example_asr_ctc_experiment.py)中,你将获得准备好使用的信号批次。请注意,这里你可以通过按所需管线编码来添加任何类型的处理(例如,添加噪声、语速变化、动态混合等)。

对于脚本要处理的所有条目,都应该编写类似的函数。例如,最小示例也读取音素标签序列

    @sb.utils.data_pipeline.takes("phn")
    @sb.utils.data_pipeline.provides("phn_list", "phn_encoded")
    def text_pipeline(phn):
        phn_list = phn.strip().split()
        yield phn_list
        phn_encoded = label_encoder.encode_sequence_torch(phn_list)
        yield phn_encoded

这里,我们读取音素列表,按空格分隔每个条目,并将音素列表转换为其对应的索引(使用本教程中描述的 label_encoder)。

正如你所见,我们将数据读取管线直接暴露在主脚本中,因为这增加了大量的透明度和灵活性。

自定义前向计算和成本计算方法

用户通常想要定制的另一件事是从输入到最终成本函数的计算序列。在实验文件中,要求用户在 forwardcompute_objectives 方法中指定它们。在最小示例中,forward 方法定义如下

    def compute_forward(self, batch, stage):
        "Given an input batch it computes the output probabilities."
        wavs, lens = batch.sig
        feats = self.hparams.compute_features(wavs)
        feats = self.modules.mean_var_norm(feats, lens)
        x = self.modules.model(feats)
        x = self.modules.lin(x)
        outputs = self.hparams.softmax(x)

输入是变量 batch,它包含数据加载器中指定的所有条目(例如,我们有 batch.sigbatch.phn_encoded)。正如你所见,我们计算特征,进行均值和方差归一化,然后调用模型。最后,应用线性变换 + softmax。

计算目标函数如下所示

    def compute_objectives(self, predictions, batch, stage):
        "Given the network predictions and targets computed the CTC loss."
        predictions, lens = predictions
        phns, phn_lens = batch.phn_encoded
        loss = self.hparams.compute_cost(predictions, phns, lens, phn_lens)

        if stage != sb.Stage.TRAIN:
            seq = sb.decoders.ctc_greedy_decode(
                predictions, lens, blank_id=self.hparams.blank_index
            )
            self.per_metrics.append(batch.id, seq, phns, target_len=phn_lens)

        return loss

我们获取前向计算步骤中进行的预测,并使用 batch.phn_encoded 中的编码标签计算成本函数。在验证/测试期间,我们还对语音序列执行实际解码(在此示例中使用了贪心解码器,更通常情况下使用 beam search)以监控性能。

Brain 类

为了让训练更容易,我们实现了一个简单的训练器,称为 Brain 类。Brain 类定义了一组可定制的例程,这些例程实现了标准训练和验证循环中所需的所有步骤。在定义数据管线、前向计算、目标计算以及其他自定义方法后,你可以调用 Brain 类的 fit 方法进行训练(以及 eval 方法进行测试)

    # Trainer initialization
    ctc_brain = CTCBrain(hparams["modules"], hparams["opt_class"], hparams)

    # Training/validation loop
    ctc_brain.fit(
        range(hparams["N_epochs"]),
        train_data,
        valid_data,
        train_loader_kwargs=hparams["dataloader_options"],
        valid_loader_kwargs=hparams["dataloader_options"],
    )
    # Evaluation is run separately (now just evaluating on valid data)
    ctc_brain.evaluate(valid_data)

有关更详细的说明,请查看此处的 Brain 类教程

预训练和使用

有时你可能只想使用预训练模型,而不是从头开始训练。例如,你可能想在你的脚本中转录音频文件、计算说话人嵌入、应用语音活动检测器以及执行许多其他操作。为了使这更容易,我们在HuggingFace上传了几个模型。这些模型使用推理类来简化推理。例如,要使用使用 librispeech 训练的模型转录音频文件,你可以简单地执行以下操作

from speechbrain.inference.ASR import EncoderDecoderASR

asr_model = EncoderDecoderASR.from_hparams(source="speechbrain/asr-crdnn-rnnlm-librispeech", savedir="pretrained_models/asr-crdnn-rnnlm-librispeech")
asr_model.transcribe_file('speechbrain/asr-crdnn-rnnlm-librispeech/example.wav')

如你所见,在这种情况下,说话人说出的文本与音频文件的内容是匹配的。我们为说话人识别、语音分离、语音增强提供了类似的功能。

文件夹组织

主文件夹按以下方式组织

  • SpeechBrain 包含 SpeechBrain 的主要库。你可以在这里找到实现核心功能(例如 Brain 类)的 core.py 文件。你还可以在这里找到用于数据加载、解码器、神经网络、信号处理等许多库。在 lobe 文件夹下,你可以找到我们认为对语音和音频处理有用的基本功能的组合。例如,你可以在这里找到 FBANKs 和 MFCCs 等特征的实现、数据增强函数以及 recipes 中经常使用的一些流行的神经网络。

  • Recipes 包含多个语音数据集的训练脚本。例如,你可以找到 LibriSpeech, TIMIT, VoxCeleb, VoiceBank 等许多数据集的 recipes。

  • Samples 是一个微小的语音数据集,用于训练最小示例和执行调试测试。

  • Test 是单元测试和集成测试的集合,我们用于调试和持续集成。

Tensor 格式

SpeechBrain 中的所有张量都遵循以下约定格式化

tensor=(批次, 时间步长, 通道[可选])

批次始终是第一个元素,时间步长始终是第二个。剩余维度是通道,它们是可选的,数量可以按需增减)。

现在来看一些示例。例如,我们尝试计算输入信号的 FBANKS

import torch
from speechbrain.lobes.features import Fbank

signal = torch.rand([4, 16000]) # [batch, time]
print(signal.shape)

fbank_maker = Fbank()
fbanks = fbank_maker(signal) # [batch, time, features]
print(fbanks.shape)

The Fbank function expects in input a signal formatted as [批次, 时间]. It returns the features in the format [批次, 时间, 特征].

现在我们尝试计算任意音频信号的 STFT

import torch
from speechbrain.dataio.dataio import read_audio
from speechbrain.processing.features import STFT

signal = torch.rand([4, 1600]) # [batch, time]
print(signal.shape)

compute_STFT = STFT(sample_rate=16000, win_length=25, hop_length=10, n_fft=400)
signal_STFT = compute_STFT(signal) #[batch, time, channel1, channel2]
print(signal_STFT.shape)

这里的输出是 [批次, 时间, 通道1, 通道2],其中 通道1 是特征轴,通道2 是实部和虚部。

为什么需要张量格式?定义张量格式使得模型组合更容易。许多格式是可能的。对于 SpeechBrain,我们选择此格式是因为它在循环神经网络中常用。

在 SpeechBrain 中,神经网络的基本构建块(例如,RNNCNN归一化池化等)设计为支持相同的张量格式,因此可以平滑地组合。

为了说服你,我们尝试使用 SpeechBrain 组合 CNN 和 RNN

from speechbrain.nnet.CNN import Conv1d
from  speechbrain.nnet.RNN import LSTM

inp_tensor = torch.rand([10, 15, 40])
print(inp_tensor.shape)

# CNN
CNN = Conv1d(input_shape=inp_tensor.shape, out_channels=8, kernel_size=5)
cnn_out = CNN(inp_tensor)
print(cnn_out.shape)


# RNN
RNN = LSTM(input_shape=cnn_out.shape, hidden_size=256, num_layers=1)
rnn_out, _ = RNN(cnn_out)
print(rnn_out.shape)

组合无需任何张量重塑操作(例如,我们无需转置、挤压、解挤压)。基本的 nnet 函数是原始 pytorch 函数的包装器。不同之处在于,我们为你管理所有繁琐的张量重塑操作。这使得代码更清晰且易于理解。我们尝试用原始 PyTorch 执行相同的操作

import torch

inp_tensor = torch.rand([10, 15, 40])
print(inp_tensor.shape)

# CNN
CNN = torch.nn.Conv1d(in_channels=40, out_channels=8, kernel_size=5)
inp_tensor_tr = inp_tensor.transpose(1,2) # requires (N,C,L)
cnn_out_tr = CNN(inp_tensor_tr)
print(cnn_out_tr.shape)

# RNN
cnn_out_tr2 = cnn_out_tr.transpose(1,2)
RNN = torch.nn.LSTM(input_size=8, hidden_size=256, num_layers=1)
rnn_out, _ = RNN(cnn_out_tr2)
print(rnn_out.shape)

原始 pytorch 方法需要两次转置操作,因为 CNN 和 RNN 模块使用的张量格式不同。在 SpeechBrain 中,这在内部管理,用户无需为此担心。

引用 SpeechBrain

如果你在研究或业务中使用了 SpeechBrain,请使用以下 BibTeX 条目进行引用

@misc{speechbrainV1,
  title={Open-Source Conversational AI with {SpeechBrain} 1.0},
  author={Mirco Ravanelli and Titouan Parcollet and Adel Moumen and Sylvain de Langen and Cem Subakan and Peter Plantinga and Yingzhi Wang and Pooneh Mousavi and Luca Della Libera and Artem Ploujnikov and Francesco Paissan and Davide Borra and Salah Zaiem and Zeyu Zhao and Shucong Zhang and Georgios Karakasidis and Sung-Lin Yeh and Pierre Champion and Aku Rouhe and Rudolf Braun and Florian Mai and Juan Zuluaga-Gomez and Seyed Mahed Mousavi and Andreas Nautsch and Xuechen Liu and Sangeet Sagar and Jarod Duret and Salima Mdhaffar and Gaelle Laperriere and Mickael Rouvier and Renato De Mori and Yannick Esteve},
  year={2024},
  eprint={2407.00463},
  archivePrefix={arXiv},
  primaryClass={cs.LG},
  url={https://arxiv.org/abs/2407.00463},
}
@misc{speechbrain,
  title={{SpeechBrain}: A General-Purpose Speech Toolkit},
  author={Mirco Ravanelli and Titouan Parcollet and Peter Plantinga and Aku Rouhe and Samuele Cornell and Loren Lugosch and Cem Subakan and Nauman Dawalatabad and Abdelwahab Heba and Jianyuan Zhong and Ju-Chieh Chou and Sung-Lin Yeh and Szu-Wei Fu and Chien-Feng Liao and Elena Rastorgueva and François Grondin and William Aris and Hwidong Na and Yan Gao and Renato De Mori and Yoshua Bengio},
  year={2021},
  eprint={2106.04624},
  archivePrefix={arXiv},
  primaryClass={eess.AS},
  note={arXiv:2106.04624}
}