Training and Validation Set Losses
When the code is only measuring loss, that bookkeeping is not needed, so turning it off saves work.
Why One Loss Number Is Not Enough
Training a large language model produces a stream of numbers, and one of the most important is the loss. A single loss value on its own does not tell you very much. Is the model learning something useful, or is it only getting better at the particular text it has already seen? This article focuses on one practical answer to that question: computing the loss on two different sets of data, the training set and the validation set, and reading the two results together.
The source states the purpose directly: computing the training and validation set losses is a way to assess the quality of LLM-generated text during training. As you read, keep one question in mind: what does each of the two losses tell you that the other one cannot?
The script under study is small, but almost every line answers a practical question. Which hardware should run the computation? Should the program keep track of gradients? Which data goes into the loss calculation? The sections below answer those questions in the order the script asks them.
Choosing the Device at Run Time
The first line of the script selects a device. A device is the hardware that performs the computation. The source describes two options: a CPU, which is the general processor, and a GPU, which is the graphics processor supported by CUDA. The selection is written as follows.
The expression checks whether a CUDA-supported GPU is available by calling torch.cuda.is_available(). If that check returns true, the device is set to CUDA. If it does not, the device falls back to the CPU. The flowchart below shows the two branches and where the chosen device is applied next.
The source makes an important practical point: if the machine has a CUDA-supported GPU, the LLM trains on the GPU without any changes to the code. The same script therefore works on a laptop with no GPU and on a workstation with one. The selection happens at run time, so you do not need separate versions of the program.
Once the device is chosen, the script calls model.to(device). This moves the model's parameters onto the selected hardware. The source also states that the data must be loaded onto that same device. A model on the GPU with data still on the CPU would not be working on the same hardware, so the device value is passed explicitly into the loss function as well. The device setting is what keeps the model and the data together.
Switching Off Gradient Tracking
The next construct in the script is a with block. The source explains that the with torch.no_grad(): line wraps the two loss calculations and disables gradient tracking for efficiency, because the model is not being trained at this point in the script. The script is measuring loss, not learning from it.
Gradient tracking is the bookkeeping PyTorch normally does so that a model can be updated during training. It is necessary when weights are going to change. When the code is only measuring loss, that bookkeeping is not needed, so turning it off saves work. The source frames this as an efficiency choice; it does not change which data is measured or how the loss is defined.
Every line inside the with block runs with gradient tracking turned off. That means both loss values are computed under the same condition. If one loss were computed with tracking on and the other with tracking off, the two numbers would not be measured under identical conditions.
Tracing calc_loss_loader Twice
The two loss lines are nearly identical. Each one calls calc_loss_loader with a data loader, the model, and the device. Only the loader changes: train_loader is used for the training loss and val_loader for the validation loss. Because the function is the same in both calls, both numbers are computed the same way, which makes them directly comparable.
Read the diagram from left to right. Two loaders enter on the left, one carrying training data and one carrying validation data. The same model and the same device feed both calls. Each call produces one number, and those numbers are train_loss and val_loss. The source does not spell out the internal steps inside calc_loss_loader, so the diagram shows only its inputs and outputs. What matters for the lesson is that the same function, model, and device are used in both calls, and only the data changes.
Tracing one run of the loss-evaluation script
Follow the script through the steps it takes, without assuming any particular numeric values. The source gives the sequence of steps but no measured results.
- 1Select the device
torch.device checks
torch.cuda.is_available(). If true, the device is CUDA; if false, it is the CPU. - 2Move the model
model.to(device)places the model's parameters on the selected hardware. - 3Enter the no-gradient scope
The with
torch.no_grad(): block turns off gradient tracking for every line inside it. - 4Compute the training loss
calc_loss_loaderreceivestrain_loader, model, and device, and returnstrain_loss. - 5Compute the validation loss
calc_loss_loaderreceivesval_loader, model, and device under the same conditions, and returnsval_loss. - 6Read the pair
The two values are compared together to judge how the model handles data it learns from and data it is checked against.
The script produces two loss values, train_loss and val_loss, computed on the same hardware, with the same model, and with gradient tracking off. Their difference in data is the only deliberate difference between the two measurements.
Where the Script Can Go Wrong
The device line and the loader calls depend on each other. The source's logic implies several ways the script can fail or mislead when the pieces do not line up.
Common mistake Moving the model to one device and leaving the data on another
What it looks likeThe model is moved to CUDA with model.to(device), but a batch of data stays on the CPU when it reaches the loss calculation.
Why it failsThe source says the model and the data must be on the same device. A model on the GPU and data on the CPU are not working on the same hardware.
Do this insteadPass the same device value into every loss call, as the script does, so the data is loaded onto the device the model uses.
Common mistake Hard-coding the device to CUDA
What it looks likedevice = torch.device("cuda") on a laptop with no CUDA-supported GPU.Why it failsThe script is written to run on either kind of machine. A hard-coded CUDA device removes the fallback that
torch.cuda.is_available()provides.Do this insteadKeep the conditional expression so the device is chosen at run time.
Common mistake Passing the training loader for both losses
What it looks likeBoth calls use train_loader, so val_loss is really a second training loss.
Why it failsThe script changes only the loader between the two calls. Using the same loader for both removes the second dataset that the assessment depends on.
Do this insteadCall calc_loss_loader with train_loader for train_loss and with val_loader for val_loss.
Common mistake Computing one loss outside the no-gradient block, so the two losses are not measured under identical conditions
Training Loss Versus Validation Loss
The two losses are computed by the same function, but they are measurements on different data. The table below sets out what stays the same and what changes between the training pass and the validation pass, using only what the source describes.
| Aspect | Training set loss | Validation set loss |
|---|---|---|
| Data used | The training set, the data the model learns from | The validation set, the data the model is checked against |
| Loader passed in | train_loader | val_loader |
| Function called | calc_loss_loader | calc_loss_loader |
| Model and device | Same model, same device | Same model, same device |
| Gradient tracking | Off inside the no_grad block | Off inside the no_grad block |
| Output name | train_loss | val_loss |
What is the same and what is different in the two loss calculations
Because the conditions are held constant, any difference between the two numbers can be traced to the data rather than to a change in method. The source does not give numeric values for either loss, so no particular result should be assumed from the script alone.
Reading the Two Losses Together
The source's purpose for the two losses is to assess the quality of LLM-generated text during training. The training set loss shows how the model does on the text it learns from. The validation set loss shows how it does on text it is checked against. Neither number answers the purpose alone. The assessment comes from the pair.
The source states that the two losses are separate measurements that are read together, not interchangeable numbers. It also states that judging text quality from a single loss leaves out part of the evidence. Returning to the opening question: the training loss alone cannot show whether the model is learning something useful or only getting better at the text it has already seen, because it is measured on that same text. The validation loss supplies the second view.
Common mistake Treating train_loss and val_loss as interchangeable
Why it failsThe source calls them separate measurements that are read together. Each answers a different question about the model.
Do this insteadRead the two values side by side and ask what each one tells you that the other cannot.
Common mistake Judging text quality from one loss only
Why it failsThe source says a single loss leaves out part of the evidence needed to assess quality.
Do this insteadUse the training and validation losses together as the basis for the assessment.
Common mistake Assuming no_grad changes what is being measured
Why it failsThe source gives no-grad as an efficiency measure because the script is measuring loss rather than training. It turns off bookkeeping, not data.
Do this insteadThink of the no-gradient block as a way of measuring with less work, while the data and the loss definition stay the same.
Predicting the Device Choice
What do you think happens?
A workstation has a CUDA-supported GPU and the script runs unchanged. Which device does torch.device("cuda" if torch.cuda.is_available() else "cpu") select?
Reveal answer
cuda
When torch.cuda.is_available() is true, the expression selects CUDA. The model and the data are then both placed on that device, and the same code runs without changes.
A colleague moves the model to the GPU, but their loss calculation still receives batches that were loaded onto the CPU. Using the source's rule, explain what is wrong and what single change in the script would address the issue.
Hints
- Look at what the source says must happen to the data after the model is moved.
- The device value is passed explicitly into the loss function as well.
Key Takeaways
- The device line selects CUDA when
torch.cuda.is_available()is true and falls back to the CPU otherwise, so the same code runs on either kind of machine. - The model and the data must be on the same device, so the device value is passed into every loss call.
- The with
torch.no_grad(): block turns off gradient tracking for efficiency because the script is measuring loss rather than training. calc_loss_loaderis called once with the training loader and once with the validation loader, sotrain_lossandval_lossare computed under identical conditions and can be compared.- Computing training and validation set losses is a way to assess the quality of LLM-generated text during training, and the two values are read together rather than in isolation.