AI Engineer World's Fair 2025
Text-to-Speech Data Preparation and Fine-tuning Workshop - Ronan McGovern
Read the talk
Fine-tuning CSM-1B to sound like a specific voice
Turn a single-speaker recording into paired audio and text, adapt CSM-1B with LoRA, and compare what reference audio and fine-tuning each contribute to the generated voice.
From a talk by Ronan McGovern
Before you start: You should be comfortable running Python notebook cells and have a single-speaker recording whose voice you have permission to adapt.
From a recording to a recognizable voice
How do you make a pretrained text-to-speech model sound like a particular person? Ronan McGovern’s workshop starts with Sesame CSM-1B and builds toward that goal through data preparation and fine-tuning. The Trelis Research workshop repository provides the Colab notebook and slides. CSM is a token-based speech model; Orpheus, from Canopy Labs, is another example of the same broad approach.
The workflow takes a recording, converts it into a voice dataset, fine-tunes with Unsloth, and compares speech before and after training. You can record yourself or use a single-speaker video for which you have permission to adapt the voice; Sesame’s usage guidance requires explicit consent when mimicking a real individual. One notebook covers the process. For a deeper architecture introduction, McGovern also points to an approximately 90-minute companion explanation linked through the repository, including discussion of Moshi.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Predicting audio instead of the next word
A text language model predicts the next token, appends it to the input, and repeats. Token-based speech generation extends that loop: supply text and predict audio tokens. A conversational speech model can also consume earlier text and audio, so its next utterance depends on how the preceding conversation sounded as well as what was said. The output is a sequence of discrete audio tokens that can be decoded into speech.
To turn a continuous waveform into discrete tokens, use a codebook: a dictionary of learned vectors. A token selects an entry from that dictionary. The representation needs enough capacity to preserve both acoustic detail and information relevant to meaning. In the introductory training picture, an encoder converts a waveform into tokens, a decoder reconstructs the waveform, and reconstruction error supplies a learning signal to improve the representation. This explains the codec’s purpose, rather than the complete training objective of a modern codec.
One token per audio window is too restrictive for the representation McGovern describes. Multiple codebook tokens let the model represent a window at several levels of detail. CSM uses 32 audio codebook tokens per window: its main transformer predicts token zero across time, while a smaller depth decoder predicts the remaining 31 tokens within each window. The main transformer has one billion parameters. This separates progression through the utterance from completion of each window’s audio representation.
The workshop does not train this architecture or its codec from scratch. It begins with a model that already accepts text and audio and generates speech. McGovern then uses a recording from his own YouTube channel to shift the generated voice toward his own.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Extract, transcribe, and correct the recording
Open the notebook in Colab and select a GPU through Change runtime type. The 2025 workshop uses a T4 and describes it as available on Colab’s free tier; availability and YouTube download access are not present-day guarantees. The first notebook stage uses Whisper to make paired audio snippets and transcripts, targeting clips up to 30 seconds long.
McGovern’s demonstration uses 41 rows. He reports that an earlier trial with 20 ten-second snippets was insufficient, and suggests roughly 50 thirty-second snippets as a starting point for an audible effect. These are his small-data heuristics, not a measured minimum dataset size. The resulting pairs will train CSM through Unsloth.
Prepare the source in this order:
- Choose one speaker. A multi-speaker video needs diarization to separate identities; this basic notebook does not provide it.
- Select the transcription model. McGovern recommends Whisper turbo as close to large-model quality but faster, and prefers it to small, base, or tiny for this task.
- Install the extraction and transcription tools. The notebook uses
yt-dlpand OpenAI Whisper. McGovern recommends Colab because he has encountered YouTube authentication blocks outside it. - Transcribe on the GPU. Use CUDA and save the transcription as a local JSON file. That file contains text, token details, and timestamped segments.
Read the transcript before constructing training examples. McGovern’s recurring correction is the brand name: Whisper may produce the English word Trellis, while his channel is Trelis. Correct the relevant occurrences and re-upload the JSON to Colab. The text paired with the audio should reflect what the speaker actually said, including names the transcriber does not know. This Python version applies that same correction to both the full transcript and individual segment text while leaving their timing fields intact:
python
import json
from pathlib import Path
source = Path("transcript.json")
transcript = json.loads(source.read_text(encoding="utf-8"))
transcript["text"] = transcript["text"].replace("Trellis", "Trelis")
for segment in transcript["segments"]:
segment["text"] = segment["text"].replace("Trellis", "Trelis")
Path("transcript.corrected.json").write_text(
json.dumps(transcript, ensure_ascii=False, indent=2),
encoding="utf-8",
)
Use the corrected file as the input to dataset construction.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Turn transcript segments into audio-text rows
Whisper produces short segments; training rows can combine adjacent segments into longer passages. The notebook accumulates segments toward a configured 30-second maximum and pairs their text with the corresponding source audio. McGovern describes both crossing the threshold and enforcing a maximum, so inspect clips near the boundary rather than assuming the narration specifies a strict clipping rule.
For the optional Hugging Face upload, authenticate and set the organization, repository name, and maximum row duration. McGovern uses conf-trelis as his organization. Point the construction cell at the extracted audio and corrected JSON, then generate the rows. The run produces 41 clips; setting the upload flag to False keeps the dataset local. McGovern estimates transcription should take a few minutes even for a source video of 30–60 minutes in this workflow.
Inspect the result by reading the text and playing the paired audio. The dataset viewer makes that relationship visible: each row has a transcript and an audio player. Retain the organization and dataset name for loading it during fine-tuning.
A duration limit alone does not produce good linguistic boundaries. The basic implementation can finish a row in the middle of a sentence. McGovern suggests detecting sentence boundaries with NLTK or a regular expression so that rows contain complete sentences or paragraphs where possible. This is an improvement to the data preparation, not a feature already demonstrated by the simple segment accumulator.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Load CSM and identify the trainable parts
Install Unsloth, then load CSM-1B with a configured maximum sequence length. Leaving dtype=None lets the loading path choose the precision: the workshop uses torch.float16 on T4 and describes torch.bfloat16 for supported newer GPUs, including Ampere, Hopper, and Blackwell. The Transformers model class is CsmForConditionalGeneration. Native CSM support was already available in Transformers 4.52.1 before this workshop; current documentation is not a frozen copy of its notebook APIs.
McGovern loads the base model without quantization, describing its few-gigabyte size as fitting within the T4’s roughly 15 GB of available memory. Watch GPU allocation while the shards load. An unexpectedly high reading makes him consider whether the model was loaded twice and whether a runtime restart is needed, but the usage drops and he continues.
Printing the architecture exposes three different responsibilities:
- Backbone: the main transformer that advances through the audio sequence.
- Depth decoder: the smaller transformer that predicts the other 31 codebook tokens for each window.
- Codec: the components that convert between waveforms and discrete audio representations.
Fine-tuning does not update every parameter in those components. The workshop attaches trainable adapters to selected attention and other linear layers, reducing the memory and work required for adaptation.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Hear the base model before changing its weights
Before applying adapters, skip ahead to inference. McGovern deliberately tests the sentence “We just finished fine-tuning a text-to-speech model” before any fine-tuning has happened. The notebook offers two input paths: apply a chat template, or prepare tensors more directly from the text and speaker ID. Both lead to text-only, zero-shot generation.
Without reference context, the base model can produce different speaker identities. Nonzero-temperature sampling adds variation, but temperature is not a way to specify the desired person. Voice cloning supplies reference audio at inference time without updating model weights. The notebook takes an utterance and its transcript from the prepared dataset, places them in the conversation, and asks for a new utterance. That context gives the model an example of the voice it should continue.
Load the dataset before running the reference-conditioned call. It must contain audio and text columns. An optional source column identifies speakers, starting at zero; the notebook defaults missing identity information to source=0. That is appropriate for McGovern’s single-speaker video, but it does not separate speakers in mixed audio. The loading code also checks maximum audio length for the trainer that will be configured later. After commenting out a line during the live run, McGovern starts the cloning call.
The first text-only playback uses the fine-tuning sentence, and McGovern identifies the result as a woman’s voice. A second test uses “Sesame is a super cool TTS model which can be fine-tuned with Unsloth.” The contrast between these samples illustrates how variable the unconditioned speaker can be.
He then plays the reference-conditioned version of the second sentence, interrupting and replaying it while comparing the voice. McGovern judges it closer to his own, including some of the drawn-out quality he hears in his speech, but still short of what he hopes to obtain from training. The next experiment therefore changes the weights and repeats the comparison.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Attach LoRA adapters and format multimodal inputs
Return to the loaded model and attach LoRA adapters to the attention projections—Q, K, V, and O—and the MLP gate, up, and down projections. McGovern chooses lora_alpha=16, which he recommends for a model of roughly this size, and rank r=32. Rank controls the dimensions of the adapter matrices; increasing it gives the update more capacity.
The recording calls its rescaling option rescale_lora and describes it in terms of learning rate. For the documented rank-stabilized option, PEFT’s LoRA reference uses use_rslora: it changes the multiplier on the adapter contribution from α/r to α/√r, rather than directly setting the optimizer learning rate. This distinction should not be treated as confirmation of the notebook’s exact historical argument name. Calling print_trainable_parameters() shows just under 2% of parameters trainable in McGovern’s configuration. Additional modules such as lm_head or embed_tokens could be trained and saved through modules_to_save, but he leaves them unchanged because this adaptation does not change the token inventory.
The raw data is already loaded, with source=0 for the single speaker. Next, determine the maximum audio and text lengths so preprocessing can prepare compatible inputs. The notebook displays an audio length of approximately 700,000 and a text length of 587; McGovern explicitly corrects himself after calling the audio value tokens. Keep these as notebook length values, not durations or a verified token count. Supply the respective lengths through the text and audio keyword arguments, along with the audio sampling rate.
Apply the preprocessing mapping across the examples. Unsloth’s notebook preparation creates the input IDs, attention mask, labels, input values, and cutoffs needed by the multimodal trainer. Inspect the resulting columns and row count before passing the processed dataset, together with the adapter-equipped model, into training.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Train the adapters and watch the right signals
The planned virtual batch size is eight. McGovern usually prefers 32, but reduces it for this small dataset. He initially describes one epoch over 41 rows as roughly five steps. Against such a short schedule, three warm-up steps would consume much of training, so he suggests reducing warm-up to one. The remaining optimization choices are a learning rate of 2e-4, hardware-appropriate float16 or bfloat16, an 8-bit AdamW optimizer, weight decay, and a constant learning rate after warm-up. Training artifacts go to the configured output directory.
Before training, McGovern reports nearly 7 GB occupied out of roughly 15 GB of available GPU memory. He estimates about ten minutes for training and describes loss falling from approximately 6.34 to about 3.7. A still-declining training loss suggests there may be room to train further, and he expects reducing warm-up to help this short run. The actual batch size is two, with a virtual batch size of eight; because the reported memory use is only about half the GPU, he suggests that an actual batch of four may fit.
Queue the inference cells so the trained model produces the same kinds of saved audio as the base model. This creates four conditions to compare:
| Model weights | Reference audio | What changes |
|---|---|---|
| Base | Absent | Text-only baseline |
| Base | Present | Voice supplied in context |
| Fine-tuned | Absent | Voice adaptation in weights |
| Fine-tuned | Present | Adaptation plus context |
Using both axes separates the contribution of training from the contribution of a reference utterance.
The live progress later shows two of 60 steps, which does not match the earlier one-epoch, roughly five-step description. The recording therefore does not establish a single consistent executed schedule. More consequentially, this run has no evaluation dataset: its displayed loss is training loss, not a held-out measure of voice quality. McGovern recommends splitting training and evaluation data and watching whether evaluation loss continues to fall. If it stalls, reconsider the number of epochs or learning rate.
He also recommends monitoring gradient norm, suggesting around or below one as a heuristic. TensorBoard can expose these signals: install it, enable reporting to it, and set logging_dir, for example to logs. That monitoring is discussed but left disabled in the basic demonstration. McGovern points to his longer video and planned follow-ups for more detail.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Save adapters or merge a full model
While training proceeds, choose how to retain the result. A local save writes to Colab’s temporary storage. For a Hub repository, the notebook exposes separate publishing choices for the model and processor; retain both so the saved model has its corresponding input-processing configuration.
There are two useful distribution forms:
- Adapter-only: save the trained LoRA weights as a lightweight artifact that accompanies the base model.
- Merged model: merge the adapter update into the base weights and save the full model, with a 16-bit merge option in the notebook.
Notebook flags control saving and uploading. McGovern is unsure whether the merged helper automatically includes the processor, so check that it is present rather than assuming the merge handled it. To reload the adaptation, return to the loading cell and replace the base-model identifier with the repository you saved. This is model persistence and distribution; the workshop does not set up a hosted inference service.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Compare identity, pacing, and pronunciation
At completion, McGovern reports a training loss of 3.72 and memory use just under half of the available GPU capacity. He then listens to the fine-tuned model without reference audio. On the first sentence, he hears a male voice with some Irish character: the sample is closer to his own voice than the unconditioned baseline.
The pacing is still imperfect. McGovern suggests filtering source segments with excessive pauses, although his edited YouTube videos already remove many pauses. This identifies a separate target from speaker identity: a voice can acquire recognizable characteristics while retaining awkward timing.
The second fine-tuned, text-only sample also sounds somewhat Irish to him, but the T in tuned still sounds partly American. He suggests more data could improve it. Fine-tuning has moved the voice in the desired direction without fully reproducing its pronunciation.
Finally, he plays the fine-tuned model with reference audio. McGovern judges this the strongest sample: it has the Irish pronunciation of tuned and even a small irregularity he finds natural. On replay, he identifies a remaining mismatch in intonation around super cool, wording he would not usually use himself. These conclusions are his listening judgments on the demonstrated samples; the workshop provides no held-out quantitative voice-similarity score.
For the next experiment, McGovern proposes about 500 thirty-second rows, particularly to improve generation without reference conditioning. That larger dataset is a suggestion, not a tested result here. He retrospectively estimates that a roughly 30-minute source video was enough for this small-data example combining fine-tuning and cloning; source-video length does not establish how much audio was retained in the training rows. The demonstrated path is to use adaptation and reference context together, then improve data coverage and timing where listening reveals weaknesses.
The workshop repository remains the starting point for the notebook and slides. McGovern closes by pointing toward more detailed Trelis videos on data preparation and fine-tuning hyperparameters, and invites questions in the recording’s YouTube comments.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Resources
From the talk
Official speech-generation implementation with setup instructions and examples with and without conversational context.
Official model card with access requirements and Transformers inference and training examples.
Speech-transcription installation instructions, model tradeoffs and Python examples.
Canopy's Llama-based speech model with inference and fine-tuning materials.
Kyutai's spoken-dialogue framework and streaming audio codec, with architecture explanations and implementations.
Further reading
Reference for adapter rank, alpha, rank-stabilized scaling and additional trainable modules.
Updates since the talk
Current architecture, processor and generation API documentation for CSM.
Read the complete timestamped transcript
- 0:00
Welcome to this workshop on text-to-speech model fine-tuning. I'm Ronan of the Trelis Research YouTube channel, and today I'll be walking you through data preparation and fine-tuning of Sesame's CSM-1B model.
- 0:13
You should be able to follow along. All of the materials are available on the Trelis Research GitHub. If you go to ai-worlds-fair-2025, you'll be able to find the Colab notebook and also the slides that I'm going to go through for this workshop.
- 0:29
Now, by the end of this workshop, you should be able to train a text-to-speech model so that it sounds like a specific voice. You should be able, uh, to understand how token-based text-to-speech models work.
- 0:42
The one we're going to train today is Sesame CSM-1B, but Orpheus from Canopy Labs is also a token-based text-to-speech model.
- 0:50
We'll go through how to create a voice data set. You can record one yourself, uh, but today we're just going to pull audio from a YouTube video. You can pick which video and use that as a basis for the fine-tuning.
- 1:03
We'll cover then fine-tuning using Unsloth, which is a library based around Transformers. Many of you might be familiar with it. And last of all, we'll evaluate performance, uh, both before the fine-tuning and after the fine-tuning.
- 1:17
Now, before I start to get into the Colab notebook, it's just going to be one Colab note-book, book that covers all of these steps. I want to describe a little bit how these models work very briefly.
- 1:27
If you do want to understand in much more detail, uh, I'll point you to a much longer video, about an hour and thirty minutes, where I talk about token-based, uh, text-to-speech models.
- 1:38
I also talk a little bit about Moshi. I'll put a link to this in the GitHub repo as well. It helps to start off with reviewing how a token-based text model works, such as, say, the ChatGPT models, uh, from OpenAI, uh, like GPT-4o or Llama series of models.
- 1:55
Those models, which you'll be familiar with, most likely involve taking in a string of text and predicting the next, uh, word or token, and then passing that around so that you recursively predict the next word.
- 2:07
Now, when we talk about text-to-speech, what we want to do is take in a series of text tokens and predict the next audio token, if you can bear with me as to what an audio token might be.
- 2:18
And in fact, we'd like to be able to pass in a history of text and also audio from the earlier conversation and then decode. Recursively, we'd like to produce the next audio token, so we have a string of audio tokens that re-represent the output speech, uh, that we want.
- 2:33
So conceptually, this is what we're trying to do. We need a transformer model that instead of taking in only text and outputting text tokens, can input ideally text and audio and output a string of audio tokens.
- 2:46
So the question, the first question then is: how do you make tokens for audio? How do you take a wave, a sound wave like this and represent it, uh, in terms of discrete tokens rather than continuous, uh, frequencies or sound waves?
- 3:01
And the answer is, you can represent audio or a little piece of audio as a choice from a codebook. A codebook is like a dictionary, and inside the dictionary, there are a series of vectors where a vector represents a given, uh, sound.
- 3:17
Now, these codebooks could be fairly large in order to represent a wide variety of sounds, and that could be the acoustics and also the semantics. But it is possible to take sound and represent it in a discrete way, much like text, uh, represents meaning in a discrete way.
- 3:34
The way that these codebooks are trained, I won't get into too much detail, but broadly speaking, you need to train, um, an encoder-decoder where you take a sound wave, you then have some kind of transformer that converts it into tokens, and then you have another transformer that will decode it back, uh, into a wave.
- 3:53
And the question you ask during training is whether the output here, after encoding it to tokens and decoding it back, you ask the question of whether it matches the input.
- 4:03
And to the extent it doesn't match, you will use that difference or the loss to back propagate and update these weights here. So it is possible, giving sufficient data on sound waves, to then train, uh, a codebook so you can represent those, uh, that sound in a discrete form.
- 4:20
Now, it turns out if you use that approach, uh, you can represent sound using a codebook, but it turns out insufficient to just have one token for each time step.
- 4:30
Actually, what works out better is to have a hierarchical representation, or at least have multiple tokens allowed at the same kind of timestamp or same window, so that you can have a more detailed representation.
- 4:42
So this is the idea that's used in most token-based audio models. Instead of just having one token at each window, you will have multiple tokens potentially set up in a hierarchy.
- 4:51
So you represent higher level kind of meaning or acoustics, and then you have more granular, more granular, and so you have these multiple layers of representation. Now, there are a few ways to set things up to have different tokens represent sound at a given window.
- 5:05
The one we're going to focus on is the Sesame model, uh, where there are thirty-two tokens at every, uh, window that will represent the sound. There is a zeroth token that will be predicted by the main transformer here, but then there's a secondary transformer that's being used to decode out the other thirty-one, uh, tokens of meaning.
- 5:27
So whereas here we basically have Sesame decoding out with a main transformer autoregressively these first zeroth tokens, the kind of stacked tokens are being decoded by a second transformer.
- 5:39
So actually, Sesame is two models. It's the main one billion parameter model and then a much smaller model that will decode out kind of these hierarchical tokens here. So this is a quick overview of how the model is going to work.
- 5:53
We will start off with a pre-trained model that is capable of taking in text and audio and outputting, uh, a stream of audio here, and we'll see how it performs just when we give it some text.
- 6:02
We'll see it generate or we'll hear it generate some sound. But then we're going to take a data set And it's, uh, in my case, it's gonna be of one of my YouTube videos with my voice, and we're gonna see if we can make the model sound a little bit more like me by fine-tuning it, uh, on
- 6:16
that data. So with that, we will move over to the Colab notebook. As I said, you can find it, uh, on Trelis Research if you look for the AI Worlds Fair 2025 repo.
- 6:29
And if you open that up in Colab, you should be able to run it for free using, uh, a T5. So when you open up the notebook, make sure that, uh, you are connected to a GPU.
- 6:41
This should be available for free. If you're not connected to a GPU, uh, you can say, uh, Change runtime type, and then you'll want to select, uh, the TPU here.
- 6:51
So I am connected, and there are two sections in, in this notebook. If you're following along, you can just open it up in your browser now. There are two sections we'll go through.
- 7:02
The first is data generation, where we're gonna pick a YouTube video, we're gonna use Whisper in the Colab notebook to transcribe the YouTube video, and then we're going to convert it into a data set.
- 7:13
The data set will have audio snippets of up to 30 seconds, and they will have text, which will be the transcription, uh, using Whisper.
- 7:23
Given that data set of snippets, I think I will have maybe around, let's see, 41 rows of data, which is actually enough to have an effect on the quality.
- 7:33
I have previously done trainings with maybe shorter snippets of only 10 seconds and 20, 20 of those, and I don't think that's enough data. So I would say if you've got 30-second snippets, probably having roughly 50 of them is enough to start to have an effect.
- 7:48
And given that data set, we'll then fine-tune the Unsloth model. Or sorry, we'll fine-tune the CSM model, the Sesame model, using Unsloth, and we will measure performance at the end and see how that fares.
- 8:03
So let's start off with the data generation, and you can pick your own YouTube video if you like. I recommend picking a video where you just have one speaker.
- 8:12
You could pick a video with two speakers, but you need to do more data processing. You need to do diarization to split that, uh, audio up into multiple speakers, which is not supported in this basic notebook here.
- 8:23
So I recommend taking a video where you just have one speaker. This is a, a Trelis video here. You can then pick the model size, the Whisper model size that you want to use for transcription.
- 8:35
I recommend using turbo, which is almost as good as large. Um, I think it's better than definitely small, base, and tiny, but it's actually a lot faster than large.
- 8:44
So we'll go with turbo. Run the installs. So you'll need white, uh, youtube-dlp. Note that this works well in Colab. If you try and run this notebook outside of Colab, you can run into authentication issues where you'll get blocked from downloading from YouTube, so I recommend running this in Colab.
- 9:01
Um, you also need to install Whisper, which is, uh, from OpenAI. And with all of that installed, you should be ready to then load the Whisper model here and run the transcription.
- 9:12
And we're gonna use the GPU, so we'll use CUDA here, and it should then save your transcript to a file, to a JSON file, which will be saved here locally.
- 9:22
So what you should find is a JSON file, uh, that's saved and, uh, you should be ready to, to then use that file as a basis for the training.
- 9:31
Now, when you run the transcription, I'm not gonna run it here 'cause it's, uh, gonna take a little bit of time, and when you have that JSON file, um, you can take a look at that file, which I'll show you now.
- 9:45
Okay. So I've opened up the JSON file, and it has a bunch of text and also details around the exact tokens. It also will have the segments of, uh, the transcription.
- 9:57
And what I recommend doing is just take a quick read through it. I know you won't be able to see 'cause of the font size here, but take a look through for any words that you think might be misspelled, and what you can do is a quick find/replace on some of those variables.
- 10:11
So, for example, often, uh, I'll see the word Trelis with two Ls, 'cause that's how the correct English word is spelt, but the word, uh, Trelis for the name of Trelis Research is one L, and so I'll often just do a find/replace, uh, for Trelis and put in T-R-E-L-I-S, and then I'll replace that throughout.
- 10:29
Now, I've already done that, so it's, uh, completed, but if you want to make those fixes, what you can do is do the find/replace and then re-upload the file to Google Colab.
- 10:40
Just re-up- upload it here, uh, so that you can read it in and use that corrected transcript then as the basis for your fine-tuning data set. So at this point, what we've done is we've grabbed the audio from YouTube.
- 10:53
We have transcribed it. We've corrected it then manually, and we're ready now to split what's a long, uh, transcript consisting of short segments from Whisper. And what we wanna do is combine some of those segments so that we have longer chunks, up to about 30 seconds in length.
- 11:09
Uh, we're gonna use a sim- simple algorithm. We're simply gonna stack them until they reach more than 30 seconds, and then we'll move to creating a new row of data.
- 11:17
So what we ultimately want is rows of data that are max 30 seconds in length.
- 11:23
So that's what we're gonna do here. I'm gonna just log into Hugging Face so that I can push a data set up to Hugging Face. Now, I've done this earlier, so I'm not gonna, uh, log in right now.
- 11:34
I'm gonna define the organization. So if you've created an account, you can create an organization. Mine is [REDACTED:username]trelis. I'll create a u- a repo name, and I'll create the maximum length I want for the rows of data.
- 11:48
Then I will grab the source audio, which should be saved from running the YouTube extraction earlier, and I'll also specify the path to my JSON file, which could be an updated JSON file with those corrections.
- 12:00
Then I'll load that JSON file, and I'm now going to run through basically taking the segments here And adding them together until we get to the maximum duration. And when we're at the maximum duration, we'll call that a row of data, and we'll move then, uh, to the next data set.
- 12:19
So with that done, as you can see, uh, here, I've generated forty-one clips. Uh, I've pushed it up to YouTube if you-- Or sorry, pushed it up to Hugging Face.
- 12:29
If you don't wanna push it up, just set this to false, and yeah, my data set should now be available.
- 12:35
So if you're following along, it will take a bit of time. You might need to pause the video because you need to run the transcription. Should take a few minutes at most, even if you have, like, half an hour or an hour worth of video.
- 12:47
And then you should find your data set up here.
- 12:50
And you can see my snippets of text, and then you can see the sound. So I could, uh, for example, play some of these sounds and test them out.
- 12:59
Now, this is kind of a basic implementation, what I'm showing, but you can see already an opportunity for improvement. Here,
- 13:07
right now I'm adding segments of text, but I'm not being careful as to whether the segments are ending. It would be better if I ended every row of data here on a full stop.
- 13:18
That could be done by using a library such as NLTK to detect, uh, sentence boundaries, or you could even use just a regular expression. So there is room for improvement of how these data sets are being compiled.
- 13:31
Shouldn't make a huge difference, but generally, you want to have kind of paragraphs that are complete within a row of data here. All right, so at this point, hopefully you have your data set, uh, pushed up and you've noted what your org name and your data set name is.
- 13:45
So we're ready to move on, uh, to fine-tuning. Now, the first thing for fine-tuning is to install Unsloth. Uh, so I've actually run this, and I have run it just, uh, earlier this morning, so we're gonna be able to follow on with these cells.
- 14:01
We're installing, um, Unsloth here, which includes Transformers and a number of packages for loading and fine-tuning models. Next, with Unsloth installed, we're gonna load the base model that we want to train, which is CSM-1B.
- 14:14
This is the Sesame model. We're going to load it with a maximum sequence length. This is the maximum tex-text length, which is probably quite a bit longer than thirty seconds, uh, which it should be.
- 14:25
We will leave the data type as none. Yeah, the data type, if I'm on a T4, will be, uh, float sixteen. So it'll be float,
- 14:34
uh, Torch.floatsixteen on T4, and it'll be, uh, Torch.bfloat sixteen
- 14:43
on, um, anything more recent than that, so on Hopper or Blackwell GPUs.
- 14:50
Also Ampere as well. The auto model is CSM for conditional generation, so Sesame models are now supported by Transformers, and that's what U-Unsloth is leveraging here. You could quantize the base model, but it's not necessary 'cause CSM-1B is just a few gigabytes in size, and the T4 is fifteen gigabytes, so it will fit easily, uh, within this
- 15:10
here. So the model is gonna download, and it will then be loaded into the GPU. When the shards are loading, you should see that, uh, the GPU should start to get full with the shards.
- 15:21
Now, this here looks like my GPU usage is kind of high. It might suggest I've loaded the model twice, uh, so I may have to restart my runtime and reload it again.
- 15:32
Uh, but we'll see. It's, it's lowering down now, so maybe that's okay.
- 15:36
And what you can do then is print out the model architecture just to take a look. And yeah, there's quite a lot in the CSM model. There's
- 15:47
the backbone model. This is the main Transformer model here. And then there's the depth decoder, so this is where you're decoding out those thirty-one other tokens. And then there's also the model that allows you to convert from the waveform into tokens and tokens back to the waveform.
- 16:03
So you've got the codec model, and there's also the decoder down here as well.
- 16:08
So this is the architecture. It's relevant to print the architecture because actually we're not gonna train all of these parameters. We're just gonna train a subset. We're gonna focus on the linear layers.
- 16:18
And actually, we're not even gonna train all of the parameters in the linear layers. We're gonna train adapters that plug on. So that's gonna save memory and also make the training faster.
- 16:27
So you'll just take note of some of these modules here. These are the attention modules that we're gonna train. These are the linear layers. We're gonna train these as well.
- 16:35
Okay, so that's the model printed out, and we're ready now to apply those adapters I just mentioned. So apply little adapters to train and apply them to these modules here.
- 16:46
I'm gonna come back though before I apply this because I'm gonna skip rudder because I want to see how the base model is performing without any fine-tuning. So we're gonna skip the data preparation just for now.
- 16:58
We're gonna skip the training, and we're gonna run straight to inference just to hear what some of these sounds are like.
- 17:05
So, um, yeah, let's start off and just pass in a piece of text here saying we just finished fine-tuning a text-to-speech model. We didn't finish 'cause we're just starting, but let's run this here and see, um, what it sounds like.
- 17:20
There's an alternative way of running here. Basically, here we're using a chat template to apply to text that we're passing in, whereas here we're preparing, uh, the tensors in a more raw format just by passing in, um, the speaker ID and text here.
- 17:35
So essentially, we're bypassing the chat template, whereas here we're using the chat template. Just two different ways to call the model. So this is just a basic called zero-shot inference.
- 17:44
You're passing in text, you get out audio. You're gonna get a random speaker's voice here because, uh, the temperature is non-zero. So you could get female, you could get [REDACTED:gender], you could get deep, you could get high-pitched.
- 17:55
Uh, you could really get any speaker. And one way you can avoid that is by doing what's called voice cloning. So with voice cloning, which is different to fine-tuning, with voice cloning, we will pass in a sample, and we will then get the model, uh, to speak.
- 18:11
So basically, we pass in audio And then we get the model to generate audio, and the model will tend to generate audio that sounds more like the audio we passed in.
- 18:20
This is called voice cloning, and we're just gonna use some of the data set that we already created for this purpose. And yeah, that data set will get passed in, and then we will ask the model to speak this text here, and this should be quite a bit closer to my voice than the first example because we've
- 18:40
passed in a sample. Uh, but it still won't be as close as we get after fine-tuning. Now, to be able to run this, I do need to have loaded the data set, so I need to r- load the raw data set.
- 18:51
Uh, so for that, I'm actually gonna go up here to the fine-tuning. I've got Unsloth, uh, data prep. I'm not gonna... Yeah, and then I do need to run the loading here.
- 19:02
So I will just run the data preparation script here so that I load in my raw data set from my YouTube TTS. Just a note that when we load the data set, we do need to have a data set with an audio column and a text column.
- 19:17
Optionally, you can have a source column, and the source would refer to the speaker number, which would start at zero. So if you have two speakers, it could be zero, and then it could be one for a different speaker.
- 19:27
Um, if you don't have a speaker column, what this code here is doing is simply assigning a speaker zero. So we'll just, uh, if there's no speaker found, it will use the default of source, which is zero.
- 19:41
So it will apply a column effectively of source being equal to zero. Okay, so I'm just loading the data set here
- 19:49
because we need to have some data if we're gonna clone. And yeah, the message is showing here that the speaker is being set to zero for each of the rows, which is good 'cause it's a video where I just have one speaker.
- 20:01
And now it's just checking the maximum audio length, which we're gonna need when we set up the trainer later. So we configured a trainer to also have, uh, that maximum audio length.
- 20:12
But while that's running, uh, we can just go down so that we're ready to run the cloning script, and this is just going to run inference with cloning.
- 20:23
And I do have an issue here. I need to comment that out. And the cloning is now underway, so we're basically gonna pass in that, well, a sample of my voice, and then we're going to try and generate text, uh, to say this right here.
- 20:39
So in the meantime, I've already generated, um, a sample without the cloning, so let's just play that.
- 20:46
We just finished fine-tuning a text-to-speech model-
- 20:50
Okay. So yeah, you can hear it's, um, it's a woman's voice. Um, and as I said, it can be unpredictable exactly what voice you get based on temperature. And here's another sample.
- 21:02
Sesame is a super cool TTS model which can be fine-tuned with Unsloth.
- 21:07
So yeah, you can see that there's a very wide variance in the type of speaker that you're gonna get if you just do zero-shot inference.
- 21:15
I have got a sound file now of the cloned audio.
- 21:19
Sesame is a super cool TTS-
- 21:22
And here is that audio
- 21:24
... model which can be fine-tuned with-
- 21:27
And here's that audio.
- 21:28
Sesame is a super cool TTS model which can be fine-tuned with Unsloth.
- 21:36
So it's not too bad. My voice has quite a bit of drag on it.
- 21:39
Sesame is a super cool TTS model which can be fine-tuned with Unsloth. Sesame is a sup-
- 21:47
So it's definitely closer to my voice, but not as good as we can get with fine-tuning. So what we're gonna do now is go back up to the start, uh, and we'll continue on after having loaded the model to apply LoRA adapters and then run through the training, and we'll see if the fine-tuned model performs better than
- 22:03
just using cloning on its own. So I'm gonna scroll back up here to the point where we loaded the model.
- 22:11
The model is, uh, is still loaded. Um, I'll just rerun this to kinda make it more compact. And now we're gonna apply those adapters. So we're gonna target the linear layers, the QKV-o, and then also the MLP linear layers here, gate up and down.
- 22:28
We'll use a LoRA alpha of 16. It's a pretty small model. I usually recommend 16 if the, if it's about one billion in size, and we will use, uh, rescale_lora.
- 22:37
This means that the learning rate for the LoRA adapters will be scaled according to the size of the adapters, because if you have larger adapters, you need to train them more slowly and vice versa.
- 22:48
So we'll go ahead with this. We'll use a rank of 32. That means, um, the rank, kinda like the width or the, the height of the adapter matrices.
- 22:57
You can make that larger if you want more granularity, but 32 should be fine. And with that, we will apply the adapters, and we should get a printout of the number of trainable parameters.
- 23:07
So yeah, I think we've already applied everything there. Um, it's not actually showing us the trainable parameters, but we could probably do that by just running print trainable_params. And yeah, so print trainable parameters.
- 23:25
You can see that we've got about just under 2% of the parameters are trainable. Technically, you could train more parameters, say, by training the embeddings. If you want to do that, you would add in here modules, uh, modules to save, and the ones that you typically would want to save or that you would typically want to train
- 23:44
are LM head and LM embed or embed tokens. But, um, because we're not changing the embeddings, we're not changing the tokens here, there's not really any reason to train them, so just applying adapters to the linear layers, uh, should be plenty.
- 24:00
Okay, so we have the adapters applied, and we're ready to move to training. We've already loaded the data set, uh, the raw data set. We've set the source to zero because we've just got one speaker.
- 24:11
There's a little more formatting we need to do though. And for that formatting, basically preparing the data for the trainer, we do need to know the maximum audio length.
- 24:19
We've already got that printed here. It's about 700,000, um, audio steps,
- 24:27
uh, audio, uh, tokens in length. Sorry, not tog- tokens, but it's the length, uh, of the audio. Then we need to measure the length of the text, so I'll just run that cell here, and the max is at 587.
- 24:41
And we're gonna pass that max text length and the max audio length into the trainer down here. So you can see when we're preparing the inputs, we need to prepare text keyword arguments, and we need to prepare audio keyword arguments.
- 24:53
Uh, we need to set a sampling rate, and we do need to set the text length and the audio length right here.
- 24:59
So I've got this set and Unsloth is doing the work to prepare the input IDs, the attention mask, the labels, the input values, and the cutoffs. So all this is, uh, set up nicely for us per the original Unsloth notebook, and we just need to apply this mapping now, uh, to our pre-processed examples to get a processed
- 25:17
dataset. So I'll go ahead and do that now,
- 25:20
and then I'll be able to print out the dataset, which will show the number of rows at the input IDs and, uh, these other columns here. Next, we're ready to train the model so we can pass that processed dataset, uh, into the trainer.
- 25:34
You can see that's being passed in here. The model is being passed in here. We've already applied the adapters. We will use, um, a virtual batch size of eight.
- 25:43
You probably can go a bit bigger within the T4. Um, I usually would like to have this of 32 in size, so the product being 32, but we don't have that much data, so we won't go with that large of a virtual size.
- 25:55
We'll go with eight. With 41 rows, there'll be, uh, five different steps for every epoch of training. We'll just train for one epoch.
- 26:04
Um, the warm-up steps of three, this means that we're gonna increase the learning rate slowly. To be honest, three is a bit much if there's only five, uh, in terms of total steps, so you could just reduce that to one maybe.
- 26:16
The learning rate, 2E minus four, is probably good for a one billion parameter model. This will automatically decide whether to use Float 16 or Brain Float 16, depending on the TPU that's being used.
- 26:26
Uh, we'll use AdamW 8-bit optimizer to reduce memory requirements a little bit. Uh, weight decay prevents overfitting, probably not essential. We will train at a constant learning rate,
- 26:38
and we will save our outputs here, uh, to the output directory. So with that, I think we can, uh, set up training, check our current memory stats. So we've got almost 15 gigabytes in the GPU, and nearly seven of them are used up.
- 26:53
And we can start our training, and it's gonna take about, uh, 10 minutes if you run the training. And what you'll find is during that training, uh, the loss will fall from roughly 6.34, uh, down to, you know, somewhere around 3.7.
- 27:10
It looks like the loss is still falling, so probably there's potential for improvement here, uh, in terms of the quality of the outputs. Um, maybe it'll help a little that I reduced the number of warm-up steps because that means we'll have a bit higher of a learning rate.
- 27:26
So I'd expect if I run this now, I probably should, uh, do a little bit better. Let's get the training going.
- 27:33
And when the training is done, we'll print out the final training stats. You can see with that batch size that I specified, the virtual size of eight or the actual batch of two, there's only half of the memory being used.
- 27:43
So you probably could increase up to a batch size of four without a problem. So I would run this, and then what we'll do is run the inference again, uh, but this time on the fine-tuned model and see how the performance compares.
- 27:57
And what we'll do, um, what will happen automatically is we will get the saving of some of these, um, of some of these sound files, so we'll be able to just play back the zero-shot example here.
- 28:09
We've got the zero-shot audio and the cloned audio on the base model, but now we're gonna generate, uh, voice samples using the fine-tuned model.
- 28:18
So I'll just run this here, run all of these cells so that when the training is done, and then run the, uh, cloning test for when the training is done.
- 28:29
Training should be underway here now. If I scroll up, you can see we're at, uh, two out of 60 steps. Note that there's no eval dataset. Ideally, you would split your training set into train and eval, and you would inspect the eval loss, uh, make sure that that's falling.
- 28:44
If it stops falling, that means you probably need to stop training. Either you've gone too far in epochs or maybe your learning rate is too high. You also typically want to monitor the Grad Norm, make sure it's around one or less than one.
- 28:55
You would do that typically by adding in here, uh, TensorBoard. So you would report to TensorBoard. You also need to set a log directory. So I think it's logging dir that you need to set here.
- 29:07
Dir. Typically, you would set it to something like logs.
- 29:12
Uh, but I'm gonna leave this because we're just looking at a basic setup for now. But if you do that, you can pip install TensorBoard, and then you can look up, uh, how to monitor the losses.
- 29:21
Now you can see that in the longer video I already published. I'll probably publish a more detailed video after this, uh, too, on the us- on the Trelis Research YouTube channel.
- 29:30
So training is going on. It looks like our loss is falling, which is good. And while that's happening, I'll just explain once, uh, the training and the inference is done, you have the option to save the model and also push it to Hub.
- 29:42
If you just run this cell here, it's gonna save a copy of the model locally, so it'll be in your temporary storage on Colab. If you want to push it to Hub, you need to set one of these to true, uh, both of them to true because you need the model and processor.
- 29:55
This will just push the LoRA adapters, uh, which is lightweight. It'll be a smaller repo. But if you want to push the full model, uh, you need to merge it first, and that's where, uh, this comes into play here.
- 30:06
You can save the model in merged form. Um, well, this is just saving again in, as LoRA adapters, but if you want to merge it to 16-bit format, you can set these two to true, and you can save the model and also push it to Hub.
- 30:18
And I believe these will actually save both the model and the processor as well. So if you want to push to Hub, yeah, you just set this one, uh, here true.
- 30:27
It will merge the model and then push it up to Hub. By the way, if you do want to reload, um, a model that you have previously created, you would do that right back up here where we loaded the model.
- 30:38
Instead of specifying the base model name, you would specify the, um, the fine-tuned model name, which in my case is going to be trellis my-youtube-tts. Okay, so training is done.
- 30:50
We've gotten down to 3.72 as loss and used just under half of the memory available. Next, uh, we'll run inference using this fine-tuned model. So actually, this is run.
- 31:00
We should have a sample here. Uh, we should have a second sample here, which we'll listen to now, and then we have a cloned example, which we expect, uh, should be the best.
- 31:09
So let's give a listen to the fine-tuned model here. We just finished fine-tuning. Let's have a listen. We just finished fine-tuning
- 31:17
a text-to-speech model. It's pretty good. So it sounds a little bit like me, but, um, yeah, definitely it's a [REDACTED:gender] voice, so that randomness of it being any kind of voice has been removed, and it does sound a little bit [REDACTED:origin].
- 31:33
You can see the pacing is not quite right. That perhaps would be improved by better filtering the original data set so that I don't have segments from my YouTube video where there's just a lot of pause.
- 31:44
Now, there isn't a lot of pause anyway in my YouTube videos typically because I cut those out, but you could better do some filtering on the data if you want to improve that.
- 31:52
Let's see a second sample though because there is some randomness. Sesame is a super cool TTS model which can be fine-tuned with Unsloth. So it's a little bit of an [REDACTED:origin] accent.
- 32:03
Probably indicates more data is needed to improve the quality here. The T in tuned is still a little bit American, although it's kind of slightly [REDACTED:origin], so some room for improvement here.
- 32:14
But what we see now is probably the best chance we have, which is using the cloning plus the fine-tuning. So I'll play this sample now. Sesame is a super cool TTS model which can be fine-tuned with Unsloth.
- 32:28
Okay, so that was great. It even has the [REDACTED:origin], uh, tuned. It even kind of makes this little bit of an error here, which actually sounds natural. Let's give it one more listen.
- 32:36
Sesame is a super cool TTS model which can be fine-tuned with Unsloth. Yeah. Now, maybe the super cool, that's not something I would say, so maybe it hasn't got my tonat- uh, my intonation correct.
- 32:49
But broadly speaking, this, uh, this sounds pretty great.
- 32:53
So at this point, we've pretty much finished the workshop. Uh, you should have a sound ideally with the cloning that sounds something more like what you do or what the person in the video you chose sounds like.
- 33:04
If you wish, um, you could create more data, aim for maybe 500 rows of 30 seconds, and I think you would see better performance, particularly without the voice cloning.
- 33:14
But you can see here that if you combine fine-tuning with cloning, you're able to get pretty good performance even with a relatively small amount of data. Uh, just say I think a video of 30 minutes was enough in this case here.
- 33:26
So that rounds up the workshop. As I said at the start, you can get all of the resources on GitHub. That's, uh, TrelisResearch/ai-worlds-fair-2025. I will probably make, uh, I will make future videos on voice that you can check out on the Trelis YouTube channel.
- 33:41
Probably some more detailed videos covering in depth, a little more depth, the data preparation and some of the hyperparameters around the fine-tuning itself. Uh, in the meantime, let me know if you've any questions by dropping some of your questions or comments below in the YouTube comments.
- 33:57
Cheers, folks.