会议转录
在本节中,我们将使用 Whisper 模型为两个或多个说话者之间的对话或会议生成转录文本。然后,我们将它与一个说话人分段模型配对,以预测“谁在什么时候说话”。通过将 Whisper 转录的的时间戳与说话人分段模型的时间戳匹配,我们可以预测端到端的会议转录,并为每个说话者提供完整格式的开始/结束时间。这是您可能在 Otter.ai 等在线会议转录服务中看到的会议转录服务的简单版本。
说话人分段
说话人分段(或分段)是将未标记的音频输入作为输入并预测“谁在什么时候说话”的任务。通过这样做,我们可以预测每个说话人轮次的开始/结束时间戳,对应于每个说话人开始说话和结束说话的时间。
🤗 Transformers 目前在库中没有包含说话人分段模型,但在 Hub 上有一些检查点可以相对轻松地使用。在本例中,我们将使用来自 pyannote.audio 的预训练说话人分段模型。让我们开始并使用 pip 安装软件包。
pip install --upgrade pyannote.audio
太棒了!此模型的权重托管在 Hugging Face Hub 上。要访问它们,我们首先必须同意说话人分段模型的使用条款:pyannote/speaker-diarization。随后是分段模型的使用条款:pyannote/segmentation。
完成后,我们可以在本地设备上加载预训练的说话人分段管道。
from pyannote.audio import Pipeline
diarization_pipeline = Pipeline.from_pretrained(
"pyannote/[email protected]", use_auth_token=True
)
让我们在一个示例音频文件上试一试!为此,我们将加载 LibriSpeech ASR 数据集的一个示例,该数据集由两个不同的说话者组成,它们已连接在一起以形成单个音频文件。
from datasets import load_dataset
concatenated_librispeech = load_dataset(
"sanchit-gandhi/concatenated_librispeech", split="train", streaming=True
)
sample = next(iter(concatenated_librispeech))
我们可以收听音频以了解其声音。
from IPython.display import Audio
Audio(sample["audio"]["array"], rate=sample["audio"]["sampling_rate"])
酷!我们可以清楚地听到两个不同的说话者,在大概 15 秒处有一个过渡。让我们将此音频文件传递给分段模型以获取说话人开始/结束时间。请注意,pyannote.audio 期望音频输入为形状为 (channels, seq_len)
的 PyTorch 张量,因此我们需要在运行模型之前执行此转换。
import torch
input_tensor = torch.from_numpy(sample["audio"]["array"][None, :]).float()
outputs = diarization_pipeline(
{"waveform": input_tensor, "sample_rate": sample["audio"]["sampling_rate"]}
)
outputs.for_json()["content"]
[{'segment': {'start': 0.4978125, 'end': 14.520937500000002}, 'track': 'B', 'label': 'SPEAKER_01'}, {'segment': {'start': 15.364687500000002, 'end': 21.3721875}, 'track': 'A', 'label': 'SPEAKER_00'}]
看起来不错!我们可以看到,第一个说话者预测为一直说话到 14.5 秒标记,第二个说话者从 15.4 秒开始。现在我们需要获取我们的转录!
语音转录
在本单元中,我们将第三次使用 Whisper 模型作为我们的语音转录系统。具体来说,我们将加载 Whisper Base 检查点,因为它足够小,可以在合理的转录准确度下提供良好的推理速度。如前所述,您可以随意使用 Hub 上的任何语音识别检查点,包括 Wav2Vec2、MMS ASR 或其他 Whisper 检查点。
from transformers import pipeline
asr_pipeline = pipeline(
"automatic-speech-recognition",
model="openai/whisper-base",
)
让我们获取示例音频的转录,并返回段级时间戳,以便我们知道每个段的开始/结束时间。您还记得在单元 5 中,我们需要传递参数 return_timestamps=True
来激活 Whisper 的时间戳预测任务。
asr_pipeline(
sample["audio"].copy(),
generate_kwargs={"max_new_tokens": 256},
return_timestamps=True,
)
{ "text": " The second and importance is as follows. Sovereignty may be defined to be the right of making laws. In France, the king really exercises a portion of the sovereign power, since the laws have no weight. He was in a favored state of mind, owing to the blight his wife's action threatened to cast upon his entire future.", "chunks": [ {"timestamp": (0.0, 3.56), "text": " The second and importance is as follows."}, { "timestamp": (3.56, 7.84), "text": " Sovereignty may be defined to be the right of making laws.", }, { "timestamp": (7.84, 13.88), "text": " In France, the king really exercises a portion of the sovereign power, since the laws have", }, {"timestamp": (13.88, 15.48), "text": " no weight."}, { "timestamp": (15.48, 19.44), "text": " He was in a favored state of mind, owing to the blight his wife's action threatened to", }, {"timestamp": (19.44, 21.28), "text": " cast upon his entire future."}, ], }
好的!我们看到转录的每个片段都有一个开始和结束时间,说话者在 15.48 秒处发生变化。现在,我们可以将此转录与从分段模型获得的说话人时间戳配对,以获得最终的转录。
语音盒
为了获得最终的转录,我们将分段模型的时间戳与 Whisper 模型的时间戳对齐。分段模型预测第一个说话者在 14.5 秒结束,第二个说话者在 15.4 秒开始,而 Whisper 分别预测段边界在 13.88、15.48 和 19.44 秒。由于 Whisper 的时间戳与分段模型的时间戳不完全匹配,因此我们需要找到其中哪些边界最接近 14.5 秒和 15.4 秒,并相应地按说话者对转录进行分段。具体来说,我们将通过最小化两者之间的绝对距离来找到分段和转录时间戳之间的最接近对齐。
幸运的是,我们可以使用 🤗 Speechbox 包来执行此对齐。首先,让我们从主分支使用 pip 安装 speechbox
。
pip install git+https://github.com/huggingface/speechbox
现在,我们可以通过将分段模型和 ASR 模型传递给 ASRDiarizationPipeline
类来实例化我们组合的分段加转录管道。
from speechbox import ASRDiarizationPipeline
pipeline = ASRDiarizationPipeline(
asr_pipeline=asr_pipeline, diarization_pipeline=diarization_pipeline
)
ASRDiarizationPipeline
。pipeline = ASRDiarizationPipeline.from_pretrained("openai/whisper-base")
让我们将音频文件传递给复合管道并查看我们得到的结果。
pipeline(sample["audio"].copy())
[{'speaker': 'SPEAKER_01', 'text': ' The second and importance is as follows. Sovereignty may be defined to be the right of making laws. In France, the king really exercises a portion of the sovereign power, since the laws have no weight.', 'timestamp': (0.0, 15.48)}, {'speaker': 'SPEAKER_00', 'text': " He was in a favored state of mind, owing to the blight his wife's action threatened to cast upon his entire future.", 'timestamp': (15.48, 21.28)}]
太好了!第一个说话者被分段为从 0 到 15.48 秒说话,第二个说话者从 15.48 到 21.28 秒说话,并包含每个说话者的相应转录。
我们可以通过定义两个辅助函数来更漂亮地格式化时间戳。第一个将时间戳元组转换为字符串,并四舍五入到指定的小数位数。第二个将说话人 ID、时间戳和文本信息组合到一行上,并将每个说话者拆分到他们自己的行上,以便于阅读。
def tuple_to_string(start_end_tuple, ndigits=1):
return str((round(start_end_tuple[0], ndigits), round(start_end_tuple[1], ndigits)))
def format_as_transcription(raw_segments):
return "\n\n".join(
[
chunk["speaker"] + " " + tuple_to_string(chunk["timestamp"]) + chunk["text"]
for chunk in raw_segments
]
)
让我们重新运行管道,这次根据我们刚刚定义的函数格式化转录。
outputs = pipeline(sample["audio"].copy())
format_as_transcription(outputs)
SPEAKER_01 (0.0, 15.5) The second and importance is as follows. Sovereignty may be defined to be the right of making laws. In France, the king really exercises a portion of the sovereign power, since the laws have no weight. SPEAKER_00 (15.5, 21.3) He was in a favored state of mind, owing to the blight his wife's action threatened to cast upon his entire future.
就是这样!有了它,我们既对输入音频进行了分段,又进行了转录,并返回了说话者分段的转录。虽然用于对齐分段时间戳和转录时间戳的最小距离算法很简单,但在实践中效果很好。如果您想探索更高级的组合时间戳方法,ASRDiarizationPipeline
的源代码是一个不错的起点:speechbox/diarize.py