Распознавание речи (транскрибация) по аудиозаписям диалогов. Whisper. Личный опыт

от автора

Поставленная задача:
распознавать речь по аудиозаписям диалогов сотрудников и клиентов.

Применяем Whisper.
Работаем в Colab.

Помимо распознавания речи данная модель Whisper имеет штатную функцию тайминга.
Исходный ресурс и документация здесь.

По документации:

Whisper — это универсальная модель распознавания речи. Она обучена на большом наборе данных разнообразного аудио, а также представляет собой многозадачную модель, которая может выполнять многоязычное распознавание речи, перевод речи и идентификацию языка.

Базовое применение по документации

Модель устанавливается стандартно.

Есть несколько модификаций (tiny, base, small, medium, large), в Colab помещаются все.

!pip install -U openai-whisper import whisper  model_name = 'large' model = whisper.load_model(model_name)

Базовое применение модели также простое и стандартное.

result = model.transcribe(file) print(result["text"])

Мы же будем распознавать речь двух участников в двухканальном аудиофайле и добавим тайминг.

Загружаем аудиофайл

Загружаем файл с компьютера, запоминаем название, создаем папку для будущего архива.

import os from google.colab import files  uploaded = files.upload() file = next(iter(uploaded.keys())) source_file_name = file.replace('.wav','')  path = "/content/" + source_file_name os.mkdir(path)

Убеждаемся, что верно сохранили наименование файла и что в файле 2 канала.

print(source_file_name)  audio_file = wave.open(source_file_name + '.wav') CHANNELS = audio_file.getnchannels() print("Количество каналов:", CHANNELS)

Определяем язык

# load audio and pad/trim it to fit 30 seconds audio = whisper.load_audio(source_file_name + '.wav') audio = whisper.pad_or_trim(audio)  # make log-Mel spectrogram and move to the same device as the model mel = whisper.log_mel_spectrogram(audio).to(model.device)  # detect the spoken language _, probs = model.detect_language(mel) print(f"Detected language: {max(probs, key=probs.get)}")

Необходимо обратить внимание, что определение языка осуществляется по первым 30 секундам. Это иногда вводит в заблуждение, так как собеседники могут поприветствовать друг друга на одном языке, а потом определить, что им удобнее разговаривать на другом языке и перейти на другой язык.

Создаем 2 файла по одной дорожке

Эта часть не относится непосредственно к Whisper и к распознаванию речи. Здесь читаем из файла все семплы, обнуляем каждый четный и создаем новый файл. Аналогично поступаем с нечетными семплами.

Код разбивки файла на 2 дорожки
import wave, struct  # файл делится на две дорожки и создается два файла  audio_file = wave.open(source_file_name + '.wav')  SAMPLE_WIDTH = audio_file.getsampwidth() # глубина звука CHANNELS = audio_file.getnchannels() # количество каналов FRAMERATE = audio_file.getframerate() # частота дискретизации N_SAMPLES = audio_file.getnframes() # кол-во семплов на каждый канал  N_FRAMES = audio_file.getnframes()  # Определяем параметры аудиофайла  nchannels = CHANNELS sampwidth = SAMPLE_WIDTH framerate = FRAMERATE nframes = N_FRAMES  comptype = "NONE"  # тип компрессии compname = "not compressed"  # название компрессии  # узнаем кол-во семплов и каналов в источнике N_SAMPLES = nframes CHANNELS = nchannels  def create_file_one_channel(name):    # создаем пустой файл в который мы будем записывать результат обработки в режиме wb (write binary)   out_file = wave.open(name, "wb")    # в "настройки" файла с результатом записываем те же параметры, что и у "исходника"   out_file.setframerate(framerate)   out_file.setsampwidth(sampwidth)   out_file.setnchannels(CHANNELS)    # обратно перегоняем список чисел в байт-строку   audio_data = struct.pack(f"<{N_SAMPLES * CHANNELS}h", *values_copy)    # записываем обработанные данные в файл с резхультатом   out_file.writeframes(audio_data)  ##########  print('started')  # читаем из файла все семплы samples = audio_file.readframes(N_FRAMES)  # просим struct превратить строку из байт в список чисел # < - обозначение порядка битов в байте (можно пока всегда писать так) # По середине указывается общее количество чисел, это произведения кол-ва семплов в одном канале на кол-во каналоов # h - обозначение того, что одно число занимает два байта  values = list(struct.unpack("<" + str(N_FRAMES * CHANNELS) + "h", samples)) print(values[:20])  values_copy = values[:]  # обнулим каждое четное значение for index, i in enumerate(values_copy):     if index % 2 == 0:       values_copy[index] = 0  create_file_one_channel('1_channel.wav') print(values_copy[:20])  values_copy = values[:]  # обнулим каждое нечетное значение for index, i in enumerate(values_copy):     if index % 2 != 0:       values_copy[index] = 0  create_file_one_channel('2_channel.wav') print(values_copy[:20])

Получили файл 1-го канала и файл 2-го канала: 1_channel.wav и 2_channel.wav.

Транскрибация 1 канала

Транскрибируем файл с 1 каналом

from datetime import timedelta  source_file_name_channel = '1_channel' print(source_file_name_channel)  # load audio and pad/trim it to fit 30 seconds audio = whisper.load_audio(source_file_name_channel + '.wav') audio = whisper.pad_or_trim(audio)  # make log-Mel spectrogram and move to the same device as the model mel = whisper.log_mel_spectrogram(audio).to(model.device)  # detect the spoken language _, probs = model.detect_language(mel) print(f"Detected language: {max(probs, key=probs.get)}") print('started...') print()  result = model.transcribe(source_file_name_channel + '.wav',)  segments = result['segments']  text_massive = []  for segment in segments:     startTime = str(0)+str(timedelta(seconds=int(segment['start'])))     endTime = str(0)+str(timedelta(seconds=int(segment['end'])))     text = segment['text']     segmentId = segment['id']+1     segment = f"{segmentId}. {startTime} - {endTime}\n{text[1:] if text[0] == ' ' else text}"     #print(segment)     text_massive.append(segment)  print() print('Finished')

и сохраняем результат в файл формата docx.

!pip install python-docx from docx import Document  # сохраняем текст с таймингом  text = text_massive  # создаем новый документ doc = Document()  # добавляем параграф с текстом doc.add_paragraph(source_file_name + '_' + source_file_name_channel + '_' + model_name) for key in text:   doc.add_paragraph(key)  # сохраняем документ doc.save(path + '/' + source_file_name + '_' + source_file_name_channel + '_text_timing_' + model_name + '.docx')

Со вторым каналом все то же самое, только вместо «1_channel» применяем «2_channel».
Также возможно применить цикл из этих двух значений, не принципиально.

На данном этапе получили 2 текстовых файла с текстом и таймингом, и эти файла лежат в созданной ранее папке с соответствующим названием. Осталось создать архив и скачать.

Создание и скачивание архива

import shutil/  # Создание архива с файлами из папки shutil.make_archive(path + '_' + model_name, 'zip', path)  # Скачивание архива files.download(path + '_' + model_name + '.zip')

Удаление файлов и папок

Если нужно обработать «вручную» еще файл, то следующий код удалит из Colab применяемые и полученные ранее файлы и папки.

# задаем сразу все и сразу все удаляем  file_massive = [     source_file_name + '.wav',     source_file_name + '_' + model_name + '.zip',     '1_channel.wav',     '2_channel.wav',     path + '/' + source_file_name + '_1_channel_text_timing_' + model_name + '.docx',     path + '/' + source_file_name + '_2_channel_text_timing_' + model_name + '.docx'   ]  # Удаление файла for key in file_massive:   print(key)   os.remove(key) print()  # Удаление папки print(path) os.rmdir(path) print()  print('Finished')

Colab очищен и можно работать с другим файлом.

Дополнение

Если файлов несколько и хочется автоматизировать, то возможно закачать все аудиофайлы и запустить все фрагменты подряд в едином цикле.

Совсем для автоматизации возможно скачивать файлы из карточки клиента или из специального хранилища и после транскрибации сохранять диалог обратно в карточку клиента или в специальное хранилище.

Транскрибация сразу на английский

В Whisper есть интересная возможность — переводить сразу на английский, минуя текстовый вывод. Это может быть полезно, если файлы на разных языках и их нужно анализировать единым способом. В этом случае целесообразно приводить все диалоги к английскому и в дальнейшем обрабатывать уже на английском.

Для транскрибации сразу на английский применяется дополнительное указание.

result = model.transcribe(source_file_name_channel + '.wav', task="translate")

Примечание

Если Вы обнаружили в статье неточность, или считаете полезным что-либо добавить — пожалуйста, сообщите в комментариях.


ссылка на оригинал статьи https://habr.com/ru/articles/768562/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *