Concepts / Data Loaders and Batching

Data Loaders and Batching

Dataset choice is therefore a matter of goal and constraints.

  • Programming

Why Start With a Small Public Domain Text

Before a model can be trained, its data has to exist in a form the training code can use. This article picks up at that step. The goal is to prepare a training dataset and a validation dataset, and then to compute the cross entropy loss on both. Those two losses are the central quantities of model training, so the data pipeline has to be correct before any loss value means anything.

The running text is 'The Verdict', a short story by Edith Wharton. It was already used in chapter 2. Choosing it solves two problems at once. Because the story is in the public domain, there is no question about usage rights. Because it is small, the code runs on a standard laptop in a matter of minutes, even without a high-end GPU.

To see how far this example is from production work, consider the 7 billion parameter Llama 2 model, which the source describes as a relatively popular openly available LLM. Training it required 184,320 GPU hours on A100 GPUs and processed 2 trillion tokens. Dataset choice is therefore a matter of goal and constraints. A small public domain text answers the questions of licensing and speed, which matters when the aim is to learn mechanics. A larger corpus is the route toward more extensive training, at a much higher computational cost.

Reading the Text and Counting Two Units

The first code step reads the whole story into a single string. The file is opened in read mode with UTF-8 encoding, and file.read() returns its full contents. The file name below is a placeholder used for illustration.

python
Notes on the code
  • open(...) with mode "r" opens the file for reading only
  • encoding="utf-8" tells Python how to turn the bytes in the file into characters
  • file.read() returns the full contents as one string, stored in text_data
  • len(text_data) counts characters in the string

Once the text is in memory, the code measures it in two different units. The number of characters comes from len(text_data). The number of tokens comes from passing the same text through the tokenizer and measuring the result. Both measurements are compared directly, because they describe the same span of text in different units.

UnitHow it is obtainedWhat it describesValue in this text
Characterslen(text_data)The raw text as typedNot stated numerically in the source
TokensRun the same text through the tokenizer and measure the resultHow the model will actually read the text5,145 tokens

Two counts for the same text

Computing the Split Index

Next the text is divided into a training part and a validation part. The validation part is used for model evaluation during training, so it must be kept separate from the training part. The split is set by a train_ratio of 0.90, which means 90 percent of the characters go to training and the remaining 10 percent go to validation. The division point is called split_idx, and it is computed from train_ratio. Roughly, if N is the total number of characters, the split index sits near $\approx 0.90 \times N$, with training covering the characters before it and validation covering the characters from it onward.

Training partcharacters 0 up tosplit_idxValidation partcharacters split_idx to theendsplit_idx
Which character positions fall before the split index and which fall after it, with train_ratio set to 0.90?
Worked example

Computing the split for a hypothetical text

Suppose a text has 10,000 characters and train_ratio is 0.90. Where does the split fall, and how many characters go to each part? The numbers here are hypothetical, chosen only to show the arithmetic.

  1. 1
    Identify the total

    The total is N = 10,000 characters.

  2. 2
    Apply the ratio

    The training part takes 90 percent, so split_idx is about 0.90 times 10,000, which is 9,000.

  3. 3
    Assign the characters

    Characters 0 through 8,999 form the training part, which is 9,000 characters. Characters 9,000 through 9,999 form the validation part, which is 1,000 characters.

  4. 4
    Check the parts

    9,000 plus 1,000 equals 10,000, so no character is lost or counted twice.

Result

split_idx is 9,000. Training gets 9,000 characters and validation gets 1,000.

Tracing Split Text Into Batches

A split subset is still plain text, and the model needs fixed-size tensors. The source describes the preparation as a chain of operations: the subset is tokenized, the token sequence is divided into chunks of a user-specified length, the chunks are shuffled, and the shuffled chunks are organized into batches. The batch size sets how many chunks are processed together. Follow one training subset through each stage in the diagram below.

Training textfirst 90% of charactersToken sequencetoken IDs from thetokenizerChunkseach 256 tokens longShuffled chunksorder varies under a fixedseedBatches2 samples, shape (2, 256)tokenizedivide into chunksshufflegroup by batch size
In what order do the training text, chunks, shuffled samples, and batches flow, and what shape does the data have at each stage?

Notice that the shape of the data changes at each stage. Text is a string, the token sequence is a flat list of token IDs, a chunk is a fixed-length window of that list, and a batch stacks several chunks together. The final batch shape is (batch_size, context_length), so with a batch size of 2 and 256-token chunks, each batch holds two samples of 256 tokens.

Each loader yields a pair: an input x and a target y. Each chunk therefore serves two roles, and the two windows are always taken from the same token sequence. The explanation below depends on that pairing.

Input xtokens 0 to 255Target ytokens 1 to 256shifted by one
Where does each chunk start and end in the token sequence, and how does the target window shift one position relative to the input?

The one-position shift is the usual convention for next-token prediction: the target is the same stream of tokens as the input, moved forward by one position, so the model is asked to predict each next token. The source's loop confirms that each batch is an input and target pair, so the shift is the part to check when you inspect the windows.

Configuring the Training and Validation Loaders

The loaders are built with the create_dataloader_v1 function from chapter 2. Both calls use the same batch size, max_length, and stride. The settings that differ are drop_last and shuffle, and those differences encode the training-versus-validation distinction. The code also fixes a random seed so the shuffling is repeatable.

SettingTraining loaderValidation loaderReason given in the source
Batch size22Same for both loaders
max_length256256Same for both loaders
strideSame as validationSame as trainingSame for both loaders
shuffleOnOffTraining order should vary; a fixed validation order keeps evaluation consistent
drop_lastTrueFalseTraining drops an incomplete final batch; validation keeps every chunk

Matching settings and the settings that differ

Setting drop_last to True on the training loader discards an incomplete final batch, so every training batch has the full size. The validation loader sets it to False, so no validation chunk is silently thrown away. Shuffling is switched on for training, where the order of examples should vary, and off for validation, where a fixed order keeps evaluation consistent.

Checking Batch Shapes by Printing

The source suggests an optional check. Iterate through each loader and print the shape of every input and target tensor. Each loader yields a pair, so the loop unpacks two values per batch. Try predicting the result before you read the answer.

python
Notes on the code
  • The loop unpacks each batch into an input x and a target y
  • print(x.shape, y.shape) reports the tensor dimensions of each batch
  • The same check runs on the validation loader to confirm its batches

What do you think happens?

How many batches does the training loader yield, and what shape does each input x have?

  • Two batches, each of shape (256, 2)

  • Nine batches, each of shape (2, 256)

  • One batch, of shape (2, 9)

Reveal answer
Answer:

Nine batches, each of shape (2, 256)

The source reports that the training loader yields nine batches, each with two samples of 256 tokens. The validation loader yields one batch of two input examples, which matches the expectation that only about a tenth of the text goes to validation.

Mistakes Beginners Make With Splits and Loaders

  • Common mistake

    Counting characters and assuming the model reads that many units

    What it looks like

    Reporting the len(text_data) result as the size of the model's input

    Why it fails

    The character count describes the raw text, while the token count describes how the model reads it.

    Do this instead

    Report token counts when describing what the model reads, and keep character counts for the raw text.

  • Common mistake

    Shuffling the validation loader

    What it looks like

    Setting shuffle to True on both loaders for consistency

    Why it fails

    Validation uses a fixed order so evaluation stays consistent from run to run.

    Do this instead

    Set shuffle to True only on the training loader, as the source does.

  • Common mistake

    Setting drop_last to True on validation

    What it looks like

    Copying the training loader's settings to the validation loader

    Why it fails

    Validation would silently lose its incomplete final batch.

    Do this instead

    Keep drop_last False on the validation loader.

  • Common mistake

    Letting the subsets overlap

    Why it fails

    Validation would then measure text the model has already seen during training.

    Do this instead

    Use one split_idx so the training part and the validation part never share characters.

  • Common mistake

    Treating a batch size of 2 as a model-scale setting. The source uses 2 only because the dataset is small.

Choosing a Batch Size

Batch size is also a resource choice. The source uses a batch size of 2 because the dataset is small. It notes that training LLMs with batch sizes of 1,024 or larger is not uncommon in practice. The batch size used in a small exercise should therefore be read as a classroom setting that keeps runs quick, not as a recommendation for real training.

Practice: Compute a Split and Its Batches

MEDIUM

A hypothetical text has 8,000 characters and train_ratio is 0.75. Find split_idx, the number of characters in the training part, and the number in the validation part. Then say how many characters of the validation part would be in the training loader if the subsets were mixed by mistake.

Hints
  • Multiply the total character count by train_ratio to get split_idx
  • Characters before split_idx belong to training, and the rest belong to validation
  • Mixing the subsets would give the training loader characters that belong to validation

Key Takeaways

  • A small public domain text such as 'The Verdict' lets the whole pipeline run on a laptop in minutes, which suits learning, while larger corpora and real pretraining scale up the cost sharply.
  • Characters and tokens are different counts. The token count describes what the model reads, so it is the count that matters for the model.
  • A split_idx computed from train_ratio divides the text into a training part and a validation part that are never mixed.
  • Split text is tokenized, chunked into fixed-length windows, shuffled, and grouped into batches of shape (batch_size, context_length).
  • The training loader shuffles and drops incomplete batches, while the validation loader keeps a fixed order and keeps every chunk; printed batch shapes confirm the loaders were built correctly.

Related concepts

More concepts starting with D