<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://jordandare.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://jordandare.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2026-04-05T19:40:05+00:00</updated><id>https://jordandare.github.io/feed.xml</id><title type="html">blank</title><subtitle># A simple, whitespace theme for academics. Based on [*folio](https://github.com/bogoli/-folio) design. </subtitle><entry><title type="html">Echo</title><link href="https://jordandare.github.io/blog/2025/echo/" rel="alternate" type="text/html" title="Echo"/><published>2025-11-20T00:00:00+00:00</published><updated>2025-11-20T00:00:00+00:00</updated><id>https://jordandare.github.io/blog/2025/echo</id><content type="html" xml:base="https://jordandare.github.io/blog/2025/echo/"><![CDATA[<h2 id="overview">Overview</h2> <p><strong>Code:</strong> <a href="https://github.com/jordandare/echo-tts">https://github.com/jordandare/echo-tts</a></p> <p><strong>Demo:</strong> <a href="https://huggingface.co/spaces/jordand/echo-tts-preview">https://huggingface.co/spaces/jordand/echo-tts-preview</a></p> <p>Recently, we have been working on a diffusion text-to-speech model, named Echo, following our prior work on <a href="https://jordandarefsky.com/blog/2024/parakeet/">Parakeet</a>. This work was made possible through the <strong>TPU Research Cloud (TRC)</strong> program<d-cite key="trc"></d-cite>, to which we are deeply grateful.</p> <p>Parakeet, which is autoregressive in DAC token space<d-cite key="kumar2023dac"></d-cite>, struggled with consistency - - it would often take many tries to generate a semantically correct sample - - and exhibited poor voice cloning capability. Given our goal of generating high quality speech samples with performant voice cloning (and an indifference towards real-time interaction capabilities, which may favor autoregressive methods), we felt it reasonable to try a diffusion approach.</p> <p><strong>We train a 2.4B DiT, Echo</strong>, that is able to generate audio segments of up to 30 seconds, conditioned on target text and up to two minutes of speaker reference audio. We generate Fish Speech S1-DAC latents and as a result are <strong>able to produce 44.1kHz audio</strong>.</p> <p>Our model is quite fast at generating single samples (at least relative to frontier open-source autoregressive approaches). <strong>For example, given a 120-second audio prompt, we are able to generate 30 seconds of audio in only 1.45 seconds on an A100 (RTF &lt; 0.05).</strong></p> <p>Below are samples, and further below is a more detailed description of the model.</p> <h2 id="samples">Samples</h2> <details class="samples-section" open="" id="samples-showcase"> <summary>Samples (showcase)</summary> <p class="samples-description"> These samples have been minimally cherry-picked to showcase strengths and capabilities of our model. (Many of the samples were generated on the first try; CFG was adjusted from default settings in the singing and videogame character examples<sup id="fnref:videogame-character-note" role="doc-noteref"><a href="#fn:videogame-character-note" class="footnote" rel="footnote">1</a></sup>. First-try outputs can be found in the sample comparisons section below.) </p> <details class="sample-row showcase-row" open=""> <summary> Podcast 0 — [S1] I'm not trying to make a good statement about the current.... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_podcast_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] I'm not trying to make a good statement about the current. [S2] Yeah, yeah, yeah. [S1] For, uh, operant, but maybe I am. But, like, the actual level of understanding is very different from the level of control. [S2] Yeah, I think that's fair. That's a good pushback. I think, like, um, I guess I expect loss of, uh, both. [S1] Mm. [S2] (laughs) Yeah. [S1] How come? I mean, loss of understanding is obvious, but why loss of control? [S2] So, uh, so we're really far into a territory of, uh,</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_podcast_0.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Podcast 1 — [S1] But the in-context learning itself is not gradient descent, in the... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_podcast_1.wav"></audio> <strong>Text prompt</strong> <p>[S1] But the in-context learning itself is not gradient descent, in the same way that our lifetime intelligence as humans, to be able to do things, is conditioned by evolution, but our actual learning during our lifetime is like- [S2] Yeah. [S1] ... happening through some other process. [S2] I actually don't fully agree with that, but you should continue with how- [S1] Oh, okay. [S2] (laughs) [S1] I, actually, then I, I'm very curious to understand how that analogy breaks down. [S2] I think I'm hesitant to say that in-context learning is not doing gradient descent,</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_podcast_1.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Podcast 2 — [S1] pre-training helps build the kind of entity which can learn better.... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_podcast_2.wav"></audio> <strong>Text prompt</strong> <p>[S1] pre-training helps build the kind of entity which can learn better. It teaches meta-learning, and therefore it is a sim- similar to like finding an algorithm. [S2] Mm-hmm. [S1] Um, but if, if it's like evolution gives us knowledge and pre-training gives us knowledge, they're not, that analogy seems to break down. [S2] Yeah, yeah. So it's subtle, and I think you're, you're right to push back on it, but basically, the thing that pre-training is doing, so you're basically getting the next- [S1] Yeah. [S2] ... token predictor on, over the internet, and you're training that into a neural net.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_podcast_2.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Reading 0 — [S1] The old lighthouse keeper had seen many storms in his thirty... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_reading_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] The old lighthouse keeper had seen many storms in his thirty years on the rock, but nothing like this. The fog rolled in thick as wool, swallowing the beam of light before it could reach the churning waves below. Then he heard it, three short bells from the channel, where no ship should be at this hour. Something was out there, something that shouldn't exist.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_reading_0.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Speaker 0 — [S1] And they don't do a lot of the things that you've... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_speaker_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] And they don't do a lot of the things that you've alluded to earlier, you know. They don't have continued learning. You can't just tell them something and they'll remember it. And they're just cognitively lacking, and it's just not working. And I just think that it will take about a decade to work through all of those issues. I could be wrong (laughs).</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_speaker_0.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Speaker 1 — [S1] (singing) The LLM will often give you some stuff, which is... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_speaker_1.wav"></audio> <strong>Text prompt</strong> <p>[S1] (singing) The LLM will often give you some stuff, which is roughly correct. (singing) But if you give it the full chapter and ask it questions, you're going to get much better results because it's now loaded in the working memory of the model.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_speaker_1.wav"></audio> </div> </div> </div> </details> <details class="sample-row showcase-row" open=""> <summary> Speaker 2 — [S1] Actually, recently, I, uh, also went back all the way to... </summary> <div class="showcase-row-content"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/reference/s_speaker_2.wav"></audio> <strong>Text prompt</strong> <p>[S1] Actually, recently, I, uh, also went back all the way to 1989, which was kind of a fun, uh, exercise for me a few years ago, uh, because I was reproducing, uh, Jan LeCun's 1989 convolutional network, which was the first neural network I'm aware of trained via gradient descent, like modern neural network-trained.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo generation</span> <audio controls="" preload="none" src="/assets/audio/echo/showcase/generated/s_speaker_2.wav"></audio> </div> </div> </div> </details> </details> <details class="samples-section" open="" id="samples-comparisons"> <summary>Samples (comparisons)</summary> <div class="samples-description"> <p> For each (reference-audio, text-prompt) pair, we sample from Echo, Higgs Audio v2, and VibeVoice-7B<d-cite key="bosonai2025higgs"></d-cite><d-cite key="vibevoice2025site"></d-cite> and select the first result from each (i.e., no cherry-picking for any of the models). Echo (flat) and Echo (sharp) are different sampling settings, though both start with the same random noise per prompt and use 30 sampling steps. Note that the two Echo settings may sound quite similar depending on your listening setup and arguably the latter should have been excluded from the demo for succinctness. See the footnote for details on all model sampling.<sup id="fnref:sampling-settings" role="doc-noteref"><a href="#fn:sampling-settings" class="footnote" rel="footnote">2</a></sup> </p> <p> In our opinion, Echo is able to capture the reference speaker qualities (and generate correct semantics) fairly well. There is an artifact around “or not” in the Echo-generated podcast_0 samples; this artifact is eliminated by increasing the number of sampling steps from 30 to 60 (which still only takes 2.7 seconds on an A100). See footnote for the 60-step audio (same random seed, first-try with 60 steps).<sup id="fnref:podcast-0-60step" role="doc-noteref"><a href="#fn:podcast-0-60step" class="footnote" rel="footnote">3</a></sup> </p> <p> These prompts are stylistically “in-distribution” of Echo, and it’s likely that better samples for the other two models could be obtained via prompt (or hyperparameter) optimization. </p> <p> The samples below take <strong>1.45s</strong>, ~12s, and ~55s to generate on an A100 for Echo, Higgs Audio v2, and VibeVoice-7B respectively in our setup (per 30 seconds of audio). More optimized implementations may yield some speed-up for the other two models. </p> </div> <details class="sample-row" open=""> <summary> Podcast 0 — [S1] I, I think this, I think this was probably public. Uh,... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/podcast_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] I, I think this, I think this was probably public. Uh, but basically, if you're using an LM judge for a reward, so you just give it a solution from a student and ask it if the student did well or not. We were training with reinforcement learning against that reward function, and it worked really well, and then, um, suddenly, the reward became extremely large. Like it was massive jump and it did perfect. And you're looking at it like, "Wow, this, this means the student is perfect in all these problems. It's fully solved math."</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/podcast_0.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/podcast_0.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/podcast_0.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/podcast_0_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Podcast 1 — [S1] That's actually more boilerplate-y. So actually vibe coded part, partially some... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/podcast_1.wav"></audio> <strong>Text prompt</strong> <p>[S1] That's actually more boilerplate-y. So actually vibe coded part, partially some of that stuff. That was fine. Um, because it's not like mission critical stuff and it works fine. And then the other part is when I was rewriting the tokenizer in Rust, uh, I'm actually not as good at Rust because I'm fairly new to Rust.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/podcast_1.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/podcast_1.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/podcast_1.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/podcast_1_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Reading 0 — [S1] Deep beneath the ocean's surface, where sunlight fades to perpetual twilight,... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/reading_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] Deep beneath the ocean's surface, where sunlight fades to perpetual twilight, extraordinary creatures have evolved in ways that defy imagination. Bioluminescent jellyfish pulse with ethereal blue light, while giant squid hunt in the crushing darkness. At depths of over two miles, the pressure is immense, yet life persists.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/reading_0.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/reading_0.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/reading_0.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/reading_0_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Reading 1 — [S1] The telegram arrived on a Tuesday morning in June, nineteen forty-three.... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/reading_1.wav"></audio> <strong>Text prompt</strong> <p>[S1] The telegram arrived on a Tuesday morning in June, nineteen forty-three. Margaret's hands trembled as she tore open the envelope, dreading the words she knew might be inside. Her brother had shipped out to North Africa six months ago, and his letters had grown increasingly sparse. The weight of the war pressed down on everyone in the village, a collective holding of breath, waiting for news that could shatter families in an instant.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/reading_1.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/reading_1.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/reading_1.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/reading_1_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Sport 0 — [S1] Yeah, you know, um, look, we, we had our chances early.... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/sport_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] Yeah, you know, um, look, we, we had our chances early. Got guys on base, uh, just couldn't really capitalize. Tommy's been throwing the ball well all season, right? He just, uh, he left a couple pitches up and, and they made us pay.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/sport_0.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/sport_0.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/sport_0.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/sport_0_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Character 0 — [S1] I think they might be the same contractor. No problem officer.... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/vg_0.wav"></audio> <strong>Text prompt</strong> <p>[S1] I think they might be the same contractor. No problem officer. Just glad we got this tightened out. I'll contact the ICP database immediately. Call back tomorrow. Hopefully they have dug up something useful to your investigation by then. The only constant seems to be that the mercenaries are always deployed in small countries.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/vg_0.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/vg_0.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/vg_0.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/vg_0_generated.wav"></audio> </div> </div> </div> </details> <details class="sample-row" open=""> <summary> Character 1 — [S1] After giving everything some more thought, I've decided it's in the... </summary> <div class="sample-body"> <div class="sample-reference"> <span>Audio reference</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/reference_audio/vg_1.wav"></audio> <strong>Text prompt</strong> <p>[S1] After giving everything some more thought, I've decided it's in the best interest of humanity to acquire Nexus AI. Yes. I've spoken with the CEO and he's on board. Well, at least that's the impression he gave initially.</p> </div> <div class="sample-outputs"> <div> <span class="our-generation">Echo (flat) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_flat/vg_1.wav"></audio> </div> <div> <span class="our-generation">Echo (sharp) -- ours</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/echo_sharp/vg_1.wav"></audio> </div> <div> <span>Higgs Audio v2</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/higgs_generated/vg_1.wav"></audio> </div> <div> <span>VibeVoice-7B</span> <audio controls="" preload="none" src="/assets/audio/echo/comparisons/vv_generated_normalized/vg_1_generated.wav"></audio> </div> </div> </div> </details> </details> <h2 id="details">Details</h2> <h3 id="dataset">Dataset</h3> <p>We collect a dataset consisting of around 160K hours of podcast-like audio. Similar to Parakeet, we split the data into (≤) 30-second segments and transcribe all segments individually with (our) WhisperD<d-cite key="darefsky2024whisperd"></d-cite>, a Whisper model fine-tuned to output speaker diarization, disfluencies, and non-verbal events, based on Whisper<d-cite key="radford2023whisper"></d-cite>.</p> <h3 id="autoencoder">Autoencoder</h3> <p>We use the Fish-Speech S1-DAC autoencoder<d-cite key="fishaudio2024s1mini"></d-cite><d-cite key="openaudio2024techreport"></d-cite>, which is licensed under CC BY-NC-SA 4.0 (and therefore the full generation pipeline in our setup is non-commercial). S1-DAC is a discrete codec that encodes 44.1kHz audio into 10 discrete residual codebooks at ~21.5Hz. Thus, ~30 seconds of audio corresponds to a (640, 10) integer array.</p> <p>We choose to encode/store/load all of our data discretely as codes. Since we will use diffusion to model the data, we will need to represent the codes as continuous latents. Similar to DAC, S1-DAC forms its pre-decoder continuous latent by summing individual up-projections of each of the 10 codebook entries; these entries themselves have dimension 8, and are up-projected to dimension 1024. Thus, the resulting 1024-dimensional output subspace must have rank ≤ 80. We hoped to (in some sense) preserve the geometry of the pre-decoder latent space, so rather than concatenate codebook entries, we apply PCA to the pre-decoder latents and extract the first 80 indices of the rotated latents.<sup id="fnref:pca-comment" role="doc-noteref"><a href="#fn:pca-comment" class="footnote" rel="footnote">4</a></sup></p> <h3 id="model">Model</h3> <p>Echo is a 2.4B DiT<d-cite key="peebles2023dit"></d-cite> (though decoder compute per diffusion timestep is closer to a 1.4B transformer with seq_len = 640 and kv_seq_len ≤ 2048).<sup id="fnref:config-comment" role="doc-noteref"><a href="#fn:config-comment" class="footnote" rel="footnote">5</a></sup> The architecture is relatively simple.</p> <figure class="echo-architecture"> <img src="/assets/img/echo/echo_diagram.png" alt="High-level Echo architecture diagram showing reference encoder, text encoder, and diffusion decoder."/> </figure> <p>Echo consists of a speaker reference transformer, text transformer, and diffusion decoder:</p> <ul> <li>The speaker reference transformer processes up to two minutes of (clean) speaker reference latents. This originally corresponds to a latent sequence length of 2560. To reduce sequence length relative to target, we patch these latents (patch_size = 4) so that the transformer sequence length is 640. This encoder is causal.<sup id="fnref:speaker-causal-comment" role="doc-noteref"><a href="#fn:speaker-causal-comment" class="footnote" rel="footnote">6</a></sup></li> <li>The text transformer processes utf-8 bytes of maximum length 768 during training, with bidirectional attention (and appropriate masking).</li> <li>The diffusion decoder denoises noised latents (sequence length 640). It is a traditional transformer with: <ul> <li>Self-attention replaced with joint-self-cross-attention, where the keys/values consist of the self(denoiser) keys/values concatenated with the speaker and text transformer output keys/values.<sup id="fnref:joint-attn-comment" role="doc-noteref"><a href="#fn:joint-attn-comment" class="footnote" rel="footnote">7</a></sup></li> <li>AdaLN with LoRA<d-cite key="gupta2023walt"></d-cite> to reduce param count, with a tanh applied on gate.</li> </ul> </li> </ul> <p>We use a SwiGLU MLP<d-cite key="shazeer2020glu"></d-cite>, QK-norm<d-cite key="henry2020qknorm"></d-cite>, RoPE<d-cite key="su2021roformer"></d-cite> (though in decoder applied to only half the heads), and gated attention<d-cite key="qiu2025gatedattention"></d-cite>.</p> <h3 id="speaker-reference-selection">Speaker reference selection</h3> <p>We need training tuples of the form (speaker_reference_audio, target_text, target_audio); while we have the target text and audio from the aforementioned segmentation, extracting speaker reference audio is slightly more involved. At this stage, our data consists of long audio samples (generally 30–60 minutes, think podcast-like; we will use the term “episode” moving forward) that have been segmented into &lt;30-second clips. The most naive approach is to, for a given target clip, randomly select a subset of (distinct) segments from the same episode and concatenate up to 120 seconds of latents. This works reasonably well, though we try to improve upon this by diarizing each episode and selecting other clips based on a fuzzy/heuristic approach that tries to match speaker composition of reference concatenation and target.<sup id="fnref:speaker-composition-note" role="doc-noteref"><a href="#fn:speaker-composition-note" class="footnote" rel="footnote">8</a></sup></p> <p>The upside of such a setup is that it is simple and allows us to drop in any single-or-multi-speaker audio clip(s) as speaker reference, without any additional labeling/transcribing/processing. The downside is that if we have two speaker reference clips where the speakers are in different “acoustic environments” and use the concatenation of these clips as the speaker reference, generating conversation between the two speakers is likely out-of-distribution for the model. Another downside is that given a reference clip of two speakers conversing, there is little (if any) control over the correspondence between [S1], [S2], …, and the reference speakers, and sampling over different random seeds might be necessary to obtain a desired permutation.</p> <p>That said, these two missing behaviors/functions could be (relatively) straightforwardly fine-tuned into the model given the proper data. (And it might be possible to synthesize such data via diarization and audio augmentation.)</p> <h3 id="training">Training</h3> <p>We train on a TPU v4-64 pod in JAX/flax (linen). We use Muon<d-cite key="optaxMuon"></d-cite> (which seemed to outperform Adam in our smaller ablations) with a batch size of 768 for 800K steps, with a WSD LR schedule. Computation is performed in BF16 (weights and optimizer states are kept in FP32). We train with independent speaker condition dropout (10%) and text condition dropout (10%) for enabling CFG at inference.</p> <p>Since many audio segments are less than 30 seconds (i.e., latent sequence length &lt; 640), we zero-pad all latent sequences to length 640 and treat this padded, fixed-sized latent as the denoiser target. During inference, we sample starting from noise with shape (batch, 640, 80), and crop the generated audio by heuristically identifying trailing regions of (near) zeros in the generated latent.</p> <p>We train with a Rectified Flow setup<d-cite key="liu2022rectifiedflow"></d-cite>, using a stratified logit-normal timestep distribution<d-cite key="esser2024rfscaling"></d-cite>.</p> <h3 id="sampling">Sampling</h3> <p>We try a few different sampling methods, generally consisting of Euler sampling with the RF ODE. Below is an example of sampling hyperparameters.</p> <ul> <li>Number of steps (30 generally works well)</li> <li>CFG (select one version below)<d-cite key="ho2022cfg"></d-cite> and choose guidance scales <ul> <li>Joint unconditional (2× NFE) <ul> <li><code class="language-plaintext highlighter-rouge">v_pred = v_pred_cond + w * (v_pred_cond - v_pred_uncond)</code> where <code class="language-plaintext highlighter-rouge">v_pred_uncond</code> drops both text and speaker conditions</li> </ul> </li> <li>Independent guidance (3× NFE) <ul> <li><code class="language-plaintext highlighter-rouge">v_pred = v_pred_cond + w_text * (v_pred_cond - v_pred_uncond_text) + w_speaker * (v_pred_cond - v_pred_uncond_speaker)</code></li> </ul> </li> <li>Alternating guidance (2× NFE) <ul> <li><code class="language-plaintext highlighter-rouge">v_pred_cond + w_M * (v_pred_cond - v_pred_uncond_M)</code> where <code class="language-plaintext highlighter-rouge">M</code> alternates between text and speaker each step</li> </ul> </li> </ul> </li> <li><code class="language-plaintext highlighter-rouge">CFG_min_t</code>, <code class="language-plaintext highlighter-rouge">CFG_max_t</code> (we find 0.5 for min T often works well)</li> <li><code class="language-plaintext highlighter-rouge">initial_noise_scale</code> <ul> <li>(sometimes scaling the initial noise by a factor of 0.8 or 0.9 reduces artifacts; as far as I understand, this is totally unprincipled and suggestive that something is “off”; should be looked into further)</li> </ul> </li> <li>Temporal score rescaling<d-cite key="xu2025tsr"></d-cite> <ul> <li>Anecdotally (and with low confidence) <code class="language-plaintext highlighter-rouge">k = 1.2, sigma = 3.0</code> may result in more robust/consistent/less-“artifacty”/flatter samples, while <code class="language-plaintext highlighter-rouge">k = 0.96, sigma = 3.0</code> results in sharper samples (maybe higher cloning potential/ceiling for certain voices)?</li> </ul> </li> </ul> <p>We have not conducted a formal evaluation of different sampling hyperparameters. In our experience, the “Independent guidance” CFG option works well and has the benefit of decoupling the text and speaker guidance scales (though alternating guidance also decouples, and even joint unconditional CFG seems to work well in many cases). We have also at various points tried APG<d-cite key="sadat2024apg"></d-cite>, Karras SDEs<d-cite key="karras2022edm"></d-cite>, CFG++<d-cite key="chung2024cfgpp"></d-cite>, DPM-Solver++<d-cite key="lu2022dpmsolverpp"></d-cite>, etc. We leave further investigation and evaluation of such methods to future work.</p> <h3 id="dynamic-block-wise-diffusion">(Dynamic-)Block-wise diffusion</h3> <figure class="blockwise-diagram"> <img src="/assets/img/echo/blockwise_diagram.png" alt="Diagram of block-wise diffusion showing latent-prefix encoder feeding multiple generation blocks."/> </figure> <p>Our model as described above has the following limitations:</p> <ol> <li>It always will generate (zero-padded) 30-second chunks, even when the user anticipates the content will be much shorter (this still will generate proper-length content but spends more compute than necessary; consider time-to-first-byte (TTFB)).</li> <li>We cannot generate audio continuations (e.g., audio from seconds 10 to 20 given audio from the first 10 seconds). This also hinders generating longer-than-30-second samples via overlapping window approaches.</li> </ol> <p>We propose fine-tuning Echo to perform dynamic-block-wise diffusion, which addresses both these points.</p> <p>Specifically, we introduce a latent-prefix encoder (which shares the same architecture as the speaker reference encoder) that will causally process the clean <code class="language-plaintext highlighter-rouge">(640, 80)</code> target latents.<sup id="fnref:speaker-patch-note" role="doc-noteref"><a href="#fn:speaker-patch-note" class="footnote" rel="footnote">9</a></sup> During fine-tuning, we randomly partition <code class="language-plaintext highlighter-rouge">x_t ∈ (640, 80)</code> into (up to) 3 separate segments<sup id="fnref:segment-prob-note" role="doc-noteref"><a href="#fn:segment-prob-note" class="footnote" rel="footnote">10</a></sup> with block indices (0, 1, 2). In the decoder joint-self-cross-attention, the noised latents only can attend to other noised latents within the same block. We extend the existing keys/values with the (kv-projected) latent-prefix outputs<sup id="fnref:rope-half-note" role="doc-noteref"><a href="#fn:rope-half-note" class="footnote" rel="footnote">11</a></sup> and mask so that each noised latent attends only to latent-prefix keys from (strictly) previous blocks.</p> <p>This allows us to specify any block-size to generate at inference: We simply process all previously generated (or given) latents with the latent-prefix encoder<sup id="fnref:kv-cache-note" role="doc-noteref"><a href="#fn:kv-cache-note" class="footnote" rel="footnote">12</a></sup> and then sample from the RF ODE with initial <code class="language-plaintext highlighter-rouge">x_t ∈ (desired_length, 80)</code><sup id="fnref:rope-offset-note" role="doc-noteref"><a href="#fn:rope-offset-note" class="footnote" rel="footnote">13</a></sup>. Since S1-DAC is causal, we can stream blocks of generated audio. If we want to decrease TTFB, we can first generate a short block and immediately decode it (and then generate longer blocks if desired).</p> <p>To fine-tune this behavior, we initialize the latent-prefix encoder from the speaker reference encoder, initialize new w_k_latentprefix/w_v_latentprefix, and adapt the latent-prefix encoder (along with the MLP/attention of the decoder) with LoRA (all weights besides the LoRA layers and new w_k/w_v are frozen). We add RoPE (with patch-adjusted frequency) to the latent-prefix keys. We fine-tune with Adam for 100K steps. (Earlier versions of our training setup employed block-wise methods during pretraining, but for Echo we chose to leave this for fine-tuning.)</p> <p>It is unlikely that we will include the block-wise fine-tuned weights in our initial release.</p> <h3 id="acknowledgments">Acknowledgments</h3> <p>We are immensely grateful to the TPU Research Cloud program for their continued support. We’d also like to thank Professor Duan, Ge Zhu, and the AIR lab (https://labsites.rochester.edu/air/) at University of Rochester. Lastly, we thank Anaheim and https://huggingface.co/MrDragonFox from the OpenSesame audio research Discord server for their valuable early feedback and advice.</p> <p>Audio reference samples are brief excerpts used for technical demonstration purposes only. All rights belong to original creators.</p> <h2 id="bibtex">Bibtex</h2> <p>To cite this blog post, please use:</p> <p><code class="language-plaintext highlighter-rouge">bibtex @misc{darefsky2025echo, author = {Darefsky, Jordan}, title = {Echo-TTS}, year = {2025}, url = {https://jordandarefsky.com/blog/2025/Echo/} }</code></p> <hr class="post-end-divider"/> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:videogame-character-note" role="doc-endnote"> <p>Interestingly, in examples such as the videogame example where the text appears out-of-distribution for the speaker, the model will often ignore the speaker prompt and generate a speaker it deems more fit. To mitigate this, one can try a combination of increasing speaker CFG, decreasing text CFG, and/or using alternating guidance. <a href="#fnref:videogame-character-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:sampling-settings" role="doc-endnote"> <p>Echo (flat) uses 30-step Euler sampling on the RF ODE with independent CFG (text = 3., speaker = 5.0), cfg_min_t = 0.5, truncation_factor = 0.8, temporal score rescale k = 1.2 and sigma = 3. Echo (sharp) changes: truncation factor = 0.9, temporal score rescale k = 0.96. Higgs Audio v2 samples are produced with the official repo (https://github.com/boson-ai/higgs-audio) and default settings (temperature = 0.3). VibeVoice-7B samples are produced using the VibeVoice community fork https://github.com/vibevoice-community/VibeVoice with default settings (cfg scale = 1.3). <a href="#fnref:sampling-settings" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:podcast-0-60step" role="doc-endnote"> <p>Podcast 0 Echo (flat) sample resampled with 60 Euler steps instead of 30 (same random seed).<br/><audio controls="" preload="none" src="/assets/audio/echo/other/podcast_0_flat_60steps.wav"></audio> <a href="#fnref:podcast-0-60step" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:pca-comment" role="doc-endnote"> <p>It might be more exact to orthogonalize the concatenation of up-projections, but we felt it might be interesting to have latents with some notion of varying importance along the latent dimension. With this PCA approach, the variances of the individual channels are now non-uniform; in some early ablations that may or may not be replicable, we found that per-channel normalization did not help (seemed to hurt), and that a random rotation of the latent space that resulted in roughly uniform channel variances also did not help (though didn’t significantly hurt). Given this, and the possibility of inference methods that exploit the PCA representations (left for future work), we choose to use this PCA approach. We transform this representation by a scalar, which was determined empirically (and roughly) through a few ablations. <a href="#fnref:pca-comment" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:config-comment" role="doc-endnote"> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span> <span class="o">=</span> <span class="nc">EchoDiT</span><span class="p">(</span>
    <span class="n">latent_size</span><span class="o">=</span><span class="mi">80</span><span class="p">,</span>
    <span class="n">model_size</span><span class="o">=</span><span class="mi">2048</span><span class="p">,</span>
    <span class="n">num_layers</span><span class="o">=</span><span class="mi">24</span><span class="p">,</span>
    <span class="n">num_heads</span><span class="o">=</span><span class="mi">16</span><span class="p">,</span>
    <span class="n">intermediate_size</span><span class="o">=</span><span class="mi">5888</span><span class="p">,</span>
    <span class="n">norm_eps</span><span class="o">=</span><span class="mf">1e-5</span><span class="p">,</span>
    <span class="n">max_seq_len</span><span class="o">=</span><span class="mi">640</span><span class="p">,</span>
    <span class="n">text_vocab_size</span><span class="o">=</span><span class="mi">256</span><span class="p">,</span>
    <span class="n">text_model_size</span><span class="o">=</span><span class="mi">1280</span><span class="p">,</span>
    <span class="n">text_num_layers</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
    <span class="n">text_num_heads</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
    <span class="n">text_intermediate_size</span><span class="o">=</span><span class="mi">3328</span><span class="p">,</span>
    <span class="n">text_max_seq_len</span><span class="o">=</span><span class="mi">768</span><span class="p">,</span>
    <span class="n">speaker_patch_size</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span>
    <span class="n">speaker_model_size</span><span class="o">=</span><span class="mi">1280</span><span class="p">,</span>
    <span class="n">speaker_num_layers</span><span class="o">=</span><span class="mi">14</span><span class="p">,</span>
    <span class="n">speaker_num_heads</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
    <span class="n">speaker_intermediate_size</span><span class="o">=</span><span class="mi">3328</span><span class="p">,</span>
    <span class="n">speaker_max_patched_seq_len</span><span class="o">=</span><span class="mi">640</span><span class="p">,</span>
    <span class="n">timestep_embed_size</span><span class="o">=</span><span class="mi">512</span><span class="p">,</span>
    <span class="n">adaln_rank</span><span class="o">=</span><span class="mi">256</span>
<span class="p">)</span>
</code></pre></div> </div> <p><a href="#fnref:config-comment" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:speaker-causal-comment" role="doc-endnote"> <p>Unnecessarily, but this is carried over from earlier setups where the same encoder is used for prefix representations for generating continuations; see section on block-wise sampling later (which helps justify the decision in this setup). <a href="#fnref:speaker-causal-comment" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:joint-attn-comment" role="doc-endnote"> <p>There are separate w_k and w_v projections for each modality (so 3 w_ks and 3 w_vs). <a href="#fnref:joint-attn-comment" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:speaker-composition-note" role="doc-endnote"> <p>The final approach is a bit convoluted but involves, with some probabilities, choosing either</p> <p>a) concatenating individual segments (to a random length &lt; 120 seconds), each of whose composition ratio is similar to target or b) concatenating segments to some length such that the total composition ratio falls into some randomly selected bin (with something like most probability mass in 0.66&lt; r &lt; 1.5, but some probability mass in lower/higher ratios, in some sense to add “slack” or flexibility to the model). This path involves precomputing candidate segment sequences and crop lengths. <a href="#fnref:speaker-composition-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:speaker-patch-note" role="doc-endnote"> <p>patch size of 4, so output will be length 160 <a href="#fnref:speaker-patch-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:segment-prob-note" role="doc-endnote"> <p>with some probabilities, we only use 1 or 2 segments <a href="#fnref:segment-prob-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:rope-half-note" role="doc-endnote"> <p>we apply RoPE to first half of heads with properly-patch-dilated time indexing <a href="#fnref:rope-half-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:kv-cache-note" role="doc-endnote"> <p>technically (as the encoder is causal) only need to process the most recently generated latents if we have the encoder kv cache of earlier generated latents <a href="#fnref:kv-cache-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:rope-offset-note" role="doc-endnote"> <p>with proper RoPE offsets <a href="#fnref:rope-offset-note" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div>]]></content><author><name>Jordan Darefsky</name></author><category term="TTS"/><summary type="html"><![CDATA[Diffusion-based text-to-speech with fast, high-fidelity voice cloning]]></summary></entry><entry><title type="html">Diffusion-Parakeet</title><link href="https://jordandare.github.io/diffusion-draft/" rel="alternate" type="text/html" title="Diffusion-Parakeet"/><published>2024-10-25T00:00:00+00:00</published><updated>2024-10-25T00:00:00+00:00</updated><id>https://jordandare.github.io/diffusion-draft</id><content type="html" xml:base="https://jordandare.github.io/diffusion-draft/"><![CDATA[<h2 id="overview">Overview</h2> <p>In a previous blog post, we described Parakeet, an autoregressive approach to generating natural, conversational speech. Here, we introduce our more recent work that explores using diffusion instead of autoregression.</p> <div class="container .l-page"> <table style="width:100%; border-collapse: collapse; table-layout: fixed;"> <tr style="border: 1px solid #ddd;"> <th style="border: 1px solid #ddd;">(capability)</th> <th style="border: 1px solid #ddd;">Text prompt</th> <th style="border: 1px solid #ddd;">Audio</th> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;">Celebrity Voice Cloning</td> <td style="border: 1px solid #ddd;">[S1] Hey, this is Joe Biden. This is a special message for Gee Zhu, wait, uh, Guh Zhu, sorry, I think I'm pronouncing this wrong. I heard you're working on, uh, music generation models.</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <div style="margin-bottom: 20px;"> <div style="font-size: 0.9em; margin-bottom: 5px;">Prompt:</div> <figure> <audio src="/assets/audio/diffusion/biden_speech_example.mp3" controls=""/> </figure> </div> <div> <div style="font-size: 0.9em; margin-bottom: 5px;">Generated:</div> <figure> <audio src="/assets/audio/diffusion/biden_generated.mp3" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;">Conversational cloning (NotebookLM style)</td> <td style="border: 1px solid #ddd;">[S1] So we're talking about this paper, right, "Attention is All You Need." Sounds simple enough, like some self-help mantra or something. [S2] Yeah, totally. Like, pay attention, kids. [S1] But here's the thing - it's sneaky, man. This paper, it just comes out of nowhere in 2017 and completely upends the whole field of machine translation. (Claude-generated text prompt)"</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <div style="margin-bottom: 20px;"> <div style="font-size: 0.9em; margin-bottom: 5px;">Prompt:</div> <figure> <audio src="/assets/audio/diffusion/notebook_lm_prompt.wav" controls=""/> </figure> </div> <div> <div style="font-size: 0.9em; margin-bottom: 5px;">Generated:</div> <figure> <audio src="/assets/audio/diffusion/notebook_lm_generated.wav" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;">Conversational cloning (real podcast)</td> <td style="border: 1px solid #ddd;">[S1] I've always wanted to learn how to play the guitar. [S2] What kind of guitar do you have in mind? [S1] Um, I'm not sure, I guess I'd, uh, like to learn to play both acoustic and electric. [S2] Yeah, that's a great idea (laughs). Both types of guitars have their own, uh, their own unique sounds and, uh, and playing styles. (Text prompt from Google's SoundStorm)</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <div style="margin-bottom: 20px;"> <div style="font-size: 0.9em; margin-bottom: 5px;">Prompt:</div> <figure> <audio src="/assets/audio/diffusion/dwarkesh_prompt_excerpt.mp3" controls=""/> </figure> </div> <div> <div style="font-size: 0.9em; margin-bottom: 5px;">Generated:</div> <figure> <audio src="/assets/audio/diffusion/dwarkesh_generated_2.wav" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;">Singing cloning attempt</td> <td style="border: 1px solid #ddd;">[S1] (singing) Is this the real life, or is this just fantasy. Caught in a landslide, no escape from reality. Open your eyes, look up to the skies and see.</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <div style="margin-bottom: 20px;"> <div style="font-size: 0.9em; margin-bottom: 5px;">Prompt (autoencoder reconstruction, not good singing quality):</div> <figure> <audio src="/assets/audio/diffusion/ladygaga_ae.wav" controls=""/> </figure> </div> <div> <div style="font-size: 0.9em; margin-bottom: 5px;">Generated:</div> <figure> <audio src="/assets/audio/diffusion/ladygaga_generated.wav" controls=""/> </figure> </div> </td> </tr> </table> </div> <style>audio{width:200px}</style>]]></content><author><name>Jordan Darefsky</name></author><category term="TTS"/><summary type="html"><![CDATA[A natural sounding, conversational text-to-speech diffusion model]]></summary></entry><entry><title type="html">Parakeet</title><link href="https://jordandare.github.io/blog/2024/parakeet/" rel="alternate" type="text/html" title="Parakeet"/><published>2024-05-12T00:00:00+00:00</published><updated>2024-05-12T00:00:00+00:00</updated><id>https://jordandare.github.io/blog/2024/parakeet</id><content type="html" xml:base="https://jordandare.github.io/blog/2024/parakeet/"><![CDATA[<p><a href="#Demo">Jump to Demo</a></p> <h2 id="overview">Overview</h2> <p>In this blog post, I describe my work with Ge Zhu and Professor Zhiyao Duan to develop an initial version of a text-to-speech (TTS) model we call Parakeet. The research presented in this post was completed early last fall and was supported by Google’s TPU Research Cloud (TRC) program. This project would not have been possible without their immense generosity.</p> <p>Parakeet takes in as input a text prompt, optionally containing multiple speakers or non-verbal events like “laughter,” and outputs up to 30 seconds of corresponding audio. In designing Parakeet, we had two main goals in mind for our model:</p> <ol> <li>To be architecturally simple — as close to end-to-end as possible.</li> <li>To be able to produce natural, conversational speech. This includes generating multi-speaker samples, as well as generating common “events” found in dialogue, such as laughter and coughing.</li> </ol> <p>A brief overview of our methodology is as follows:</p> <ol> <li>We curate a ~100,000 hour dataset of audio-transcription pairs. ~60,000 hours come from Spotify Podcast Dataset <d-cite key="clifton-etal-2020-100000"></d-cite>; we fine-tune a Whisper model to provide speaker/event annotated transcriptions and then backtranslate the entirety of the Spotify Podcast audio using this model.</li> <li>We train an autoregressive transformer to predict audio tokens (specifically, DAC <d-cite key="kumar2023highfidelity"></d-cite> codes), conditioned on raw transcription text. We develop a modified classifier-free guidance (CFG) technique, which we call CFG-filter, to improve quality.</li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/parakeet_blog_images/overview_diagram-480.webp 480w,/assets/img/parakeet_blog_images/overview_diagram-800.webp 800w,/assets/img/parakeet_blog_images/overview_diagram-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/parakeet_blog_images/overview_diagram.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>We plan to release our fine-tuned whisper models and possibly the generative model (and/or future improved versions). The generative model would have to be released under a non-commercial license due to our datasets.</p> <p>This project is a work in progress, but below are samples from our model, followed by a more detailed methodology. These samples are cherry-picked (in general, best-of-<em>generally-between-4-and-16-samples</em>). We hope to eventually improve our model so that our outputs are more consistently high-quality.</p> <h2 id="Demo">Demo</h2> <h3 id="unconditional-speech-generation">Unconditional speech generation</h3> <p>Parakeet is able to generate conversational audio (right column) given a text prompt (left column). As seen in text prompts below, [S1], [S2], etc., are used to denote different speakers. Non-verbal events such as “laughs” can be specified in parentheses.</p> <div class="container .l-page"> <table style="width:100%; border-collapse: collapse;"> <tr style="border: 1px solid #ddd;"> <th style="border: 1px solid #ddd;">Text</th> <th style="border: 1px solid #ddd;">Audio</th> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;"><b>Text prompt:</b><br/>[S1] What's sort of cool, is that, uh, you can produce coughs if you have to. [S2] What do you mean? [S1] Well (coughs), there, I just coughed.</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/coughprompt.mp3" controls=""/> </figure> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;"><b>Text prompt:</b><br/>[S1] Something really funny happened to me this morning. [S2] Oh wow, what? [S1] Well, uh, I woke up as usual- [S2] Mm-hmm. [S1] … went downstairs to have uh breakfast- [S2] Yeah. [S1] … started eating. Then, uh, 10 minutes later I realized it was the middle of the night. [S2] Oh no way, (laughs) that's so funny.</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/sprompt.mp3" controls=""/> </figure> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd;"><b>Text prompt (different seeds):</b><br/>[S1] Okay, now uh, I'm going to demo this text to speech system. [S2] Mm-hmm. [S1] The speech you're hearing right now, uh, isn't real. It's- it's fake. [S2] No, that's hard to believe- [S1] I'm serious.</td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/unconditional_s1.mp3" controls=""/> </figure> <figure> <audio src="/assets/audio/unconditional_s2.mp3" controls=""/> </figure> </td> </tr> </table> </div> <h3 id="audio-prompting--speaker-cloning">Audio prompting / speaker cloning</h3> <p>Our model is able to perform zero-shot voice cloning simply by prefilling the decoder with an audio prompt and setting the text condition to be the concatenation of text from the audio prompt and the desired text.</p> <p>In the below table for each sample, the audio column contains the audio prompt and the full audio continuation (including the prompt).</p> <div class="container .l-page"> <table style="width:100%; border-collapse: collapse;"> <tr style="border: 1px solid #ddd;"> <th style="border: 1px solid #ddd;">Text</th> <th style="border: 1px solid #ddd; text-align: center;">Audio (prompt / continuation)</th> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: left; vertical-align: middle;"> <b>2-second cloning example:</b><br/><u>[S1] ... Gapes, that is the first symptom. </u> This part is generated. Uh, everything after two- two seconds was generated. (prompt is underlined) </td> <td style="border: 1px solid #ddd;"> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/vb_prompt.mp3" controls=""/> </figure> </div> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/vb_continuation.mp3" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: left; vertical-align: middle;"> <b>Whispering example (prompt text not included):</b><br/>April fools. You don't need to run. I was just kidding. </td> <td style="border: 1px solid #ddd;"> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/whisper_prompt.mp3" controls=""/> </figure> </div> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/whisper_continuation.mp3" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: left; vertical-align: middle;"> <b>Emotional example (prompt text not included):</b><br/> I'm just so angry right now! </td> <td style="border: 1px solid #ddd;"> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/angry_prompt.mp3" controls=""/> </figure> </div> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/angry_continuation.mp3" controls=""/> </figure> </div> </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: left; vertical-align: middle;"> <b>Multilingual example:</b><br/><u>[S1] (speaking in foreign language)</u> | Let’s go to the beach now. </td> <td style="border: 1px solid #ddd;"> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/multilingual_prompt.mp3" controls=""/> </figure> </div> <div style="text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/multilingual_continuation.mp3" controls=""/> </figure> </div> </td> </tr> </table> </div> <h2 id="dataset-methodology">Dataset Methodology</h2> <p>Our dataset is comprised of three different sources, Spotify Podcast Dataset, LibriVox, and Common Voice.</p> <h3 id="spotify-dataset-and-whisperd">Spotify Dataset and WhisperD</h3> <p>Spotify Podcast Dataset consists of over 100,000 podcast episodes totaling approximately 60,000 hours of audio (the dataset is no longer maintained by Spotify as of December 2023). Though it provides transcriptions, they are machine-generated (and not of great quality) and unsuitable for training a generative model. We first split the podcast into segments of up to 30 seconds in length using pyannote <d-cite key="Bredin23"></d-cite> and obtain approximately 9,000,000 segments.</p> <p>Ideally, our transcriptions would contain both speaker labels as well as events (i.e. “[S1] Hey! [S2] (sighs) Um, how’s it going?”). We initially tried prompting Whisper <d-cite key="radford2022robust"></d-cite> to elicit this style of transcription but found the model struggled to do so. We thus fine-tune Whisper-v2-large on a small dataset of (audio, ideal-annotation) pairs. We call this model WhisperD (for dialogue).</p> <p>The initial version of WhisperD was created through a two-stage fine-tuning process, where we first fine-tune on lower-quality automatic transcriptions and then fine-tune again on higher-quality human data. Our reasoning was that human-annotated transcriptions are expensive to obtain, and that a larger dataset of automatic transcriptions could be inexpensively used to roughly calibrate Whisper to perform speaker annotation before a higher-quality fine-tuning pass. We used online transcription services for both the automatic and human transcriptions to transcribe random subsets of our podcast data. The automatic portion consisted of ~20 hours, while the human portion consisted of slightly under 2 hours.</p> <p>After the fine-tuning process, we’re able to generate transcriptions such as:</p> <div class="container"> <table style="width:100%; border-collapse: collapse;"> <tr style="border: 1px solid #ddd;"> <th style="border: 1px solid #ddd; text-align: center;">Spotify Podcast Audio</th> <th style="border: 1px solid #ddd; text-align: center;">WhisperD Transcription</th> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/podcast_example_0.mp3" controls=""/> </figure> </td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> [S1] ... this very important that, um, your spouse is, is supportive and, and willing to sacrifice because it is a sacrifice to be a, a coach's wife or husband. And so, um, you know, I heard coaches say that when I was young, you know, before I was married, that, you know, your wife is very important because, you know </td> </tr> <tr style="border: 1px solid #ddd;"> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> <figure> <audio src="/assets/audio/podcast_example_1.mp3" controls=""/> </figure> </td> <td style="border: 1px solid #ddd; text-align: center; vertical-align: middle;"> [S1] But, you know, what can you do in a country town? You don't have a lot of choices. You just, eh. [S2] (laughs) Sorry, I just made like a royal mess of new desk, but anyway. [S1] It's not new, it's all right. [S2] Okay, that's all right. Um, so- [S1] But yes. [S2] Oh, no. [S1] So, I mean, that's- </td> </tr> </table> </div> <p>We use this model to generate transcriptions for all of the VAD-split podcast segments. We also trained what we hoped would be a better version of WhisperD with a more sophisticated method involving bootstrapping, though meaningful evaluation turned out to be challenging (manual inspection seemed to be more informative than many of the eval methods we devised). We also generate transcriptions using this newer WhisperD and randomly sample which transcription to use when training our generative model later.</p> <p>Lastly, we train two additional WhisperD models and use them to transcribe subsets of our dataset:</p> <ol> <li>Fluent WhisperD: It may be unintuitive for users to write convincing-sounding disfluencies into text prompts. If we train our model (a percentage of the time) with “fluentized” text (accompanied with a tag), the model will learn to optionally generate disfluencies even when none are present in the text prompt. This functionality also may be useful in cases where one wants to fine-tune on a single speaker (i.e. train on fluentized text only and allow the modelto learn disfluency patterns) or generate disfluent audio continuations given fluent text. We add an “[F]” tag to the start of fluentized text prompts.</li> <li>Fuzzy WhisperD: One possible issue with synthetic transcriptions is that if the transcriptions all have the same style, our generative model may not be robust to user input. We thus use GPT to generate stylistically-varied versions of a set of transcriptions, and then fine-tune Whisper on these “fuzzied” transcriptions. Though one could argue the fuzzying could be done by a text-only model, 1) using a Whisper model was practical / convenient given our pipeline and 2) it’s theoretically possible (albeit practically unlikely) that audio-aware fuzzing may provide benefits.</li> </ol> <p>During generative training later, with some relatively small probability, transcriptions are replaced with either fluent or fuzzy versions.</p> <h3 id="librivox-dataset">LibriVox Dataset</h3> <p>LibriVox is an archive of public domain audiobooks. We take ~30,000 hours, and we attempt to balance speaker durations during sampling. We again perform VAD to split into 30 second (maximum) segments and use Whisper-v2-medium to generate transcriptions. Interestingly, we find that the pretrained Whisper models omit text such as “This is a LibriVox recording…,” (this is likely due to Whisper training on these samples and using ground-truth transcriptions that only contain book text) so we very briefly fine-tune Whisper-medium with examples containing these sorts of phrases.</p> <h3 id="common-voice-dataset">Common Voice Dataset</h3> <p>We also use the English subset of Common Voice 14 <d-cite key="ardila-etal-2020-common"></d-cite>. It contains ~3,300 hours of audio and has high speaker diversity, containing over 85,000 unique speakers. Since the median segment length is short (&lt; 10 seconds), we sometimes concatenate segments (randomly choosing from 1 to 5 segments per sample) from the same speaker. We use the provided transcriptions, and when we concatenate audio, we concatenate text with the vertical bar “|”. Though zero-shot speaker cloning can be naively achieved through audio continuation, it’s possible including these concatenated segments in our train dataset will facilitate more in-distribution cloning during inference.</p> <h2 id="modeling-methodology">Modeling Methodology</h2> <h3 id="dac-prediction">DAC Prediction</h3> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/parakeet_blog_images/rqvae_diagram-480.webp 480w,/assets/img/parakeet_blog_images/rqvae_diagram-800.webp 800w,/assets/img/parakeet_blog_images/rqvae_diagram-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/parakeet_blog_images/rqvae_diagram.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>Rather than predicting raw audio directly, we instead predict latent audio codes / tokens, using the pre-trained Descript-Audio-Codec (DAC) as our autoencoder. DAC is a residual-quantized VAE <d-cite key="lee2022autoregressive"></d-cite> with 9 residual codebooks, each with vocab-size 1024, and processes 44,100 kHz monophonic audio with a time-wise downsampling rate of 512. Thus, a second of 44,100 kHz audio will be encoded into ~86 x 9 integer codes ranging from 0-1023. Note that DAC is trained with random dropout of higher-level codebooks, so lower-level codebooks tend to contain most of the semantic information. We process the entirety of our dataset with DAC and cache the corresponding token sequences.</p> <p>We now train a model to autoregressively predict these DAC tokens, conditioned on raw text. In the model featured in this blog post, we use an encoder-decoder transformer, though we’ve also experimented with decoder-only models.</p> <p>Given that there are 9 residual codebooks, we need to make a decision on how to predict the 86 x 9 (= 774) codes per second. Though flattening would be an option, this would leave us with 30-second sequence lengths of 23,220 (the ratio of attention flops to MLP flops would be high with a vanilla setup given relatively small model size), and the model might be inclined to spend more-than-optimal compute on predicting less important residual codebooks (though this depends on training dynamics, as it’s possible the model will learn to “think ahead” in the residual streams of less-important codebook levels).</p> <p>There are two more practical options with which we experimented.</p> <ol> <li>Hierarchical-transformer: Similar to Hourglass Transformers <d-cite key="Nawrot_2023"></d-cite> and MegaByte <d-cite key="yu2023megabyte"></d-cite>, as well as UniAudio <d-cite key="yang2023uniaudio"></d-cite>, who concurrently trained a text-to-audio model using such an approach. The core idea is to have a big transformer whose input is the full residual stack, and a small transformer (applied at every timestep) that takes in the big transformer’s final hidden state and predicts the 9 residual codes. The benefit of this approach is that it is maximally expressive (it can model any distribution of codes) but the downside is its architectural complexity.</li> <li>Predicting codes jointly / delay-pattern prediction: Naively, one can jointly predict all 9 residual codes at each timestep (i.e. 9 separate linear layers (or MLPs) projecting the final hidden state to each residual code logit vector). However, this would assume that all 9 codes for a particular timestep are conditionally independent (given previous timesteps), which is likely an unreasonable assumption. A clever method (from Kharitonov et al. <d-cite key="kharitonov2022textfree"></d-cite> and adapted in MusicGen <d-cite key="copet2024simple"></d-cite> is to manufacture a more favorable conditional independence assumption by shifting higher level codebooks rightward. See the below figure, or the description in MusicGen. Rather than use a uniform delay for all codebooks, we use a more significant delay for the second residual level. Specifically, we use a delay pattern of \((8, 9, 10, \ldots, 15)\). We also apply a 4x loss weight on the first level predictions.</li> </ol> <figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/parakeet_blog_images/codebook_pattern-480.webp 480w,/assets/img/parakeet_blog_images/codebook_pattern-800.webp 800w,/assets/img/parakeet_blog_images/codebook_pattern-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/parakeet_blog_images/codebook_pattern.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <p>We found that delay pattern prediction seemed to match hierarchical transformer setups in performance. We also tried combining delay-pattern with hierarchical prediction, which we found improved results over hierarchical prediction without any codebook delay but still was not obviously superior to the non-hierarchical approach. Given this, we choose to train a non-hierarchical model with a delay pattern. Note that if the conditional independence assumed by the delay-pattern proves to be a bottleneck in terms of quality, we could convert our model to a hierarchical one by training a small transformer to replace the linear output projections.</p> <h3 id="training-details">Training Details</h3> <p>We train a 3B parameter encoder-decoder transformer model. The encoder’s input is raw bytes of text, which closely corresponds to English characters, as the majority of our data is English aside from a small portion of podcast data.</p> <p>The encoder (text) sequence length is 768, while the decoder (DAC token) length is 2048. To enable classifier-free guidance (see later section) at inference, we drop out the text condition for 15% of samples during training.</p> <p>The encoder has model size 1536 with 12 layers and 16 heads. The decoder has model size 2560 with 32 layers and uses GQA, with 32 query heads and 8 KV heads. The model uses rotary positional embeddings in self-attention modules and SwiGLU activation. We train our model with batch size 256 for 110,000 steps with a cosine learning rate decay, with peak learning rate 2e-4. We train on a v3-256 TPU pod, provided graciously by TPU research cloud, implementing parallel training in JAX <d-cite key="jax2018github"></d-cite> / FLAX <d-cite key="flax2020github"></d-cite>. We train with data-parallelism across all devices and shard the model and optimizer via intra-host FSDP.</p> <p>After training, we fine-tune the model on a small higher-quality subset of our dataset (we filter using PESQ <d-cite key="hwang2023torchaudio"></d-cite>). Since the cosine schedule had not fully annealed by 110,000 steps, this approach is slightly analogous to MiniCPM <d-cite key="hu2024minicpm"></d-cite>. We found this emperically helps with generating higher quality unconditional samples. Since the training of this model, we’ve also explored adding high-quality tags to text conditions and/or performing dataset filtering. One (admittedly hacky) trick to encourage better unconditional generations would be to omit loss terms for the first $n$ seconds of poor quality samples.</p> <h3 id="classifier-free-guidance">Classifier-Free Guidance</h3> <p>First introduced in diffusion models <d-cite key="ho2022classifierfree"></d-cite>, classifier-free guidance (CFG) has also been adapted to text-to-something autoregressive models <d-cite key="crowson2022"></d-cite>.</p> <p>CFG in autoregressive models involves training both a conditional model \(P_c(x_t \vert x_{1,\ldots,t-1}, c)\), where c represents the text condition, and an unconditional model \(P_u(x_t \vert x_{1,\ldots,t-1})\). In practice the unconditional model is learned by dropping out the text condition for a small portion of batch samples during training. Then, when sampling a particular code, given conditional logits \(l_c\) and unconditional logits \(l_u\), the final logits \(l_{cfg}\) are obtained with:</p> \[l_{cfg} = l_c + \alpha (l_c - l_u)\] <p>where \(\alpha\) is a hyperparameter scalar controlling the degree of guidance. After CFG, we can apply top-k or top-p sampling; we choose top-k with k = 50.</p> <p>When we apply CFG to Parakeet sampling, quality is significantly improved. However, on inspecting generations, there tends to be a dramatic speed-up over the duration of the sample (i.e. the rate of speaking increases significantly over time). Our intuition for this problem is as follows: Say that is our model is (at some level) predicting phonemes and the ground truth distribution for the next phoneme occuring is 25% at a given timestep. Our conditional model may predict 20%, but because our uncondtional model cannot see the text transcription, its prediction for the correct next phoneme will be much lower, say 5%. With a reasonable level of CFG, because \((l_c - l_u)\) will be large for the correct next phoneme, we’ll obtain a much higher final probability, say 50%, which biases our generation towards faster speech. And this effect compounds! After an initial speed-up, a good model would now “think” it’s predicting for a fast speaker, so the conditional model will predict even higher probabilities for the next phoneme occuring soon, leading to an even larger bias after CFG is applied.</p> <p>Here is an audio sample with CFG (with \(\alpha = 3\)), and for reference a sample without any CFG.</p> <div style="text-align: center; margin: 20px 0;"> CFG sample (observed speed-up): <figure> <audio src="/assets/audio/audio_cfgpost.mp3" controls=""/> </figure> Without CFG sample (no speed-up but worse quality): <figure> <audio src="/assets/audio/audio_nocfg.mp3" controls=""/> </figure> </div> <p>The quality and text-alignment of the CFG sample is higher (and the difference is often larger than the above examples), but the speed-up is a significant issue.</p> <h4 id="cfg-filter">CFG-filter</h4> <p>To address this, we introduce CFG-filter, a modification to CFG that mitigates the speed drift. The idea is to first apply the CFG calculation to obtain a new set of logits \(l_{cfg}\) as before, but rather than use these logits to sample, we use these logits to obtain a top-k mask to apply to our original conditional logits. Intuitively, this serves to constrict the space of possible “phonemes” to text-aligned phonemes without heavily biasing the relative probabilities of these phonemes (or for example, start next word vs pause more).</p> <p>Here is the same text prompt with CFG-filter:</p> <div style="text-align: center; margin: 20px 0;"> CFG-filter sample (improved pace and quality): <figure> <audio src="/assets/audio/audio_precfgminorpost.mp3" controls=""/> </figure> </div> <p>The pseudocode is as follows:</p> <figure class="highlight"><pre><code class="language-python" data-lang="python">  <span class="k">def</span> <span class="nf">mask_by_top_k</span><span class="p">(</span><span class="n">l_c</span><span class="p">:</span> <span class="n">jax</span><span class="p">.</span><span class="n">Array</span><span class="p">,</span> <span class="n">cfg_logits</span><span class="p">:</span> <span class="n">jax</span><span class="p">.</span><span class="n">Array</span><span class="p">,</span> <span class="n">k</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">jax</span><span class="p">.</span><span class="n">Array</span><span class="p">:</span>
    <span class="c1"># returns x where x[i] = l_c[i] if cfg_logits[i] in top k elements of cfg_logits else -inf
</span>  
  <span class="k">def</span> <span class="nf">cfg_filter</span><span class="p">(</span><span class="n">l_c</span><span class="p">:</span> <span class="n">jax</span><span class="p">.</span><span class="n">Array</span><span class="p">,</span> <span class="n">l_u</span><span class="p">:</span> <span class="n">jax</span><span class="p">.</span><span class="n">Array</span><span class="p">,</span> <span class="n">alpha</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">k</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="n">cfg_logits</span> <span class="o">=</span> <span class="n">l_c</span> <span class="o">+</span> <span class="n">alpha</span> <span class="o">*</span> <span class="p">(</span><span class="n">l_c</span> <span class="o">-</span> <span class="n">l_u</span><span class="p">)</span>
    <span class="n">sample_logits</span> <span class="o">=</span> <span class="nf">mask_by_top_k</span><span class="p">(</span><span class="n">l_c</span><span class="p">,</span> <span class="n">cfg_logits</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">sample_logits</span></code></pre></figure> <p>Note that we can optionally reapply a smaller level of CFG after masking (i.e. rather than returning sample logits as is, we could set \(\text{new_sample_logits} = \text{sample_logits} + \beta (\text{sample_logits} - l_u)\), where intuitively we should have $\beta &lt; \alpha$. We can also again apply top-k or top-p sampling before returning the final sample_logits.</p> <h2 id="future-work">Future Work</h2> <h3 id="improving-autoregressive-parakeet">Improving Autoregressive Parakeet</h3> <p>A reasonable next step for improving autoregressive versions of Parakeet would be to test replacing DAC with a different autoencoder. It’s possible that given DAC’s relatively high number of codes per second (86 base codes per second, and 774 codes including residual levels), it may not be particularly conducive to autoregressive generation. Training a new RQ-VAE with some combination of a higher downsampling rate and less residual levels (with a possibly larger vocab size) would be a sensible starting point. It also might be exploring FSQ <d-cite key="mentzer2023finite"></d-cite> as a potential quantization method.</p> <p>Though higher autoencoder compression might result in worse decoder reconstruction quality, one could train a more powerful decoder (for example, diffusion) to mitigate this. However, a caveat is that ideally the latents / codes should contain most of the perceptible audio information, as leaving decision making / ambiguitity to the decoder might complicate audio prompt continuation.</p> <h3 id="diffusion">Diffusion</h3> <p>Some of our more recent work has involved transitioning from autoregression to diffusion. A downside of the current autoregression approach is that mistakes in the sampling process cannot be corrected. Diffusion allows (hand-wavingly) for such correction, but a vanilla approach would involve generating 30 second segments regardless of text prompt length or training a duration predictor, both of which have disadvantages. Additionally, for text-to-speech, the approach of generating entire long segments at once may have theoretical downsides having to do with a certain time-wise asymmetry (e.g. it’s easier to predict the phoneme occuring at the 2-second-mark than the 28-second-mark); a more thorough exploration is left to future blog posts.</p> <p>Some of our work on diffusion involved splitting audio latent segments into “blocks” to allow for autoregressive sampling of blocks, and concurrently Aran Komatsuzaki proposed a particularly elegant block-wise approach <d-cite key="Komatsuzaki2023"></d-cite>, explained <a href="https://twitter.com/arankomatsuzaki/status/1719865780114952291">here</a>. I think this approach could be especially practical for longer generations, e.g. video. A limitation of this method is that it complicates solving inverse problems relative to single-block / vanilla diffusion; however, in preliminary experiments, we’ve found that it’s possible to adapt a diffusion model trained on small blocks to generate longer sequences via fine-tuning.</p> <p>We hope to make more progress on this and share our work in the future.</p> <h3 id="fully-streaming-tts-audio-turing-test">Fully-Streaming TTS (Audio Turing Test)</h3> <p>While Parakeet could be adopted for AI-Human conversational TTS, a much more important project would involve a fully-streaming TTS, a project to which I have given some thought since the development of our initial Parakeet model. Rather than current conversational TTS systems, which involve a human talking, the AI waiting for the human to finish talking, the AI responding, etc., a fully-streaming TTS would involve a constant stream of AI audio output, which could include laughter, interruptions, etc. A well-performing streaming TTS model might be able to pass the audio version of a Turing test: a human might not be able to tell whether they’re speaking to another human or an AI system. Though it would require significant engineering work, I believe this project is relatively low-hanging fruit (with a high ratio of impressiveness to engineering capital). I’ve been thinking about different approaches and am excited to see what the future holds in this area.</p> <h2 id="bibtex">Bibtex</h2> <p>To cite this blog post, please use:</p> <p><code class="language-plaintext highlighter-rouge">bibtex @misc{darefsky2024parakeet, author = {Darefsky, Jordan and Zhu, Ge and Duan, Zhiyao}, title = {Parakeet}, year = {2024}, url = {https://jordandarefsky.com/blog/2024/parakeet/} }</code></p>]]></content><author><name>Jordan Darefsky</name></author><category term="TTS"/><summary type="html"><![CDATA[A natural sounding, conversational text-to-speech model]]></summary></entry></feed>