Síntesis de voz de la red neuronal utilizando la arquitectura Tacotron 2, o "Obtener alineación o morir en el intento"



A nuestro equipo se le encomendó la tarea: repetir los resultados del trabajo de la red neuronal artificial de síntesis de voz de la autoría de Tacotron2 DeepMind. Esta es una historia sobre el camino espinoso que recorrimos durante la implementación del proyecto.

La tarea de síntesis de voz por computadora ha sido de interés para científicos y expertos técnicos. Sin embargo, los métodos clásicos no permiten la síntesis del habla, indistinguible de la humana. Y aquí, como en muchas otras áreas, el aprendizaje profundo vino al rescate.

Veamos los métodos de síntesis clásicos.

Síntesis de voz concatenativa


Este método se basa en pregrabar fragmentos cortos de audio, que luego se combinan para crear un discurso coherente. Resulta ser muy limpio y claro, pero completamente desprovisto de componentes emocionales y entonacionales, es decir, suena poco natural. Y todo porque es imposible obtener una grabación de audio de todas las palabras posibles pronunciadas en todas las combinaciones posibles de emociones y prosodia. Los sistemas de concatenación requieren grandes bases de datos y combinaciones de codificación para formar palabras. El desarrollo de un sistema confiable lleva mucho tiempo.

Síntesis paramétrica del habla


Las aplicaciones TTS concatenacionales son limitadas debido a los altos requisitos de datos y al tiempo de desarrollo. Por lo tanto, se desarrolló un método estadístico que explora la naturaleza de los datos. Genera voz combinando parámetros como frecuencia, espectro de amplitud, etc.

La síntesis paramétrica consta de dos etapas.

  1. Primero, las características lingüísticas, como los fonemas, la duración, etc., se extraen del texto.
  2. Luego, para el vocoder (sistema que genera formas de onda), se extraen características que representan la señal de voz correspondiente: cepstrum, frecuencia, espectrograma lineal, espectrograma de tiza.
  3. Estos parámetros configurados manualmente, junto con las características lingüísticas, se transfieren al modelo de vocoder y realiza muchas transformaciones complejas para generar una onda de sonido. Al mismo tiempo, el vocoder evalúa los parámetros del habla, como fase, prosodia, entonación y otros.

Si podemos aproximar los parámetros que definen el habla en cada una de sus unidades, entonces podemos crear un modelo paramétrico. La síntesis paramétrica requiere significativamente menos datos y trabajo duro que los sistemas concatenativos.

Teóricamente, todo es simple, pero en la práctica hay muchos artefactos que conducen a un habla amortiguada con un sonido "zumbido", que no se parece en nada a un sonido natural.

El hecho es que en cada etapa de la síntesis codificamos algunas características y esperamos obtener un discurso que suene realista. Pero los datos seleccionados se basan en nuestra comprensión del habla, y el conocimiento humano no es absoluto, por lo tanto, los signos tomados no serán necesariamente la mejor solución posible.

Y aquí Deep Learning entra en escena en todo su esplendor.

Las redes neuronales profundas son una herramienta poderosa que, en teoría, puede aproximarse a una función compleja arbitraria, es decir, traer un poco de espacio de datos de entrada X al espacio de datos de salida Y. En el contexto de nuestra tarea, estos serán texto y audio con voz, respectivamente.

Preprocesamiento de datos


Para comenzar, determinaremos qué tenemos como entrada y qué queremos obtener en la salida.

La entrada será texto y la salida será un espectrograma de tiza . Esta es una representación de bajo nivel obtenida aplicando la transformada rápida de Fourier a una señal de audio discreta. Cabe señalar de inmediato que los espectrogramas obtenidos de esta manera todavía deben normalizarse comprimiendo el rango dinámico. Esto le permite reducir la relación natural entre el sonido más alto y el más bajo de la grabación. En nuestros experimentos, el uso de espectrogramas reducidos al rango [-4; 4] demostró ser el mejor.


Figura 1: espectrograma de tiza de la señal de audio de voz reducida al rango [-4; 4].

Como conjunto de datos de entrenamiento, elegimos el conjunto de datos LJSpeech , que contiene 13,100 pistas de audio durante 2-10 segundos. y un archivo con el texto correspondiente al discurso en inglés grabado en audio.

El sonido que usa las transformaciones anteriores se codifica en espectrogramas de tiza. El texto está tokenizado y transformado.

en una secuencia de enteros. Debo enfatizar de inmediato que los textos están normalizados: todos los números están escritos verbalmente y se descifran las posibles abreviaturas, por ejemplo: “Sra. Robinson "-" Missis Robinson ".

Por lo tanto, después del preprocesamiento, obtenemos conjuntos de matrices numpy de secuencias numéricas y espectrogramas de tiza grabados en archivos npy en el disco.

Para que en la etapa de entrenamiento coincidan todas las dimensiones en los tensores del parche, agregaremos rellenos a secuencias cortas. Para secuencias en forma de textos, estas se reservarán para el relleno 0, y para los espectrogramas, cuadros cuyos valores son ligeramente más bajos que los espectrogramas mínimos determinados por nosotros. Esto se recomienda para aislar estos rellenos, separándolos del ruido y el silencio.

Ahora tenemos datos que representan texto y audio que son adecuados para su procesamiento por una red neuronal artificial. Veamos la arquitectura de Feature prediction net, que por el nombre del elemento central de todo el sistema de síntesis se llamará Tacotron2.

Arquitectura


Tacotron 2 no es una red, sino dos: función de predicción de red y NN-vocoder WaveNet . El artículo original, así como nuestra propia visión del trabajo realizado, nos permite considerar Feature prediction net como el primer violín, mientras que el vocoder WaveNet desempeña el papel de un sistema periférico.

Tacotron2 es una arquitectura secuencia a secuencia . Consiste en un codificador (codificador), que crea una idea interna de la señal de entrada (símbolos simbólicos), y un decodificador (decodificador), que convierte esta representación en un espectrograma de tiza. También un elemento extremadamente importante de la red es el llamado PostNet , que está diseñado para mejorar el espectrograma generado por el decodificador.


Figura 2: Arquitectura de red Tacotron 2.

Consideremos con más detalle los bloques de red y sus módulos.

La primera capa del codificador es la capa de incrustación. Basado en una secuencia de números naturales que representan caracteres, crea vectores multidimensionales (512 dimensiones).

A continuación, los vectores de incrustación se introducen en un bloque de tres capas convolucionales unidimensionales. Cada capa incluye 512 filtros de longitud 5. Este valor es un buen tamaño de filtro en este contexto, ya que captura un cierto carácter, así como sus dos vecinos anteriores y dos posteriores. A cada capa convolucional le sigue la normalización de mini lotes y la activación de ReLU.

Los tensores obtenidos después del bloqueo convolucional se aplican a capas de LSTM bidireccionales, 256 neuronas cada una. Los resultados de envío y devolución se concatenan.

El decodificador tiene una arquitectura recurrente, es decir, en cada paso posterior, se utiliza la salida del paso anterior. Aquí serán un cuadro del espectrograma. Otro elemento importante, si no clave, de este sistema es el mecanismo de atención suave (entrenada), una técnica relativamente nueva que está ganando cada vez más popularidad. En cada paso del decodificador, la atención para formar un vector de contexto y actualizar el peso de la atención utiliza:

  • la proyección del estado oculto anterior de la red RNN del decodificador en una capa totalmente conectada,
  • proyección de la salida del codificador en una capa totalmente conectada,
  • así como pesos de atención aditivos (acumulados en cada paso de tiempo del decodificador).

La idea de atención debe entenderse de la siguiente manera: "qué parte de los datos del codificador se debe utilizar en el paso actual del decodificador".


Figura 3: Esquema del mecanismo de atención.

En cada paso del decodificador, se calcula el vector de contexto C i (indicado en la figura anterior como "salidas codificadas atendidas"), que es un producto de la salida del codificador ( h ) y los pesos de atención ( α ):



donde α ij son los pesos de atención calculados por la fórmula:



donde e ij es la llamada "energía", cuya fórmula de cálculo depende del tipo de mecanismo de atención que utilice (en nuestro caso, será un tipo híbrido, utilizando tanto la atención basada en la ubicación como la atención basada en el contenido). La energía se calcula mediante la fórmula:

e ij = v aT tanh (Ws i-1 + Vh j + Uf i, j + b)

donde:
  • s i-1 : estado oculto anterior de la red LSTM del decodificador,
  • α i-1 - pesos de atención anteriores,
  • h j es el enésimo estado oculto del codificador,
  • W , V , U , v a y b son parámetros de entrenamiento,
  • f i, j - signos de ubicación calculados por la fórmula:

    f i = F * α i-1

    donde F es la operación de convolución.


Para una comprensión clara de lo que está sucediendo, agregamos que algunos de los módulos descritos a continuación suponen el uso de información del paso anterior del decodificador. Pero si este es el primer paso, entonces la información será tensores de valores cero, lo cual es una práctica común al crear estructuras de recurrencia.

Ahora considere el algoritmo de operación .

Primero, la salida del decodificador del paso de tiempo anterior se alimenta a un pequeño módulo PreNet, que es una pila de dos capas completamente conectadas de 256 neuronas, que se alternan con capas de caída con una tasa de 0.5. Una característica distintiva de este módulo es que el abandono se usa en él no solo en la etapa de entrenamiento del modelo, sino también en la etapa de salida.

La salida PreNet en concatenación con el vector de contexto obtenido como resultado del mecanismo de atención se alimenta a la entrada en una red LSTM unidireccional de dos capas, con 1024 neuronas en cada capa.

Luego, la concatenación de la salida de las capas LSTM con el mismo vector de contexto (y posiblemente diferente) se alimenta a una capa totalmente conectada con 80 neuronas, que corresponde al número de canales del espectrograma. Esta capa final del decodificador forma el espectrograma predicho cuadro por cuadro. Y ya su salida se suministra como entrada para el siguiente paso de tiempo del decodificador en PreNet.

¿Por qué mencionamos en el párrafo anterior que el vector de contexto ya puede ser diferente? Uno de los enfoques posibles es recalcular el vector de contexto después de obtener el estado latente de la red LSTM en este paso. Sin embargo, en nuestros experimentos este enfoque no se justificó.

Además de la proyección en una capa totalmente conectada de 80 neuronas, la concatenación de la salida de las capas LSTM con un vector de contexto se alimenta a una capa completamente conectada con una neurona, seguida de la activación sigmoidea: esta es una capa de "predicción de token de parada". Él predice la probabilidad de que el marco creado en este paso del decodificador sea final. Esta capa está diseñada para generar un espectrograma de longitud no fija, pero arbitraria en la etapa de salida del modelo. Es decir, en la etapa de salida, este elemento determina el número de pasos del decodificador. Se puede considerar como un clasificador binario.

La salida del decodificador de todos sus pasos será el espectrograma predicho. Sin embargo, esto no es todo. Para mejorar la calidad del espectrograma, se pasa a través del módulo PostNet, que es una pila de cinco capas convolucionales unidimensionales con 512 filtros en cada una y con un tamaño de filtro de 5. La normalización por lotes y la activación tangente siguen a cada capa (excepto la última). Para volver a la dimensión del espectrograma, pasamos los datos de salida posteriores a la red a través de una capa totalmente conectada con 80 neuronas y agregamos los datos recibidos con el resultado inicial del decodificador. Obtenemos el espectrograma de tiza generado a partir del texto. Ganancia

Todos los módulos convolucionales se regularizan con capas de abandono con una tasa de 0,5 y capas de recurrencia con el método Zoneout más reciente con una tasa de 0,1. Es bastante simple: en lugar de aplicar el estado latente y el estado de celda obtenido en el paso actual al siguiente paso de tiempo de la red LSTM, reemplazamos parte de los datos con los valores del paso anterior. Esto se hace tanto en la etapa de entrenamiento como en la etapa de retiro. En este caso, solo el estado oculto (que se pasa al siguiente paso de LSTM) se expone al método Zoneout en cada paso, mientras que la salida de la celda LSTM en el paso actual permanece sin cambios.

Elegimos PyTorch como marco de aprendizaje profundo. Aunque en el momento de la implementación de la red se encontraba en un estado de prelanzamiento, ya era una herramienta muy poderosa para construir y entrenar redes neuronales artificiales. En nuestro trabajo, utilizamos otros marcos como TensorFlow y Keras. Sin embargo, este último se descartó debido a la necesidad de implementar estructuras personalizadas no estándar, y si comparamos TensorFlow y PyTorch, entonces, al usar el segundo, no hay sensación de que el modelo esté arrancado del lenguaje Python. Sin embargo, no nos comprometemos a afirmar que uno de ellos es mejor y el otro peor. El uso de un marco particular puede depender de varios factores.

La red se entrena mediante el método de retropropagación. ADAM se usa como optimizador, el error cuadrático medio antes y después de PostNet, así como la entropía cruzada binaria por encima de los valores reales y pronosticados de la capa de predicción de token de parada, se usan como funciones de error. El error resultante es una simple suma de estos tres.

El modelo fue entrenado en una sola GPU GeForce 1080Ti con 11 GB de memoria.

Visualización


Cuando se trabaja con un modelo tan grande, es importante ver cómo va el proceso de aprendizaje. Y aquí TensorBoard se convirtió en una herramienta conveniente. Rastreamos el valor del error en las iteraciones de entrenamiento y validación. Además, mostramos espectrogramas objetivo, espectrogramas predichos en la etapa de entrenamiento, espectrogramas predichos en la etapa de validación y alineación, que es un peso de atención acumulado aditivamente en todos los pasos del entrenamiento.

Es posible que al principio su atención no sea demasiado informativa:


Figura 4: Un ejemplo de escalas de atención mal entrenadas.

Pero después de que todos sus módulos comiencen a funcionar como un reloj suizo, finalmente obtendrá algo como esto:


Figura 5: Ejemplo de escalas de atención exitosamente entrenadas.

¿Qué significa esta tabla? En cada paso del decodificador, intentamos decodificar un cuadro del espectrograma. Sin embargo, no está claro qué información necesita usar el codificador en cada paso del decodificador. Se puede suponer que esta correspondencia será directa. Por ejemplo, si tenemos una secuencia de texto de entrada de 200 caracteres y un espectrograma correspondiente de 800 cuadros, entonces habrá 4 cuadros para cada carácter. Sin embargo, debe admitir que el discurso generado sobre la base de tal espectrograma estaría completamente desprovisto de naturalidad. Pronunciamos algunas palabras más rápido, algunas más lento, en algún lugar donde hacemos una pausa, pero en algún otro lugar donde no lo hacemos. Y considerar todos los contextos posibles no es posible. Es por eso que la atención es un elemento clave de todo el sistema: establece la correspondencia entre el paso del decodificador y la información del codificador para obtener la información necesaria para generar un marco específico. Y cuanto mayor sea el valor de los pesos de atención, más "se debe prestar atención" a la parte correspondiente de los datos del codificador al generar el marco del espectrograma.

En la etapa de capacitación, también será útil generar audio, y no solo evaluar visualmente la calidad de los espectrogramas y la atención. Sin embargo, aquellos que han trabajado con WaveNet estarán de acuerdo en que usarlo como un vocoder en la etapa de entrenamiento sería un lujo inaceptable en términos de tiempo. Por lo tanto, se recomienda utilizar el algoritmo Griffin-Lim , que permite la reconstrucción parcial de la señal después de transformaciones rápidas de Fourier. ¿Por qué parcialmente? El hecho es que cuando convertimos la señal en espectrogramas, perdemos información de fase. Sin embargo, la calidad del audio así obtenido será suficiente para entender en qué dirección se está moviendo.

Lecciones aprendidas


Aquí compartiremos algunas ideas sobre cómo construir el proceso de desarrollo, enviándolas en el formato de sugerencias. Algunos de ellos son bastante generales, otros son más específicos.

Sobre la organización del flujo de trabajo :

  • Utilice el sistema de control de versiones, describa clara y claramente todos los cambios. Esto puede parecer una recomendación obvia, pero aún así. Al buscar la arquitectura óptima, los cambios ocurren constantemente. Y después de haber recibido un resultado intermedio satisfactorio, asegúrese de hacerse un punto de control para poder realizar los cambios posteriores de manera segura.
  • Desde nuestro punto de vista, en tales arquitecturas uno debe adherirse a los principios de encapsulación: una clase - un módulo Python. Este enfoque no es común en las tareas de ML, pero lo ayudará a estructurar su código y acelerar la depuración y el desarrollo. Tanto en el código como en su visión de la arquitectura, divídala en bloques, bloques en módulos y módulos en capas. Si el módulo tiene código que desempeña un rol específico, combínelo en un método de clase de módulo. Estas son verdades comunes, pero no fuimos demasiado flojos para decir sobre ellas nuevamente.
  • Proporcione clases de estilo numpy con documentación . Esto simplificará enormemente el trabajo tanto para usted como para sus colegas que leerán su código.
  • Dibuja siempre la arquitectura de tu modelo. En primer lugar, le ayudará a darle sentido y, en segundo lugar, una vista lateral de la arquitectura y los hiperparámetros del modelo le permitirán identificar rápidamente las inexactitudes en su enfoque.
  • Mejor trabajar en equipo. Si trabaja solo, reúna colegas y discuta su trabajo. Como mínimo, pueden hacerle una pregunta que lo llevará a algunas reflexiones, pero al máximo le señalarán una inexactitud específica que no le permite entrenar con éxito el modelo.
  • Otro truco útil ya está asociado con el preprocesamiento de datos. Suponga que decide probar algunas hipótesis y hacer los cambios apropiados al modelo. Pero reiniciar el entrenamiento, especialmente antes del fin de semana, será arriesgado. El enfoque puede ser inicialmente incorrecto y perderá tiempo. ¿Qué hacer entonces? Aumente el tamaño de la ventana Transformación rápida de Fourier. El parámetro predeterminado es 1024; aumentarlo en 4, o incluso 8 veces. Esto "exprimirá" los espectrogramas en el número apropiado de veces y acelerará enormemente el aprendizaje. El audio recuperado de ellos tendrá una calidad inferior, ¿pero esta no es tu tarea ahora? En 2-3 horas ya puede obtener la alineación ("alineación" de las escalas de atención, como se muestra en la figura anterior), esto indicará la corrección arquitectónica del enfoque y ya se puede probar en grandes datos.

Modelos de construcción y entrenamiento :

  • Sugerimos que si los lotes no se formaran al azar, pero en función de su longitud, acelerarían el proceso de entrenamiento del modelo y mejorarían los espectrogramas generados. El supuesto lógico, que se basa en la hipótesis de que cuanto más se alimenta una señal útil (y no relleno) a la red de entrenamiento, mejor. Sin embargo, este enfoque no se justificó a sí mismo; en nuestros experimentos, no pudimos capacitar a la red de esta manera. Esto probablemente se deba a la pérdida de aleatoriedad en la selección de instancias para el entrenamiento.
  • Utilice algoritmos modernos de inicialización de parámetros de red con algunos estados iniciales optimizados. Por ejemplo, en nuestros experimentos, utilizamos la inicialización de peso uniforme de Xavier. Si en su módulo necesita utilizar la normalización por mini-lote y alguna función de activación, entonces deben alternar entre sí en este orden. De hecho, si aplicamos, por ejemplo, la activación de ReLU, inmediatamente perdemos toda la señal negativa que debería estar involucrada en el proceso de normalización de los datos de un lote en particular.
  • Desde un paso de aprendizaje específico, use una tasa de aprendizaje dinámica. Esto realmente ayuda a reducir el valor de error y aumentar la calidad de los espectrogramas generados.
  • Después de crear el modelo y los intentos fallidos de entrenarlo en lotes de todo el conjunto de datos, será útil intentar volver a entrenarlo en un lote. , alignment, ( ). , , .

    . . , – . , . , .
  • RNN- . . , . ? LSTM- -.
  • , LSTM-, « »: « , LSTM-. «» bf. , , , LSTM- ft 1/2. , : , «» 1/2, . bf , 1 2: ft ».
  • seq2seq- . — , . ? , ( ).
  • Ahora una recomendación específica para el marco PyTorch. Aunque la capa LSTM en el decodificador es esencialmente su propia celda LSTM, que recibe información para solo un elemento de la secuencia en cada paso del decodificador, se recomienda utilizar la clase torch.nn.LSTM lugar de torch.nn.LSTMCell . La razón es que el backend LSTM se implementa en la biblioteca CUDNN en C y el LSTMCell en Python. Este truco te permitirá aumentar significativamente la velocidad del sistema.

Y al final del artículo, compartiremos ejemplos de generación de discurso a partir de textos que no estaban contenidos en el conjunto de capacitación.

Source: https://habr.com/ru/post/es436312/


All Articles