16. Juni 2022

Implementierung von Capacitron – einem ausdrucksstarken Text-To-Speech-VAE-Modell

Ein Masterarbeitsprojekt

Voder: A speech synthesizer from 1930 by Bell Labs [Pinterest]

Dieser Beitrag ist eine kurze und ausgewählte technische Zusammenfassung meiner Implementierung eines ausdrucksstarken Text-to-Speech-Modells von Google im Rahmen meiner Masterarbeit an der TU Berlin.

Da meine eingereichte Abschlussarbeit online verfügbar ist, handelt es sich bei diesem Beitrag um eine hybride Arbeit, die einige Audiobeispiele aus dem Modell sowie einige interessante technische Implementierungsdetails zeigt, die für den Leser von Interesse sein könnten.

Kurze Zusammenfassung

Im Rahmen meiner Masterarbeit habe ich die erste Open-Source-Realisierung der Capacitron VAE-Erweiterung des Standardsystems Tacotron 1 von Google erfolgreich in die TTS-Bibliothek von Coqui AI implementiert. Durch die modulare Gestaltung des VAE-Moduls des Prosodie-Encoders kann dieser Encoder nun auch mit der Tacotron 2-Architektur verwendet werden, was die Stabilität und Qualität deutlich verbessert und somit eine erweiterte, verbesserte und Open-Source-Version der ursprünglichen Methode ist, die im Artikel von Battenberg et al. [5] vorgestellt wurde. Durch die Bereitstellung von Trainingsrezepten, zwei vortrainierten Capacitron-Modellen und einem vortrainierten HiFiGAN-Vocder-Modell basierend auf Catherine Byers‘ Blizzard2013-Datensatz innerhalb des „Model Zoo“ von Coqui AI kann die Community nun sofort mit neuen ausdrucksstarken Tacotron-basierten Systemen experimentieren und diese trainieren.

Links

Google Colab for experimenting with trained models

Prior audio examples

Posterior audio examples

Model pull request to the Coqui TTS

Themenwahl und Motivation

In einem Artikel von Aide-Zade et al. [1] aus dem Jahr 2013 wurden zwei Kriterien definiert, mit denen synthetische Sprachproduktionsmaschinen bewertet werden können. Das erste davon ist die „Sprachverständlichkeit“, die grob durch die Frage „Was wird geäußert?“ beschrieben werden kann. Frühere Sprachsynthesetechniken wie konkatenative und parametrische Technologien werden von neueren, auf Deep Learning basierenden Modellen auf einer Skala zur subjektiven Bewertung von Qualität und Natürlichkeit, dem sogenannten Mean Opinion Score, deutlich übertroffen. Diese neueren Technologien sind sehr nahe daran, die Lücke zwischen der subjektiven Wahrnehmungsqualität synthetischer und echter menschlicher Sprache zu schließen, sodass sie die Kriterien der Sprachverständlichkeit erfüllen, wie in Abbildung 1 dargestellt.

Das zweite Kriterium ist die „Natürlichkeit des Klangs“, die grob durch die Frage „Wie wird eine bestimmte Textaufforderung ausgesprochen?“ definiert werden kann. Die Sprachsynthese ist ein unterbestimmtes Problem, da eine einzelne Textaufforderung viele verschiedene Umsetzungen als Äußerung haben kann. Diese Umsetzungen werden durch die Intonation, Betonung, den Rhythmus und den Sprechstil verkörpert – wir bezeichnen diese Aspekte zusammenfassend als „Prosodie“. Die Kontrolle dieser Attribute auf eine Weise, mit der wir benutzerdefinierte, ausdrucksstarke synthetische Sprache synthetisieren können, ist einer der heiligen Grale dieser Technologie und ein stark erforschtes Thema. Darüber hinaus würden solche Systeme auch das Eins-zu-viele-Abbildungsproblem der Sprachsynthese lindern, bei dem frühere Methoden eine bestimmte Textaufforderung nur auf eine einzige Weise synthetisieren konnten.

Als ich mir vor ein paar Jahren einige der Beispiele auf der Website des Google TTS Research Teams anhörte, entdeckte ich eine unglaublich beeindruckende Reihe von Audiobeispielen, die die präzise Übertragung der Prosodie zwischen einem Referenzsprachsignal und einer synthetisierten Äußerung ermöglichten. Einige dieser Samples klangen so umwerfend, dass ich beschloss, dieses System verstehen und manipulieren zu können.

Tacotron basierte VAE

In Abbildung 2 sehen wir die standardmäßige Text-to-Speech-Architektur von seq2seq Tacotron, bei der das Modell Text als Eingabe verwendet und ihn in Zeicheneinbettungen umwandelt, die dann durch die vielen verschiedenen Schichten geleitet werden, um Ausgabe-Spektrogramm-Frames zu erzeugen – diese Frames können dann mithilfe der Griffin-Lim-Rekonstruktion oder neuronaler Vocoder in Audio umgewandelt werden.

In Abbildung 3 sehen wir eine Erweiterung dieses Standardsystems, bei der ein separates Subnetz Referenzspektrogrammscheiben aufnimmt, um eine einzelne Prosodieeinbettung zu erzeugen, die anschließend mit den Zeicheneinbettungen verknüpft wird. Während des Trainings ist das Referenzspektrogramm das eigentliche Spektrogramm, das das Modell zu rekonstruieren versucht. Wir können dieses Referenzencoder-Netzwerk also dazu anregen, eine Darstellung des Prosodieraums des Eingangssprechers zu lernen, wenn wir es gut genug parametrisieren. Genau um eine einzigartige Parametrisierung dieser Netzwerkarchitektur geht es in dieser Arbeit – eine Variational-Autoencoder-Erweiterung des rechts gezeigten Systems, genannt Capacitron [5].

Abbildung 4 zeigt ein Standard-Autoencoder-Netzwerk, das als eine Art Komprimierungsalgorithmus funktioniert - die Eingabedaten werden durch ein Encoder-Netzwerk geleitet, um einen festen, komprimierten Vektor z zu erstellen, der dann vom Decoder-Netzwerk dekodiert wird, um die ursprüngliche Eingabe zu rekonstruieren.

In Abbildung 5 sehen wir eine erweiterte Version dieser Architektur, bei der der Encoder keine komprimierte und feste Darstellung der Eingabe mehr ausgibt, sondern Parameter für eine Verteilung erzeugt – hier könnten mu und sigma z. B. Parameter einer multivariaten Standardnormalverteilung sein, die tatsächlich eine approximierte Posterior-Verteilung q(z|x) bildet. Dann können wir aus dieser Verteilung eine Stichprobe ziehen, um die tatsächliche latente Einbettung zu erzeugen – diese ist nicht mehr festgelegt, sodass wir mit dieser Architektur eine Art kontrollierte Zufälligkeit in das Netzwerk einführen können.

Die VAE-Architektur ermöglicht 3 verschiedene Möglichkeiten, ein trainiertes Modell abzuleiten. Erstens: Wenn wir dem Netzwerk ein Referenzspektrogramm zuführen, fordern wir den Encoder auf, die Prosodie-Einbettung aus dieser Referenz abzurufen und sie entweder auf denselben Text wie in der Referenz anzuwenden – was uns Same-Text-Prosody-Transfer (STT) gibt – oder wir können beliebigen Text ableiten und dadurch den allgemeinen Stil von einer Referenz auf einen völlig neuen Text übertragen – wir nennen dies Inter-Text-Style-Transfer (ITT).

Die zweite Möglichkeit, das Modell abzuleiten, besteht darin, keine Referenz einzugeben, sondern das Modell dazu zu bringen, aus der vorherigen Verteilung des latenten Raums zu sampeln. Dadurch entsteht ein echtes generatives Modell, bei dem jedes Mal, wenn wir die Synthese einleiten, eine realistische, aber zufällige Prosodie abgetastet wird. Dadurch wird das Eins-zu-viele-Mapping-Problem des Standard-Tacotron-Systems eliminiert.

Sie können sich einige dieser Beispiele in diesen beiden Videos anhören:

Model Loss Funktion

Ohne zu sehr auf die Details der Funktionsweise von VAEs einzugehen (mehr über die Theorie können Sie in Kapitel 3 meiner Dissertation lesen), werde ich jetzt die erweiterte Verlustfunktion des Capacitron-Modells vorstellen und eine intuitive Vorstellung davon geben, wie die Kapazität dieses Modells die Ausdruckstiefe der synthetisierten Sprache steuern kann.

Der erste Term von Gleichung 1 ist der erwartete Rekonstruktionsverlust des generativen Modells, wobei wir den grundlegenden Verlust des Tacotron L1-Decoders als Ersatz für die negative Log-Likelihood eines generierten Spektrogramms x bei latenter Einbettung z und Text y_{T} verwenden. Der zweite Term von Gleichung 1 ist die erweiterte KL-Term-Berechnung zwischen der ungefähren Posterior- und Prior-Vermutung, einschließlich der automatisch abgestimmten konstanten Beta, die einem Lagrange-Multiplikator ähnelt, und der Variationskapazitätsgrenze C. Wir beschränken Beta auf Nichtnegativ, indem wir einen uneingeschränkten Parameter durch eine Softplus-Nichtlinearität leiten, wodurch die Kapazitätsbeschränkung zu einer Grenze und nicht zu einem Ziel wird [5].

Die beiden Ziele dieser Verlustfunktion bestehen darin, den Decoderverlust des Standard-Tacotron-Modells zu verringern und die KL-Divergenz zwischen der approximierten Posterior- und Prior-Verteilung (d. h. die Obergrenze der gegenseitigen Information zwischen den Daten X und dem latenten Prosodieraum Z) auf einen gewünschten Variationskapazitätswert C zu optimieren. Je höher der C-Wert, desto mehr wird der Referenzencoder dazu ermutigt, Informationen aus dem Prosodieraum Z zu kodieren, was bedeutet, dass wir durch die Kontrolle der Obergrenze der gegenseitigen Information zwischen den Daten und dem latenten Raum den Grad der Ausdruckskraft steuern können, den der Prosodieencoder aus den Daten lernt. Andererseits kann die strukturelle Kapazität der VAE-Architektur leicht angepasst werden, indem die Dimensionalität der latenten Einbettung z geändert wird. Das Einstellen dieser Dimensionalität zwingt den Referenzencoder, den latenten Raum auf einen festen Tensor zu übertragen, dessen Größe manipuliert werden kann, um zu steuern, wie groß die Variationseinbettung des Netzwerks sein soll.

Diverse Implementation Details

In diesem Abschnitt werden einige ausgewählte Beispiele der Modellimplementierung vorgestellt, die für den Leser interessant sein könnten.

Doppelte Optimierung

Ein Großteil der Implementierung konzentrierte sich auf die in [5] beschriebene doppelte Optimierungsroutine. Die in Gleichung 1 definierte Verlustfunktion sollte durch zwei separate Prozesse optimiert werden. Der Haupt-ADAM-Optimierer wird für alle Modellparameter außer dem einzelnen Skalarparameter Beta verwendet, während ein separater SGD-Optimierer nur für Beta verwendet wird. Diese doppelte Optimierung wurde durchgeführt, indem die Verlustfunktion in zwei separate Zuweisungen aufgeteilt wurde, die jeweils nur die zu minimierenden Parameter enthielten. Dabei wurde die .detach()-Methode von PyTorch verwendet, um Parameter von der automatischen Differenzierungs-Engine zu trennen. Die Implementierung dieser Routine finden Sie in der Klasse TacotronLoss.

Faltungsmaskierung

Einer der entscheidenden Aspekte der Implementierung ist eine nicht explizit erwähnte Funktion des Faltungsnetzwerks im Referenzencoder. Einfache Faltungsnetzwerke nehmen normalerweise eine Eingabe und komprimieren sie auf eine gewünschte Ausgabe, indem sie unterschiedliche Werte für die Parameter kernel_size und stride verwenden, die das rezeptive Feld und das Gleiten der Faltungsfilter definieren. Diese Basisnetzwerke arbeiten mit festen Eingabegrößen – normalerweise Bildern mit statischer Größe –, auf denen sie die Faltungsschritte ausführen, um die Daten zu reduzieren und zu komprimieren.

Eine einzigartige Funktion bei der Arbeit mit Sprachdaten besteht darin, dass das Audio, das den Netzwerken zugeführt wird, unterschiedliche Längen hat. Innerhalb eines Trainingsbatches werden diese unterschiedlich langen Einzelproben in absteigender Reihenfolge sortiert, sodass Beispiele ähnlicher Länge aufeinander folgen. Die längste Probe im Batch definiert jedoch immer die endgültige Größe des Eingabetensors innerhalb eines bestimmten Trainingsschritts. Andere, kürzere Proben werden natürlich mit Nullen aufgefüllt, um der Größe der längsten Probe zu entsprechen, sodass der Eingabetensor eine einheitliche Form hat.

Innerhalb des ReferenceEncoder-Moduls wird dieser Eingabetensor mit den mit Nullen aufgefüllten Instanzen durch einen Stapel aus 6 Faltungsschichten mit 3x3 Filtern, 2x2 Schrittweite und Batch-Normalisierung geleitet. Die 6 Schichten haben jeweils 32, 32, 64, 64, 128 und 128 Filter. Um zu demonstrieren, warum dieser Vorgang besondere Aufmerksamkeit erfordert, zeigt Abbildung 7 ein einfaches 1D-Beispiel eines Eingabetensors mit input_length=5. Das Faltungsmodul von PyTorch ermöglicht die Angabe zusätzlicher Nullenauffüllungen auf verschiedenen Achsen des Eingabetensors, in diesem Fall wird eine Nullenauffüllung von 2 auf der Breitendimension angezeigt. Dies soll sicherstellen, dass das rezeptive Feld des Faltungsfilters die Informationen an den Kanten des Eingabetensors richtig enthält. Mit filter_width=3 und Schrittweite=2 sehen wir, dass der Filter 3 Eingabewerte verwendet, um diese Werte in einen einzigen Ausgabewert zu falten. Der Filter verschiebt dann 2 Werte entlang der Breitenachse, um das nächste Werte-Tripel einzuschließen, und so weiter. Diese singuläre Faltungsoperation komprimiert nun den Eingabetensor von Eingabelänge = 5 auf Ausgabelänge = 4.

Der vorherige Fall musste offensichtlich nicht anders behandelt werden als normale Faltungsoperationen – PyTorch verfügt über alle integrierten Hilfsfunktionen und -methoden, um die Datenkomprimierung zu berücksichtigen. In unserem speziellen Fall der Arbeit mit Audiodaten variabler Länge zeigt Abbildung 8 jedoch, wo dieser Vorgang genauer berücksichtigt werden muss. In diesem Fall befindet sich dasselbe valid_length-Beispiel in einem anderen Batch, wo es nicht mehr die längste Probe ist, sodass am Ende des Signals bereits eine Nullenauffüllung angewendet wurde. In diesem speziellen Fall wird der Eingang bereits vom Datenzufuhrnetzwerk mit zwei Nullwerten aufgefüllt. Mit derselben Faltungsoperation wie oben beschrieben hat die Ausgabe dieser Faltung nicht mehr die Länge 4, sondern 5. Der letzte Filter wendet Faltung auf Nullwerte an, aufgrund der Verzerrung werden diese Terme jedoch nach dem Faltungsschritt nicht Null sein. Selbst wenn sie klein sind, macht in unserem Fall eines Stapels von 6 solchen Faltungen die Menge an ungültigen Informationen dieses Faltungsnetz im Wesentlichen unfähig, die Eingabedaten richtig zu verarbeiten und während der Testzeit sinnvolle Ausgaben zu erzeugen.

Die Lösung für dieses Problem besteht darin, die gültige Länge jeder Instanz im Batch nach einem einzigen Faltungsdurchgang zu berechnen und alle ungültigen Werte auszublenden, bevor diese Ausgabe wieder in die nächste Faltung eingespeist wird. Mit dieser Faltungsmaskierung, die in Abbildung 9 dargestellt ist, wird das Eingangsaudio mit variabler Länge ordnungsgemäß in eine gültige, komprimierte Darstellung heruntergesampelt. Dieses Implementierungsdetail erwies sich als wesentlicher Aspekt der Capacitron-Methode – Modelle, die ohne diese Faltungsmaskierung trainiert wurden, konnten den latenten Raum der Eingangsdaten überhaupt nicht lernen und erzeugten während der Testzeit unverständliches Audio.

Wie diese Maskierung implementiert wird, erfahren Sie in der Klasse CapacitronLayers.

Referenzen:

[1] : Aida–Zade et al.: The Main Principles of Text-to-Speech Synthesis System. 2013

[2] : WaveNet: A generative model for raw audio blog post. deepmind.com/blog/ article/wavenet-generative-model-raw-audio

[3] : Wang et al.: Tacotron: Towards End-to-End Speech Synthesis. 2017

[4] : Skerry-Ryan et al.: Towards End-to-End Prosody Transfer for Expressive Speech Synthesis with Tacotron. 2018

[5] : Battenberg et al.: Effective Use of Variational Embedding Capacity in Expressive End-to-End Speech Synthesis. 2019