Creating a Custom Dataset for Instruction Data
Each split is loaded by creating an InstructionDataset for it and then wrapping that Dataset in a DataLoader.
Three Splits, One Repeated Pattern
Instruction fine-tuning works with three separate splits of data: training data, validation data, and test data. Each split has to reach the model in batches, and the code that does this follows one repeated pattern. For each split, you first wrap the raw data in a custom Dataset object, then pass that Dataset to PyTorch's DataLoader along with the settings that control how batches are formed.
The goal of this article is not to memorize a setup listing line by line. The goal is to learn to read such a listing so that, for any loader, you can tell which data it reads, how it groups that data into batches, and how its ordering and parallelism are set.
What do you think happens?
Suppose a split has 7 examples, batch_size is 3, and shuffle=False with drop_last=False. How many examples are in the final batch, and does shuffling change that count?
Reveal answer
The final batch has 1 example, and shuffling does not change the count.
Seven examples split into batches of 3 gives two full batches of 3 (6 examples) and one leftover example. With drop_last=False, that leftover example is kept as a smaller final batch. Shuffling only changes which examples land in each batch, not how many examples there are, so the final batch still holds 1 example.
Tracing One Split from Raw Data to Batches
Each split follows the same two steps. First, a dataset is created from the split. In the listing this article is based on, that Dataset is an InstructionDataset, a custom Dataset class built for instruction data. Its constructor takes the raw split and a tokenizer, as in InstructionDataset(train_data, tokenizer). Second, that dataset is wrapped in a DataLoader, which produces the batches the model consumes.
The DataLoader does not know how instruction data is stored. It relies on the Dataset to supply individual examples. The Dataset answers the question where do the examples come from, while the DataLoader answers how many examples go together and in what order they are handed out.
- InstructionDataset wraps the raw training split and the tokenizer
- DataLoader receives the dataset as its first argument
batch_sizesets how many examples form one batchcollate_fnnames the function that assembles a list of examples into a batch- shuffle,
drop_last, andnum_workerscontrol ordering, batch completeness, and parallel loading
Reading a DataLoader Call Argument by Argument
A DataLoader needs two inputs: a Dataset instance and a batch_size. The Dataset tells the loader where the examples come from, and batch_size tells it how many examples to group together. The other keyword arguments adjust how batches are formed and loaded. In the listing this article follows, every DataLoader call passes the same set of keyword arguments after the dataset, and each one changes a different part of the loading behavior.
| Argument | What it controls | Role in this listing |
|---|---|---|
| dataset | The Dataset instance the loader reads examples from | Each split's InstructionDataset |
| batch_size | How many examples are grouped into one batch | Shared across the three loaders |
| collate_fn | How a list of individual examples is assembled into a batch | Shared across the three loaders |
| shuffle | Whether the order of examples is randomized | True for training, False for validation and test |
| drop_last | Whether an incomplete final batch is discarded | True for training, False for validation and test |
| num_workers | How many worker processes load data in parallel; 0 means no parallel processing | Controls parallel loading |
The arguments in a DataLoader call and the role each one plays
To read a call, start with the first argument to find the dataset, then check batch_size for the group size. Next, look at shuffle and drop_last, which decide the order and the completeness of the batches. Finally, look at num_workers to see whether loading happens in parallel. Reading the call in this order lets you answer for any loader which data it reads and how that data is grouped.
Following Indices Through shuffle and drop_last
Consider a training split with 7 examples, numbered by their dataset index from 0 to 6, and a loader with batch_size=3, shuffle=True, and drop_last=True. Shuffling changes the order in which indices are visited, so the same examples may be grouped differently on a different pass. The grouping below is one possible result for a single pass.
One shuffled pass with drop_last=True
Seven examples (indices 0 through 6), batch_size=3, shuffle=True, drop_last=True. Which indices form each batch, and which are discarded?
- 1Count the full batches
Seven examples divided into groups of 3 gives two full batches, which hold 6 examples, and 1 leftover example.
- 2Shuffle the visiting order
With shuffle=True the indices are visited in a randomized order. For this pass the order is 4, 0, 6, 2, 5, 1, 3.
- 3Fill batch 1
The first three indices in the shuffled order are 4, 0, and 6, so batch 1 contains examples 4, 0, and 6.
- 4Fill batch 2
The next three indices are 2, 5, and 1, so batch 2 contains examples 2, 5, and 1.
- 5Apply drop_last
The leftover index 3 does not fill a complete batch. With
drop_last=True it is discarded for this pass.
Batch 1 holds indices 4, 0, 6. Batch 2 holds indices 2, 5, 1. Index 3 is dropped for this pass.
The diagram shows the key point of this section. Shuffling decides which indices share a batch, and drop_last decides what happens to the remainder. Index 3 is not lost because of the shuffle itself; it is lost because the number of examples did not divide evenly into full batches and drop_last discarded the incomplete group.
What num_workers Changes in the Loading Flow
num_workers controls parallel data loading. A value of 0 means there is no parallel processing: examples are loaded in the main process. Higher values allow loading to be spread across worker processes, and whether those higher values work depends on operating system support.
The path a batch takes is the same regardless of num_workers. Examples come from the Dataset, pass through the collate function that assembles them into one batch, and the resulting batch is handed to the training loop. What num_workers changes is where the reading of examples happens: with 0 it happens in one process, and with more workers the reading can proceed in parallel before the batches reach the training loop.
The diagram is a simplified model. The source confirms only that num_workers controls parallel loading and that 0 means no parallel processing. It does not describe the internal mechanics of worker processes, so treat the middle node as the place where num_workers acts, not as a full description of how it is implemented.
Mistakes When Setting Loader Options
Common mistake Treating shuffle as a setting that changes how many examples there are
What it looks likeExpecting a 7-example split with batch_size=3 to produce three full batches after shuffling
Why it failsShuffle only changes the order of visiting examples. The count of examples and the count of full batches are determined by the dataset size and
batch_size.Do this insteadCount the batches from the dataset size and batch_size, then use shuffle to decide which examples fill them.
Common mistake Leaving shuffle=True on validation or test loaders
What it looks likeCopying the training loader's arguments into the test loader unchanged
Why it failsThe source's settings for these loaders are shuffle=False and
drop_last=False. Keeping the training values changes the ordering and drops examples from the evaluation data.Do this insteadSet shuffle=False and drop_last=False for validation and test loaders, as the source does.
Common mistake Assuming a higher num_workers is always better
Why it failsThe source says higher values depend on operating system support, and 0 is the setting that means no parallel processing.
Do this insteadStart from num_workers=0 and only raise it when the environment supports it.
Common mistake Forgetting that the Dataset supplies the examples
Why it failsThe DataLoader does not know how instruction data is stored. If the Dataset is not built correctly, the loader has nothing correct to batch.
Do this insteadCheck that each split's InstructionDataset is created from the right split before wrapping it in a loader.
Building the Train, Validation, and Test Loaders
The three loaders share a batch size and a collate function, so the differences between them come down to the dataset each one wraps and the two flags that change ordering and batch completeness. The training loader shuffles and drops incomplete batches, while the validation and test loaders keep a fixed order and keep every example.
No output is printed; the three loaders are created and ready to iterate.- Each split gets its own Dataset and its own loader
batch_sizeandcollate_fnare identical across all three loaders- Only shuffle and
drop_lastdiffer between the training loader and the other two
The comparison shows that only the training column differs in its loading flags. A reader who understands this pattern can look at any new loader call and check first whether it matches the training pattern or the evaluation pattern.
Practice Reading a Loader Call
A loader is created as DataLoader(test_dataset, batch_size=8, collate_fn=my_collate_fn, shuffle=True, drop_last=True, num_workers=0). Identify the dataset it wraps, the batch size, and whether its ordering and completeness match the source's pattern for a test loader. Explain what you would change and why.
Hints
- The first positional argument is the dataset, so look for the name passed right after DataLoader(
- Compare the shuffle and
drop_lastvalues with the source's settings for validation and test loaders - Think about whether shuffling a test split changes the examples it contains or only the order they are visited in
Key Takeaways
- Each split is loaded by creating an InstructionDataset for it and then wrapping that Dataset in a DataLoader.
- A DataLoader requires a Dataset and a
batch_size;collate_fn, shuffle,drop_last, andnum_workersadjust how batches are formed and loaded. - shuffle changes the order in which examples are visited and
drop_lastdecides whether an incomplete final batch is discarded. num_workerscontrols parallel loading, with 0 meaning no parallel processing and higher values depending on operating system support.- The training loader uses shuffle=True and
drop_last=True, while validation and test loaders use shuffle=False anddrop_last=False, sharing batch size and collate function.