Транскрибирование встречи
В этом заключительном разделе мы используем модель 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 ожидает, что входной аудиофайл будет представлять собой тензор PyTorch формы (channels, seq_len)
,
поэтому перед запуском модели нам необходимо выполнить это преобразование:
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 вы помните, что для активации задачи прогнозирования временных меток в Whisper нам необходимо передать аргумент return_timestamps=True
:
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 секунды. Теперь мы можем сопоставить эту транскрипцию с временными метками дикторов, полученными с помощью модели диаризации, и получить окончательную транскрипцию.
Speechbox
Чтобы получить окончательную транскрипцию, совместим временные метки, полученные с помощью модели диаризации, с временными метками, полученными с помощью модели Whisper. Модель диаризации предсказала окончание речи первого диктора на 14,5 с, а второго - на 15,4 с, в то время как Whisper предсказал границы сегментов на 13,88, 15,48 и 19,44 с соответственно. Поскольку временные метки, полученные с помощью Whisper, не полностью совпадают с данными модели диаризации, нам необходимо найти, какие из этих границ ближе всего к 14,5 и 15,4 с, и соответствующим образом сегментировать транскрипцию по дикторам. В частности, мы найдем наиболее близкое совпадение между временными метками диаризации и транскрипции, минимизировав абсолютное расстояние между ними.
К счастью для нас, мы можем использовать пакет 🤗 Speechbox для выполнения этого выравнивания. Сначала давайте установим пакет speechbox
из main:
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
directly непосредственно из предварительно обученных моделей, указав идентификатор
модели ASR на Hub:
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 секунды, с соответствующими транскрипциями для каждого из них.
Для более удобного форматирования временных меток можно определить две вспомогательные функции. Первая преобразует кортеж временных меток в строку, округленную до заданного количества знаков после запятой. Вторая объединяет идентификатор диктора, временную метку и текстовую информацию в одну строку, а для удобства чтения разбивает каждого диктора на отдельные строки:
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