Skip to content

Build a Bigram Character-Level Language Model with PyTorch

Creating a Character-Level Language Model: Bigram Model from Scratch

This guide builds a "Make More" neural network step by step, starting with the simplest possible character-level language model, the bigram model. You'll learn the fundamentals of predicting the next character in a sequence, which forms the foundation for more advanced architectures like RNNs and Transformers.

Understanding the Dataset: names.txt

The dataset contains approximately 32,000 names gathered from a government website. Each name (like "Emma", "Olivia", "Ava") is treated as a sequence of individual characters. For example, "Reese" becomes: r → e → e → s → e.

Each word provides multiple training examples:

  • The first character (I in "Isabella") is likely to start a name
  • Each character predicts the next one (s follows I, a follows s, etc.)
  • The final character tells us the name is likely to end

Building a Bigram Language Model

A bigram model only looks at one previous character to predict the next one. It's simple but a great starting point. To understand how this fits into the broader landscape, check out this introduction to what large language models are and how they work.

Step 1: Extract Bigrams from Words

To capture all useful information, we add special start (.) and end (.) tokens to each word:

import torch

words = open('names.txt', 'r').read().splitlines()

# Create a mapping from characters to integers
chars = sorted(list(set(''.join(words))))
stoi = {s:i+1 for i,s in enumerate(chars)}  # Start indexing at 1 for . at position 0
stoi['.'] = 0
itos = {i:s for s,i in stoi.items()}

Step 2: Count Character Pair Frequencies

Create a 27×27 count matrix (26 letters + special token) to tally how often each pair occurs:

N = torch.zeros((27, 27), dtype=torch.int32)

for w in words:
    chs = ['.'] + list(w) + ['.']
    for ch1, ch2 in zip(chs, chs[1:]):
        ix1 = stoi[ch1]
        ix2 = stoi[ch2]
        N[ix1, ix2] += 1

The resulting matrix shows patterns like:

  • Row for . gives starting character probabilities (e.g., J starts words 175 times)
  • Column for . gives ending character probabilities
  • Internal cells show character transitions (e.g., how often 'a' follows 'b')

Step 3: Convert Counts to Probabilities

Normalize each row so it sums to 1, creating a proper probability distribution using PyTorch broadcasting:

P = (N+1).float()  # Add 1 for smoothing (avoids zero probabilities)
P /= P.sum(1, keepdim=True)  # Normalize each row

Important: Always use keepdim=True when normalizing rows. Without it, broadcasting silently converts your column sums into row vectors, normalizing columns instead of rows!

Step 4: Sample New Names

Generate names by starting with the . token and iteratively sampling the next character:

g = torch.Generator().manual_seed(2147483647)

for _ in range(20):
    out = []
    ix = 0  # Start token
    while True:
        p = P[ix]  # Get probability distribution for current character
        ix = torch.multinomial(p, num_samples=1, replacement=True, generator=g).item()
        if ix == 0:  # End token
            break
        out.append(itos[ix])
    print(''.join(out))

Example outputs: "mor", "keanu", "oreilly" – the model learns some name-like patterns but produces mostly nonsense because it only looks one character back. This is a fundamental concept you can explore further in our guide on building and understanding GPT from Shakespeare to ChatGPT.

Evaluating Model Quality with Log-Likelihood

To measure how good our model is, we use the log-likelihood:

log_likelihood = 0.0
n = 0

for w in words:
    chs = ['.'] + list(w) + ['.']
    for ch1, ch2 in zip(chs, chs[1:]):
        ix1 = stoi[ch1]
        ix2 = stoi[ch2]
        prob = P[ix1, ix2]
        logprob = torch.log(prob)
        log_likelihood += logprob
        n += 1

print(f'Average negative log-likelihood: {-log_likelihood/n:.4f}')

Why log-likelihood?

  • Product of many small probabilities becomes tiny and unmanageable
  • Log converts multiplication to addition: log(a·b·c) = log a + log b + log c
  • Higher probabilities give log values closer to 0 (good), lower probabilities give very negative values (bad)
  • Random guessing (uniform distribution over 27 tokens) gives a cross-entropy loss of log(27) ≈ 3.30
  • A good model will have a lower (better) loss

If you're new to some of the terminology used here, our article on 20 key AI terms every engineer must know explained simply is a great resource.

Key Takeaways

  1. Bigram models are the simplest language models, looking at only one previous character
  2. Counting and normalizing gives us a statistical model with explicit probability distributions
  3. Sampling uses torch.multinomial with a seeded generator for reproducibility
  4. Broadcasting is powerful but dangerous, always check dimensions with keepdim=True
  5. Log-likelihood provides a single-number quality metric that's numerically stable

For a deeper understanding of how these concepts extend to modern AI systems, you might find our complete guide to LangChain models: language and embedding helpful.

Keep this summary

Save it to LunaNotes and it becomes a real note in your library — editable, searchable, and ready to turn into flashcards or a diagram. Free to start.

Save to LunaNotes

Or summarise for another video.

32K+ people have turned 65K+ videos into notes

“Very useful tool. I'm using it for the last couple weeks and I'm loving it so far. Makes learning on YouTube a great experience.”
Bhai Chrome Web Store review

This summary and transcript were automatically generated using AI with the Free YouTube transcript summary tool by LunaNotes.

Related summaries

How Large Language Models Actually Predict the Next Word

How Large Language Models Actually Predict the Next Word

This video provides a clear, step-by-step explanation of how large language models (LLMs) work, from the core concept of next-word prediction to the intricacies of transformers and training at scale. Discover how GPT-3 and similar models are built, how they learn from trillions of text examples, and why their outputs can feel so natural and useful.

Building and Understanding GPT: From Shakespeare to ChatGPT

Building and Understanding GPT: From Shakespeare to ChatGPT

Explore how GPT models like ChatGPT work under the hood, including the Transformer architecture, training on Tiny Shakespeare data, and innovations like self-attention and multi-head attention. Learn how scaling up these principles leads to powerful language models and the stages involved in creating ChatGPT.

Generative AI Career: Top 10 Basic Keywords Explained Simply

Generative AI Career: Top 10 Basic Keywords Explained Simply

Kickstart your Generative AI career by mastering core concepts. This video covers 10 essential keywords from tokenization to RAG, helping you build a strong foundation before moving to production.

20 Key AI Terms Every Engineer Must Know Explained Simply

20 Key AI Terms Every Engineer Must Know Explained Simply

This video breaks down 20 essential AI and large language model (LLM) terms, from tokenization and vectors to RAG and reinforcement learning. Designed for engineers, it clarifies confusing jargon and connects concepts like transformers, fine-tuning, and MCP to real-world applications.

Complete Guide to LangChain Models: Language & Embedding Explained

Complete Guide to LangChain Models: Language & Embedding Explained

Explore the LangChain model component in depth, covering language and embedding models. Learn how to code with OpenAI, Anthropic, Google Gemini, and open-source models using Hugging Face, plus build a document similarity app.

Found this summary useful?

Take it with you. One click puts it in your own LunaNotes library.

Save to LunaNotes

Start taking better notes today with LunaNotes