Concepts / Preparing Datasets for Fine-Tuning

Preparing Datasets for Fine-Tuning

Instantiating a PyTorch dataset means calling a custom class such as SpamDataset with a CSV file name and keyword arguments, producing a dataset object.

  • Programming

Where Dataset Preparation Fits

By the time fine-tuning begins, two large pieces of work are already finished. The first is a model that has been pre-trained and loaded into memory. The second is a dataset of instructions that has been cleaned, formatted, and turned into batches. Instruction fine-tuning is the step that connects these two pieces: the model is trained further, this time on instruction examples, so that it learns to respond to the kind of input the dataset describes.

This article looks at the moment just before that training step. The goal is to understand what has been prepared, what the fine-tuning step is allowed to rely on, and how to tell which part of the overall process you are looking at when something goes wrong. The work starts with a single setup listing that creates three dataset objects, so that is where we begin.

Reading the Setup Listing

The listing that prepares the data has three main parts. The first part is the imports: PyTorch, the Dataset base class from torch.utils.data, the tiktoken library, and the SpamDataset class, which the listing imports from a module named chapter06. The second part creates a single tokenizer object with tiktoken.get_encoding("gpt2"). The third part creates three dataset objects, one each for the training, validation, and test CSV files.

A useful habit when reading this listing is to follow it line by line before reading any explanation. For each line, note what the variable is and what the call receives. The tokenizer is created once and that same object is passed to every dataset call, so all three splits are prepared with the same tokenizer.

Calling SpamDataset Like Any Class

Creating a dataset object in this section means calling the custom SpamDataset class the same way you would call any Python class: you write the class name and pass arguments in parentheses. The source describes three arguments. The first is the name of a CSV file, such as train.csv. The second is max_length, a keyword argument. The third is tokenizer, another keyword argument that receives the tokenizer object created earlier.

SpamDatasetcustom class being called"train.csv"file name, first positionalargumentmax_length=keyword argument forsequence lengthtokenizer=keyword argument receivingthe tokenizer objectarg 1arg 2arg 3
Which part of the SpamDataset call chooses the file, which sets the sequence length, and which supplies the tokenizer

The listing uses keyword arguments for max_length and tokenizer. A keyword argument names the parameter it fills, so the call reads as a set of labeled settings rather than a bare list of values. The file name is passed positionally, as the first argument. Keeping the names visible makes it easier, when you later trace a problem, to see which setting went where.

python
Notes on the code
  • The tokenizer is created once and shared by all three calls
  • The training call passes max_length=None
  • The validation and test calls read max_length from the training dataset object
  • The validation.csv and test.csv file names are placeholders chosen for this illustration; the source does not name them

Sharing max_length Across Splits

The training dataset is created with max_length=None. The validation and test datasets are created with max_length=train_dataset.max_length. The listing therefore reads a value from the training dataset object and reuses it for the other two splits. Reading an attribute from an existing object is a normal Python pattern: the dot followed by a name looks up a stored value on that object.

train_datasetcreated withmax_length=Nonetrain_dataset.max_lengthstored value read from theobjectval_datasetmax_length=train_dataset.max_lengthtest_datasetmax_length=train_dataset.max_lengthattribute lookuppassed as max_lengthpassed as max_length
The max_length value is read from the training dataset object and passed to the validation and test dataset calls

What do you think happens?

Before reading on: what value of max_length does val_dataset receive, and is it the same value that train_dataset received?

  • None, the same as the training dataset

  • train_dataset.max_length, which is read from the training dataset

  • A value computed separately from the validation file

Reveal answer
Answer:

val_dataset receives train_dataset.max_length. The test dataset does the same. This is the value read from the training dataset object, so the splits share one setting.

The listing creates train_dataset with max_length=None, then passes train_dataset.max_length to the validation and test calls. Those splits do not compute their own value.

The purpose of this pattern is consistency. If the validation and test datasets were created with a different maximum length than the training dataset, the splits could be prepared differently even though they come from the same data source. The source does not explain the internal logic inside SpamDataset that determines the max_length value, because the class is imported from chapter06 and its definition is not in the listing. What the listing does show is which value gets passed where, and that is what you can check when tracing a run.

Ordering Datasets Before Data Loaders

The source states that data loaders are created after the dataset objects have been instantiated. In this section's sequence, the three dataset objects come first, and the data loaders are built from them afterward. The dataset objects hold the prepared data; the data loaders are the next step that depends on those objects existing.

The source text at this point does not include the code that builds the data loaders, so this article does not show that code. What you can rely on is the ordering: a dataset object must exist before a data loader can be created from it. If a loader were built before its dataset existed, the name it refers to would not yet be defined.

Three Stages of Instruction Fine-Tuning

Instruction fine-tuning is organized as a three-stage process. Stage 1 prepares the dataset. Stage 2 fine-tunes the LLM. Stage 3 evaluates the LLM. The diagram below places the steps in order and groups each step under the stage it belongs to.

Stage 1: Preparedatasetproduces batched dataloadersLoad pretrained LLMstep inside stage 2Stage 2: Fine-tuneLLMonly stage where weightsare trainedRun instructionfine-tuningstep inside stage 2Inspect modeling lossstep inside stage 2Stage 3: EvaluateLLMextracts and scoresresponsesbatched data loadersstep 1step 2step 3tuned model responses
Where dataset preparation, fine-tuning, and evaluation sit in sequence, and the steps inside the fine-tuning stage

Notice where the fine-tuning work sits. Stage 1 and Stage 3 surround it. Stage 1 produces the batched data loaders that the training step will consume, and Stage 3 looks at the responses the tuned model produces afterward. Stage 2 is the only stage in which the model's weights are trained, and it contains three steps: loading the pretrained LLM, running instruction fine-tuning, and inspecting the modeling loss.

Inputs Meeting at the Fine-Tuning Step

Fine-tuning does not begin from random weights. It continues training the model that already exists, which is the model loaded in the previous section. The data side is the instruction dataset prepared earlier in the chapter. That dataset was processed at the start of the chapter, so the fine-tuning step does not repeat that preparation work.

InputWhat it already isWhat it contributes to fine-tuning
Pre-trained LLMA model that has been pre-trained and loaded into memoryThe starting point: training continues from this model rather than from random weights
Instruction datasetInstructions that have been cleaned, formatted, and turned into batchesThe examples that the further training uses, so the model learns to respond to instruction-style input

What each input brings to the fine-tuning step

Fine-tuning also needs a way to compute loss and run training. The chapter already contains code for both, so the fine-tuning code does not rewrite them. It imports the two functions calc_loss_loader and train_model_simple from chapter 5. Four inputs meet at the single fine-tuning step: the pre-trained LLM, the instruction dataset, and the two chapter 5 functions that supply the loss calculation and the training procedure.

Mistakes Beginners Make with Dataset Setup

  • Common mistake

    Recalculating max_length separately for the validation and test datasets

    What it looks like

    Passing a freshly computed value to val_dataset and test_dataset instead of train_dataset.max_length

    Why it fails

    The splits could then be prepared differently even though they come from the same data source

    Do this instead

    Read train_dataset.max_length and pass that value to both the validation and test calls

  • Common mistake

    Passing the file name in the wrong slot

    What it looks like

    Writing SpamDataset(tokenizer=tokenizer, "train.csv", max_length=None), which places a positional argument after a keyword argument

    Why it fails

    The CSV file name is the first positional argument; placing it after keyword arguments breaks the call pattern the listing uses

    Do this instead

    Pass the file name first, then the labeled keyword arguments max_length and tokenizer

  • Common mistake

    Creating a data loader before its dataset object exists

    What it looks like

    Referring to a loader built from train_dataset when train_dataset has not been assigned yet

    Why it fails

    The dataset name is not yet defined, so the loader has nothing to be built from

    Do this instead

    Instantiate the three dataset objects first, then build the data loaders from them

  • Common mistake

    Rewriting the loss and training functions in the fine-tuning code

    What it looks like

    Copying calc_loss_loader and train_model_simple into the fine-tuning file

    Why it fails

    Those routines already exist in chapter 5 and are reused by import, so copies can drift out of sync with the original

    Do this instead

    Import calc_loss_loader and train_model_simple from chapter 5

Tracing a Failure Through the Stages

Suppose a run of the process produces a result you did not expect. The three-stage structure gives you a way to narrow the search. Ask first which stage the problem most likely belongs to. If the data loaders are wrong, the cause lies in Stage 1. If the model is loaded incorrectly or the loss and training steps misbehave, the cause lies in Stage 2. If the responses look wrong after training finishes, the cause may be in Stage 3, where responses are extracted and scored.

MEDIUM

A run shows that the validation split was prepared with a different maximum length from the training split. Which line of the setup listing should you check first, and which stage does that line belong to?

Hints
  • Look at the arguments passed to the validation dataset call
  • Ask which argument should read train_dataset.max_length
  • Dataset objects are created in Stage 1, the stage that produces the batched data loaders
EASY

Using the three-stage process, decide where each of these belongs: preparing the three dataset objects, loading the pretrained LLM, and extracting responses for scoring. Then name the stage that a loss-calculation fault would be checked against first, and explain why the chapter 5 functions are the first place to look.

Hints
  • Stage 1 prepares the dataset and produces batched data loaders
  • Stage 2 contains loading the pretrained LLM, running instruction fine-tuning, and inspecting the modeling loss
  • Stage 3 evaluates the LLM and covers extracting and scoring responses

Key Takeaways

  • Instantiating a PyTorch dataset means calling a custom class such as SpamDataset with a CSV file name as the first positional argument and labeled keyword arguments for max_length and tokenizer
  • The training dataset is created with max_length=None, and the validation and test datasets read train_dataset.max_length so that all splits share one setting
  • Instruction fine-tuning has three stages: Stage 1 prepares the dataset, Stage 2 fine-tunes the LLM, and Stage 3 evaluates the LLM
  • Fine-tuning continues training a pre-trained LLM on the instruction dataset and imports the loss and training functions calc_loss_loader and train_model_simple from chapter 5 rather than rewriting them
  • When a result diverges, map the symptom to a stage first, then narrow the search to the steps inside that stage

Related concepts

More concepts starting with P