The best way to understand AI is to build it yourself — starting small. In this article, we'll build four progressively more capable AI models using toy examples. Each example teaches a real concept used in production AI systems. No prior machine learning knowledge required — just basic Python familiarity.
What We'll Build
- 🌡️ Model 1 — Linear Regression: Predicting temperature from altitude
- 🐾 Model 2 — Logistic Regression: Is this a cat or a dog? (binary classification)
- 🔢 Model 3 — Neural Network from scratch: Recognizing handwritten digits
- 💬 Model 4 — Tiny Language Model: Predicting the next character
Setup — Install These Libraries
pip install numpy matplotlib scikit-learn torch
- Python 3.8+ recommended. All examples run in a Jupyter notebook or any Python file.
Model 1 — Linear Regression: Learning a Straight Line
The concept: Linear regression finds the best straight line through data. Every AI model at its core is doing something similar — finding patterns in numbers.
The toy problem: Temperature drops about 6.5°C for every 1,000m increase in altitude. Can a model learn this from data?
import numpy as np
import matplotlib.pyplot as plt
# Our toy dataset: altitude (meters) → temperature (°C)
altitude = np.array([0, 500, 1000, 1500, 2000, 2500, 3000])
temp = np.array([20, 17, 14, 10, 7, 3, 0])
# === THE MODEL: y = weight * x + bias ===
# We find weight and bias using the closed-form solution
X = np.column_stack([altitude, np.ones(len(altitude))]) # add bias column
weight, bias = np.linalg.lstsq(X, temp, rcond=None)[0]
print(f"Learned: temp = {weight:.4f} × altitude + {bias:.2f}")
# Output: temp = -0.0065 × altitude + 20.02 ← model learned the real rule!
# Predict temperature at 1,800m
prediction = weight * 1800 + bias
print(f"Predicted temp at 1800m: {prediction:.1f}°C") # → ~8.3°C
The model learned the rule "temperature drops 6.5°C per 1000m" purely from data — nobody told it the formula.
What This Teaches
Key Concepts in Linear Regression
- Weight — how much the output changes per unit of input (slope)
- Bias — the baseline output when input is zero (intercept)
- Training — finding the weight and bias that minimize prediction error
- Generalization — the model can now predict for altitudes it never saw
Model 2 — Logistic Regression: Yes or No?
The concept: Classification — deciding which category something belongs to. The backbone of spam filters, fraud detection, medical diagnosis, and more.
The toy problem: Given an animal's weight and height, predict: cat or dog?
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np
# Toy dataset: [weight_kg, height_cm] → label (0=cat, 1=dog)
X = np.array([
[4.0, 25], # cat
[3.5, 23], # cat
[5.0, 28], # cat
[25.0, 55], # dog
[30.0, 60], # dog
[20.0, 50], # dog
[6.0, 30], # cat (larger cat)
[18.0, 48], # dog (smaller dog)
])
y = np.array([0, 0, 0, 1, 1, 1, 0, 1]) # 0=cat, 1=dog
# Scale features (important! keeps numbers in similar range)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Train the model
model = LogisticRegression()
model.fit(X_scaled, y)
# Predict new animals
new_animal = scaler.transform([[8.0, 35]]) # 8kg, 35cm tall
prediction = model.predict(new_animal)
probability = model.predict_proba(new_animal)
print(f"Prediction: {'Dog' if prediction[0]==1 else 'Cat'}")
print(f"Confidence: Cat={probability[0][0]:.0%}, Dog={probability[0][1]:.0%}")
What's Actually Happening
Logistic regression applies the sigmoid function to the weighted sum, squashing the output to a probability between 0 and 1:
σ(z) = 1 / (1 + e^(-z))
Where z = weight₁ × weight_kg + weight₂ × height_cm + bias
If σ(z) > 0.5 → Dog
If σ(z) ≤ 0.5 → Cat
Model 3 — Neural Network from Scratch
The concept: A multi-layer neural network learns complex non-linear patterns by stacking simple operations. This is the foundation of everything in modern deep learning.
The toy problem: Learn the XOR function — the problem that stumped the Perceptron in 1969.
import numpy as np
# XOR truth table: one layer CAN'T solve this, two layers CAN
X = np.array([[0,0], [0,1], [1,0], [1,1]]) # inputs
y = np.array([[0], [1], [1], [0]]) # expected outputs (XOR)
# === NETWORK: 2 inputs → 4 hidden neurons → 1 output ===
np.random.seed(42)
W1 = np.random.randn(2, 4) * 0.5 # weights: input → hidden
b1 = np.zeros((1, 4)) # biases for hidden layer
W2 = np.random.randn(4, 1) * 0.5 # weights: hidden → output
b2 = np.zeros((1, 1)) # bias for output
def sigmoid(z): return 1 / (1 + np.exp(-z))
def sigmoid_grad(z): return sigmoid(z) * (1 - sigmoid(z))
# Training loop
lr = 0.5
for epoch in range(10000):
# Forward pass
z1 = X @ W1 + b1 # hidden pre-activation
a1 = sigmoid(z1) # hidden activation
z2 = a1 @ W2 + b2 # output pre-activation
output = sigmoid(z2) # final prediction
# Loss (mean squared error)
loss = np.mean((output - y) ** 2)
# Backward pass (backpropagation)
d_out = (output - y) * sigmoid_grad(z2)
dW2 = a1.T @ d_out
db2 = d_out.sum(axis=0)
d_h = d_out @ W2.T * sigmoid_grad(z1)
dW1 = X.T @ d_h
db1 = d_h.sum(axis=0)
# Update weights
W2 -= lr * dW2; b2 -= lr * db2
W1 -= lr * dW1; b1 -= lr * db1
if epoch % 2000 == 0:
print(f"Epoch {epoch:5d} | Loss: {loss:.4f}")
# Final predictions
print("\nFinal Predictions (should be ~0, 1, 1, 0):")
print(np.round(output, 2))
After training, the network correctly outputs near-zero for 0 XOR 0 and 1 XOR 1, and near-one for 0 XOR 1 and 1 XOR 0. The key: the hidden layer learned to create intermediate representations the output layer could use — something a single layer never could.
Model 4 — Tiny Character-Level Language Model
The concept: A language model learns the statistical patterns of text and can generate new text that follows those patterns. This is the ancestor of GPT.
The toy problem: Train on a tiny text and predict the next character.
import torch
import torch.nn as nn
import torch.nn.functional as F
# Our training text (toy example — works better with more text!)
text = "hello world. the quick brown fox jumps over the lazy dog. " * 20
# Build vocabulary
chars = sorted(set(text))
vocab = len(chars)
ch2ix = {c: i for i, c in enumerate(chars)}
ix2ch = {i: c for c, i in ch2ix.items()}
encode = lambda s: [ch2ix[c] for c in s]
decode = lambda l: ''.join([ix2ch[i] for i in l])
data = torch.tensor(encode(text), dtype=torch.long)
# Simple single-layer language model (tiny transformer-like)
class TinyLM(nn.Module):
def __init__(self, vocab_size, embed_dim=32, context=8):
super().__init__()
self.embed = nn.Embedding(vocab_size, embed_dim)
self.linear = nn.Linear(embed_dim * context, vocab_size)
self.context = context
def forward(self, x):
B, T = x.shape
embeds = self.embed(x).view(B, -1) # flatten embeddings
return self.linear(embeds)
# Training
context = 8
model = TinyLM(vocab_size=vocab, context=context)
optim = torch.optim.Adam(model.parameters(), lr=1e-2)
for step in range(3000):
# Random batch of sequences
ix = torch.randint(len(data) - context - 1, (32,))
xb = torch.stack([data[i:i+context] for i in ix])
yb = torch.stack([data[i+context] for i in ix])
logits = model(xb)
loss = F.cross_entropy(logits, yb)
optim.zero_grad(); loss.backward(); optim.step()
if step % 500 == 0: print(f"Step {step}: loss={loss.item():.3f}")
# Generate text
def generate(seed, n=50):
idx = torch.tensor([encode(seed[-context:])], dtype=torch.long)
out = seed
for _ in range(n):
logits = model(idx)
probs = F.softmax(logits, dim=-1)
next_c = torch.multinomial(probs, 1).item()
out += ix2ch[next_c]
idx = torch.cat([idx[:,1:], torch.tensor([[next_c]])], dim=1)
return out
print("\nGenerated:", generate("the ", n=60))
This tiny model uses exactly the same principles as GPT — embeddings, context windows, next-token prediction. GPT-3 is this model, but 175 billion times larger.
📚 Further Reading & Next Steps
The Progression: What You've Built
| Model | Type | What It Learns | Real-World Use |
| Linear Regression | Supervised | Linear relationships | House prices, sales forecasting |
| Logistic Regression | Classification | Decision boundaries | Spam filters, fraud detection |
| Neural Network | Deep Learning | Non-linear patterns | Image recognition, recommendations |
| Tiny LM | Generative AI | Language patterns | ChatGPT, Claude, Copilot |