30k аудиозаписей: наводим порядок

в 6:37, , рубрики: audio processing, notion, notionapi, open source, speech-to-text, utilities, блек-джек и плюшки

Зачем писать дневник, если можно его надиктовывать? Казалось бы, здравая мысль, но как потом с ним работать? И нужен ли он вообще в таком виде? Сейчас есть довольно большое количество программ, которые сразу сделают speech‑to‑text, и проблемы не будет. А что, если такая идея возникла «‑дцать» лет назад, когда деревья были большими, а в телефонах только‑только появилась функция «диктофон»? За многие годы в нашей домашней аудиотеке накопилось более 30,000 таких записей. Пришла пора разложить всё по полкам, конечно же, с «преферансом и куртизанками».

30k аудиозаписей: наводим порядок - 1

Несколько лет назад, параллельно с фотографиями, я также начал раскладывать все аудиозаписи по папкам «Год/Месяц». Однако, если для фотографий я реализовал распознавание лиц, предпросмотр альбома и другие функции, то с аудиозаписями всё по‑прежнему оставалось печально. Да, добавление даты записи в имя файла уже улучшило наглядность и структуру, но хотелось большего.

Я начал с изучения решений, которые уже были на рынке. К сожалению, все они ориентировались на то, что записи будут изначально создаваться в них, либо предлагали добавлять каждую запись вручную. Понятно, что в случае 30,000 файлов это задача далека от тривиальной.

Тогда было решено создать свой инструмент, тем более что за плечами был успех с фотографиями. План максимум включал: распознавание голоса говорящего, распознавание текста и составление заголовка или краткого содержания записи. Конечно, с использованием нейросетей — куда же сейчас без них? Облачные решения, как и в случае с фотографиями, пришлось отклонить из‑за стоимости. К тому же, на моем домашнем сервере есть видеокарта с поддержкой CUDA, так что локальный вариант был более интересен.

Изначальный идеальный вариант выглядел так: 

30k аудиозаписей: наводим порядок - 2

Но оказалось, что не всё так просто. Да, есть весьма достойные алгоритмы диаризации (например тут или тут), но все они более‑менее неплохо работают, когда заранее известно количество спикеров. В моем случае это не так. Да и, как показал предварительный анализ, в 95% случаев спикер только один, и это почти всегда жена, так что на идентификацию тоже было решено забить.

Распознавание текста — задача уже более типичная, и готовых библиотек уйма. Я остановился на whisper. Она довольно проста в использовании, поддерживает ускорение с помощью CUDA и при этом дает весьма приличный результат. Ну, по крайней мере, так изначально показалось.

На хороших записях распознавание почти идеальное, а учитывая то, что она ещё расставляет пунктуацию, то просто отлично. Проблемы начались, когда стали попадаться записи, которые содержали не голоса, а, например, музыку, пение птиц или чьё‑то пыхтение (гусары, молчать!). В таких случаях нейросеть всё равно не сдаётся и начинает «галлюцинировать», т. е. придумывать то, чего нет, выдавая это за истину. Этот неприятный эффект я обнаружил уже после того, как было обработано несколько тысяч записей. Причем первое обнаружение было очень забавным: в одном из файлов в качестве текста было «С вами был Игорь Негода!». Я глубоко уважаю данного блогера‑самоделкина и даже заподозрил, что фраза могла реально попасть случайно на запись. Но поиск показал, что таких записей несколько десятков, а в самих файлах тишина. Дальнейший анализ выявил, что данную нейросеть, судя по всему, обучали на субтитрах с YouTube, так как 90% галлюциногенных фраз состояло из слов, которые видеоблогеры говорят в конце роликов: «Ставьте лайки», «Подписывайтесь на канал» и так далее. Поэтому я составил пару десятков регулярных выражений, которые покрыли практически все галлюциногенные фразы. По крайней мере, пара сотен прослушанных и выверенных записей больше не показали ничего странного.

Следующим этапом было составление заголовков, но и тут меня постигло фиаско: с десяток проверенных нейросетей выдавали просто треш. Судя по всему, добрую их часть обучали на новостях, так как большинство заголовков звучали как из бульварной прессы, а на пустые тексты часть нейронок выдавала вообще фразы про главу государства. Я уж даже было подумал обратиться к всемогущему ChatGPT, да это уже не локальное решение, но и он сплоховал. Нет, он работал хорошо, вот только заголовки, по большей части, были вроде «Семейная беседа». Да, это так, но, к сожалению, не очень выполняет основную функцию быстрой идентификации записей. И тут, как ни странно, оказалось, что то, что мне было нужно, было совсем рядом. В первом предложении чаще всего содержалась, если не тема записи, то, по крайней мере, явные ориентиры, о чем она будет. Так что, как говорится, всё гениальное просто: в качестве заголовка я взял первое предложение, и это оказалось достаточно информативным.

После распознавания записей встал вопрос: как с этим всем работать? Просто 30,000 txt‑файлов — явно не то, с чем хочется работать в 21 веке. Первая мысль была: закинуть всё в базу данных и написать простенький фронтенд. Но поскольку я не очень умею «во фронтенд», и, к тому же, хотелось сохранить это где‑то вне своих серверов для репликации данных, я решил рассмотреть альтернативы. Первый вариант был Google Docs, но встраивание ссылки на аудио там, без танцев с бубном, не работает, да и вариант иметь один документ на все записи, так же как и вариант иметь 30,000 документов, казался не очень удобным.

Тогда я обратил внимание на Notion: удобный API, возможность создать структуру документов, встроить аудио и, главное, возможность искать по записям «из коробки». Набросал прототип страницы, попробовал попользоваться, оказалось очень удобно.

30k аудиозаписей: наводим порядок - 3

Начал писать программу для выкладки. Наиболее удобной библиотекой показалась notion‑py. Всё было хорошо, пока я не выложил первые пять тысяч записей. Оказалось, что система кеширования в этой библиотеке организована таким образом, что обновление индекса происходит одним запросом, и, в какой‑то момент, это превышает максимальный размер данных, принимаемых сервером. Плюс к этому, когда записей становится много, библиотека начинает жутко тормозить. Сначала я попробовал пофиксить баги в библиотеке самостоятельно, но оказалось, что их там немало, а библиотека, по сути, заброшена и не поддерживается.

Так что, после пары вечеров копания в легаси, я решил рассмотреть альтернативы. А их, к сожалению, немного, только официальный API. В нём всё хорошо, но… Он не полный. Первое и самое главное — он не поддерживает загрузку файлов на сайт, только возможность вставить ссылку. Второе — он не позволяет заблокировать страницу, и по умолчанию она остаётся в режиме редактирования, что также не очень удобно, так как это у меня архив и редактировать его на сайте — плохая функция, вводящая в заблуждение. А ещё через него нельзя удалять записи (что, например, необходимо для обновления страниц). Поэтому, в конечном варианте, пришлось использовать оба подхода. Основная часть взаимодействия идёт через официальный API, а недостающая — через notion‑py (с одним критичным для моей задачи багфиксом).

В итоге получилась система из двух скриптов:

  • Первый скрипт распознает аудио и создает в той же папке json-файл с таким же именем, который содержит текст, описание и прочие необходимые атрибуты. Это длительная фоновая работа, которую я запускал на ночь по месяцам.

    ~> mmdiary-transcriber-run имя_папки

    Пример файла:

{
  "caption": "Audio Note Caption 1",
  "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit....",
  "model": "whisper/medium",
  "type": "audio",
  "source": "2020-02-07_22-11-16.mp3",
  "duration": 5.3,
  "recordtime": "2020-02-07 22:11:16",
  "processtime": "2024-03-15 01:35:13"
}
  • Второй скрипт синхронизирует локальные файлы с Notion. Относительно быстрая операция, которую можно выполнить и сразу увидеть результат на сайте.

    ~> mmdiary-notion-upload имя_папки

Но, после отладки, (и добавления функции работы с видео, но об этом в следующей статье) был написан общий скрипт, который выполняет сразу все этапы и, к тому же, может запускаться автоматически по cron‑у.

~> mmdiary --audio --notion

Теперь, как и с фотографиями, достаточно вставить флешку (или поместить файлы в особую папку на сервере), нажать кнопку для импорта, и, спустя некоторое время, все записи, сопровождаемые расшифровкой в удобном текстовом виде с возможностью поиска и прослушивания аудиозаписей, появятся в Notion:

30k аудиозаписей: наводим порядок - 4

В дополнение к системе просмотра записей мне захотелось иметь что‑то, что будет периодически напоминать о них. В этом плане мне нравится, как устроен Google Photos, который раз в день присылает различные воспоминания. А поскольку в нашей семье основной платформой для взаимодействия и общения является Telegram, то было решено просто написать бот. Функционал простой: раз в день присылать случайную аудиозапись, а также предоставлять записи за определенный день по запросу. Описывать весь процесс создания телеграм‑бота будет излишним, так как это уже отдельная тема, и статей об этом великое множество, так что просто оставлю ссылку на код бота.

Ну и, собственно, код всего проекта также доступен:

https://github.com/sashacmc/mmdiary

Как всегда, все open source, буду рад если мой опыт ну или код будет полезен еще кому-то.

Автор: sashacmc

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js