Pytorch vs Tensorflow - CNN

Pytorch
Tensorflow
CNNs
Comparing Pytorch with Tensorflow
Published

January 1, 2024

Pytorch vs Tensorflow - CNNs!

Choosing Pytorch vs Tensorflow, which should you use? Which is better and what are the differences between these two frameworks both made for deep learning. Well in this series I am going to compare them side by side so you could see the differences and simliarities clearly, in code with crips examples.

Also, when I switched from Tensorflow to pytorch, I just wished I had a “Rosetta Stone” to translate between frameworks, which I hope this series will be for you. A nice overview that practitioners to leverage the strengths of both libraries.

In this notebook

In the first notebook of this series, I will start with the basics by creating a simple Convolutional Neural Network (CNN) in both PyTorch and TensorFlow. We will train this so called CNN on the MNIST dataset, which consists of handwritten digit images, to illustrate fundamental concepts and workflows in each framework. This foundational exercise will set the stage for more complex models and comparisons in subsequent notebooks.

For both PyTorch and TensorFlow, the steps for training a model are essentially the same:

  1. Importing the necessary libraries.
  2. Defining the model parameters and hyperparameters.
  3. Loading the dataset.
  4. Preprocessing the data to make it suitable for training.
  5. Initializing the model architecture.
  6. Training the model using the defined parameters and dataset.

These steps form the core workflow in building and training deep learning models, regardless of the framework used.

import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# Plotting the training metrics with Plotly
def plot_tensorflow_training_results(history):
    fig = make_subplots(rows=1, cols=2, subplot_titles=("Loss Over Epochs", "Accuracy Over Epochs"))

    # Loss plot
    fig.add_trace(go.Scatter(x=list(range(len(history.history['loss']))),
                             y=history.history['loss'],
                             mode='lines',
                             name='Training Loss'),
                  row=1, col=1)
    fig.add_trace(go.Scatter(x=list(range(len(history.history['val_loss']))),
                             y=history.history['val_loss'],
                             mode='lines',
                             name='Validation Loss'),
                  row=1, col=1)

    # Accuracy plot
    fig.add_trace(go.Scatter(x=list(range(len(history.history['accuracy']))),
                             y=history.history['accuracy'],
                             mode='lines',
                             name='Training Accuracy'),
                  row=1, col=2)
    fig.add_trace(go.Scatter(x=list(range(len(history.history['val_accuracy']))),
                             y=history.history['val_accuracy'],
                             mode='lines',
                             name='Validation Accuracy'),
                  row=1, col=2)

    # Updating layout
    fig.update_layout(title='Training Metrics',
                      xaxis_title='Epoch',
                      yaxis_title='Value',
                      showlegend=True)

    # Update xaxis labels
    fig.update_xaxes(title_text='Epoch', row=1, col=1)
    fig.update_xaxes(title_text='Epoch', row=1, col=2)

    # Update yaxis labels
    fig.update_yaxes(title_text='Loss', row=1, col=1)
    fig.update_yaxes(title_text='Accuracy', row=1, col=2)

    fig.show()
    
import plotly.graph_objects as go
from plotly.subplots import make_subplots

def plot_pytorch_training_results(n_epochs, train_losses, test_losses, train_accuracies, test_accuracies):
    # Plotting the training metrics with Plotly
    fig = make_subplots(rows=1, cols=2, subplot_titles=("Loss Over Epochs", "Accuracy Over Epochs"))

    # Loss plot
    fig.add_trace(go.Scatter(x=list(range(n_epochs)), y=train_losses, mode='lines', name='Training Loss'), row=1, col=1)
    fig.add_trace(go.Scatter(x=list(range(n_epochs)), y=test_losses, mode='lines', name='Validation Loss'), row=1, col=1)

    # Accuracy plot
    fig.add_trace(go.Scatter(x=list(range(n_epochs)), y=train_accuracies, mode='lines', name='Training Accuracy'), row=1, col=2)
    fig.add_trace(go.Scatter(x=list(range(n_epochs)), y=test_accuracies, mode='lines', name='Validation Accuracy'), row=1, col=2)

    # Updating layout
    fig.update_layout(title='Training Metrics', xaxis_title='Epoch', yaxis_title='Value', showlegend=True)

    # Update xaxis labels
    fig.update_xaxes(title_text='Epoch', row=1, col=1)
    fig.update_xaxes(title_text='Epoch', row=1, col=2)

    # Update yaxis labels
    fig.update_yaxes(title_text='Loss', row=1, col=1)
    fig.update_yaxes(title_text='Accuracy', row=1, col=2)

    fig.show()
pixel_columns = [f"pixel{i}" for i in range(784)]
all_df = pd.read_csv("/kaggle/input/digit-recognizer/train.csv")

indices = np.arange(len(all_df))
np.random.shuffle(indices)

# Split the indices into 80% train and 20% validation
trn_len = int(0.8 * len(all_df))
trn_ind = indices[:trn_len]
val_ind = indices[trn_len:]

trn_df = all_df.iloc[trn_ind]
val_df = all_df.iloc[val_ind]
x_trn = trn_df[pixel_columns].values
y_trn = trn_df["label"].values

x_val = val_df[pixel_columns].values
y_val = val_df["label"].values

tst_df = pd.read_csv("/kaggle/input/digit-recognizer/test.csv")
x_tst = tst_df[pixel_columns].values
#1. Importing the necessary libraries.
import tensorflow as tf
import numpy as np

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.losses import CategoricalCrossentropy

from tensorflow.keras.datasets import mnist

# 2. Defining the model parameters and hyperparameters.
kernels_layer_1 = 32
kernels_layer_2 = 64
n_classes = 10
n_epochs = 10
batch_size = 32
img_size = 28
img_size_flattened = img_size * img_size
learning_rate = 0.001
n_classes = 10

x_trn = x_trn/255.0
x_val = x_val/255.0
x_tst = x_tst/255.0

x_trn = x_trn.reshape(-1, img_size, img_size, 1)
x_val = x_val.reshape(-1, img_size, img_size, 1)
x_tst = x_tst.reshape(-1, img_size, img_size, 1)

y_trn_encoded = np.zeros(shape=(y_trn.shape[0], n_classes), dtype=int)
y_val_encoded = np.zeros(shape=(y_val.shape[0], n_classes), dtype=int)

y_trn_encoded[np.linspace(0, len(y_trn)-1, len(y_trn)).astype(int), y_trn]=1
y_val_encoded[np.linspace(0, len(y_val)-1, len(y_val)).astype(int), y_val]=1

# 5. Initializing the model architecture.
model = Sequential([
    tf.keras.layers.Conv2D(kernels_layer_1, kernel_size=(3, 3), activation="relu"),
    tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),
    tf.keras.layers.Conv2D(kernels_layer_2, kernel_size=(3, 3), activation="relu"),
    tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(units=n_classes, activation="softmax")
])
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=learning_rate), loss=CategoricalCrossentropy(), 
    metrics=["accuracy"])

# 6. Training the model using the defined parameters and dataset.
callback = model.fit(
    x=x_trn, 
    y=y_trn_encoded, 
    validation_data=(x_val, y_val_encoded), 
    batch_size=batch_size, 
    epochs=n_epochs)
Epoch 1/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 13s 12ms/step - accuracy: 0.8149 - loss: 0.5747 - val_accuracy: 0.9730 - val_loss: 0.0848

Epoch 2/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 12s 11ms/step - accuracy: 0.9675 - loss: 0.1046 - val_accuracy: 0.9807 - val_loss: 0.0645

Epoch 3/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 11s 11ms/step - accuracy: 0.9768 - loss: 0.0739 - val_accuracy: 0.9833 - val_loss: 0.0514

Epoch 4/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 20s 11ms/step - accuracy: 0.9802 - loss: 0.0623 - val_accuracy: 0.9869 - val_loss: 0.0425

Epoch 5/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 11s 11ms/step - accuracy: 0.9819 - loss: 0.0545 - val_accuracy: 0.9888 - val_loss: 0.0373

Epoch 6/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 12s 11ms/step - accuracy: 0.9832 - loss: 0.0496 - val_accuracy: 0.9870 - val_loss: 0.0421

Epoch 7/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 12s 11ms/step - accuracy: 0.9845 - loss: 0.0467 - val_accuracy: 0.9896 - val_loss: 0.0347

Epoch 8/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 12s 11ms/step - accuracy: 0.9848 - loss: 0.0438 - val_accuracy: 0.9892 - val_loss: 0.0362

Epoch 9/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 21s 12ms/step - accuracy: 0.9871 - loss: 0.0378 - val_accuracy: 0.9907 - val_loss: 0.0318

Epoch 10/10

1050/1050 ━━━━━━━━━━━━━━━━━━━━ 12s 12ms/step - accuracy: 0.9886 - loss: 0.0352 - val_accuracy: 0.9882 - val_loss: 0.0399
plot_tensorflow_training_results(history=callback)
y_prd = model.predict(x_tst)
y_prd_labels = np.argmax(y_prd, axis=1)
875/875 ━━━━━━━━━━━━━━━━━━━━ 4s 4ms/step
tst_df["Label"] = y_prd_labels
tst_df = tst_df.reset_index(names="ImageId")
tst_df["ImageId"] += 1
tst_df[["ImageId", "Label"]].to_csv("tensorflow-prediction.csv", index=False, header=True)
# 1. Importing the necessary libraries.
import os
import torch
import torchvision
import torch.nn.functional as F
from tqdm import tqdm
import torch.nn as nn
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from plotly.subplots import make_subplots


class NumpyDataset(torch.utils.data.Dataset):
    def __init__(self, data, labels=None, train=True):
        self.data = torch.from_numpy(data).float()
        if train:
            self.labels = torch.from_numpy(labels).long()

        self.train = train

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        sample = self.data[idx]
        if self.train:
            return sample, self.labels[idx]
        return sample

x_trn = np.expand_dims(np.squeeze(x_trn, axis=-1), axis=1)    
x_val = np.expand_dims(np.squeeze(x_val, axis=-1), axis=1)
x_tst = np.expand_dims(np.squeeze(x_tst, axis=-1), axis=1)

# 3. Loading the dataset.
mnist_dataset_trn = NumpyDataset(data=x_trn, labels=y_trn, train=True)
mnist_dataset_val = NumpyDataset(data=x_val, labels=y_val, train=True)
mnist_dataset_tst = NumpyDataset(data=x_tst, labels=None,  train=False)

# 4. Preprocessing the data to make it suitable for training.
trn_loader = torch.utils.data.DataLoader(
    dataset=mnist_dataset_trn, 
    batch_size=batch_size, 
    shuffle=True)
val_loader = torch.utils.data.DataLoader(
    dataset=mnist_dataset_val, 
    batch_size=batch_size, 
    shuffle=True)
tst_loader = torch.utils.data.DataLoader(
    dataset=mnist_dataset_tst, 
    batch_size=batch_size, 
    shuffle=False)

# 5. Initializing the model architecture.
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, kernels_layer_1, kernel_size=3)
        self.conv2 = nn.Conv2d(kernels_layer_1, kernels_layer_2, kernel_size=3)
        self.fc = nn.Linear(5*5*64, n_classes)

    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2(x), 2))
        x = x.view(-1,5*5*64 )
        return F.softmax(self.fc(x), dim=1)

model = CNN()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
loss_function = nn.CrossEntropyLoss()

# Tracking metrics
train_losses = []
test_losses = []
train_accuracies = []
test_accuracies = []

# 6. Training the model using the defined parameters and dataset.
for epoch in range(n_epochs):
    model.train()
    running_loss = 0
    correct_train = 0
    total_train = 0

    for batch_idx, (x_trn_batch, y_trn_batch) in tqdm(enumerate(trn_loader)):
        optimizer.zero_grad()
        y_pred_batch = model(x_trn_batch)
        loss = loss_function(y_pred_batch, y_trn_batch)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        _, predicted = torch.max(y_pred_batch, 1)
        correct_train += (predicted == y_trn_batch).sum().item()
        total_train += y_trn_batch.size(0)

    train_losses.append(running_loss / len(trn_loader))
    train_accuracies.append(correct_train / len(x_trn))

    model.eval()
    test_loss = 0
    correct_test = 0
    total_test = 0

    with torch.no_grad():
        for x_val_batch, y_val_batch in val_loader:
            y_pred_batch = model(x_val_batch)
            loss = loss_function(y_pred_batch, y_val_batch)
            test_loss += loss.item()
            _, predicted = torch.max(y_pred_batch, 1)
            correct_test += (predicted == y_val_batch).sum().item()
            total_test += y_val_batch.size(0)

    test_losses.append(test_loss / len(val_loader))
    test_accuracies.append(correct_test / len(x_val))

    print(f'Epoch {epoch+1}/{n_epochs}, Train Loss: {train_losses[-1]:.4f}, Train Accuracy: {train_accuracies[-1]:.4f}, Test Loss: {test_losses[-1]:.4f}, Test Accuracy: {test_accuracies[-1]:.4f}')


plot_pytorch_training_results(n_epochs, train_losses, test_losses, train_accuracies, test_accuracies)
1050it [00:12, 85.69it/s]
Epoch 1/10, Train Loss: 1.5524, Train Accuracy: 0.9181, Test Loss: 1.4922, Test Accuracy: 0.9714
1050it [00:11, 89.86it/s]
Epoch 2/10, Train Loss: 1.4903, Train Accuracy: 0.9727, Test Loss: 1.4898, Test Accuracy: 0.9738
1050it [00:11, 87.88it/s]
Epoch 3/10, Train Loss: 1.4841, Train Accuracy: 0.9784, Test Loss: 1.4847, Test Accuracy: 0.9774
1050it [00:11, 91.19it/s]
Epoch 4/10, Train Loss: 1.4805, Train Accuracy: 0.9818, Test Loss: 1.4800, Test Accuracy: 0.9818
1050it [00:11, 89.92it/s]
Epoch 5/10, Train Loss: 1.4769, Train Accuracy: 0.9849, Test Loss: 1.4786, Test Accuracy: 0.9832
1050it [00:11, 88.57it/s]
Epoch 6/10, Train Loss: 1.4750, Train Accuracy: 0.9865, Test Loss: 1.4764, Test Accuracy: 0.9858
1050it [00:11, 92.62it/s]
Epoch 7/10, Train Loss: 1.4737, Train Accuracy: 0.9880, Test Loss: 1.4780, Test Accuracy: 0.9842
1050it [00:11, 89.66it/s]
Epoch 8/10, Train Loss: 1.4725, Train Accuracy: 0.9890, Test Loss: 1.4770, Test Accuracy: 0.9851
1050it [00:11, 91.02it/s]
Epoch 9/10, Train Loss: 1.4711, Train Accuracy: 0.9905, Test Loss: 1.4758, Test Accuracy: 0.9861
1050it [00:11, 88.03it/s]
Epoch 10/10, Train Loss: 1.4700, Train Accuracy: 0.9915, Test Loss: 1.4758, Test Accuracy: 0.9852
y_pred_tot = []
with torch.no_grad():
    for x_tst_batch in tst_loader:
        y_pred_batch = model(x_val_batch)
        _, pred = torch.max(y_pred_batch, 1)
        y_pred_tot += list(pred.numpy())
tst_df["Label"] = y_prd_labels
tst_df[["ImageId", "Label"]].to_csv("pytorch-prediction.csv", index=False, header=True)