Intuition The one core idea
A model learns by looking at data, but the data lives on your hard drive (huge, slow, messy) while the model lives on the GPU (fast, hungry, needs neat rectangular numbers). A Dataset says "here is sample number i " and a DataLoader says "here are 32 of them, stacked and shuffled, ready to eat." Everything on the parent page is just the careful engineering of that one sentence.
Before you can read the parent note, you must be able to read its alphabet . Below is every symbol and idea it silently assumes, built from nothing, in an order where each rung of the ladder rests on the one below it.
A sample is one complete training example: one picture together with the answer we want ("cat"), or one sentence together with its meaning. We write a sample as a pair ( x , y ) .
x = the input (the picture, the sentence). Read the letter x as "the thing we show the model."
y = the label (the correct answer). Read y as "the thing the model should output."
We have many samples, so we number them 0 , 1 , 2 , … The number is called an index and we write it i . Sample number i is ( x i , y i ) — the little i hanging below is just "which one."
Intuition Why we number things
A computer cannot say "give me that cat picture." It can only say "give me item number 5 ." Indexing turns a messy folder of files into an addressable list. This is why the parent's __getitem__(idx) takes a number: idx is our i .
N — how many samples
N is the total number of samples . If your folder has 1000 cat/dog pictures, N = 1000 . The parent's __len__() returns exactly this N .
Because we start counting at 0 (computers do), the valid indices run
i = 0 , 1 , 2 , … , N − 1
That range is written [ 0 , N − 1 ] — a compact way of saying "from 0 up to and including N − 1 ."
∈
∈ means =="is an element of" / "belongs to"==. The line i ∈ [ 0 , N − 1 ] reads out loud as: "i is one of the numbers from 0 to N minus 1." If somebody hands you i = 1000 when N = 1000 , that is out of range — there is no sample number 1000, the last one is 999.
Z
Z is the set of all whole numbers (… , − 2 , − 1 , 0 , 1 , 2 , … ). The parent writes Dataset : Z → … to say "you feed it a whole number (an index) and it hands you back a sample." Indices are whole numbers because there is no "picture number 3.7."
Common mistake Off-by-one trap
Beginners write range(1, N) and silently skip sample 0, or write range(N+1) and crash on the missing sample N . Always: first index 0, last index N − 1 , count is N .
The parent writes the whole dataset as
D = {( x i , y i ) } i = 1 N
Let us dismantle this piece by piece.
Definition Curly braces = a collection
{ … } means "the collection of all the things inside." {( x i , y i )} means "the collection of all sample pairs."
The little $i=1$ underneath and $N$ on top is a recipe for the collection : "let i run from 1 to N , and for each value drop the pair ( x i , y i ) into the bag." So the fancy line just says:
D = {( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N )}
The curly D is simply a name for that whole bag of data — "D for Dataset."
Common mistake Math counts from 1, code counts from 0
The parent's math writes i = 1 , … , N but the code writes indices 0 , … , N − 1 . Same N samples — just two dialects. Don't let it confuse you.
→
A → B means a ==machine that takes an A and returns a B ==. The parent's Dataset Contract
Dataset : Z → X × Y
reads: "the Dataset is a machine: put in a whole number (index), get out a pair of (data, label)."
X = the space of all possible inputs (all possible images).
Y = the space of all possible labels (all possible answers).
X × Y (the × is "paired with") = the space of all (input, label) pairs .
function , not a list ?
A stored list of a million loaded images would need ~150 GB of RAM before training even starts. A function stores nothing — it only knows how to fetch item i the moment you ask. That is exactly the parent's "lazy loading." The Dataset is a promise, not a warehouse.
Two properties fall straight out of "it's a function":
Deterministic : ask for index 5 twice, get the same sample twice. (Randomness lives elsewhere — see §7.)
Independent : fetching sample 5 does nothing to sample 6.
Definition Batch and batch size
B
A batch is a small handful of samples processed together. The number in the handful is the batch size , written B . Typical B = 32 .
The reason batches exist is the gradient formula the parent quotes:
g ^ = B 1 ∑ i = 1 B ∇ θ L ( x i , y i ; θ )
Let us earn every symbol here so it is not scary.
Definition The summation symbol
∑
∑ i = 1 B ( stuff i ) means =="add up 'stuff' for i = 1 , 2 , … , B ." == It is just a compact "+ + + + ...". So ∑ i = 1 3 a i = a 1 + a 2 + a 3 .
L (curly L) = the loss : one number saying "how wrong was the model on this sample" (big = very wrong, 0 = perfect).
θ (Greek "theta") = the weights of the model — the knobs it is allowed to turn.
∇ θ (upside-down triangle, "nabla") = the gradient w.r.t. θ : an arrow pointing in the direction that would increase the loss fastest; we step the opposite way to reduce it.
B 1 ∑ … = average the per-sample directions over the batch.
g ^ (a g wearing a hat) = the estimate of the true gradient. The hat always means "our best guess of," not the exact thing.
Intuition Why average over a batch instead of one sample?
One sample's arrow is jittery and noisy — like judging the wind from one leaf. Averaging B arrows cancels the jitter (variance shrinks like 1/ B ), giving a steadier step downhill. This is the whole reason a DataLoader hands you batches . Deeper story: 2.2.03-Mini-Batch-Gradient-Descent .
If N samples are cut into groups of B , how many groups? Usually N doesn't divide evenly, so we need three little tools.
⌊ ⌋ and Ceiling ⌈ ⌉
⌊ a ⌋ = ==round down to the nearest whole number==. ⌊ 3.7 ⌋ = 3 .
⌈ a ⌉ = ==round up to the nearest whole number==. ⌈ 3.2 ⌉ = 4 .
mod
N mod B = ==the remainder left over after packing full batches==. 10 mod 3 = 1 (three groups of 3, one left over).
Now the parent's two options make total sense. With N = 100 , B = 32 :
⌊ 100/32 ⌋ = 3 ( full batches ) , 100 mod 32 = 4 ( leftovers )
drop_last=True → throw away the 4 leftovers → ⌊ N / B ⌋ = 3 batches, all size 32.
drop_last=False → keep them as a small final batch → ⌈ N / B ⌉ = 4 batches (last one size 4).
Worked example Quick check
N = 1000 , B = 32 . Then 1000 = 31 × 32 + 8 , so 1000 mod 32 = 8 , ⌊ 1000/32 ⌋ = 31 , ⌈ 1000/32 ⌉ = 32 . Drop-last gives 31 batches; keep gives 32 (last one has 8 samples).
To shuffle is to randomly reorder the indices 0 , 1 , … , N − 1 before cutting them into batches. The samples themselves never change; only the order of visiting them does.
Intuition Why shuffle every epoch?
If your folder is cat, cat, cat, ..., dog, dog, dog, then early batches are all cats and the model over-focuses on cats, then swings to dogs. Shuffling mixes them so each batch is a fair sample of the whole world. A full sweep over all N samples is called an epoch ; we reshuffle at the start of each one so the model never memorises a fixed sequence. This feeds straight into the training loop .
A tensor is just a box of numbers with a fixed rectangular shape . A number is a 0-D tensor; a list of numbers is 1-D; a grid is 2-D; a stack of grids is 3-D, and so on. An image is a 3-D tensor of shape ( 3 , 224 , 224 ) : 3 colour channels, each a 224 × 224 grid of pixel brightnesses.
Definition Shape and the space
R B × d
R = the set of real (decimal) numbers . R B × d means "a rectangular block of real numbers with B rows and d columns." So ( X j , Y j ) ∈ R B × d × R B from the parent reads: "batch number j is B input rows (each d numbers wide) paired with B labels."
Intuition Why must a batch be rectangular?
GPUs multiply matrices, and a matrix is a perfect rectangle — no ragged edges allowed. So 32 samples of shape ( 3 , 224 , 224 ) get stacked into one tensor of shape ( 32 , 3 , 224 , 224 ) . That stacking is exactly what the parent calls collation , and when samples have different lengths (sentences!) you must pad the short ones to make the box rectangular again.
set notation and the mapping arrow
Dataset as a function fetch by index
floor ceiling modulo count batches
tensor shape and rectangle
Datasets and DataLoaders 3.3.4
Everything on the left is foundation ; it all funnels into the Datasets and DataLoaders topic itself, which then powers the training loop , scales up with distributed training , and reuses pretrained data pipelines in transfer learning .
Read each question, answer in your head, then reveal.
What does a single "sample" ( x i , y i ) contain? An input x i (e.g. an image) paired with its label y i (the correct answer).
Why do we start indices at 0 and end at N − 1 ? Computers count from 0, so N items occupy positions 0 through N − 1 ; index N would be out of range.
Read aloud: i ∈ [ 0 , N − 1 ] . "i is one of the whole numbers from 0 up to N minus 1."
What does the curly D = {( x i , y i ) } i = 1 N mean in words? The collection of all N sample pairs, one for each index i from 1 to N.
Why is a Dataset a function (Z → X × Y ) rather than a stored list? A function fetches sample i only when asked (lazy loading), so RAM stays tiny; a stored list would need to hold everything at once.
What does ∑ i = 1 B tell you to do? Add up the following expression for i = 1, 2, ..., B.
In g ^ = B 1 ∑ ∇ θ L , what is g ^ and why the hat? An estimate of the true gradient (the average step-direction over the batch); the hat means "our best guess," not the exact value.
Why average the gradient over a batch instead of using one sample? One sample's gradient is noisy; averaging B of them cancels the jitter and gives a steadier descent step.
With N = 100 , B = 32 : how many batches with drop_last=True vs False? ⌊ 100/32 ⌋ = 3 with drop_last=True; ⌈ 100/32 ⌉ = 4 with drop_last=False (last batch has 4 samples).
What does N mod B compute? The remainder — the number of leftover samples after packing as many full batches of size B as possible.
Does shuffling change the samples themselves? No — only the order in which they are visited each epoch; the pairs are untouched.
Why must a batch tensor be rectangular (shape like ( 32 , 3 , 224 , 224 ) )? GPUs operate on rectangular matrices/tensors; every sample in the batch must share the same shape, which is why unequal-length data must be padded.