Concepts / Creating PyTorch Dataset Objects

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.

  • Programming

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?

  • Each receives None, the same as the training dataset

  • Each receives a fresh number chosen for that split

  • Each reads a max_length value from the training dataset object

Reveal answer
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.

python
Notes on the code
  • The tokenizer is created once with tiktoken.get_encoding("gpt2").
  • train_dataset is the only call that passes max_length=None directly.
  • val_dataset and test_dataset read train_dataset.max_length.
  • The file names val.csv and test.csv are placeholders. The source names train.csv and 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.

SpamDatasetclass being called"train.csv"positional: CSV file namemax_length=Nonekeyword: length settingtokenizer=tokenizerkeyword: tokenizer object
Which part of the SpamDataset(...) call controls the file read, the padding or truncation length, and the tokenization?

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.

CSV text rowread from the named filetokenizersame object for every splitfixed lengthpadded or truncated tomax_lengthdataset itemstored in the datasetobjecttokenizedadjusted to max_lengthprepared
What happens to a raw text row from the CSV file as it passes through the tokenizer and is padded or truncated to max_length before becoming a dataset item?

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.

train_datasetcreated withmax_length=Nonetrain_dataset.max_lengthattribute read from theobjectval_datasetmax_length from trainingtest_datasetmax_length from trainingstores settingpassed as max_lengthpassed as max_length
Where does the max_length value originate, and how does it flow from the training dataset instantiation into the validation and test dataset instantiations?
Worked example

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.

  1. 1
    Line for train_dataset

    The call passes max_length=None directly. This is the only place the listing sets the value by hand.

  2. 2
    Line for val_dataset

    The call passes max_length=train_dataset.max_length. Python first looks up the max_length attribute on the existing train_dataset object and passes that stored value.

  3. 3
    Line 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.

  4. 4
    Why 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.

Result

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 like

    SpamDataset("train.csv", ...) used on the line that is meant to create test_dataset

    Why it fails

    The 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 instead

    Check 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 like
    val_dataset = SpamDataset("val.csv", tokenizer=tokenizer)
    Why it fails

    The listing passes max_length on every dataset call, and the validation and test calls read train_dataset.max_length. Dropping the argument removes the shared setting that keeps the splits consistent.

    Do this instead

    Keep 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 like
    test_dataset = SpamDataset("test.csv", max_length=None, tokenizer=tokenizer)
    Why it fails

    This passes a value different from the training object's setting, so the splits can be prepared differently.

    Do this instead

    Read the value from the training object with train_dataset.max_length.

  • Common mistake

    Creating a separate tokenizer for each split

    What it looks like

    Calling tiktoken.get_encoding("gpt2") again inside each dataset call

    Why it fails

    The 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 instead

    Create the tokenizer once and pass the same variable to each call.

  • Common mistake

    Building a data loader before its dataset exists

    Why it fails

    A 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 instead

    Instantiate the train, validation, and test datasets first, then create the data loaders.

Practice Tracing the Setup

MEDIUM

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_length and 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 read train_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.

More concepts starting with C