Примечание. Эта статья представляет собой широкий и высокоуровневый обзор процесса создания пользовательского конвейера синтеза речи. Подробное руководство запланировано на обозримое будущее.

Введение

Был холодный октябрьский день, когда я случайно просматривал Интернет. Тратя свое время, не зная, каким будет мой следующий проект, я наткнулся на идею синтеза речи. В последний раз я пытался возиться с такой технологией, наверное, в 2013 году. Я решил посмотреть, что будет возможно в 2021 году.

Поэтому я начал свои исследования. Мысленно я хотел найти инструмент (или набор инструментов), который позволил бы мне обучать модель машинного обучения на чьей-то речи и впоследствии воспроизводить эту речь. Другими словами, я искал набор инструментов, который позволил бы мне взять чей-то голос, распространить его через нейронную сеть и заставить эту сеть «говорить» как человек, на чьем голосе она была обучена.

Пока я копал, всплывало одно имя: Tacotron — модель преобразования текста в речь, разработанная Google для синтеза речи из пар (текст, аудио). Проще говоря, Tacotron — это черный ящик, который учится говорить как кто-то, слыша его голос и видя соответствующий текст — транскрипцию. Повторите это тысячу или около того раз, и вы получите довольно надежную модель, способную довольно хорошо обобщить чей-то голос.

Следующим шагом был поиск набора данных. В настоящее время существует множество таких ресурсов, доступных в Интернете: аудиокниги, интервью, подкасты и т. д. Однако Tacotron (и, как выясняется, другие модели преобразования текста в речь) требует, чтобы набор входных данных был соответствующего формата, а именно: (текст, аудио) пары.

Хотя уже есть несколько доступных наборов данных в этом формате (например, знаменитый набор данных LJSpeech), мы хотели бы обучать нашу модель с помощью определенного голоса, а не какого-то случайного, найденного в Интернете. Мы хотим, чтобы наша модель говорила как Джордан Петерсон или Барак Обама.

Итак, путешествие начинается.

Получение набора данных

Чтобы найти и подготовить наш набор данных, нам сначала нужно выбрать человека, которому мы хотим «подражать». Я решил пойти с доктором Петерсоном, потому что у него довольно интересный голос. Независимо от того, кого вы выберете, процесс будет более или менее одинаковым. Мы попытаемся создать парный набор данных (текст, аудио) для данного человека. Это означает, что нам нужно найти большое количество слов, произнесенных этим человеком, а такжетранскрипцию этих слов.

Если интересующее нас лицо является автором, одним из способов сделать это может быть приобретение (законной) копии чьей-либо книги и сопровождающей ее аудиокниги (если таковая существует). Получив и книгу, и аудиокнигу, можно с небольшими усилиями создать нужный нам набор данных. Эти усилия обычно влекут за собой синтаксический анализ исходного текстового файла, чтобы сделать его простым и удобным для чтения программой. Например, иногда текст может быть «спрятан» внутри документа XML или HTML. Затем нужно будет проанализировать содержимое и извлечь необработанные текстовые данные.

Если нам удастся найти чье-то аудио без какой-либо доступной транскрипции, нам нужно будет либо расшифровать аудио самостоятельно, либо использовать инструмент, который сделает это за нас. Доступен ряд таких инструментов, например pyTranscriber или pocketsphinx. (Использование таких инструментов является относительно простым процессом и не будет рассматриваться в этой статье.)

Предполагая, что теперь у нас есть и звуковое, и текстовое представление, пора переходить к следующему шагу.

Подготовка набора данных

Прежде чем мы сможем использовать наш набор данных, нам сначала нужно его подготовить. В этом контексте «подготовка» означает реструктуризацию набора данных, чтобы он был в точном формате, в котором модель нейронной сети сможет его интерпретировать. По сути, это сводится к списку пар текста и аудио. Как мы увидим позже, эти пары будут примерно соответствовать существующим предложениям.

Для простоты предположим, что имеется только один аудиофайл и соответствующий ему текстовый файл. В случае нескольких файлов следующий процесс может быть повторен (т. е. автоматизирован) для всех файлов в наборе данных. Другой вариант — объединить все аудиофайлы в один, а также объединить все текстовые данные в один текстовый файл.

Чтобы получить пары (текст, аудио), мы будем использовать aeneas, который является блестящим инструментом для автоматической синхронизации аудио и текста. Другими словами, при наличии аудиофайла и текстового файла инструмент будет выдавать точные временные метки и продолжительность для каждого расшифрованного фрагмента аудио (например, каждого предложения).

Чтобы aeneas мог прочитать ваш текстовый файл, он должен быть в одном из доступных форматов. Самый простой — поместить каждое предложение в отдельную строку файла. Таким образом, у нас есть один аудиофайл и один текстовый файл с кучей предложений, каждое в своей строке. Передача этих двух аргументов в aeneas (вместе с некоторыми второстепенными параметрами конфигурации) дает нам карту синхронизации. Карта синхронизации — это файл, который сопоставляет каждое предложение из текстового файла с соответствующим фрагментом речи в аудиофайле. Если вам интересно посмотреть, как это (даже) работает, ознакомьтесь с этим документом.

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

На данный момент у нас должен быть наш аудиофайл, наш текстовый файл и наша карта синхронизации. Следующим шагом будет разделение аудиофайла на фрагменты, соответствующие предложениям из нашего текстового файла — эта информация теперь хранится в нашей карте синхронизации. Чтобы извлечь фрагменты, нужно просто пройтись по карте синхронизации и разрезать исходный аудиофайл на более мелкие фрагменты. Поскольку каждая запись карты синхронизации содержит временные метки (начало и конец), очень полезная библиотека Python, которая может это сделать, — pydub. Он позволяет выполнять простые манипуляции со звуком, такие как загрузка, нарезка и экспорт аудио, что нам и нужно.

С небольшим количеством кода у нас должен быть скрипт, который загружает исходный аудиофайл, проходит карту синхронизации и извлекает меньшие фрагменты в выходную папку. В результате на нашем жестком диске появится куча фрагментов речи, каждый со своим собственным именем. Самый простой формат именования — назначить индекс каждого фрагмента в качестве имени файла. Например, если мы делаем голос Губки Боба, мы можем назвать наши файлы как-то вроде SB-0001, SB-0002 и т. д., с количеством нулей, определяемым общее количество фрагментов (например, в данном случае тысяча).

Чтобы доработать наш набор данных и подготовиться к процессу машинного обучения, осталось одно. Нам нужно создать CSV-файл, аналогичный файлу из LJSpeech, о котором упоминалось ранее. Файл CSV содержит такое же количество строк, как и наш текстовый файл. Разница лишь в том, что перед каждым сегментом текста нам нужно написать имя соответствующего аудиофайла, который мы только что нарезали. Они будут разделены символом вертикальной черты (|). Тогда наш CSV-файл будет выглядеть примерно так:

SB-0001|Для вождения бутерброда права не нужны
SB-0002|Лучшее время носить полосатый свитер… это все время!
.
.
.

Возможно, самый простой способ создать CSV-файл — сделать это в том же скрипте, в котором мы нарезаем аудио. При циклическом просмотре карты синхронизации мы можем хранить предложения в соответствующей структуре данных и сопоставлять их с именами фрагментов. После цикла по всему набору данных мы можем использовать pandas, чтобы сохранить эту информацию в файле metadata.csv в ранее описанном формате.

Создав наш CSV-файл, мы официально завершили подготовку набора данных и готовы начать машинное обучение.

Использование ФорвардТакотрон

При синтезе речи процесс машинного обучения обычно делится на две части. Первая часть посвящена генерации спектрограмм. Спектрограмма — это, по сути, график, показывающий, как частоты звука меняются во времени. Другими словами, это отпечаток речевого звука, который мы пытаемся воссоздать.

Цель этой первой модели — научиться генерировать такие спектрограммы. Для этого модель сначала проходит через весь набор данных пар (текст, аудио). Для каждой пары он берет звуковой компонент и генерирует представление его спектрограммы (это быстрый и простой процесс, который обычно означает всего одну строку кода). Затем модель пытается научиться генерировать ту же самую спектрограмму, но из текста, а не из аудио. Думать об этом. Модель сначала берет сегмент звуковой речи (произнесенное предложение) и создает график его частот. Затем он берет текстовое представление этого предложения и пытается сгенерировать те же частоты, но только из слов —абсолютно увлекательно!

Этот процесс одинаков для всех моделей, целью которых является генерация спектрограмм. Однако вместо Такотрона, о котором мы упоминали в начале этой статьи, мы будем использовать его более свежую модификацию под названием ФорвардТакотрон. Использование модели довольно простое, так как для ее обучения требуется всего несколько шагов. Единственным недостатком является то, что ForwardTacotron (как и все другие подобные модели) достаточно ресурсоемки. Действительно, обучение модели может занять часы или дни, чтобы достичь удовлетворительного уровня точности.

По этой причине вам понадобится надежный графический процессор, чтобы иметь возможность обучать эту модель. Если у вас нет собственного графического процессора, есть дешевые (или даже бесплатные) альтернативы на таких платформах, как vast.ai, Google Colab и Google Cloud Platform.

Использование WaveRNN

Через некоторое время процесс обучения ForwardTacotron приведет к созданию полностью обученной модели, способной генерировать спектрограммы из текста. На данный момент нам нужна вторая часть модели. А именно, та часть, которая берет спектрограмму и преобразует ее в звук. Такая модель называется вокодером, и одним из таких вокодеров является WaveRNN.

Чтобы обучить WaveRNN, нам нужно предоставить ему кучу пар (аудио, спектрограмма). Из этих пар модель научится сопоставлять функции спектрограммы с функциями звука. Поскольку первая часть процесса обучения дала модель, которая может генерировать спектрограммы из текста, эта вторая часть позволит нам взять эти спектрограммы и, наконец, использовать их для создания звука.

Подобно первой части (генерация спектрограммы), вторая часть (генерация звука) также требует больших ресурсов. Чтобы ускорить процесс обучения, можно использовать существующие предварительно обученные модели, которые были обучены на другом наборе данных, то есть на другом голосе. Поскольку речевой звук содержит схожие характеристики независимо от говорящего, предварительно обученная модель уже содержит множество необходимых нам речевых характеристик. Таким образом, необходимо обучать модель только в течение некоторого времени, прежде чем она изучит конкретные характеристики голоса в нашем наборе данных. Этот процесс называется трансферным обучением.

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

Собираем все вместе

Теперь у нас есть обе части нашей модели. Это означает, что мы можем взять ForwardTacotron, дать ему произвольное предложение, дождаться, пока он сгенерирует спектрограмму, передать эту спектрограмму в WaveRNN и получить результирующий звуковой речевой файл. К счастью, весь этот процесс уже встроен в конвейер ForwardTacotron. Нужно только указать ForwardTacotron использовать WaveRNN в качестве речевого вокодера.

После передачи текста в ForwardTacotron обе модели (ForwardTacotron и WaveRNN) будут работать в тандеме для создания аудиофайла речи из заданного текстового ввода. На этом наша работа закончена. Мы успешно создали сквозной конвейер синтеза речи для создания пользовательской речи из текста.