Creating PyTorch Dataset Objects
Instantiating a PyTorch dataset means calling a custom class such as SpamDataset with a CSV file name and keyword arguments, producing a dataset object.
Why Dataset Objects Come First
Before a model can learn from spam messages, the text has to be turned into a form the training code can use. In PyTorch, that preparation is packaged into a dataset object. A dataset object holds prepared data for one split of the work: training, validation, or testing. This article follows one setup that creates three such objects from three CSV files, and it treats that setup as a small mechanism to trace step by step rather than a finished recipe to copy.
Instantiating a PyTorch dataset means calling a custom class such as SpamDataset with a CSV file name and keyword arguments, producing a dataset object. Keep that definition in mind as you read the rest of the article, because every line you study is an instance of that single action repeated three times.
What do you think happens?
Before reading on, look at the two lines that create val_dataset and test_dataset. What value of max_length does each one receive, and is it the same value that train_dataset received?
Reveal answer
Each reads a max_length value from the training dataset object, using train_dataset.max_length. The validation and test datasets do not receive a new value of their own.
The training dataset is created with max_length=None. The other two datasets read the attribute train_dataset.max_length, so they reuse whatever setting the training object holds. The sections below explain why the listing is built this way.
Tracing the Three-Part Setup
The setup 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 is imported from a module named chapter06. The second part creates a single tokenizer object. The third part creates three dataset objects, one each for the training, validation, and test CSV files.
Reading the setup in this order matters. The imports make names available, the tokenizer is created once and stored under a name, and the three dataset calls then use that name. When you trace the code, track what each variable holds after each line. A variable that is named but not yet assigned cannot be passed to a call, so the order of lines is part of the meaning.
- The tokenizer is created once with
tiktoken.get_encoding("gpt2"). train_datasetis the only call that passesmax_length=None directly.val_datasetandtest_datasetreadtrain_dataset.max_length.- The file names
val.csvandtest.csvare placeholders. The source namestrain.csvand describes validation and test CSV files without giving their file names.
Anatomy of the SpamDataset Call
Creating a dataset object works 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.
The file name is the first argument and is passed positionally, so it is identified by where it sits in the call. The listing uses keyword arguments for max_length and tokenizer, so those two values are identified by their names. Keyword arguments name the parameter they fill, which makes the call read as a set of labeled settings rather than a bare list of values. When you later trace a problem, those names show which setting went where.
The source states what the three arguments are for, but it does not explain how SpamDataset uses them internally. The class is imported from chapter06 and its definition is not part of the listing. What you can rely on is the mapping from each call argument to its parameter name, and the fact that the file name is the source of the rows the dataset will prepare.
The Tokenizer as a Shared Object
The listing creates one tokenizer with tiktoken.get_encoding("gpt2") and passes that same object to every dataset call. The tokenizer is therefore a single shared object, not three separate tokenizers. Each dataset receives a reference to the same tokenizer through its tokenizer argument, which keeps the three splits consistent with one another in how they turn text into inputs.
The diagram shows the path the source implies at a high level. A row of text is handled by the tokenizer, and the max_length setting governs the length the prepared item must reach through padding or truncation. The source does not detail the exact padding or truncation rules, so treat the middle step as the role of max_length, not as a full specification of the process.
Following max_length from Train to Validation and Test
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 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.
Tracing max_length Through the Three Calls
The three SpamDataset calls appear in order: train, validation, test. Trace which value of max_length each call receives and where that value comes from.
- 1Line for train_dataset
The call passes
max_length=None directly. This is the only place the listing sets the value by hand. - 2Line for val_dataset
The call passes
max_length=train_dataset.max_length. Python first looks up themax_lengthattribute on the existingtrain_datasetobject and passes that stored value. - 3Line for test_dataset
The call uses the same expression,
train_dataset.max_length. It reads the same stored setting, so the test split is configured like the validation split. - 4Why the value is shared
If validation and test used a different maximum length from the training dataset, the splits could be prepared differently even though they come from the same data source. Reading one object's attribute keeps all three aligned.
train_dataset receives None directly. val_dataset and test_dataset both receive the max_length value stored on train_dataset. The source does not state what that stored value resolves to, since the internal logic of SpamDataset is not in the listing.
Why Data Loaders Wait for Datasets
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, and 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.
Mistakes When Instantiating Dataset Objects
Common mistake Passing the wrong file name to a split
What it looks likeSpamDataset("train.csv", ...) used on the line that is meant to create test_dataset
Why it failsThe file name is the first argument and decides which CSV rows the object reads. A wrong file name means the test object is built from training rows.
Do this insteadCheck that each call's file name matches the split the variable name says it holds.
Common mistake Forgetting max_length on the validation or test call
What it looks likeval_dataset = SpamDataset("val.csv", tokenizer=tokenizer)Why it failsThe listing passes
max_lengthon every dataset call, and the validation and test calls readtrain_dataset.max_length. Dropping the argument removes the shared setting that keeps the splits consistent.Do this insteadKeep max_length=train_dataset.max_length on the validation and test lines, as the listing does.
Common mistake Setting max_length by hand on the validation or test call
What it looks liketest_dataset = SpamDataset("test.csv", max_length=None, tokenizer=tokenizer)Why it failsThis passes a value different from the training object's setting, so the splits can be prepared differently.
Do this insteadRead the value from the training object with train_dataset.max_length.
Common mistake Creating a separate tokenizer for each split
What it looks likeCalling tiktoken.get_encoding("gpt2") again inside each dataset call
Why it failsThe listing creates one tokenizer and passes that same object to every dataset call. Separate tokenizer objects break the single shared setup the listing depends on.
Do this insteadCreate the tokenizer once and pass the same variable to each call.
Common mistake Building a data loader before its dataset exists
Why it failsA dataset object must be instantiated before a data loader can be created from it. Until then, the name the loader refers to is not defined.
Do this insteadInstantiate the train, validation, and test datasets first, then create the data loaders.
Practice Tracing the Setup
Using the reconstructed setup from earlier, suppose a teammate changes the test line to read max_length=None while the validation line still reads max_length=train_dataset.max_length. Explain which splits now receive the shared setting and which do not, and name the line you would change to restore consistency.
Hints
- Look at which line reads
train_dataset.max_length and which line passes None directly. - Think about what the training dataset's stored setting is supposed to control for the other splits.
- The fix changes only one line: the test call.
Key Takeaways
- Instantiating a PyTorch dataset means calling a custom class such as SpamDataset with a CSV file name and keyword arguments, producing a dataset object.
- In the listing, the file name is passed positionally, while
max_lengthand tokenizer are passed as keyword arguments that name the setting each one fills. - The training dataset is created with
max_length=None, and the validation and test datasets readtrain_dataset.max_length so that all three splits share one setting. - One tokenizer, created with
tiktoken.get_encoding("gpt2"), is passed to every dataset call, and dataset objects must exist before data loaders are created from them.