AI Engineer Europe 2026
Training an LLM from Scratch, Locally
Read the talk
Training a Small Language Model from Scratch
Build a character-level Shakespeare model in PyTorch, following the path from vocabulary design and causal attention to training, sampling, and the limits of a tiny model.
From a talk by Angelos Perivolaropoulos
Before you start: Basic Python familiarity is helpful; following the code also requires comfort with tensors, array shapes, and PyTorch modules.
What can you train without pretrained weights?
Can you train a language model on a laptop without downloading someone else’s weights? That is the starting point of Angelos Perivolaropoulos’s workshop: use PyTorch and a few basic libraries to build the model, train it, and generate text. Perivolaropoulos introduces himself as the lead of ElevenLabs’ speech-to-text team, working on real-time transcription for agents. He also introduces Scribe v2 and its public-benchmark leadership claim; ElevenLabs’ announcement makes that claim for transcription word error rate and distinguishes the batch model from Scribe v2 Realtime.
The exercise exposes the core machinery while leaving large-scale optimization and specialization for later. The workshop repository offers two routes: train locally, with roughly 16 GB of memory as the suggested starting point, or use Google Colab’s GPU runtime. The model is deliberately small enough that acquiring a cluster is not the first problem to solve.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Four pieces, one small training run
Karpathy’s nanoGPT is the motivating reference: a compact PyTorch project that makes language-model training approachable. This workshop uses a small GPT-2-style causal decoder. Its workflow has four pieces: choose a tokenizer, assemble the architecture, write the training loop, and implement generation. A data-limited exercise favors a small vocabulary and small embeddings; the decoder then combines causal self-attention, feedforward layers, and normalization. Longer contexts and greater training throughput introduce additional engineering constraints.
Perivolaropoulos emphasizes training and post-training as major sources of capability gains, citing successive GPT and Gemini releases. His comparison between Gemini 3 and Gemini 3.1 concerns improvements on particular benchmarks, not a doubling of overall capability, and does not establish which training changes caused them. For this exercise, the immediate goal is simpler: make a small model learn, then run its inference on ordinary hardware.
Use Python 3.12 and choose the execution path that matches your machine:
- Local: install
uv, open the repository, and runuv syncto create the environment and install dependencies. The workshop uses a scratchpad for assembling code. - Colab: create a notebook and install
torch,numpy,tqdm, andtiktoken. The character tokenizer itself does not requiretiktoken; it is included for experimentation. - Device: the code supports Apple Silicon through
mps, NVIDIA GPUs throughcuda, and CPU execution. Memory availability constrains batch size, while the device affects training speed.
bash
uv sync
For the notebook dependency cell:
python
%pip install torch numpy tqdm tiktoken
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Start with a vocabulary the dataset can support
The first design decision is how to represent the data. Perivolaropoulos illustrates its importance with a TTS planning example: a team might spend six months considering the tokenizer and two considering the architecture. A language model processes vectors, so text must first become integer IDs and then learned embeddings. The Shakespeare corpus used here contains 65 distinct characters, making a character-level tokenizer a small, transparent starting point.
Build a sorted character vocabulary, assign each character an integer, and invert the mapping for decoding. stoi is simply a string-to-integer dictionary, not a separate library. The corpus is supplied with the project; the mapping must remain the same when generating from a saved model.
python
class CharacterTokenizer:
def __init__(self, text: str):
self.chars = sorted(set(text))
self.stoi = {ch: i for i, ch in enumerate(self.chars)}
self.itos = {i: ch for ch, i in self.stoi.items()}
def encode(self, text: str) -> list[int]:
return [self.stoi[ch] for ch in text]
def decode(self, ids: list[int]) -> str:
return "".join(self.itos[i] for i in ids)
The embedding layer later turns these IDs into trainable vectors. An unfamiliar character is outside this particular tokenizer’s vocabulary; unlike a byte-based tokenizer, this dictionary has no universal byte fallback.
With 65 characters, there are 65² = 4,225 possible ordered bigrams. That small space motivates the choice: repeated adjacent-character patterns are easier to encounter in a small corpus than combinations from a huge vocabulary. Perivolaropoulos extends this into a vocabulary-squared heuristic for data requirements, using a 200,000-token vocabulary as the contrast. Treat that as intuition about sparsity, not a minimum-data formula: natural language does not require every possible token pair to occur. The practical warning is that a large off-the-shelf vocabulary is a poor match for this tiny training budget.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Small vocabulary, longer sequences
Character tokenization moves work into the transformer. In the sentence The sky is blue, a model using word-sized pieces can learn a relationship between sky and blue directly. A character model must first compose s, k, y and b, l, u, e into useful representations. It also processes and generates more tokens for the same text. These costs explain the workshop’s trade-off without requiring the stronger claim that character models can never learn well.
Byte-pair encoding, or BPE, learns reusable pieces by merging frequent adjacent patterns in training data. It offers a path beyond the exercise when more data and compute are available. Vocabulary size also changes the embedding table directly: each vocabulary entry needs a vector of the chosen width.
| Vocabulary | Width | Token-embedding parameters |
|---|---|---|
| 65 characters | 384 | 24,960 |
| Approximately 50,000 tokens | 384 | Approximately 19.2 million |
These counts are just vocabulary size multiplied by embedding width. The larger table alone would exceed the size of the workshop’s small model, before counting attention or feedforward layers.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
What each transformer component contributes
The architecture is approachable because a few components repeat. Perivolaropoulos encourages implementing them first and deepening the mathematical understanding through experiments. Many newer systems retain these components while changing attention efficiency, context handling, or other details; hybrid architectures can depart further from this baseline.
- Multi-head self-attention lets each position combine information from earlier positions. Different heads can learn different relationships. Supporting a much longer context requires managing the resulting computational and memory costs.
- The MLP, also called the feedforward network, transforms each position’s contextual representation into features that subsequent layers and the output head can use.
- Residual connections add a learned update to the incoming representation. A layer can refine what is already present rather than replacing it wholesale.
- Layer normalization regulates activation scale. Perivolaropoulos illustrates the problem with repeated tenfold amplification: an initially small value could become enormous after several layers.
Together these components make repeated processing trainable; they do not assign a manually specified meaning to each neuron or head.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Novel identifiers and the model’s configuration
An audience question makes the tokenizer trade-off concrete: Python has common keywords, but programmers invent variable and function names constantly. A learned tokenizer may encode frequent pieces such as for, enumerate, foo, or bar compactly. An unusual identifier instead breaks into smaller known pieces, potentially down to bytes. It does not need a dedicated vocabulary entry to be representable. The corpus determines which patterns become compact; poorly represented languages or unusual naming conventions can therefore consume more tokens.
The model configuration separates vocabulary size, context length, depth, attention structure, and representation width. The workshop starts with a vocabulary of 65, a context of 256 tokens, six layers, and width 384. Here, 384 is the exercise’s embedding width, not a general specification for GPT-2. Heads may learn patterns associated with punctuation or grammar, but those roles are examples of possible learned behavior, not labels assigned in code. A top-level GPT module receives this configuration and organizes its submodules, using PyTorch’s ModuleDict in the demonstration.
Context length and parameter capacity are different controls. A small model can process long sequences, and a large model can process short ones. Full causal attention can look across the supplied past; windowed attention restricts how far back a position looks. This exercise trains on sequences no longer than 256 tokens, so it cannot simply be asked to handle arbitrary longer inputs.
Asked which settings to increase for more capacity, Perivolaropoulos tentatively suggests increasing depth and heads, while expressing uncertainty about width. There is no single scale-up knob: increasing the number of heads at fixed width does not by itself proportionally increase the projection parameters. A short character context also loses earlier material quickly. Jumping from 256 tokens to millions would require addressing memory, attention cost, positional representation, and training stability—not just editing a configuration value.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
From token IDs to next-token scores
The top-level model has two embedding tables: one for token identity and one for position. Their outputs are added, passed through the transformer blocks, normalized, and projected through the language-model head. The head produces logits—unnormalized scores for the vocabulary. During generation those scores become a probability distribution for choosing the next token.
The forward method below expresses that flow. Its input has shape (batch, time), hidden representations have shape (batch, time, width), and logits have shape (batch, time, vocab_size). When targets are supplied, cross-entropy evaluates the prediction at every position; without targets, the method returns scores for inference.
python
import torch
import torch.nn.functional as F
def forward(self, idx, targets=None):
_, time = idx.shape
if time > self.block_size:
raise ValueError("Input exceeds the model context length")
positions = torch.arange(time, device=idx.device)
x = self.token_embedding(idx) + self.position_embedding(positions)
for block in self.blocks:
x = block(x)
logits = self.lm_head(self.final_norm(x))
loss = None
if targets is not None:
loss = F.cross_entropy(
logits.reshape(-1, logits.size(-1)),
targets.reshape(-1),
)
return logits, loss
For the workshop vocabulary, the final dimension contains one score for each of the 65 characters.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Inside a transformer block
Attention learns which earlier representations matter at the current position. The sky–blue example returns here: with character tokens, the model must compose the letters as well as learn the relationship. Multi-head attention projects representations into queries, keys, and values, processes heads separately, then combines their outputs through a projection. Causal masking prevents a training position from seeing the future token it is supposed to predict.
The MLP then transforms the attention-enriched representation. An audience question asks whether all transformer blocks share the same MLP weights. They do not: each block has its own attention, normalization, and MLP parameters, even when every block uses the same architecture. Separate module instances create separate learned transformations at successive depths.
This model uses a pre-normalization arrangement: normalize before attention, add the attention update, normalize again, and add the MLP update. The residual is visible directly in the two additions:
python
from torch import nn
class Block(nn.Module):
def __init__(self, width, heads):
super().__init__()
self.ln1 = nn.LayerNorm(width)
self.attention = nn.MultiheadAttention(
width, heads, batch_first=True
)
self.ln2 = nn.LayerNorm(width)
self.mlp = nn.Sequential(
nn.Linear(width, 4 * width),
nn.GELU(),
nn.Linear(4 * width, width),
)
def forward(self, x):
time = x.size(1)
mask = torch.ones(
time, time, device=x.device, dtype=torch.bool
).triu(1)
normalized = self.ln1(x)
update, _ = self.attention(
normalized, normalized, normalized,
attn_mask=mask, need_weights=False,
)
x = x + update
x = x + self.mlp(self.ln2(x))
return x
The key distinction is x = x + update, rather than replacing x with update. Perivolaropoulos does not recall the historical residual implementation in the particular Karpathy resource being discussed; the connection in this model is explicit.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Where the parameters live
Collect the architecture into model.py. The most reliable parameter count comes from the instantiated model:
python
def parameter_count(model):
return sum(parameter.numel() for parameter in model.parameters())
The workshop describes the default model as approximately 10 million parameters. Most of those parameters live in the repeated transformer blocks, rather than in the small character embedding table.
| Component | Main weight calculation | Approximate count |
|---|---|---|
| Token embeddings | 65 × 384 | 25K |
| Position embeddings | 256 × 384 | 98K |
| Attention, per block | 4 × 384² | 590K |
| MLP, per block | 2 × 384 × 1,536 | 1.18M |
Attention has query, key, value, and output projections. The MLP expands the width from 384 to 1,536 and projects back. Together, attention and MLP account for roughly 1.8 million weights per block; six such blocks explain the roughly 10-million-parameter scale. The spoken description briefly calls 1.8 million the total, but that figure fits one block. Biases, normalization parameters, the output head, and any weight sharing determine the exact model total.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Turn Shakespeare into next-token examples
The training task needs to be both learnable and easy to recognize when it starts working. Shakespeare-like verse meets that requirement better than competitive programming: the progression from random characters to plausible words, names, and dialogue is visible in samples. The objective is next-token prediction. For each input position, the target is the following token.
Take a short teaching sequence, KING. Using a temporary vocabulary G: 0, I: 1, K: 2, N: 3, its IDs are [2, 1, 3, 0]. Shift the sequence to create inputs [2, 1, 3] and targets [1, 3, 0]: predict I after K, N after KI, and G after KIN. The real tokenizer uses the full corpus vocabulary; the shift operation is the same. Causal attention keeps each prediction from seeing later input positions.
One sequence becomes aligned inputs and targets
Constructed example: KING and the four-entry vocabulary are teaching values, not the workshop's actual 65-character ID mapping.
KING → [2, 1, 3, 0]; G: 0, I: 1, K: 2, N: 3
Operation: Keep the source sequence; derive inputs from all but its last token and targets from all but its first token.
Source
KING → [2, 1, 3, 0]
KING → [2, 1, 3, 0]
Inputs
Not present
KIN → [2, 1, 3]
Targets
Not present
ING → [1, 3, 0]
The supplied corpus contains about one million characters. Split it into training and validation portions, then sample contiguous windows without shuffling the characters inside them. Each batch contains 64 sequences of 256 input tokens, with targets shifted one position forward. The intentionally simple loader can be expressed as follows:
python
def get_batch(data, batch_size=64, block_size=256, device="cpu"):
starts = torch.randint(len(data) - block_size, (batch_size,))
offsets = torch.arange(block_size)
positions = starts[:, None] + offsets[None, :]
inputs = data[positions]
targets = data[positions + 1]
return inputs.to(device), targets.to(device)
def choose_device():
if torch.cuda.is_available():
return "cuda"
if torch.backends.mps.is_available():
return "mps"
return "cpu"
Call get_batch on the chosen split’s one-dimensional integer tensor. CUDA and MPS accelerate the computation where available; CPU remains a supported, slower route.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Control updates, then watch held-out loss
The learning rate controls the scale of weight updates. Too large a rate can overshoot useful regions and destabilize training. The workshop uses a 100-step warm-up, followed by cosine decay through a 5,000-step run. Perivolaropoulos prefers a nonzero final rate so training can continue with meaningful updates. Learning-rate decay and weight decay are separate mechanisms: the former changes update size over time; AdamW’s weight decay regularizes weights. A scheduler or explicit function supplies the cosine schedule—AdamW does not create it automatically.
Keep that separation explicit in code. This schedule accepts the peak and final learning rates as arguments:
python
import math
def learning_rate(step, peak, floor, warmup=100, total=5000):
if step < warmup:
return peak * (step + 1) / warmup
progress = min((step - warmup) / (total - warmup), 1.0)
cosine = 0.5 * (1.0 + math.cos(math.pi * progress))
return floor + cosine * (peak - floor)
Initialize the six-layer, six-head model with width 384 and context 256, then initialize its optimizer. tqdm makes the steps and losses visible. Training loss measures fit to sampled training data; validation loss measures predictions on held-out text. If the first keeps falling while the second rises, the model is fitting the training set at the expense of generalization.
Each optimization step updates the learning rate, clears old gradients, computes loss, backpropagates, and applies the optimizer update:
python
def train_step(model, optimizer, inputs, targets, lr):
model.train()
for group in optimizer.param_groups:
group["lr"] = lr
optimizer.zero_grad(set_to_none=True)
_, loss = model(inputs, targets)
loss.backward()
optimizer.step()
return loss.item()
@torch.no_grad()
def validation_loss(model, batches):
was_training = model.training
model.eval()
losses = [model(x, y)[1].item() for x, y in batches]
model.train(was_training)
return sum(losses) / len(losses)
The workshop saves checkpoints every 1,000 steps and generates samples along the way. For a resumable implementation, preserve optimizer and step state alongside model weights; also retain the configuration and vocabulary needed to reconstruct the model. Samples provide a qualitative view that a single loss value cannot.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
When random characters become recognizable text
A uniform predictor over 65 characters has cross-entropy ln(65) ≈ 4.17 nats per token. That gives the randomly initialized model a useful reference point: before learning, it has little basis for favoring the correct character. As training progresses, Perivolaropoulos describes the following approximate milestones for this Shakespeare exercise.
| Approximate loss | Perivolaropoulos’s interpretation |
|---|---|
| 3.3 | Character frequencies emerge |
| 2.5 | Fragments such as TH and in improve |
| 1.5–2.0 | Recognizable words appear |
| 1.0–1.2 | Names and more plausible Shakespeare-like text |
| Below 1.0 | Overfitting becomes a concern in this dataset |
These are task-specific observations for interpreting samples, not stopping thresholds to transfer to a different tokenizer or corpus. The held-out trajectory matters more than crossing a particular number.
In Perivolaropoulos’s test, 200 steps produced nonsense with validation loss around 3.5; output improved around 800 steps and again around 1,000. He reports the best point at roughly 2,400 steps, after which validation loss rose and output became less creative. Validation loss is a cheap signal for this exercise; a more serious training effort should also measure task performance. The immediate workshop milestone is still to assemble the pieces and get a first working run.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Sample a continuation
Generation repeatedly runs the model and appends one chosen token. Greedy decoding always chooses the highest-scoring token: in Perivolaropoulos’s example, a token with 80% probability wins over one with 15% every time. He prefers sampling for creative verse and contrasts that with transcription, where unexpected creativity is undesirable. These are task preferences; greedy decoding remains a valid choice when deterministic selection is wanted.
Temperature changes the sharpness of the distribution before sampling. Lower positive values concentrate probability on higher-scoring tokens; higher values spread it more broadly. Sampling can produce variety, but also repetition or unwanted stopping tokens in models that have them. Perivolaropoulos suggests 0.7 as a starting point. Top-k further restricts sampling to the highest-scoring candidates: his example keeps five plausible tokens and excludes an unlikely sixth.
The order is logits, temperature scaling, optional candidate filtering, softmax, then sampling. Only the final position’s logits choose the next token. Keep the input within the trained context window as the output grows:
python
@torch.no_grad()
def generate(model, ids, new_tokens, block_size,
temperature=0.7, top_k=None):
if temperature <= 0:
raise ValueError("Use a positive temperature for sampling")
model.eval()
for _ in range(new_tokens):
logits, _ = model(ids[:, -block_size:])
scores = logits[:, -1, :] / temperature
if top_k is not None:
if top_k < 1:
raise ValueError("top_k must be positive")
k = min(top_k, scores.size(-1))
values, indices = torch.topk(scores, k)
filtered = torch.full_like(scores, float("-inf"))
scores = filtered.scatter(1, indices, values)
probabilities = torch.softmax(scores, dim=-1)
next_id = torch.multinomial(probabilities, num_samples=1)
ids = torch.cat((ids, next_id), dim=1)
return ids
A fixed random seed makes it possible to repeat the sampling sequence in a controlled setup, which becomes useful for the workshop’s verse competition.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Assemble the files and inspect the curves
The project resolves into three files: model.py for the architecture, train.py for data loading and optimization, and generate.py for inference. A few hundred lines can expose the essential training machinery, although stronger capabilities still require appropriate data and compute. Perivolaropoulos recalls the concern surrounding GPT-2’s original release as a reminder of how accessible the underlying architecture has become.
For the Colab path:
- Download the Shakespeare dataset using the companion instructions.
- Install the dependencies.
- Assemble and connect the model, training, and generation code.
- Select a GPU runtime and invoke training with the dataset.
Perivolaropoulos reports about 15 minutes on Google Colab before obtaining good results. He specifically instructs participants to select a T4 GPU runtime; available runtimes and allocation depend on Colab access. The demonstration is a training-and-generation workflow, not a production-serving deployment.
If the default model is too demanding, start with two layers, two heads, and a reduced embedding width, then grow from a working baseline. Compare a 256-token context with 512 and plot training and validation losses with matplotlib.pyplot.
| Curve behavior | What to investigate |
|---|---|
| Training loss stays flat | Code defects or an overly small learning rate |
| Training falls, validation rises | Overfitting |
| Unexpected sharp spikes | Data, numerical stability, or training defects |
| Both curves plateau | Data limits, model capacity, or optimization |
These symptoms direct investigation rather than uniquely identifying a cause. In particular, a plateau does not by itself prove that the dataset is exhausted.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Make the verse reproducible
The workshop challenge is to train a model during the session and submit its best verse. Another poetry dataset is allowed; asking ChatGPT to write the submission is not. Participants can regenerate as often as they like, but must retain the checkpoint and generation settings that reproduce the selected output. A QR submission route and possible bracket vote let the room judge creative, funny, or well-made verses, with tentative ElevenLabs swag or credits as prizes.
The reproducibility record consists of the checkpoint, prompt, temperature, and seed used by generate.py, together with any other sampling settings such as top-k. That record connects the displayed poem to the model that produced it. More ambitious experiments include a larger model—Perivolaropoulos considers an 85-million-parameter option probably too large for the exercise—a corpus-trained BPE tokenizer, a longer context, dropout, different learning rates, and earlier stopping.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
What changes when a model learns to reason?
The closing questions move from verse generation to reasoning. Perivolaropoulos describes a shared architectural foundation: start with a capable base or instruction model, then post-train it with appropriate objectives and high-quality reasoning data. His emphasis is on curated examples, including expert-written solutions, rather than arbitrary internet text. The useful mechanism is that intermediate reasoning becomes additional context. Later answer tokens can attend to a problem restatement, a calculation, or a conclusion already written into the sequence.
Qwen3 is his example of post-training on a common model foundation. Curated reasoning examples are part of that story, but not the whole training recipe: Qwen’s published account includes long-chain-of-thought cold start, reasoning reinforcement learning, thinking-mode fusion, and general reinforcement learning. Perivolaropoulos also connects rapid capability gains to task-focused data, sometimes resembling benchmark tasks; that explanation does not isolate the cause of any particular proprietary model’s improvement.
Long reasoning traces increase the context burden, making attention efficiency relevant again. Perivolaropoulos argues that a GPT-2-style architecture could learn reasoning behavior with sufficient capacity and suitable training, while the tiny workshop model may gain little from it. He cites small Llama models as examples of adaptation without architectural replacement, but does not identify a specific experiment. The actionable distinction is between changing what a model learns and changing the machinery that makes that learning computationally feasible.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Expert labels and controlled randomness
Where does high-quality post-training data come from? Perivolaropoulos describes specialist contractors recruited through companies such as Scale AI or through internal labeling teams. Physicists are his example: domain experts produce material that general-purpose annotators may not be equipped to write. His aside about Meta buying Scale is better described by Scale’s announcement: Meta took a minority equity stake, and Scale remained independent.
Producing labels is only the first step. Small defects in valuable training data can affect model quality, so the workflow also needs review. Perivolaropoulos describes experienced labelers moving into QA roles and checking other contributors’ outputs. Organizing this human production-and-review process is part of his own work; an LLM judge is not presented as a substitute for those domain reviewers.
The discussion then returns to seeds. Sampling consumes pseudorandom numbers; setting a seed fixes the generator’s starting state. It does not make every GPU operation deterministic. PyTorch’s reproducibility guidance distinguishes controlling random generators from controlling nondeterministic operations, and warns that identical seeds do not guarantee identical results across releases, platforms, or CPU/GPU execution. For the competition, keep the checkpoint, prompt, sampling settings, software, and hardware environment fixed as far as practical.
Greedy decoding removes the random token draw, although execution details can still affect results. Perivolaropoulos recommends retaining a seed even for nominally greedy generation and mentions an unspecified vLLM behavior. Current vLLM sampling documentation explicitly defines temperature zero as greedy sampling; its handling of small positive temperatures is separate. A seed and greedy selection solve different parts of the repeatability problem.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Audio changes the representation and objective
Moving from text to audio preserves many transformer ideas but complicates representation. Text can teach language structure within an audio-model stack. Audio representation must additionally decide what to preserve: speech, speaker characteristics, music, instrument sounds, or some combination. Generated audio tokens also need a decoding process that turns them into waveforms.
An audience member points out that pitch and sound are less cleanly discrete than written characters. That leads to a discussion of objectives:
- Cross-entropy fits discrete prediction targets, including audio-token systems that use them.
- L2 loss can compare predicted and target mel-spectrogram representations, the TTS example Perivolaropoulos gives.
- KL divergence can train a smaller student to match a larger teacher’s output distribution during distillation.
The representation and supervision determine the loss. These objectives are not mutually exclusive: distillation or post-training can combine losses, and cross-entropy does not become invalid merely because the task involves audio or post-training.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
The transformer can receive vectors from another encoder
A multimodal model need not build one giant dictionary containing words, sounds, and pixels. The text embedding lookup is one way to obtain vectors; another network can produce vectors too. Perivolaropoulos sketches a video pipeline using a 30-second clip and one frame per second as an illustrative sampling choice:
- Select frames from the video.
- Pass those frames through a video encoder.
- Take its hidden representations.
- Supply appropriately dimensioned representations to the language model alongside the prompt.
This describes an architectural pattern, not a video implementation added to the workshop model.
In his sequence-level explanation, a video placeholder marks where encoder output replaces ordinary token embeddings. Audio can enter through an analogous encoder path. The receiving transformer requires compatible vector dimensions; its attention layers can then process these representations alongside text. Compatible shape does not establish compatible meaning. When asked whether a horse video’s representation resembles the embedding for the word horse, Perivolaropoulos leaves the question open: the relationship depends on how the system was trained.
Suggest correction
This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.
Learning a representation for speech and music
Music raises another modeling question: do harmonics and interdependent musical structure favor generating many elements jointly? Perivolaropoulos describes both autoregressive transformers and diffusion models as viable. In an autoregressive system, the representation must make next-token prediction useful for musical structure. He considers diffusion components easier to make work in some audio, music, and image-generation settings, but presents no controlled comparison between the approaches.
The final technical question returns to tokenization: how can audio tokens accommodate different voices, and how long should a token be? The answer is a learned representation, not a person manually assigning each spoken word a code. Perivolaropoulos describes learning recurring patterns from audio data, potentially after converting waveforms into mel-spectrogram arrays. The training distribution matters: a tokenizer learned from music can preserve different structure from one learned from human speech. Covering both well makes the design harder.
The session ends by returning to the small model participants can actually train. Continue the local or Colab run, inspect its samples, and ask for help where the pieces do not connect. The verse competition remains conditional on enough submissions, with a cutoff set to 5:45 and Perivolaropoulos available to troubleshoot. No completed vote or winning model is shown.
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
The speaker’s workshop materials guide tokenizer, transformer, training-loop and generation implementation for a small Shakespeare model.
Karpathy’s compact PyTorch GPT training repository includes a character-level Shakespeare example and larger GPT-2 reproduction workflows.
ElevenLabs’ launch account of its batch transcription model, including its benchmark claim and distinction from the Realtime model.
Qwen’s account of hybrid thinking behavior and its four-stage post-training pipeline, including reinforcement learning.
Further reading
- Gemini 3.1 Pro Model CardDocumentation
Google’s February 2026 model card reports benchmark results, thinking settings, model dependencies and limitations.
Guidance on seeding random generators, controlling nondeterministic operations and understanding reproducibility limits.
Updates since the talk
Current definitions of temperature, greedy sampling, top-k, seeds and stopping controls.
Read the complete timestamped transcript
- 0:00
[upbeat music] Thank you very much for joining for this workshop.
- 0:17
Uh, this is gonna be a hands-on workshop, so hopefully you can get your hands dirty doing some very interesting, uh, very interesting project. Um, to start with, a little bit about myself.
- 0:29
Uh, my name is Angelos. I lead the speech-to-text team, uh, at ElevenLabs. I'm a research engineer, so I spend the most of my time training new models, uh, working on inference, and also I'm on the product side, so I'm, I'm responsible for, unfortunately, talking to clients sometimes, uh, which I don't fully enjoy.
- 0:48
I'm the kind of person that really likes to go straight into research and do, uh, train models and make things that are state-of-the-art and very powerful. Uh, currently I'm working on, uh, training real-time models for transcription, uh, specifically for agents.
- 1:01
And if you have-- if you don't know already, we have the Scribe v2 model that my team trained. It's currently the best, uh, transcription model in the market in terms of like popular public benchmarks.
- 1:12
Uh, so if you have a need any transcription, uh, use cases, feel free to use it. It's, uh... I think it's quite good. Uh, now, as part of the workshop itself, uh, today we're going to be training an LLM from scratch.
- 1:25
So no pre-trained weights, no, uh, nothing that you can just grab online from like a transformers library. We're going to work purely on Torch and some, like, very basic libraries.
- 1:36
We could go on level below and, and not use Torch, but I don't want to, to torture you that much. Uh, so I think Torch is like a good level.
- 1:43
And this will be like a good indication of how like actual like, uh, research engineers like in, in big labs like design their models and
- 1:52
whatever is further than that is more like optimizations and making the scale be better and bigger and improving for specific use cases. So this, what you're gonna be doing today is like pretty much like eighty percent of, of the way there to, to create like a model from scratch.
- 2:07
Um, if you can go on this, uh, QR code, uh, you're going to find this, uh, GitHub repo. Uh, I'm going to switch to that now. We'll leave it for a little bit longer.
- 2:18
Uh, there's-- you have two options. Uh, one is to either train the model locally on your laptop. Like, if you have like a sixteen gigabytes of memory, you should be able to do it.
- 2:29
It's a small, very tiny model that you can train fast. Uh, if not, and I'm guessing because not many people have outlets, maybe a Google Colab would be a- another option.
- 2:38
Uh, Google Colab gives you free GPUs that you can use for, for training. It's a small model. So either, it will be your choice. Uh, while... I'm gonna leave this on for a little bit longer,
- 2:53
and then we can go in the actual repo itself.
- 3:03
Um, so to give you some idea of the inspiration of this, um, uh, o-o-of this project, uh, my first exposure to, to transformers and LLMs in general was through this video from, from Andrej Karpathy, who was one of the co-founders of Com-- of, uh, OpenAI, uh, which was called nanoGPT.
- 3:23
And for me that was like a very inspirational project. It's essentially like what inspires like this, this workshop as well. Uh, it's a bit lower level than what we're gonna be doing now, so it goes a bit deeper, like into how you're using NumPy for like a lot of ca-calculations.
- 3:38
But I think it's a good, uh, blueprint that we can follow for, for us to be able to create a model from scratch.
- 3:45
Um, let me move this to the screen.
- 3:54
But yeah, if you, if you don't, if you, if you, if you don't know this project, uh, I think it's, that's, it's a great introduction. If you do know this project and you've do- trained LLMs like that before, uh, this might be like sim-- a bit simple for you, but I think the, there are ways we can
- 4:09
expand this workshop to-towards, uh, doing something that's a little more of a competition, which I would see or like to see what you can come up with. For this specific, um, for this specific workshop, we're gonna work with a very small model that's based on the GP-2-- GPT-2 architecture.
- 4:25
Uh, it's a bit of an older architecture, but the, the, the base and the, the fundamental parts of it are basically haven't changed too much. Uh, and we're gonna go over, over those in, in a bit.
- 4:36
Uh, the, the four building blocks you need to, to train a model is first one is gonna be the tokenizer. Uh,
- 4:44
and depending on what your use case is, you'd wanna use a different tokenizer for that specific use case. If you want to train, for example, a very big model that can, that you want, you want to generate text for like multiple languages, you'll need a huge tokenizer, which means you're gonna need a huge amount of data as
- 4:58
well to train it. But for a s- for a smaller model, smaller tokenizer with, with, with, uh, smaller embeddings is what would work best and train the fastest when you're da- when you're data limited, which is what we are right now.
- 5:12
Uh, next would be a model architecture. Uh, to be honest, like most models like have at least at, at the, at the period that we're gonna be working in, were very similar.
- 5:21
Like in, in they were just decoder-only, co-causal decoder-only models that had a very similar, uh, way of, of using causal self-attention and the same like MLP layers and the same like layer norms a-and all kind of stuff, which we'll, we'll get, we'll get to it.
- 5:40
Um, so i-if you know how to do this, these like small models, it's very easy to go and do the same process for like bigger, like newer models. But of course, the newer models are going to be way more specialized for longer context and essentially, essentially being able to scale this train.
- 5:56
They're, they're architected in a way to be able to scale training to as many tokens as possible, which we won't need in this case. And lastly, it's a, it's a training loop, which this is generally the most important part when you're training a new model.
- 6:09
Like if you check the difference between like GPT-4, GPT-4o, and GPT 5, or even, even if you go before that,
- 6:18
what you'll see mostly is that usually the pre-training is very similar. It's the fine-tuning and post-training, and essentially what you use with the, with the same base model or like very similar base model, and the way you train it that actually makes the big difference in performances.
- 6:32
And now we see, for example, uh, Gemini III comes out and then has this, this, this many, this good benchmarks, and then three point one comes out that has like double the performance in some benchmarks, which is crazy.
- 6:43
Like, obviously, it's very sim-- the models are very similar, but actually during training, they train, they, they train the new model in smarter ways to improve performance like very substantially.
- 6:53
Now as for the... And, and lastly, of course, the inference part, like that's gonna be very easy for us, like because we're just-- it's gonna be a small model that can run everywhere.
- 7:01
Uh, so like that, that's gonna be very simple, uh, part of the, the building blocks.
- 7:08
Uh, then the prerequisites, any laptop would do, uh, that has at least six-- sixteen gigabytes of RAM. You can work with, with, with smaller laptops as well. It'll just be a bit slower.
- 7:17
Bigger laptops, you can crank up the batch sizes higher, so it will train faster.
- 7:22
Uh, Python th-- three point twelve, and I expect that, I expect that most of you have like some idea of how to write Python. Uh, like if you don't, I think it's still quite...
- 7:32
You can just copy-paste things until something works or like ask for some help. Uh, this training can-- uses, uh, Apple silicon, so the, the MPS architec-- the MPS architecture, CUDA or CPU, so you can bas-basically support everything.
- 7:47
Um, and as for getting started, uh, this, just to make things easier, like I'm using UV for this project. Uh, so if you have your laptop out, you can install UV on your machine if you haven't.
- 7:59
Uh, it's, it's quite straightforward, and there's no-- UV is quite simple. You can just run UV sync, and it creates a virtual environment for you and makes your life easier.
- 8:07
And we're gonna write the code in, uh, Scratchpad, or if you're using Google Colab, which depends if your internet is not very good, maybe that's a better idea. Uh, if you just go to,
- 8:22
um... If you just go to and create a new, um... Let me actually create it on a separate one, so you can, you can follow along.
- 8:41
Yeah. So if you open Google Colab, and you create a new, um, Colab project, you can run this command,
- 8:50
which just installs the, what we need, which is just Torch, NumPy, TQDM, and Tiktoken. Tiktoken is mostly for, uh, for testing things out.
- 9:05
And yeah, we're gonna move on to the first part of this, um,
- 9:10
uh, of this workshop, which is gonna be the tokenizer. As I mentioned, like this is generally the first thing you think about when you create a new, a new, any new transformer model, is what tokenizer you're using.
- 9:21
Uh, I come from the voice world, so this is where that-- that's the one of the most important things. Like we're thinking, "Okay, we need to train this new TTS model, and we're gonna spend maybe six months thinking about the tokenizer, and then we're gonna spend two months on the architecture."
- 9:35
So that's, that's like generally one of the most important things of deciding how to create a new transformer. For those who don't know what a tokenizer is, like LLMs don't see text.
- 9:44
They, they work with embeddings or like vectors. Uh, so we need like some kind of representation of those vectors for the model to be able to process.
- 9:54
Uh, what we're gonna be using here is a character-level alignment, uh, a character-level tokenization, sorry, just because it's, it has the lowest number of possible tokens. Um, in the, in our case, for on our, uh, dataset, it's gonna be only sixty-five embeddings essentially 'cause it's se-- sixty-five different characters that will appear in the, in our training data.
- 10:17
And the way it works, uh, like this, it's gonna be using the Shakespeare dataset. There's a few works of Shakespeare. It's part of the, um, it, it, it's part of the repo itself, but you can-- if you're using,
- 10:30
um, if you're using Colab, there's gonna be a link on how to download it on Colab a, a little bit later.
- 10:37
And essentially, we're gonna use this, this story library, which basically just converts, uh,
- 10:44
we-we'll be converting strings to integers, and that these integers will then be turned to embeddings through the embedding layer when we train the, the LLM. And it's a very, very simple, straightforward tokenizer.
- 10:54
It uses this enumerate, uh, function from, from, uh, from Python and just then selects that specific item that has been selected with, uh, uh, this dictionary.
- 11:08
So yeah, as I said before, like we use, we use character level because it's much easier to train. Uh, because we have sixty-five, sixty-five, uh, tokens only, it means that we-- the bigram combinations will be like sixty-five times sixty-five, so four thousand two hundred and twenty-five possible bigrams.
- 11:28
Bigrams are essentially like when you have one token, and then you predict the next token after it. And this concept of, of, of bigram is very, is a very important concept when, when you're training transformers 'cause you want your model to see as many possible bigrams as possible.
- 11:43
So if you have a, a model with, let's say, two hundred thousand tokens, you need two hundred thousand tokens squared at least data to be able to, to train like a, uh, from scratch in a, in a, in a, in a very good way.
- 11:56
Uh, so the-- or, or at least, like this is the magnitude that you're looking for. In our case, four thousand bigrams is like very doable. Like this, this dataset should include it.
- 12:07
All bigrams like multiple times very likely. Um, if we did try to train using a full tokenizer, this will never converge. We can just be training it for hours and hours, and then our model will never be able to, to get good results.
- 12:22
Now, the problem with, with character-level, uh, tokenizers is that they don't really scale very well. Um, because the models, they, they-- the way they work is that they need to understand correlation between different tokens.
- 12:35
Um, so you can very easily have a correlation saying, "The sky is blue." Like, these tokens combined together, like, make a lot of sense. But S-K-Y, and then I-S, and then B-L-U-E, that it's a bit harder for them, for the model to be able to make, like, good, uh, uh...
- 12:56
To attend to these tokens in, in, in a good way. Uh, so this will, this will work quite well for our example. But if you want, like, a very, very good model, uh, one, it will be expensive to train because, of course, like, you have to create, like, a ton of tokens when-- during inference and during training.
- 13:14
Uh, but also, it will just never converge to something good 'cause the model... The, the, the tokens combined don't make too much sense. Um, so that's our trade-off, but it's a trade-off that we're willing to take because, of course, we're running a, a, a small model.
- 13:27
But in the future, if you want to expand this to something better, if you want to train, like, a proper LLM, and you're happy to train for, like, a week or using bigger GPUs, uh, using a proper, uh, tokenizer, that's, uh, used, like, byte pair encoding is, like, the most common, uh, way of, like, doing tokenizers these
- 13:44
days, which essentially takes all... You can-- The way you train a BP tokenizer is that you take all your training data, and you'd find all common patterns and combine those common, uh, th-those common, like, character patterns into specific tokens that then you can reuse, and the model can understand relationships of.
- 14:05
So the, the way that this connects to the model itself is we have this embedding table, which is size, vocab size, and then it's what I said before. It takes, uh, a vector of integers, and then it returns...
- 14:19
Uh, sorry. A, a list of inters-integers and then it returns a list of vectors, which is going to be our embeddings.
- 14:27
Uh, and like, like I said before, like, that's... I-if we did use, uh, such a big, such a big tokenizer, that would also be, like, more than twice the size of our models.
- 14:40
'Cause if you just multiply this, our, uh, embedding size is three hundred and eighty-four for this model we're gonna be training. So that's already like twenty-five thousand parameters, and in that case, like, if we used G- uh, GPT-2's vocab, uh, which is fifty thousand, that would be nineteen million parameters, which is, like, more than three times the
- 15:01
model, which wouldn't make sense in our case.
- 15:04
Uh, so now moving on to the next part of this workshop, which is gonna be the transformer itself. And as I, as I mentioned before, like, the transformers, they have been kind of commoditized now.
- 15:14
Uh, they are-- The way the transformers work, like, there are different labs that find different optimizations. But the optimizations, like, in principle, is more about we have this, like, base, like, idea that works really well.
- 15:27
How can we make it train faster and, like, have bigger context and...
- 15:33
And people find ways that are more optimizations than necess-- than, than necessarily reinventing the wheel. Uh, for this, at least, there are some, of course, hybrid approaches that are more complicated.
- 15:44
Uh, so I, I'm not gonna go too deep on how transformers work. Uh,
- 15:48
and also to prove a point that you don't necessarily need to know in a very deep level how transformers work to, to be able to train something like this.
- 15:55
Uh, when I first, like, did this project myself, I didn't... I had no clue how transformers worked, and I still didn't that much at the end of the pro-- the worksh-- the previous project I worked on.
- 16:05
But it-- Once-- The more you work on it and the more you, like, you have motivation to continue pushing through, you can understand all the different concepts together and how they, they, they stack together and the reasons why they ended up the way they are.
- 16:18
Um, to go back to the big picture, uh, transformers are,
- 16:24
are u- are using, like, these four different building blocks. Uh, one is multi-head self-attention. Uh, attention is, like, what... The difference between-- that makes transformers different than other neural networks is that they can actually attend to previous tokens and understand the relationship be-between tokens that I was, I was mentioning before, and that's where a-attention comes in.
- 16:47
And of course, the bigger your attention is, the more the b-model understands tho-those relationships. And going back to what I said, that's what, like, big labs like Gemini, they're trying to do one million contexts, and they're finding ways.
- 16:58
Because if you just try to use a one million context for, for a model like this, it will just break very easily. Like, the, the math wouldn't work. So then that's when, like, the engineers from-- the researchers from Gemini found ways to, to make it work, and that's what...
- 17:13
That, that makes a difference. But again, fundamentally, it's, like, the same, the same architecture. Uh, next one is the, is the MLP, uh, or, like, the feedforward network, uh, which essentially takes these
- 17:24
different, uh, um, uh, relationships between those tokens and
- 17:30
while taking them... W-while the, the, the relationship themselves are, um, arranged all o- The relationship between the tokens themselves, it, it combines them together to, to be able to generate the logits that will then be, uh...
- 17:44
So it's essentially kind of a... It takes the context and then organizes it in ways that the model can then generate the logits and then generate the tokens. I, I hope that was a decent explanation.
- 17:55
And then you have the residual connections, which are basically there for the model to,
- 18:01
to not have to reinvent itself after every layer. Uh, re-residual basically means that every layer that pa- that passes through the, um... 'Cause as, as, as we'll talk about later, transformer is m- is m- is, is built on multiple different layers that we pass through the activations one after the other.
- 18:19
And, uh, residuals are there so that each activation doesn't completely, uh, restart things from scratch. It just changes them slightly. So it just takes the previous input and makes a small-- another small difference.
- 18:32
The next layer does the same, the next layer does the same, and this will continue on. And this way the model doesn't--
- 18:38
th-each layer doesn't make a huge change on the inputs themselves, uh, and the model can be more stable during training. And lastly with the ra-layer normalization has a very similar, uh, has, has a very similar role in, uh, being able to...
- 18:56
The, the, the layer normalization is able-- is, is allowing you to scale down those activations in ways that,
- 19:05
uh, that allows that-- those activations to not be, to not be exploding into very big values. So if one layer multiplies your activation by ten x, let's say, the layer norm is gonna push it ba-back to like normal values, so it doesn't go ten x, ten x, ten x, and then you can have like t-- millions of,
- 19:22
uh, values that start at like zero point five and then end at ten million. That's what the layer norm is for. Uh, but again, these are just the building blocks.
- 19:31
You don't necessarily need to know like why they're there and what the, what the purpose of them is. Like, you, you learn this as you start working on these models more and understand, um, how those dif-- why, why these decisions were made.
- 19:43
'Cause all of them, uh, uh, as, like, as I'm talking to you, of course, were done by-- We have a certain idea. All this didn't work. So let's add this to make it work.
- 19:52
Sorry, go ahead.
- 19:53
Are you showing some slides that we aren't seeing?
- 19:56
Oh, oops. Yeah, I was showing.
- 19:58
No worries.
- 20:01
Yeah, apologies for that. Yes. So, um, I was, I was going through the tokenization. Uh, that was the previous, uh, what, what, uh, my, my previous point. Uh, and that's, uh, if you, if you can go back and, uh...
- 20:19
You'll have to go back through and forth through the slides when you're working on your-- the model yourself 'cause you can-- you have to like copy-paste the par-- the, the parts or figure them out yourself.
- 20:28
But it's-- it was explaining why we're going with character-level tokenization and, um, and what other options we have. Then for transformer, I was explaining the different in the big picture, the different blocks of, of architecture.
- 20:44
Um, and then now going to how does this look like in code? Uh, 'cause all the things I described to you actually are very co-- there, there's very little code you need to, to act-- to implement them.
- 20:56
Uh, first we start with the basis of... Sorry, go ahead.
- 21:01
Um, when it comes to code as in Python, and because at least in English, you know, you have words, vocabulary is kind of fixed. But in Python, you have your own variables and so on.
- 21:14
So how does tokenization work in that setting? What could be at work? Sorry.
- 21:22
So in a programming language, uh, of course, you have programming syntax, uh, keywords, but then you have also normal variables, function names, and so on. So when it comes to tokenization, how does that work?
- 21:36
So yeah. A-as I mentioned to you, to-- before, uh,
- 21:40
we're gonna be using like a character-level tokenizer because it's gonna be easier for this project. But most big labs, they don't use character-level tokenization. They, they use what I was mentioning earlier, the BPE tokenizer or like, uh, byte push, um, uh...
- 21:52
Essentially they, they-- what they do is that they-- you u-u-- you look at your training data. Let's say you have like this many trillions of tokens, and your training data is going to be including like code, as you mentioned yourself.
- 22:03
Uh, and the way it works, it will just look at common patterns. Uh, so if you have like a lot of training data that is, uh, that is code itself, it will, it will see, and then it will realize, okay, for loops seem to be like a good candidate for, for that to be a token.
- 22:17
So the for is gonna be for sure a token. And then you might have some like enumerate. Enumerate you f-- do you see quite commonly in the, um, i-in the training data, so that will be another token.
- 22:29
So it will look at all these different tokens and then create this tokenizer based on the common, uh, relationship b-between them. Uh, of course, like maybe some languages that are like maybe Pascal that might not be very common in the language, so this-- the keywords of Pascal might not be like very common.
- 22:44
Although I do think that there probably is like good representation in the tokenizers too. But some o-- other of those like crazy like white space only like languages that these, these ones probably are not going to be work very well. [laughs]
- 22:56
My, my question was more on the... So one thing is the keywords of the programming language, but then you have your own variable names, which are not common among different programs.
- 23:06
Ah. I see what you're saying.
- 23:08
So making them as tokens, I'm not sure how does that help.
- 23:11
So a-again, like, there, there's no, there's no specific way. There's no like human in the loop in this process. It depends on your training data that you use to train this tokenizer.
- 23:20
Uh, if your code, like there's like foo and bar, like that's like common variables, these probably are gonna be tokens. Like if your, your to-- if your variable names are like very strange, like random characters, then yeah, probably that's not gonna be in the tokenizer.
- 23:34
And when this happens, uh, the tokenizer will fall back to character level, uh, or like, uh, b-bi- uh, bytecode level tokenization. So if your, if your token is like just random like gibbles, it would just be each character or like some-- maybe some combinations might be together, uh, as different tokens, but most of them are gonna be
- 23:53
separate tokens, and that's gonna be a bit of a pain to-- for doing inference and, uh, yeah, maybe not, not, not the best if you want, uh, efficient inference.
- 24:04
Thank you.
- 24:06
Um, but yeah, uh, going back to, to the transformer side of the things, um,
- 24:12
as I said, the, the models are generally quite, uh, qui-quite similar. Um, and the code for actually implementing these transformers is also Quite simple and, like, easy to write.
- 24:23
It's, like, maybe, maybe a hundred lines. Actually, usually more than that, uh, less than that. Um, and the first thing we have to re- to, to, uh, accept is what the parameters of this transformer should look like.
- 24:35
Uh, as I mentioned before, vocab size is the size of your tokenizer. In our case, it will be sixty-five. Block size is essentially the sequence length or, like, the, the, uh, the context window.
- 24:47
Um, in our case, that will be two hundred fifty-six, which is very, very small for these models. But because we're training a model locally, like, that's kind, kind of what we have to do.
- 24:56
Uh, bigger labs would use, like, one million context size for stuff like that. Uh, but generally, like, sixteen thousand is, like, a common middle ground. Uh, then we have the layers.
- 25:05
How many... As I mentioned before, like, uh, transformers have multiple layers, and then you run activations through each one of those layers. We're gonna go with a modest number of six.
- 25:15
And then the attention heads, like, the, the way that, uh, a-attention works is that you might... You've ha- gonna have different heads for attention that are gonna be attending for different things.
- 25:25
Like, one of the attention heads might be looking at punctuation. Maybe another attention head might be looking at the grammar. Uh, so all these different attention heads are attending to, like, a specific, uh, feature of the text, or if you're using audio, a specific feature of the audio, et cetera.
- 25:41
Uh, and lastly, that's the embedding dimension. Uh, uh, that's how big the actual vectors of those tokens that you, uh, create are. Uh, in our case, we're gonna start with, uh, three eight, three eighty-four.
- 25:52
That's, like, the standard for GPT-2. But a, a bigger value, of course, will have more information per token. A smaller value would, would have less, but that's, like, a pretty standard value we can start with.
- 26:06
Um, as for the code itself, uh, feel free to copy-paste part of this, uh, if you wanna spend more time understanding it. I don't wanna go too deep into, like, all the little details of how things work.
- 26:17
Uh, but essentially, like, you, you generally have, like, this overarching module. In our case, we're gonna call that GPT, and that overarching, uh, top-level module is gonna include all the other modules that I described earlier.
- 26:31
Uh, in this case, th- this will take the config.
- 26:37
Uh, in this case, this, this will take the config that we have, we, we had above, and then we'll create this, uh, using-- We're using torch, uh, module dict here just because it's, like, the easiest way to implement this.
- 26:50
But this is all just math. Like, you can... Everything you see here would just be, like, calculations, like two matrix multiplications that, uh, Torch allows us to abstract and make things easier.
- 27:00
And pretty much all the things here are li- are you see here are, like, neural networks, like, smaller or bigger that are combined together. Uh-
- 27:09
Can I ask a quick question with regards to these parameters?
- 27:12
Yes.
- 27:13
If you could scroll up. So you have, uh, um, the length of the sequence, right?
- 27:19
Yes.
- 27:20
Let's say we have a use case where we have-- we don't have a large sequence, right? So we have however many. Two fifty-six is fine. But this, uh, is very much related to the number of parameters you have in your model.
- 27:32
Um, not necessarily. It, it... Sorry, maybe you can repeat the question with the microphone.
- 27:37
So the, the, the size of the input sequence is related to the number of parameters in your model.
- 27:43
It, it... Not, not necessarily. You can have, like, large sequences with, like, small models or, like, small sequences with, with, uh, with, uh, bigger models. The, the, the main thing that this shows is essentially w- there are two ways you can train a model.
- 27:57
You can train a model, like, that looks at... That's a full attention model, let's say, that looks at the full attention, so it looks at the whole past, uh, essentially, uh, which is what we're building here today.
- 28:08
Or you can have a windowed model. That's where most of the, like, bigger models that are out right now that only look up to, like, this much in the past.
- 28:16
And this parameter is the size of that window that... Okay.
- 28:19
Yes. It, it's what we're gonna be using to train. So the model w- will not have seen anything greater than two hundred fifty-si- fifty-six tokens in a sequence. If you try to go above that, it will kind of, it will spaz out.
- 28:30
It will have issues.
- 28:31
But so let's say we have, like, computational capacity to increase the number of parameters to make the, help the model reason better, right? Which number would you crank up here to say, like, would you increase the number of layers, attention heads or, um...?
- 28:46
I, I would increase all of the, all, all of what you see here, except for maybe the, the embedding, uh, size and maybe dimension. I don't remember exactly what other models do, uh, the exact numbers.
- 28:54
I think that seems reasonable to me. Uh, but everything else, like, it works well for, like, a small token, uh, a small, a small model. Uh, two fifty-six block size is tiny.
- 29:04
Like, that's... If you try to run this on nanoGPT, like, it would just forget things that you wrote like ten sentences before. Uh, but
- 29:12
the, the bigger you make this, the harder it is to do the training. And that's what I was talking about scaling laws, is that you can't just say h- go here and say, "Okay, actually, I don't want two fifty-six.
- 29:22
I want two million context length." You can't train this like that, at least with this current architecture. So that's what, like, uh, uh, like, researchers after, like, GPT 3.5 came out, they were like, "Okay, people are complaining that we only have sixteen K context size.
- 29:37
We want one million. How do we do it?" You can't just change this number. You have to change the architecture to allow training to be able... Otherwise, you're just gonna go, like, out of memory, like, instantly.
- 29:47
Uh, so that's, like, one of the things that researchers now try to, to, to figure out, how we're going to be able to increase these numbers here while keeping training stable.
- 29:56
Um, or how, how do you increase these numbers here while keeping training stable and still able to, to have enough c-compute to be able to do this? I hope this answers your question.
- 30:10
Awesome. Thank you. Um, so yeah, the, the first thing we'd, we'd have to do is, uh, as I mentioned before, like create the, the top-level representation of our model, as you can see here as the GPT class.
- 30:23
Uh, I'm not gonna go again too, too, too much into the details, but like you want, uh, one would be the embed- how the, your model like understands embeddings and also how does it understand embedding position.
- 30:35
Uh, I don't wanna go into details, but essentially you need-- the tokens need to be able to understand both. Uh, then you'd have essentially here all the layers that I was mentioning earlier, and each of them is configured as, as a block, which we're gonna go at the definition of block, uh, late, uh, a little bit later.
- 30:53
But essentially a block is like a layer of the transformer that includes its own attention and, uh, its own like layer norms, and it continues the-- it feeds into each other the, the one you do the forward pass.
- 31:08
And lastly, you have the LM head, and essentially the LM head takes all the outputs of the above and connects them together into what we call like logits, which is the distribution of what the, the next, uh, token should be that should be generated.
- 31:25
Uh, 'cause I, as I mentioned earlier, like may-maybe I did, maybe I didn't, but, uh, the way transformers work is that they predict next token. So you take the previous con-- the previous, uh, context, you predict the next token, and you have to sample this token by on, on, on this distribution.
- 31:41
And the LM head creates this distribution that we, we sample from.
- 31:48
Then the, the, the next important bit that, uh, like is again like quite, quite straightforward, like and is, is quite similar in most of these models, is a forward pass, uh, which essentially allows the model to,
- 32:02
um, to go-- to, to take the input, which is gonna be the tokens that we mentioned, and push them through all these different components that, that, that I, that I mentioned earlier.
- 32:13
Uh, and just does some, um, uh, does, does some, some preprocessing, turns tokens into embeddings and positional embeddings, uh, add, uh, uh, adds them together, then goes through all the blocks of the transformer, all the different layers of the transformer, and just runs the, the forward pass of that transformer.
- 32:34
And then does the linear norms and then passes through the LM head to get the distribution of those logits that we mentioned earlier. Um, and this is if you're doing training.
- 32:47
This would also do cross-entropy loss, um, in this case. And then at the end, you're gonna get logits and loss as the outputs of your, of, uh, of your transformer model.
- 32:59
And I do have some diagrams here that you can see. Uh, like they're quite, they're quite basic, but essentially they show the flow of take token IDs, you pass them to make them into embeddings, token embeddings and positional embeddings.
- 33:11
You add them together, you pass them to the transformer, then through the layer norm that just makes the outputs, uh, in a space that the LM head can comprehend easier.
- 33:22
And then you return this distribution of like what the next token should be that has, that has this, uh, this size. And as we mentioned before, like sixty-five is the size of like your voc-- our vocabular-- or our, our token size.
- 33:37
And now self-attention is like a little bit more complicated. I don't wanna again, again to go too deep of how, how that works. But essentially att-attention is there to, to understand the relationships between the tokens.
- 33:51
Uh, uh, essentially, what, what is the important... Like if I say the sky is blue, blue and sky have a, a very big correlation. Uh, so that's what attention does.
- 34:03
Based on the, uh, how you've trained your weights, you'll understand what tokens should be attending to each other and put higher emphasis on those specific relationships. Now becau-- again, going back to what I was sa-saying before about the, the what-- that's why the tokenizer matters a lot, 'cause sky and blue are very easy to-- uh, very easy
- 34:21
for the model to make this relationship. While in our case, you have to like combine different groups of tokens together, the different characters together. That's quite a bit harder.
- 34:32
But essentially, that's, that's what attention does. It's, it's the what, what this token should be attending to in the past and what has most importance for it.
- 34:43
And it has also a forward pass, as all of these different blocks do. And in your implementation, feel free to just copy-paste these, these blocks. Uh, maybe you can spend some more time on understanding the diagrams and, and how things work.
- 34:56
Uh, but as you can see, even something as complicated as attention has a very small... It's just a few lines of code to, to implement.
- 35:06
And yeah, lastly, the, the MLP block, uh, is again, as I mentioned earlier, uh, al- also like again, I already talked about the multi-head atten-- uh, why attention has multiple heads, 'cause each head attends to different parts of what makes language what it is.
- 35:19
And then the MLP block takes the outputs of attention and, and makes sense of all those different relationships into something that the model can then understand a bit better, um, which again is just essentially just a, a neural network that just combines things into something that's a bit more understandable for the LM head to then be able
- 35:37
to, to generate distribution for logits.
- 35:41
Sorry.
- 35:42
Yes.
- 35:43
Can I interrupt you? Because I just have a question.
- 35:45
Yeah, no, you can interrupt.
- 35:46
Do we have the same MLP in all, uh, individual transformer blocks?
- 35:51
Uh-
- 35:51
Like, is it the same architecture and the, the, the same weights between the different transformer blocks?
- 35:57
No, each, each block has its own weight. Each block, uh, it, it-- the, the way each layer, essentially you'd have each layer that we'll call blocks. Um, they usually have like a different section for, like a different prefix for the weights of what...
- 36:10
Like MLP would be a different, uh, like maybe usually they're called FFN blocks, uh, instead of MLP, but it, it will still be the same. So each block has its own weights.
- 36:20
And, uh, the ML- each, each layer-
- 36:22
The different
- 36:24
Yes, each block has its own MLP. So it's, it's-- you ha-- each block has its own, um, uh, as you can see here, its own attention, its own, uh, linear le- li- linear norms and its own MLP.
- 36:38
That's what basically makes a layer of a, of a transformer. And, and that's what I-- what my point was gonna be later. Everything comes together into this block that we can see here, which is basically a layer of a transformer that has some normalization, running the attention to get the relationship between the models, and then having the
- 36:55
MLP that combines the relationship of the, uh, the... Sorry, the relationship between tokens, and then the MLP that takes those relationships and turns them into like a representation that's easy for the model to, uh, to create the logits.
- 37:10
And, uh, this, uh... Oops. This is what I was showing. Sorry. I, I have two different screens. I shouldn't be doing that.
- 37:22
But yes, this is, this is the transformer block. This is the, the building block essentially of a, of a transformer. Uh,
- 37:28
it has layer norm, the attention, and another layer norm. Like, not all of the models have this, this kind of configuration, but this specific one has this. And then the MLP that takes everything and creates it into a representation that makes sense for us to be able to make the generations.
- 37:44
And, uh, you can see here like a bit more of a, like a simple diagram of how this, this works.
- 37:52
Yes?
- 37:52
Did the original, uh, nanoGPT have the residual connection as well? Do you remember?
- 37:58
Uh, so the residual should be there. Um.
- 38:02
No, no. I mean on the, on Andre K- Andre Karpathy's nanoGPT. Did, did it have the-
- 38:08
Uh, I don't, I don't remember. Uh-
- 38:10
Okay
- 38:10
... it's been a while since I-
- 38:12
Okay
- 38:12
... like I ki- I, I had the idea but from there, but I built everything from scratch. So I don't, I don't really remember if it did or not.
- 38:20
Uh, but the residual, as you can-- the, what-- the idea of residual, uh, would be, as you can see here, instead of doing X equals the attention, you do X equals X plus attention.
- 38:32
So you, you get the difference. The, the, the values of the activations don't change that much. Uh, that's the idea of like doing residuals. Um,
- 38:44
uh, and yeah. So we have this, like if you wanted to implement this yourself, you can copy and paste all these different classes into one file that you can call model.py.
- 38:54
Um, and this is essentially all the maths of how the transformers works. Um, and as I mentioned before, then we have to decide how big the transformer is. Like the parameter count that you can see here, uh, is just the ten million parameter based on what I was showing above.
- 39:10
Uh, you can just find it yourself by just summing all the, all the different parameters of all the different, uh, parts of the different blocks that we had above.
- 39:19
Um, the token embeddings is sixty-five times three hundred and eighty-four. We said this is the vector for its embedding, so it's twenty-five K. The positional embeddings is two hundred and fifty-six.
- 39:28
Two hundred and fifty-six, remember, is the sequence length, the max sequence length of the model. So that's another ninety-eight K parameters. And then where the, the biggest part of the logic is, is in the actual transformer blocks themselves where most of the parameters live.
- 39:44
So you have, um, f- you have four X. Uh, so the, the four there comes of the way attention works, where you have key value, uh, key, q- query key and value pair.
- 39:56
So it's, it's part of the potential has like four different parameters for, for, um,
- 40:02
for three hundred and eighty-four, which is the amount of, of tokens, times the relationships between those tokens. So two hundred and eighty-four per vector times three hundred and eighty-four.
- 40:12
So that's five hundred and ninety that we have for the, for the attention per layer. And then for the MLP, you have again the similar logic of, um, don't remember what this one thousand five hundred and thirty-six is.
- 40:24
Uh, but, uh, yeah, in the end it turns out into being one point two million. So total amount of parameter size we're gonna be training is gonna be one point eight million parameters, which should be good for-- which should be easy to train for like most devices.
- 40:38
Uh, so yeah, you can go back to this. It has a lot of details that, that makes, um, uh, that, that will, that will make sense for you to go back and understand a bit deeper and do your own research on, on this.
- 40:49
But this is like the general like very, very high level idea of how these transformers work.
- 40:54
And, uh, we're gonna go now to the, the training loop,
- 41:00
and that's where most of the meat of this project is going to be. Um, where we have this, this transformer, uh, how we're going to train it and do what we want to do.
- 41:10
The objective of this training we're gonna be working today is something that one has to be very easily recognizable. Uh, like you know when the model starts working and, and groks, which means that it, it understood what it's trying to do.
- 41:24
It's gonna be very easy to understand that the model actually works now. Uh, and secondly, it should be something that's like quite easy for the model to learn. Uh, if you teach the model how to, to write, uh, Python or like a competitive programming for you, then yeah, that's a, that's, that's a, that's a very hard task,
- 41:40
so we're not gonna be able to do it here. But in, in our case, like the, the objective is going to be to create a Shakespearean-like LLM that can create, uh,
- 41:51
verses from, from Shakespeare. Um, and as we mentioned before, the, these models like are, are learning as next token prediction. So
- 42:02
the, the way that cross entropy works is that you take your current, the current tokens that you want to train. In this case, let's say that it's T zero, T one to Tn.
- 42:12
And then you want it to predict T one, T two to T plus one, T, Tn plus one. Uh, so the way this cross entropy works is that you take your sequence and you just offset it by one as like what the model needs to learn what to calculate.
- 42:26
And of course, the last one you don't have it, so you have to, like, cut it off. Uh, and then the model learns how to predict the next token based on, based on this logic.
- 42:36
Uh, now for the actual training code itself.
- 42:40
Um, first we need to load the data, and we have this function that, that loads the data. The data is already in the, uh, repo itself. It's in this da-data dir-directory.
- 42:52
It's a, it's a collection of different, um,
- 42:56
different, uh, like lines and verses from, from Shakespeare. It's, it's about one million tokens, so like a million characters.
- 43:07
And we first load the data. The data loading is, is, is, is very simple. Too simple. So that's one of the things that I think you can optimize. Um, essentially just takes all the tokens, uh, splits it into a, a validation and a, and a training set, and then just essentially shuffles it and takes
- 43:30
part, uh, two hundred and fifty-six batches, uh, of sequences of, uh, of, of, of tokens that are the-- from, from the text itself and, uh,
- 43:41
and just essentially uses it for training. Batch size is gonna be sixty-four, so it takes two hundred and fifty-six, uh, token sequences, sixty-four of them, stacks them together and passes the, the model to, to, to teach it how to, how to train.
- 43:56
So a very simple data loader. Usually these data loaders are-- can be quite complex, um, because of course, like if you have-- especially when you have higher context lengths, like the way you load data is like a very big part of how the model like list needs to learn.
- 44:10
But in our case, we have a very, very simple implementation.
- 44:15
Um, next up is the way that w-- th-this is how the code works for, um,
- 44:22
we need to g-to use a device. Uh, this code works for MPS, it works for CUDA, and it works for CPU. Depends on what your l-your laptop supports or like if you run things on Google Colab.
- 44:33
Um, like if you run Colab, it will detect CUDA, and that'll be quite fast. MPS is also quite fast. CPU will be the slowest, but that's like a-- that still would work decently well.
- 44:44
Uh, next up is the learning, the learning rate, and that's like a big part of how the, the models are able to learn. Uh, the way the models work is that you first start generally with as high of a learning rate as, as you can, as you can afford without making the model
- 45:01
b-become unstable. So it, it basically makes it go crazy. Uh, and the way it works, you start with a very high learning rate, which is essentially the, the amount of the model is able to learn per step, how much the mo-- the weights need to move into the direction that you want.
- 45:17
If you have very high learning rate, you would offshoot your target, so it can go off the rails very fast, your model. So you want to use a very appropriate learning rate, um,
- 45:27
for your model to be able to train well. And usually you have this, this concept of a warm-up, where you start with a very small learning rate so that all your optimizations, uh, the, the model weights are able to st-to, to stick to places that are, um, that again, that they don't go into like...
- 45:43
They, they can, they can start in places that are appropriate for the training to begin. So you start a very low learning rate, increase it slightly unti-until you reach the peak, and then at the peak you continue-- you start reducing the learning rate.
- 45:56
That's what we call weight decay. Um, and, uh, until you reach the point that you're, you're satisfied with it. Some people re-- For some people this is zero. Like I prefer to not go to zero 'cause then it's hard to restart the training.
- 46:09
Um, but that's the idea. You want the learning rate to be less when the model is like close to being perfect. So small-- You, you want it to start with very big changes to find like good local minima, lo-lo-global minima, and then it-- for it to calibrate as training goes, goes further.
- 46:26
Um, so we'll start with like a small warm-up of a hundred steps, and then we're gonna use a cosine decay until the max steps that we're gonna... In this case it's gonna be five thousand.
- 46:38
So you're gonna start from like very low, peak at one hundred steps, and then start going down, down to, um, to five thousand steps. And that's what the AdamW normalizer does.
- 46:49
Uh, it essentially allows this, um, th-this concept of like controlling the learning rate, um, using this cosine decay. And that's the most common normalizer people use. At least used to use.
- 47:03
Now there's better normalizers, but this is the most simple to, to start with.
- 47:10
And here's how the full training loop would look like. Again, like not, not, not that much code. You just,
- 47:18
uh, initialize your config. Uh, like in this case it would be like six, uh, six layers, six attention heads, three hundred and eighty-four, um, embedding size, and then two hundred and fifty-six, that's the sequence length.
- 47:33
Uh, you initialize your model and then you create your, um, you, you start your optimizer, you start your steps. TQDM helps with like tracking like the losses and, and all that kind of stuff.
- 47:47
You want your loss to start high and then keep going down until you reach levels that are acceptable.
- 47:53
Um, and then another important part is evaluations to make sure that your model actually works well. That's what the val loss, uh, is here. So
- 48:04
it's very easy for models to like overfit, especially like this, this, the small models, 'cause you don't have that much data. So the loss might keep going down, which means that the t- what the model predicts and what the training data is are very similar.
- 48:19
So your loss can keep going down a lot. Uh, but actually at one point, maybe your model actually overfits in this case. And when it overfits, even though the loss goes down, actually the performance of the model is worse.
- 48:30
That's why we have this val loss Which is a, is a part of the data the model has never seen, and we run a forward pass to get the loss of that specific part of the dataset.
- 48:42
And if this is very low, it means the model can-- is, is performing well because you ha-- the model has never seen this, this data, so it can't memorize things it had never seen.
- 48:52
So that's what, that's what we have here with the VAL loss.
- 48:57
And, uh, I'm not gonna explain the concept of, of, of, of, uh, of, uh, backwards losses, but essentially that's the way the model tra-- the model weights move towards the direction of being optimized.
- 49:09
Uh, so for, for each step, we have a batch size of, in our case, two hundred and fifty-six, um, two hundred and fifty-six tokens, batch size sixty-four. So we ha-- we push this matrix through our model, and then it learns, and then the optimizer does an extra step, and now the learning rates are adjusted depending on what
- 49:29
the steps you're in right now. And lastly, just to have an extra way of, uh, e-every, every one thousand steps, you save your checkpoint to be able to restart your training if you need it from that point.
- 49:41
And we're also running, uh, inference on the current checkpoint to see what the model actually predicts at this point. Uh, so what we're, we're gonna start seeing is
- 49:54
when we start from s- from be- the beginning, because this is a model that we train from scratch, the loss is going to be essentially random. And in that case, that will be natural log of sixty-five, so it will start at around four point seventeen.
- 50:07
Uh, that basically means the model like knows nothing. It has no clue of what this data is. Uh, and slowly, we're gonna start seeing the loss going down to three point three.
- 50:15
That's when the model is gonna understand character frequencies. Uh, it will still not be able to do words yet, but it might understand things like T-H as part of "the," which is a common word.
- 50:25
Like T-H is gonna be part of the things that it starts generating. Then at around two point five, it's gonna un-- it was gonna get a little bit better about this T-H, and then it will understand the word "in" and stuff like that.
- 50:36
Then at about one point five to two losses, it will start actually creating words. And then at around one, one point zero to one point two, that's when the model is going to start being decent at, at, at this task.
- 50:47
It will actually be able to understand names from the text. It will start creating things that start making sense. But then when the loss starts going below one point zero for this specific dataset, that's where we're gonna start seeing overfitting.
- 51:00
The model will still be like producing like reasonable things, but it will no longer start, uh, be getting better at it.
- 51:08
Uh, this is like an example. At two hundred steps when I was testing this, uh, it was just producing like complete nonsense.
- 51:18
Uh, and the VAL loss was around three point five. Uh, then at about eight hundred steps, it started producing like decent things. Still not, not, not great things, but it was, it was starting to get there.
- 51:30
And at a thousand steps, it got better. And there was one point that the VAL loss actually started increasing instead of decreasing, and that's where we know the model overfit.
- 51:39
So at around two hund-- two thousand four hundred steps is where the-- that was the optimal performance of this model. And then if we kept going, the, the performance was actually maybe not decreasing, but the model started becoming less creative.
- 51:52
So that's one of the things to, to keep in mind. Now, VAL loss is like not the best metric. Like if you're actually being serious about training LLMs, usually you might have like some benchmarks that are running as part of your training, and you can see like if the benchmarks are like getting worse or not.
- 52:06
Uh, but for us, that's like a very easy and cheap way of like understanding how the model is doing.
- 52:13
Um, so yeah. That-- what, um, next steps now, like, uh, would be to actually train this model yourself. Um,
- 52:24
considering the internet is not very good, I would suggest probably using Google Colab for this.
- 52:29
Um, you can, you can copy-paste stuff from, from, from, from here. You can glue things together. Uh, copy-pasting will probably like w- allow you to g- get ninety percent there.
- 52:40
Uh, there is a lot of room for, for, uh, improvement for what I written here. Like on purpose, I made it like super simple, and there are things that you can improve yourselves.
- 52:48
Uh, but the idea is to, uh, to get something working. I hope everybody will be able to get something working if, if you're interested into, into, into working on this and have something that, uh, starting from nothing and getting like a model that can actually produce like a result that seems reasonable.
- 53:06
Um, after we have trained the model, the next part is text generation, which is like the inference side of things. Uh, we're just gonna be using, uh, there's multiple ways to, to do, to do inference.
- 53:17
One way is like greedy decoding. It's what I was mentioning earlier. You have all the logits, which is just a distribution of tokens, and you just take the most likely token, and that's what greedy decoding means.
- 53:28
Uh, so let's say that you have like your-- the token T and the token H are, are both in the distribution. One has like eighty percent probability, the other one has like fifteen percent probability.
- 53:39
You always be taking the, the top, the top one. Uh,
- 53:44
that's greedy decoding. Greedy decode doesn't work very well for LLMs. It can work well for other models, but for LLMs, like this essentially makes them very boring and not very creative in what they generate.
- 53:54
So you pretty much never want to use greedy decoding for LLMs. You would want to use it for, for other, um, models. Like transcription, for example, greedy decoding is the best because there's usually like only one way you can, you can s-- transcribe something.
- 54:09
You don't want it to be creative in transcription. That doesn't, that's not a good idea. Um, so that, that's what greedy decoding is. In our case, we're not gonna use that.
- 54:16
We're gonna use temperature. So essentially what temperature is, is that you're not always choosing the highest probability token. Sometimes you might choose the second highest probab-probability token or like the third highest.
- 54:28
And, uh, even though it doesn't make sense, why would you choose like a worse token for this situation? It has proven that this actually makes the model perform better.
- 54:35
Uh- It re-- like, sometimes it might, like, go into, like, this weird loop, so there are, like, techniques you can make sure that it doesn't go, like, crazy by generating some nonsense.
- 54:46
Uh, the worst thing is if, if it predicts, like, an end of transcript token, an end of a text token, and then just, like, stops the generation, which maybe you have seen sometimes when using ChatGPT where suddenly it just, like, stops for no reason.
- 54:58
Uh, you-- sometimes it's because of that. But there are ways you can, you can, you can, um, prevent this. Um,
- 55:06
generally, like, a zero point seven, uh, temperature is, like, the best, the best middle ground to make the, the, uh, inference work well. And then you have top-k sampling, uh, which essentially, like, it prevents the model for...
- 55:20
I-if you have, like, let's say, five tokens that are very likely, and then the sixth token is, like, completely unlikely, top-k sampling pre-sampling prevents the model from predicting this suc- sixth very unlikely token even though the temperature might-- you might get unlucky and the temperature might hit it.
- 55:35
Um, so that's what top-k sampling does. And this is, like, our inference function. It's, like, it's very straightforward. It just does-- It just runs, uh, takes all the tokens as, as the input, passes it through the model, takes the mo- the logits out of the model, and runs, uh, soft...
- 55:53
This is the-- What I was describing before is called softmax. It takes using a temperature the probabilities, and then, uh, it, it decides what the, the next token should be based on, uh, on those probabilities.
- 56:08
Um, and one thing you can do, you can use seeds. And essentially seeds is that it, like, right now everything will be random if you just keep retrying it.
- 56:17
But if you, if you keep retrying it, but if you use a s- a set seed, then your inference will always be returning the same value, uh, which will be, uh, relevant in
- 56:28
later. Uh, then putting it all together. If we put all together, we should have three different files. One is, like, the model.py that includes our model architecture. One is train.py which includes the data- data set loading and the training loop.
- 56:47
And lastly, the generate.py that includes our inference. And in total, this should be, like, maybe a few hundred lines of code. Uh, ve-very straightforward, and that's with-- even with this much code, like, if we have a lot-- big hardware, we could train a good LLM using this architecture if we have enough data and enough resources.
- 57:07
That's, that's all you need, essentially. And that's what basically, like, GPT-3 and GPT-2, like when OpenAI released it. I remember when OpenAI was about to release, uh, GPT-2, and they were saying, "We're not gonna release it because it's too dangerous for, for humanity."
- 57:22
And that was, like, back then. It-- The-- That was the code that they were working on and, like, a lot of data and, of course, a bigger model. Uh, now this seems to be, like, kind of, kind of funny, but for them, like, that was, like, a very wow moment that we did this, and then the model,
- 57:36
like, actually performs really well on tasks. Um, but in the end, it was just, it was just what you see here.
- 57:43
Um, so yeah, putting it all together, uh, you can, uh, if you use Google Colab, you can use this, this, uh, snippet of code to download the, the data set.
- 57:54
And you can use this pip install command to install the different dependencies. Uh, and then it should look, uh, like something like this,
- 58:04
where you install your dependencies, you copy your code.
- 58:08
Uh, and you might need to do some, um, connection, some connections together. But essentially,
- 58:16
like, you can run a, a train... That you can use a train command to run, to, to, and use the data set that you want to use, and that this will start the training.
- 58:27
And then you can see how the performance of the model improves step by step. Like, for me, this took about, like, fifteen minutes to train on, on Google Colab until I started getting good results.
- 58:37
Uh, with some improvements, you can make it to go faster, and maybe you can make it go slower but actually get better results. Um, but essentially, like, it's, it's very simple to get, to get working.
- 58:47
One thing to remember is that you have to change your runtime, change the runtime type to T4 GPU, um, because this, this, this is free, and it will, it will run quite fast.
- 59:02
Um, yeah, feel free to train. Like, you can try, like, maybe starting with a very tiny model, the zero point five million parameter, uh, with a-- only has two layers and only two attention heads and a smaller embedding size for, for each token.
- 59:16
And then you can try bigger and bigger models until you get some usable results. Um, and as I mentioned before, you can try different context lengths. I started with two hundred and fifty-six.
- 59:26
You can try bigger, five hundred and twelve. Um, and then you can, if you want to monitor and see how your losses went down, like, in a nice way, you can use this, like, uh, uh, this PyPlot lib, uh, to see, like, your, the graphs.
- 59:40
Uh, the things that you want to look for is if your train loss is not decreasing, it means your model is not learning. Um, that problem is you have a bug in your code.
- 59:49
Uh, if your, uh... It also could be, like, a, a low training loss. If your train loss is, is decreasing but your var loss is not, is actually increasing, that means you overfit.
- 1:00:02
Uh, and if you have very weird spikes in loss, like, the loss will be, like, very smooth in general. If you have very weird spikes in your loss, it means, like, again, there's some, some kind of bug either in your data or in your training.
- 1:00:13
And, uh, when the model starts plateauing and not getting any better, it means you kind of, you have pretty much exhausted the usefulness of your current data sets. So either you're gonna use a bigger model or you're gonna need-- you essentially need more data.
- 1:00:27
Uh, now one, one, like, interesting part about what, uh, I would like us to do is, uh, I think it would be cool if we had, like, some sort of competition of who can train the best model here if, uh, anybody's able to, to get things working.
- 1:00:40
Uh, hopefully the internet is good enough. Uh- And also for further reading, you can see like a lot of the resources for this, um, for this workshop.
- 1:00:49
Uh, but essentially what the challenge is, is
- 1:00:53
we can v- we, we can vote all together to find which, which model actually produces the best verse of Shakespeare, or it could be if you use a different data set, a best poem or in that kind of category.
- 1:01:04
Um, the rules are you have to train the model yourself, like f- here and today. Like it, it can't be like some... You can't ask ChatGPT to give you like a good verse.
- 1:01:14
You have to like use your own model. And to prove this, you'd use like a seed with a specific prompt and see what outputs it gives you. You're free to like regenerate things as many times until you, you get the best results.
- 1:01:26
Um, and, uh, you... I'll have like a QR code that you can submit your results, and I can go around and like help people out that, uh, if you need any help getting, uh, training running.
- 1:01:36
And, uh, of course, uh, this training is like super bare bones. There are many ways you can optimize this and make this better. So I guess for people that are a bit more experienced, they can, they can implement this, this, uh, improvements and maybe get a better outputs of your, of their model.
- 1:01:51
Uh, the winner is gonna get some, uh, free swag from, from ElevenLabs, maybe like a hoodie or like some, some free credits. Uh, I'll see what I can actually give.
- 1:02:01
So yeah, this is the submission. Uh, it needs to be creative, and then, um, and it needs to be essentially like a good verse. And we can maybe use like a kind of, um, a, a kind of bracket.
- 1:02:15
Oh, what happened? Oh, cool. We can use a bracket, and then we can vote which verse like sounded the best. They could be funny, they could be like, like well-made, like up to you.
- 1:02:26
And the reproducibility should look something like this, uh, where you run Python generate on your best checkpoint, the prompt that you decide, that you can... It's up to you.
- 1:02:36
Temperature, and then a, a seed that, that proves that you can actually produce your results. Uh, you can try different model sizes. Uh, like I guess like eighty-five million parameters would be a bit too big for this, but if you have the resources, you can try it.
- 1:02:54
Uh, you can try better tokenizers. Like this one, of course, is like character-based, but perhaps you can train your own BPE tokenizer, uh, based on that data set. And there's also other tweaks that you can do, like bigger context.
- 1:03:07
Uh, like there are sm- some training optimizations, like using a dropout value. Uh, you can stop whenever you want, when you feel like the model is good enough, change the learning rates, um, and essentially get like as-- make the model as good as possible.
- 1:03:22
Uh, yeah, that, that is the idea. Uh, I'm gonna go around and help you out. Sorry, go ahead.
- 1:03:34
In, in, uh, reasoning models, is it finding very different for training or is it similar but just some small, some more smarter?
- 1:03:43
So, uh, uh, reasoning models, like to, to repeat, uh, wh- uh, uh, you're asking like, uh, if reasoning models are quite a bit different in training. The base-- The building blocks are, are very similar.
- 1:03:54
Like you can train the same exact model. You can post-train it, which is how like usually reasoning is, is, uh, is being taught to this model. You have a good base instruct model, and then you post-train it to be a reasoning model.
- 1:04:06
This is very data set driven, so you need very, very high quality data, and you're gonna use a loss that's like good enough to be able to, uh, to learn this data in a, in a very good sense.
- 1:04:17
Uh, the complication of reasoning models is finding this good chain of thought data. Uh, that's why like OpenAI has like all these labelers that are like PhD students, that they li- write down the reasonings of like how to, how they solve problems.
- 1:04:29
Uh, because this data needs to be very high quality. 'Cause it's, it teaches the model how to think. So you can't just go on like Reddit and just get random posts.
- 1:04:38
You're not gonna learn how to think this way, for sure. Uh, you need like some very high value, uh, good quality data to teach this reasoning process. But in the end, reasoning is, is essentially just adding to the context of the model, like this, to, to the attention essentially.
- 1:04:54
Uh, this like logic that then the model can, when it generates a response, it can go back and attend to those reasoning tokens and get a better response out.
- 1:05:03
So it could be like describing the model a bit better. Then it goes back, sees those tokens that it described and say, "Oh, actually, yeah, I already figured this out.
- 1:05:09
I'm gonna write it down now." Uh, the microphone is not working.
- 1:05:21
Is it? Yeah. Perfect. Uh, so reasoning and non-reasoning models share the same base often.
- 1:05:27
Yes.
- 1:05:27
And then one is post-trained in a different way and adds this, uh, you know, in a way different, uh, post-training.
- 1:05:33
Yes. So a l- a lot of the labs, like let's say the Qwen model that was released, like Qwen three, usually they release like a base version, an instruct version, and the usually the base version doesn't have in s- a chain of thought reasoning.
- 1:05:45
Uh, it's usually like the same model that they first pre-trained to be like quite good, maybe do some fine-tuning as well. And then the next step is doing post-training to teach it this kind of like, this, this, uh, th-this knowledge.
- 1:05:57
Uh, so that's why like, uh, you see a lot of, a lot of, uh, big improvements that happen very fast in the industry. Like Gemini three to three point one, it's essentially giving it like better reasoning data and like, like better fine-tuning post-training data for like specific problems.
- 1:06:12
It's a little bit like benchmarking. Like usually this data is like very similar to what the benchmarks, the current good benchmarks are. But yeah, essentially just taking a base model, using this new data to improve it to the next level.
- 1:06:25
Thank you. And second question is, compared to what you're showing us here, like this very bare bone model, are there like, have there been fundamental innovation in the main training of the model, like powering nowadays models?
- 1:06:41
Or is it mostly the same, but just with Smarter tricks, small tweaks, better data, and so on. So I, like-- are everyone still using the same attention layer and so on, or are there like some fundamental shifts that have been happening on the latest?
- 1:06:57
And maybe you know, maybe you don't, I don't know.
- 1:06:58
Mm-hmm. No, yeah, no, uh, so you're, you're correct. That, that-- so they-- a lot of this base is the same. They might do some changes in terms of how attention attends to those different, uh, uh, tokens because sometimes reasoning can be quite large, so you need big to- uh, sequence lengths to be able to get good results.
- 1:07:16
Uh, so there's a lot of like tricks that the new labs, the, the labs do to make this, the, to, to make the attention more efficient. But overall, you can take like a very-- because you can take this model, GPT-2, and make it into a reasoning model.
- 1:07:29
Like, if you had the, the data and like a big enough model to actually learn from that reasoning as well 'cause like these tiny models, reasoning won't help them too much.
- 1:07:37
But if you had a big enough model that can get stuff out of the reasoning, there are people that have taken like older models, like let's say Llama one B, uh, that are small and weren't trained for reasoning, and that made them into reasoning models using the exact same architecture.
- 1:07:53
Yes?
- 1:07:54
How much effort do you put in, you know, getting your golden dataset, and what is that process that you follow?
- 1:08:02
You mean for post-training?
- 1:08:04
Yeah.
- 1:08:05
Yeah. So like, uh, as I mentioned before, like usually what most labs do is that they go to companies like Scale AI, and that's probably the biggest one. And Scale AI has an army of like people that you can, you can say, "I, I want physicists.
- 1:08:21
Give me data from physicists." Then Scale AI is gonna find contract physicists, pay them like as much money as they need, and then these physicists might like write things down.
- 1:08:29
They might be contacted to do different things. Uh, but essentially like a, a lot of these companies, they-- like Scale AI provides data for like Anthropic. Now they got bought by Meta, so probably not that much anymore.
- 1:08:40
Uh, but essentially they, they, this, these datasets are provided by these like, uh, labeling companies. Uh, Scale AI is one of them. But, uh, a lot of the, the big labs have their own labeling teams too, that they hire contractors to, to, to generate these datasets for them.
- 1:08:55
Uh, but as I, as I said before, like you want, you-- if this dataset has like even some small issues, it can literally make or break your model. So these datasets are like kind of the most expensive, like they will cost tons and tons of money, but they actually are the ones that make the models like as
- 1:09:12
good as they are.
- 1:09:13
Just, just a quick follow-up. So even on that,
- 1:09:17
I mean, you still have to evaluate, right? The answer wouldn't be exactly the same. So are people are really reviewing this or it's still you're relying on LLM to evaluate?
- 1:09:30
Uh, you, you, you rely on people for this kind of stuff. Usually, the way it works, uh, a big part of my job is actually like doing this, uh, organization.
- 1:09:41
Uh, usually, the way it works is you have-- you might have like one person that generates the la-- the, this like very high-quality label data. And then you have maybe some labeler that has graduated into like a QA position, where their job is to essentially make sure that all the like other labelers, the more entry-level, their outputs
- 1:09:58
are correct. And it's, it's, it's quite the tough job because like if your QA is not good, you get fired. So it's like, it's-- that, that's the way they keep the level quite high.
- 1:10:09
Yes? So LLMs are non-deterministic, but, um, how does that seed parameter work? So- So the, the way the LLMs are non-deterministic is because they, they essentially use n- random number generators for, of like the, of, of, of your machine, uh, or like of the, the, the, the GPU that you're currently using or the ma- system.
- 1:10:32
The seed essentially like makes all those different calculations to be always, return always the same value. So things are no longer random. Like you're-- it is not only for, uh, for, for this.
- 1:10:44
These seeds can be used for any random generation. Yeah. Uh, so it could be like for password hashes. In other systems, but I wasn't- Yeah, it, it works the same exact way.
- 1:10:52
Okay. Yes?
- 1:10:53
But this is like you mentioned greedy decoding, right? So if we use that, then they are deterministic, right?
- 1:11:00
Yes. Yeah, if you use greedy decoding, uh, there, there might be still some things that like, like it's good to use a seed even if you use greedy decoding 'cause there might be some other stuff that maybe you don't, you don't, you don't control.
- 1:11:12
Like maybe you're using some-- like sometimes greedy decoding might not be actual greedy decoding. It might be zero point zero one like, uh, temperature, like for vLLM, for example, that they do some like tricks like that to be able to get, uh, good outputs.
- 1:11:24
Uh, so in, in general, it's good to use seed even if you're using greedy decoding.
- 1:11:31
Yes?
- 1:11:32
Um, this is obviously all in text. ElevenLabs is mostly audio, right?
- 1:11:37
Yes.
- 1:11:38
How different is this versus like doing this with audio?
- 1:11:42
It's surprisingly very similar. [clears throat]
- 1:11:44
Really?
- 1:11:45
It's more complicated for sure, but parts of the, the stack of like most audio models, they also can do text. 'Cause they, you need-- the, the models need to understand language itself, and the best way to teach something language is text.
- 1:11:58
Uh, of course, you can train an, an audio-only model like, uh... And again, it's like what I was talking about the tokenizer, like how do you tokenize audio? Like what, what is the concept of like a sound?
- 1:12:10
Like how, how big should your tokenizer be for audio? What-- Should it be only human speech or should it also be like, uh, music? Like do you take the notes of different music instruments as like different tokens?
- 1:12:20
These are the kind of problems that you have to solve, like if you're creating a, uh, an audio model that you wouldn't necessarily need as a text, it's a bit easier.
- 1:12:28
Uh, but the fundamentals are still like the same. Like if you want to generate audio, you, you generate like an audio token, and then that audio token would be using a tokenizer, and then you, you...
- 1:12:39
It's not exactly the same 'cause the audio token then you have to process it in some different ways, uh, to make it into like actual audio. But, uh, a lot of the same, uh, general ideas still apply.
- 1:12:53
You know, like this is the text. The text can be down to very clear, like a, a one is a one, but in audio, like a particular pitch or frequency or sound byte, like there's the ambiguity there, so it's already lost straight away, right?
- 1:13:09
Yeah, yeah. So the, the w- the way ta-- it, it works is that you don't use like this cross-entropy loss. You use different types of losses. You can use cross-entropy, but usually it's, it's losses that are more, um, specialized for what you're trying to do.
- 1:13:23
Like for example, there's a ca-- a loss called L2 loss, which essentially takes like two mel spectrograms and starts to see the difference between those two mel spectrograms, which is essentially like, uh, sound waves in-- encoded in a certain way.
- 1:13:36
And that's like a very common way for TTS models to be trained. You train on these specific types of loss. Uh, and the same thing I was mentioning earlier, cross-entropy doesn't really work as well for, for things like post-training.
- 1:13:47
You might use different types of losses there. Or if you're distilling a model from a, a big model to a smaller model, you don't use cross-entropy loss. You might use a KL divergence loss where you find the, the token distributions of like the bigger model, and you try to match the logits of the smaller model.
- 1:14:03
So there's different types of losses for different use cases. Not all of them work the same way.
- 1:14:10
Yes?
- 1:14:10
So in a sense, you have like an audio vocabulary, right? And so you train on that. But, uh, what do they do with multi-modal models? Do they just like direct regular text vocabulary with audio vocabulary, with pixel vocabulary together and train that?
- 1:14:26
So the way mu-mu-multimodal models work, uh, usually you don't use tokens in the same sense. Um,
- 1:14:34
like the-- okay, this is where like it, it becomes a bit more complicated because like these models are not really built for this, like the GPT model. Like the b-- the newer models, they have also like an embedding input, uh, which essentially as, as, as I was mentioning earlier, like you have each token then corresponds to like
- 1:14:50
a vector. But these vectors, they don't have to correspond to a specific token. You can take these vectors from other places too. And what a lot of these labs do is that instead of having a tokenizer for like video, for example, what they do is they have another transformer that they call a video encoder, and they put
- 1:15:07
the video first through that video encoder. And, uh, this video encoder will be taking, let's say you have a, a thirty-second video. It will take one frame per second of this video, and it will take those frames and then put them through this like new transformer, this encoder transformer, that works quite a bit differently than this one.
- 1:15:24
And what you do is that you take the final layer of this transformer, the hidden values, which are also vectors. You take those, those vectors out of this encoder, and you're gonna input them in the embedding layer of the, of the transformer model.
- 1:15:36
So what the model is going to see is gonna... Usually it's, it's like prefixed. So you take the, you take the video, push it through the encoder, get some vectors, and then put those vectors in the embedding input of your transformer that does text.
- 1:15:49
And the way it would look like if you look at the sequence, it would probably be like a prompt, and then it would be probably like a video token representation.
- 1:15:58
And then actually-- But the embedding of the video token is gonna be overridden by the output of the encoder.
- 1:16:05
So that's how like these, these multimodal models work.
- 1:16:08
So the frame produces a vector which is the same length as like the vector for the horse.
- 1:16:13
Yes, it's exactly the same vector. Uh, the same for audio. You have an audio encoder and do the same but, but, but for audio. And, uh, but for-- in, in terms of how-- what the model cares about, it just cares about like these embeddings, right?
- 1:16:24
It don't care if it's text or if it's audio or if it's, or if it's video. It cares about these vectors, and that's how you, you represent them into the, the same dimension as the model, the transformer expects.
- 1:16:36
So, uh, there's, there's no relation between like a video of a horse jumping and the word horse. Is there no similarity between those two vectors?
- 1:16:46
Um, maybe there is. I, I'm not-- It depends on how you train it. Like it's-- these are the kind of things that are a bit about black boxes. Maybe there is, maybe there's not.
- 1:16:54
Actually, that's a good idea to like... Uh, that would be a good research paper to, to see, like, uh, for video encoders. Do they actually match in the same dimension as the text encoders?
- 1:17:02
I, I don't know. I imagine there probably is some, uh, connection. Sorry, you had a question as well?
- 1:17:08
Uh, yeah. I was wondering about like, do you do both, uh, normal speech, but you also do some music generation? Is that like a very different problem, or will the architecture be like similar?
- 1:17:18
And also like when you have these harmonics and stuff, are you still able to do just like a basic, like a autoregressive transformer, or do you have to like do diffusion because like things depend more on each other?
- 1:17:29
Uh, and you might wanna like generate everything at the same time even.
- 1:17:33
You, you, you can do both. There's music models that are autoregressive. There's music models that are diffusers. Uh, like it depends on how you train it. Um, like I think some of the, some of the Google models are like, like transformer-based.
- 1:17:46
Some of, uh, open source models are like diffuser-based. Like both can work, can work very well. It's just that it's, it's a little bit f- as, as I said, like it's a little bit more difficult to,
- 1:17:56
uh, to, to put into perspective this, the, the concept of like odd music. If you tokenize and predict the next token, it's kind of hard because it's very abstract.
- 1:18:06
Uh, so usually diffusers work a bit better in this like image modalities for generation or like music or like even audio for some models. Like they have some kind of diffuser, diffuser, um, diffuser part of the process.
- 1:18:19
Um, both can work. Diffusers are generally a bit easier to get it working.
- 1:18:26
I hope this answered your question.
- 1:18:31
Yeah, that makes sense.
- 1:18:31
Yep.
- 1:18:32
How do you, uh, tokenize audio? Because I imagine sort of with text, you get the repetitions, like repetition happens quite a lot. It's easier to combine those different tokens, create bigger tokens.
- 1:18:45
But with audio, you have distinct voices. Uh, I don't know, like how long, how long tokens would be.
- 1:18:52
It, it's very hard.
- 1:18:54
Okay.
- 1:18:54
And it's not really something that you do by, by just sitting down and thinking, "Okay, I'm gonna tokenize this word to that." It's, it's not something that you like sit down and like decide.
- 1:19:03
You use some kind of processing. You train like an audio tokenizer through... You have a d- I- it's a very similar like case I guess in, uh, how you train like a text tokenizer.
- 1:19:14
You'd use your training data and you'd find like common patterns in the audio and then tokenize those patterns. Of course, like when we say audio, we don't, don't always just mean like the actual like sample rate and like the, the a- the audio waves.
- 1:19:26
Usually convert them to something that's a bit more easy to, to tokenize, to, to do this processing. The most common one is like, uh, is mel-spectrograms, is first you convert your audio to mel-spectrograms and then you use this, this distribu- this, uh, um, essentially arrays of like, of numbers to train your, uh, your tokenizers.
- 1:19:48
And it will be very dependent on, on, on your training set. Like if you wanted to tokenize music and use a music data set, your audio tokens for music are gonna be very different than if you had a voice data set that's going to be like focusing on like human voice.
- 1:20:04
Now the hard part is what if you want to do both voice and music? That's where like... That's, that's very hard. [laughs]
- 1:20:17
Any more questions? Okay, awesome. Um, yeah, if you, if you want, you can start working on, uh, training the model if you have a laptop out, uh, or if you have already started.
- 1:20:29
Uh, you can follow this, uh... You, you can, you, you can follow the, the workshop, uh, try to get something working and, uh, if we do have enough submissions, we can do the, the competition and,
- 1:20:46
and, uh, see who- whoever wins who- is gonna get some, some nice pri- What's the cutoff for, for getting these things in? Sorry? What's the cutoff? Uh- Voting begins shortly after?
- 1:20:57
So, uh, uh, because we don't have that much time left, let's just say 5:45.
- 1:21:06
So if you have any, any questions or you need any help, uh, please call me and I'll, I'll come over. [outro music]