Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Chapter 13: Going Deeper -- the Mechanics of PyTorch (Part 2/2)

Open in Colab Open in Kaggle

Chapter 13: Going Deeper -- the Mechanics of PyTorch (Part 2/2)


Prework

Import essentials

Check package version (optional)

Add folder to path in order to load from the check_packages.py script.

Check recommended package versions.

[OK] Your Python version is 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)]
[OK] numpy 1.22.3
[OK] pandas 2.0.3
[OK] sklearn 1.3.2
[OK] torch 2.1.1+cpu
[OK] torchvision 0.16.1+cpu

13.6.Project one - predicting the fuel efficiency of a car

In this section, we will work on a real-world project of predicting the fuel efficiency of a car in miles per gallon (MPG). We will cover the underlying steps in machine learning tasks, such as data preprocessing, feature engineering, training, prediction (inference), and evaluation.

13.6.1.Working with feature columns

In machine learning and deep learning applications, we can encounter various different types of features:

  • continuous

  • unordered categorical (nominal)

  • ordered categorical (ordinal)

Note that while numeric data can be either continuous or discrete, in the context of machine learning with PyTorch, “numeric” data specifically refers to continuous data of floating point type.

Sometimes, feature sets are comprised of a mixture of different feature types. For example, consider a scenario with a set of seven different features, as shown in the following figure.

The features shown in the figure (model year, cylinders, displacement, horsepower, weight, acceleration, and origin) were obtained from the Auto MPG dataset, which is a common machine learning benchmark dataset for predicting the fuel efficiency of a car in MPG.

<IPython.core.display.Image object>

We are going to treat five features from the Auto MPG dataset (number of cylinders, displacement, horsepower, weight, and acceleration) as “numeric” (here, continuous) features. The model year can be regarded as an ordered categorical (ordinal) feature. Lastly, the manufacturing origin can be regarded as an unordered categorical (nominal) feature with three possible discrete values, 1, 2, and 3, which correspond to the US, Europe, and Japan, respectively.

Let’s first load the data.

Loading...

Then we check how many Na are in the rows (incomplete rows), and we drop them.

MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64
Loading...

We need to partition the dataset into training and test datasets via sklearn.model_selection.

Loading...

And we also need to standardize the continuous features.

The pandas DataFrame that we created via the previous code snippet contains five columns with values of the type float. These columns will constitute the continuous features.

Loading...

Next, let’s group the rather fine-grained model year (ModelYear) information into buckets to simplify the learning task for the model that we are going to train later. Concretely, we are going to assign each car into one of four year buckets, as follows:

 bucket ={0 if year <731 if 73 ≤ year <762 if 76 ≤ year <793 if year ≥79\text { bucket }= \begin{cases}0 & \text { if year }<73 \\ 1 & \text { if 73 } \leq \text { year }<76 \\ 2 & \text { if 76 } \leq \text { year }<79 \\ 3 & \text { if year } \geq 79\end{cases}

Note that the chosen intervals were selected arbitrarily to illustrate the concepts of “bucketing.” In order to group the cars into these buckets, we will first define three cut-off values: [73,76,79][73,76,79] for the model year feature. These cut-off values are used to specify half-closed intervals, for instance, (−∞,73)(-\infty, 73), [73,76),[76,79)[73,76),[76,79), and [76,∞)[76, \infty). Then, the original numeric features will be passed to the torch.bucketize function to generate the indices of the buckets.

We add this bucketized feature column to the Python list numeric_column_names.

Next, we will proceed with defining a list for the unordered categorical feature, Origin. In PyTorch, There are two ways to work with a categorical feature:

  1. Using an embedding layer via nn.Embedding

    • The embedding layer maps each index to a vector of random numbers of the type float, which can be trained. (You can think of the embedding layer as a more efficient implementation of a one-hot encoding multiplied with a trainable weight matrix.)

    • When the number of categories is large, using the embedding layer with fewer dimensions than the number of categories can improve the performance.

  2. Using one-hot-encoded vectors (also called indicator) via one_hot

    • For example, index 0 will be encoded as [1,0,0][1,0,0], index 1 will be encoded as [0,1,0][0,1,0], and so on.

In the following code snippet, we will use the one-hot-encoding approach on the categorical feature in order to convert it into the dense format.

After encoding the categorical feature into a three-dimensional dense feature, we concatenated it with the numeric features we processed in the previous step. Finally, we will create the label tensors from the ground truth MPG values as follows.

13.6.2.Training a DNN regression model

Now, after constructing the mandatory features and labels, we will create a data loader that uses a batch size of 8 for the train data.

Next, we will build a model with two fully connected layers where one has 8 hidden units and another has 4.

Sequential( (0): Linear(in_features=9, out_features=8, bias=True) (1): ReLU() (2): Linear(in_features=8, out_features=4, bias=True) (3): ReLU() (4): Linear(in_features=4, out_features=1, bias=True) )

After defining the model, we will define the MSE loss function for regression and use stochastic gradient descent for optimization.

Now we will train the model for 200 epochs and display the train loss for every 20 epochs.

Epoch 0  Loss 536.1047
Epoch 20  Loss 8.4361
Epoch 40  Loss 7.8695
Epoch 60  Loss 7.1891
Epoch 80  Loss 6.7064
Epoch 100  Loss 6.7603
Epoch 120  Loss 6.3107
Epoch 140  Loss 6.6884
Epoch 160  Loss 6.7549
Epoch 180  Loss 6.2029

After 200 epochs, the train loss was around 5. We can now evaluate the regression performance of the trained model on the test dataset. To predict the target values on new data points, we can feed their features to the model.

Result explanation

The MSE on the test set is 9.6, and the mean absolute error (MAE) is 2.1.

Test MSE: 9.5907
Test MAE: 2.1177

13.7.Project two - classifying MNIST hand-written digits

We are going to load the MNIST dataset using the torchvision module.

  1. The setup step includes loading the dataset and specifying hyperparameters (the size of the train set and test set, and the size of mini-batches). Here, we constructed a data loader with batches of 64 samples.

Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
2.6%
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./MNIST\raw\train-images-idx3-ubyte.gz
100.0%
Extracting ./MNIST\raw\train-images-idx3-ubyte.gz to ./MNIST\raw

Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
100.0%
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./MNIST\raw\train-labels-idx1-ubyte.gz
Extracting ./MNIST\raw\train-labels-idx1-ubyte.gz to ./MNIST\raw

Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz

19.9%
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./MNIST\raw\t10k-images-idx3-ubyte.gz
100.0%
Extracting ./MNIST\raw\t10k-images-idx3-ubyte.gz to ./MNIST\raw

Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
100.0%
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./MNIST\raw\t10k-labels-idx1-ubyte.gz
Extracting ./MNIST\raw\t10k-labels-idx1-ubyte.gz to ./MNIST\raw


  1. We preprocess the input features and the labels. The features in this project are the pixels of the images we read from Step 1. We defined a custom transformation using torchvision.transforms.Compose (in preceding code snippets). In this simple case, our transformation consisted only of one method, ToTensor(). The ToTensor() method converts the pixel features into a floating type tensor and also normalizes the pixels from the [0,255][0,255] to [0,1][0,1] range. The labels are integers from 0 to 9 representing ten digits. Hence, we don’t need to do any scaling or further conversion. Note that we can access the raw pixels using the data attribute, and don’t forget to scale them to the range [0,1][0,1].

  2. Construct the NN model.

    • Note that the model starts with a flatten layer (nn.Flatten()) that flattens an input image into a one-dimensional tensor. This is because the input images are in the shape of [1, 28, 28]. The model has two hidden layers, with 32 and 16 units respectively. And it ends with an output layer of ten units representing ten classes, activated by a softmax function.

Sequential( (0): Flatten(start_dim=1, end_dim=-1) (1): Linear(in_features=784, out_features=32, bias=True) (2): ReLU() (3): Linear(in_features=32, out_features=16, bias=True) (4): ReLU() (5): Linear(in_features=16, out_features=10, bias=True) )
  1. We used the cross-entropy loss function for multiclass classification and the Adam optimizer for gradient descent. We trained the model for 20 epochs and displayed the train accuracy for every epoch.

Epoch 0  Accuracy 0.8531
Epoch 1  Accuracy 0.9287
Epoch 2  Accuracy 0.9413
Epoch 3  Accuracy 0.9506
Epoch 4  Accuracy 0.9558
Epoch 5  Accuracy 0.9592
Epoch 6  Accuracy 0.9627
Epoch 7  Accuracy 0.9650
Epoch 8  Accuracy 0.9674
Epoch 9  Accuracy 0.9690
Epoch 10  Accuracy 0.9710
Epoch 11  Accuracy 0.9729
Epoch 12  Accuracy 0.9739
Epoch 13  Accuracy 0.9750
Epoch 14  Accuracy 0.9764
Epoch 15  Accuracy 0.9777
Epoch 16  Accuracy 0.9779
Epoch 17  Accuracy 0.9798
Epoch 18  Accuracy 0.9806
Epoch 19  Accuracy 0.9813
  1. We will evaluate the training set on the testing set.

Test accuracy: 0.9647

Readers may ignore the next cell.

[NbConvertApp] Converting notebook ch13_part2.ipynb to script
[NbConvertApp] Writing 7052 bytes to ch13_part2.py