Guide To AI Logo
Unit 21

Modern AI Systems and Generative AI

understanding Transformers, generative model families, and the systems built around them

Transformer Self-Attention QKV Flow
Input Token Embedding (X)Query (Q)Key (K)Value (V)Q × K^TSoftmaxAttention Weighted Output
Read diagram labels
  • Input token embedding (X)
  • Query (Q)
  • Key (K)
  • Value (V)
  • Q × Kᵀ
  • Softmax
  • Attention-weighted output

Core Concepts Covered

  • Attention history, scaled dot-product self-attention, and complete Transformer blocks
  • Autoregressive models, VAEs, GANs, and diffusion model tradeoffs
  • LLM training, RAG, multimodality, tool use, and alignment
Local Setup Recommendation

To execute and experiment with the code cells below on your local machine, ensure you have set up your isolated virtual environments and scientific libraries by following the detailed protocols in Unit 03: Environment Setup or run them in Google Colab.

1. From Recurrent Bottlenecks to Attention and Transformers

Early neural machine translation commonly used an encoder RNN or LSTM to compress a source sentence into a fixed-length context vector, then a decoder RNN to generate the translation. The 2014 sequence-to-sequence result showed that this approach could work well, but asking one vector to preserve every detail created a bottleneck, especially for long sentences. Recurrence also makes the training path serial: hidden state hth_t depends on ht1h_{t-1}.

Bahdanau additive attention changed the interface between encoder and decoder. At every decoding step, the model scored all encoder states, normalized those scores, and formed a new weighted context. Luong attention explored effective dot-product and multiplicative scoring variants. Attention therefore began as a way for an RNN decoder to retrieve the source information it needed instead of relying on one fixed summary.

The 2017 Transformer removed recurrence from the main sequence path and built the model from attention and position-wise feed-forward layers. In 2018, GPT demonstrated generative pretraining followed by task adaptation, while BERT demonstrated deep bidirectional encoder pretraining. Later systems scaled these architectural families with more data, parameters, compute, and increasingly specialized training stages.

Transformers became dominant in large-scale language systems because tokens can be processed in parallel during training, attention gives short paths between distant positions, retrieval is content-dependent, and the architecture scales efficiently on accelerator hardware. The comparison needs nuance: autoregressive generation is still sequential, standard attention costs O(n2)O(n^2) in sequence length, position information must be supplied, and RNNs can remain attractive for low-memory streaming or tightly constrained devices.

From Recurrent Compression to Direct Attention

2014Seq2Seqone context2014–15Attentionretrieve states2017Transformerno recurrence2018+GPT / BERTpretrain + scaleRECURRENT PATHh1h2h3h4long serial path through hidden statesSELF-ATTENTION PATHx1x2x3x4direct content-dependent connections
Read diagram labels
  • 2014
  • Seq2Seq
  • one context
  • 2014–15
  • Attention
  • retrieve states
  • 2017
  • Transformer
  • no recurrence
  • 2018+
  • GPT / BERT
  • pretrain + scale
  • RECURRENT PATH
  • h1
  • h2
  • h3
  • h4
  • long serial path through hidden states
  • SELF-ATTENTION PATH
  • x1
  • x2
  • x3
  • x4
  • direct content-dependent connections

2. Scaled Dot-Product Self-Attention, Step by Step

Suppose XRn×dmodelX\in\mathbb{R}^{n\times d_{model}} holds nn token embeddings. Learned projections produce Q=XWQQ=XW_Q, K=XWKK=XW_K, and V=XWVV=XW_V. With key width dkd_k and value width dvd_v, their shapes are Q,KRn×dkQ,K\in\mathbb{R}^{n\times d_k} and VRn×dvV\in\mathbb{R}^{n\times d_v}. Row ii of QQ asks a query; every row of KK describes what that token offers; the matching weights retrieve rows of VV.

The scores, row-wise attention weights, and contextual outputs are S=QKTdk,A=softmaxrow(S),O=AV.S=\frac{QK^T}{\sqrt{d_k}},\qquad A=\operatorname{softmax}_{row}(S),\qquad O=AV. The scale dk\sqrt{d_k} prevents high-dimensional dot products from routinely pushing softmax into nearly flat-gradient regions. Each row of AA sums to 11, so output row ii is a weighted mixture of the value vectors.

For a complete three-token example, let Q=K=[100111],V=[100231].Q=K=\begin{bmatrix}1&0\\0&1\\1&1\end{bmatrix},\qquad V=\begin{bmatrix}1&0\\0&2\\3&1\end{bmatrix}. Then S[.7070.7070.707.707.707.7071.414],A[.401.198.401.198.401.401.248.248.503],S\approx\begin{bmatrix}.707&0&.707\\0&.707&.707\\.707&.707&1.414\end{bmatrix},\quad A\approx\begin{bmatrix}.401&.198&.401\\.198&.401&.401\\.248&.248&.503\end{bmatrix}, and AV[1.604.7971.4011.2031.7591.000].AV\approx\begin{bmatrix}1.604&.797\\1.401&1.203\\1.759&1.000\end{bmatrix}. For example, the first output is .401[1,0]+.198[0,2]+.401[3,1]=[1.604,.797].401[1,0]+.198[0,2]+.401[3,1]=[1.604,.797].

A decoder must not look at future tokens. A causal mask replaces entries above the score-matrix diagonal with -\infty before softmax. Their exponentials become zero, while the permitted entries in each row are renormalized. A padding mask similarly prevents attention to batch padding; it is not the same as a causal mask.

Scores Become Row-Wise Attention Weights, Then Mix Values

SCORES S.7070.7070.707.707.707.7071.414softmaxWEIGHTS A.401.198.401.198.401.401.248.248.503each row sums to 1× VCONTEXT AV1.604.7971.4011.2031.7591.000CAUSAL MASK BEFORE SOFTMAXfuture scores → −∞ → future attention weights become 0
Read diagram labels
  • SCORES S
  • .707
  • 0
  • 1.414
  • softmax
  • WEIGHTS A
  • .401
  • .198
  • .248
  • .503
  • each row sums to 1
  • × V
  • CONTEXT AV
  • 1.604
  • .797
  • 1.401
  • 1.203
  • 1.759
  • 1.000
  • CAUSAL MASK BEFORE SOFTMAX
  • future scores → −∞ → future attention weights become 0
Jupyter Code Notebook Cell
Python 3 (ipykernel)
In [1]:
import math
import torch

torch.manual_seed(0)
Q = K = torch.tensor([[1., 0.], [0., 1.], [1., 1.]])
V = torch.tensor([[1., 0.], [0., 2.], [3., 1.]])

scores = Q @ K.T / math.sqrt(Q.shape[-1])
attention = torch.softmax(scores, dim=-1)
context = attention @ V

print("S =\n", scores.round(decimals=3))
print("A =\n", attention.round(decimals=3))
print("AV =\n", context.round(decimals=3))

# Decoder-only causal version: future columns become -infinity.
causal = torch.triu(torch.ones(3, 3, dtype=torch.bool), diagonal=1)
causal_attention = torch.softmax(scores.masked_fill(causal, float("-inf")), dim=-1)
print("Causal A =\n", causal_attention.round(decimals=3))
Out [1]:
Example output (precomputed; this website does not execute code):
S =
 tensor([[0.707, 0.000, 0.707],
         [0.000, 0.707, 0.707],
         [0.707, 0.707, 1.414]])
A =
 tensor([[0.401, 0.198, 0.401],
         [0.198, 0.401, 0.401],
         [0.248, 0.248, 0.503]])
AV =
 tensor([[1.604, 0.797],
         [1.401, 1.203],
         [1.759, 1.000]])
Causal A =
 tensor([[1.000, 0.000, 0.000],
         [0.330, 0.670, 0.000],
         [0.248, 0.248, 0.503]])
Worked Example 1

Reading one row of an attention matrix

Problem

What does A2,3=0.401A_{2,3}=0.401 mean, and why is it not a hard selection?

Step-by-step solution

1.Row 22 describes the query made by token 22; column 33 refers to token 33's key and value.

2.A2,3=0.401A_{2,3}=0.401 assigns 40.1%40.1\% of the second output's mixture weight to value vector v3v_3.

Final answer and interpretation

The other weights are nonzero and the row sums to 11, so attention performs differentiable weighted retrieval rather than choosing a single token.

Rows correspond to querying positions, columns to retrieved positions, and AVAV mixes values using those row weights.

3. Inside a Complete Transformer Block

One attention head captures one learned similarity space. Multi-head attention runs hh projected heads, concatenates their outputs, and applies WOW_O: MHA(X)=Concat(head1,,headh)WO\operatorname{MHA}(X)=\operatorname{Concat}(head_1,\ldots,head_h)W_O. Different heads can specialize in different patterns, although a head is not guaranteed to have a simple human-readable role.

A block combines attention with a position-wise feed-forward network such as FFN(x)=W2σ(W1x+b1)+b2\operatorname{FFN}(x)=W_2\,\sigma(W_1x+b_1)+b_2. Residual paths preserve an identity route around each sublayer, and layer normalization stabilizes activations. Implementations may place normalization before or after a sublayer. Token embeddings also receive learned or fixed positional information because attention alone is permutation-equivariant.

Encoder-only models use bidirectional self-attention and are natural for representation tasks. Decoder-only models use causal self-attention for next-token generation. Encoder-decoder models let a causal decoder cross-attend to a separately encoded input, which suits conditional generation such as translation. Padding masks hide absent tokens; causal masks hide future tokens.

Training a decoder can score all known next-token targets in parallel, but inference must produce the next token before the following one exists. A KV cache reuses earlier keys and values instead of recomputing the full prefix at each generation step. It reduces repeated computation but consumes memory. Standard dense attention still stores or computes an n×nn\times n interaction pattern, giving the familiar O(n2)O(n^2) sequence-length tradeoff.

A Transformer Block Preserves Residual Information

TOKEN + POSITION EMBEDDINGMULTI-HEAD ATTENTIONheads → concat → WₒADD RESIDUAL + LAYER NORMFEED-FORWARD NETWORKW₂ σ(W₁x + b₁) + b₂ADD RESIDUAL + LAYER NORMMASK CHOICEencoder: paddingdecoder: causal + paddingIDENTITY PATHSpreserve signal
Read diagram labels
  • TOKEN + POSITION EMBEDDING
  • MULTI-HEAD ATTENTION
  • heads → concat → Wₒ
  • ADD RESIDUAL + LAYER NORM
  • FEED-FORWARD NETWORK
  • W₂ σ(W₁x + b₁) + b₂
  • MASK CHOICE
  • encoder: padding
  • decoder: causal + padding
  • IDENTITY PATHS
  • preserve signal

4. The Generative Modeling Landscape

Generative AI is not one architecture. An autoregressive Transformer factorizes a sequence into next-step conditionals, has no required global latent code, and samples one token at a time. It offers stable likelihood-based training and excels at discrete sequences, but long generation is sequential.

A Variational Autoencoder (VAE) learns a smooth probabilistic latent space with a reconstruction-plus-regularization objective. Sampling is usually one decoder pass and latent interpolation is meaningful, though image outputs can look blurred. A Generative Adversarial Network (GAN) learns through a generator-discriminator game. It can produce sharp samples in one pass but training balance and mode coverage are difficult.

A diffusion model learns to reverse progressive corruption, usually beginning from noise. Its denoising objective is stable and its coverage and visual quality are strong, but conventional sampling requires many iterative network evaluations. Unit 16 provides the variational-inference foundation for VAEs, and Unit 19 develops diffusion and score-based generation in depth. Model choice depends on the data, controllability, latency, likelihood needs, and failure costs, not on a universal winner.

5. Variational Autoencoders: Learning a Generative Latent Space

An ordinary autoencoder maps an input to one code and reconstructs it. A Variational Autoencoder (VAE) instead uses an encoder qϕ(zx)q_\phi(z\mid x) to predict a distribution over latent codes, usually diagonal Gaussian parameters μ(x)\mu(x) and logσ2(x)\log\sigma^2(x). A decoder pθ(xz)p_\theta(x\mid z) maps a sampled code back to a distribution over observations.

Sampling directly would obstruct backpropagation through the encoder. The reparameterization trick writes z=μ+σϵ,ϵN(0,I),σ=exp(12logσ2).z=\mu+\sigma\odot\epsilon,\qquad \epsilon\sim\mathcal{N}(0,I),\qquad \sigma=\exp\left(\tfrac12\log\sigma^2\right). Randomness now enters through ϵ\epsilon, while μ\mu and σ\sigma remain differentiable transformations.

Training minimizes a reconstruction term plus divergence from a simple prior: LVAE=Lrecon+DKL ⁣(qϕ(zx)p(z)),p(z)=N(0,I).\mathcal{L}_{VAE}=\mathcal{L}_{recon}+D_{KL}\!\left(q_\phi(z\mid x)\,\|\,p(z)\right),\qquad p(z)=\mathcal{N}(0,I). For a diagonal Gaussian, DKL=12j(1+logσj2μj2σj2)D_{KL}=-\tfrac12\sum_j(1+\log\sigma_j^2-\mu_j^2-\sigma_j^2). The reconstruction term preserves information about xx; KL regularization makes nearby latent samples decode coherently.

A structured latent space supports interpolation and conditional generation. Important failure modes include posterior collapse, where the decoder ignores zz and the approximate posterior matches the prior, and blurred outputs when a simple pixel-wise likelihood averages multiple plausible reconstructions. Capacity, the KL weight, decoder strength, and likelihood choice all influence this balance.

A VAE Samples a Regularized Latent Distribution

INPUTxENCODERqφ(z | x)μlog σ²SAMPLE zμ + σ⊙εDECODERpθ(x | z)ELBO TRADEOFFreconstruct x + keep qφ(z | x) near N(0, I)
Read diagram labels
  • INPUT
  • x
  • ENCODER
  • qφ(z | x)
  • μ
  • log σ²
  • SAMPLE z
  • μ + σ⊙ε
  • DECODER
  • pθ(x | z)
  • ELBO TRADEOFF
  • reconstruct x + keep qφ(z | x) near N(0, I)
Jupyter Code Notebook Cell
Python 3 (ipykernel)
In [1]:
import torch
import torch.nn as nn

torch.manual_seed(7)

class TinyVAE(nn.Module):
    def __init__(self):
        super().__init__()
        self.encoder = nn.Linear(4, 4)   # two mus + two log-variances
        self.decoder = nn.Linear(2, 4)

    def forward(self, x, epsilon):
        mu, logvar = self.encoder(x).chunk(2, dim=-1)
        z = mu + torch.exp(0.5 * logvar) * epsilon
        reconstruction = self.decoder(z)
        return reconstruction, mu, logvar, z

# Fix tiny weights so the forward pass has a reproducible result.
model = TinyVAE()
with torch.no_grad():
    model.encoder.weight.zero_()
    model.encoder.bias.copy_(torch.tensor([0.5, -0.5, 0.0, 0.0]))
    model.decoder.weight.copy_(torch.tensor([
        [1.0, 0.0], [0.0, 1.0], [1.0, 1.0], [-1.0, 1.0]
    ]))
    model.decoder.bias.zero_()

epsilon = torch.tensor([[0.2, -1.0]])
reconstruction, mu, logvar, z = model(torch.zeros(1, 4), epsilon)
reconstruction_loss = torch.tensor(0.80)
kl_loss = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
total_loss = reconstruction_loss + kl_loss

print("z:", z.tolist())
print("decoder output:", reconstruction.tolist())
print(f"reconstruction: {reconstruction_loss:.2f}")
print(f"KL: {kl_loss:.2f}")
print(f"total: {total_loss:.2f}")
Out [1]:
Example output (precomputed; this website does not execute code):
z: [[0.699999988079071, -1.5]]
decoder output: [[0.699999988079071, -1.5, -0.800000011920929, -2.200000047683716]]
reconstruction: 0.80
KL: 0.25
total: 1.05
Worked Example 1

One VAE latent sample and ELBO

Problem

Use μ=[0.5,0.5]\mu=[0.5,-0.5], logσ2=[0,0]\log\sigma^2=[0,0], and ϵ=[0.2,1]\epsilon=[0.2,-1]. If reconstruction loss is 0.800.80, compute zz, KL loss, and total loss.

Step-by-step solution

1.Because logσ2=0\log\sigma^2=0, each standard deviation is σ=exp(0/2)=1\sigma=\exp(0/2)=1.

2.z=μ+σϵ=[0.5,0.5]+[0.2,1]=[0.7,1.5]z=\mu+\sigma\odot\epsilon=[0.5,-0.5]+[0.2,-1]=[0.7,-1.5].

3.With unit variances, the KL term is 12(0.52+(0.5)2)=0.25\tfrac12(0.5^2+(-0.5)^2)=0.25.

Final answer and interpretation

The total loss is 0.80+0.25=1.050.80+0.25=1.05.

The reparameterized sample is differentiable with respect to the encoder outputs, while KL keeps the learned distribution near the prior.

6. Generative Adversarial Networks: Learning Through Competition

A Generative Adversarial Network (GAN) contains a generator GG that maps random noise zp(z)z\sim p(z) to synthetic samples and a discriminator DD that estimates whether an input came from the data distribution. The original minimax game is minGmaxDExpdatalogD(x)+Ezp(z)log ⁣(1D(G(z))).\min_G\max_D \mathbb{E}_{x\sim p_{data}}\log D(x)+\mathbb{E}_{z\sim p(z)}\log\!\left(1-D(G(z))\right).

Training alternates the players. During a discriminator update, real examples should approach label 11, generated examples should approach label 00, and fake samples are detached so that step does not update GG. During a generator update, gradients flow through D(G(z))D(G(z)) into GG. In practice, the non-saturating generator loss LG=EzlogD(G(z))\mathcal{L}_G=-\mathbb{E}_z\log D(G(z)) supplies stronger early gradients than minimizing log(1D(G(z)))\log(1-D(G(z))).

GANs can generate sharp samples in one forward pass, but their game is sensitive to learning rates, architecture, and the relative strength of the two players. Mode collapse occurs when many noise inputs map to a narrow set of outputs. A low generator loss alone does not prove diversity or faithful coverage, so sample quality and distributional metrics must be examined together.

GAN Training Alternates Two Competing Updates

NOISE zsample priorGENERATOR Gz → synthetic xfakeDISCRIMINATOR Dprobability of realREAL DATA xtarget = 1D UPDATEreal → 1, fake → 0detach fakeG UPDATE: −log D(G(z))gradient flows through D into G
Read diagram labels
  • NOISE z
  • sample prior
  • GENERATOR G
  • z → synthetic x
  • fake
  • DISCRIMINATOR D
  • probability of real
  • REAL DATA x
  • target = 1
  • D UPDATE
  • real → 1, fake → 0
  • detach fake
  • G UPDATE: −log D(G(z))
  • gradient flows through D into G
Jupyter Code Notebook Cell
Python 3 (ipykernel)
In [1]:
import torch
import torch.nn as nn

torch.manual_seed(11)
G = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 2))
D = nn.Sequential(nn.Linear(2, 4), nn.ReLU(), nn.Linear(4, 1))
loss_fn = nn.BCEWithLogitsLoss()
g_opt = torch.optim.Adam(G.parameters(), lr=1e-3)
d_opt = torch.optim.Adam(D.parameters(), lr=1e-3)
real = torch.tensor([[1.0, 0.8], [0.9, 1.1]])
z = torch.tensor([[0.2, -0.4], [-0.7, 0.3]])

# One discriminator update; detach prevents G from receiving gradients.
fake = G(z)
d_loss = loss_fn(D(real), torch.ones(2, 1)) + \
         loss_fn(D(fake.detach()), torch.zeros(2, 1))
d_opt.zero_grad(); d_loss.backward(); d_opt.step()

# One non-saturating generator update through the discriminator.
g_loss = loss_fn(D(G(z)), torch.ones(2, 1))
g_opt.zero_grad(); g_loss.backward(); g_opt.step()

# Exact summed probability-space arithmetic from the worked example.
d_example = -(torch.log(torch.tensor(0.9)) +
              torch.log(torch.tensor(1.0 - 0.2)))
g_example = -torch.log(torch.tensor(0.2))
print(f"example D loss: {d_example:.3f}")
print(f"example G loss: {g_example:.3f}")
Out [1]:
Example output (precomputed; this website does not execute code):
example D loss: 0.329
example G loss: 1.609
Worked Example 1

Discriminator and generator losses

Problem

For D(x)=0.9D(x)=0.9 and D(G(z))=0.2D(G(z))=0.2, calculate the summed discriminator loss and the non-saturating generator loss.

Step-by-step solution

1.The real-data term is log(0.9)0.105-\log(0.9)\approx0.105.

2.The fake-data term is log(10.2)=log(0.8)0.223-\log(1-0.2)=-\log(0.8)\approx0.223.

3.Summing them gives LD0.105+0.223=0.329\mathcal{L}_D\approx0.105+0.223=0.329.

Final answer and interpretation

The generator tries to make fake samples look real: LG=log(0.2)1.609\mathcal{L}_G=-\log(0.2)\approx1.609.

A confident discriminator creates a large generator loss; alternating updates are needed because each player's target changes with the other player.

7. The LLM Training Lifecycle

Pretraining teaches a decoder-only language model to predict the next token from preceding tokens. This broad objective learns reusable linguistic and factual patterns, but it does not by itself teach the model to follow user instructions. Encoder models such as BERT instead use masked-token objectives, so not every Transformer is pretrained with next-token prediction.

Supervised fine-tuning (SFT) trains on curated prompt-response examples. LoRA and related parameter-efficient methods freeze most base weights and learn small adapters; they often reduce trainable parameters and memory, but the exact cost reduction depends on model, optimizer, hardware, and deployment choices.

Preference tuning targets behavior beyond imitation. RLHF can fit a reward model to human comparisons and optimize a policy against it, commonly with a reference-model constraint. Direct Preference Optimization (DPO) uses chosen/rejected pairs to optimize a closed-form classification-style objective relative to a reference policy. Neither method guarantees truthfulness or safety; data quality, evaluation, system design, and deployment controls still matter.

8. Application Systems: RAG, Multimodality, and Tool Use

Retrieval-Augmented Generation (RAG) retrieves candidate passages, places selected evidence in the model context, and asks the model to answer from that evidence. It can improve grounding and update knowledge without changing model weights, but it does not guarantee factuality: retrieval can miss, rank irrelevant material, expose conflicting sources, or be ignored or misinterpreted by the generator. Production systems therefore evaluate retrieval and answer attribution separately.

Multimodal systems map text, images, audio, video, or other signals into representations a shared model can process. Designs may use modality-specific encoders, cross-attention, a unified token space, or combinations of these. The durable idea is learned communication between modalities, not any particular product version.

A tool-using agent lets a model choose structured actions such as search, calculation, code execution, or database queries, observe results, and continue. The surrounding application owns permissions, validation, stopping conditions, and audit trails. More autonomous loops create more opportunities for compounding error, so constrained tools, explicit budgets, and human approval for consequential actions are architectural requirements rather than optional polish.

Interactive Practice Quiz

Test your understanding with instant feedback

QUESTION 01

What problem did Bahdanau attention address in early encoder-decoder RNNs?

QUESTION 02

Which statement fairly compares Transformers with RNNs?

QUESTION 03

If Q,KRn×dkQ,K\in\mathbb{R}^{n\times d_k}, what is the shape of QKTQK^T?

QUESTION 04

Why is softmax applied row-wise to attention scores?

QUESTION 05

What does a causal attention mask do before softmax?

QUESTION 06

What happens after parallel attention heads produce their outputs?

QUESTION 07

With μ=[0.5,0.5]\mu=[0.5,-0.5], σ=[1,1]\sigma=[1,1], and ϵ=[0.2,1]\epsilon=[0.2,-1], what is the VAE sample zz?

QUESTION 08

What are the two main terms in the VAE loss?

QUESTION 09

Why detach G(z)G(z) during a GAN discriminator update?

QUESTION 10

What is GAN mode collapse?

QUESTION 11

Which family directly learns a smooth probabilistic latent code useful for interpolation?

QUESTION 12

Which statement about RAG is accurate?