<jupyter_start><jupyter_text>Images<jupyter_code>img, label = cifar10[79]
plt.imshow(img)
plt.show()
print(f"Label:{label} - Class:{class_names[label]}")
from torchvision import transforms
to_tensor = transforms.ToTensor()
img_t = to_tensor(img)
img_t.shape
tensor_c10 = datasets.CIFAR10(data_path, train=True, download=False, transform=transforms.ToTensor())
# load the same image and view the the tensor shape and type
img_t, _ = tensor_c10[79]
img_t.shape, img_t.dtype
# Permute columns and visualize image again
plt.imshow(img_t.permute(1, 2, 0))
plt.show()
# Normalizing data
# 1. stack the dataset tensors along an extra dimension
imgs = torch.stack([img_t for img_t, _ in tensor_c10], dim=3)
imgs.shape
# 2. compute mean per channel
imgs.view(3, -1).mean(dim=1)
# 3. compute std deviation
imgs.view(3, -1).std(dim=1)
# 4 normalize the data 
normalized_c10 = datasets.CIFAR10(data_path, train=True, download=False,
                                       transform=transforms.Compose([
                                            transforms.ToTensor(),
                                            transforms.Normalize((0.4915, 0.4823, 0.4468),(0.2470, 0.2435, 0.2616))
                                        ]))
normalized_c10_val = datasets.CIFAR10(data_path, train=False, download=False,
                                       transform=transforms.Compose([
                                            transforms.ToTensor(),
                                            transforms.Normalize((0.4915, 0.4823, 0.4468),(0.2470, 0.2435, 0.2616))
                                        ]))
# view normalized image
img_t, _ = normalized_c10[79]
plt.imshow(img_t.permute(1, 2, 0))
plt.show()
img_t.view(-1)
# fully connected NN
fc_model = nn.Sequential(
          nn.Linear(3072, 1024),
          nn.Tanh(),
          nn.Linear(1024, 512),
          nn.Tanh(),
          nn.Linear(512, 128),
          nn.Tanh(),
          nn.Linear(128, 10))
# how many parameters have our fully connected NN ?
param_list = [p.numel()
              for p in fc_model.parameters()]
sum(param_list), param_list<jupyter_output><empty_output><jupyter_text>Convolutions<jupyter_code># create Conv2d
conv = nn.Conv2d(3, 16, kernel_size=3)
conv
# Apply convolution
output = conv(img_t.unsqueeze(0))
img_t.unsqueeze(0).shape, output.shape
plt.imshow(output[0, 0].detach())
plt.show()
# create new conv to perform edge detection
conv = nn.Conv2d(3, 1, kernel_size=3, padding=1)
# disable gradient calculation and set the weights
with torch.no_grad():
    conv.weight[:] = torch.tensor([[-1.0, 0.0, 1.0],
                                   [-1.0, 0.0, 1.0],
                                   [-1.0, 0.0, 1.0]])
    conv.bias.zero_()
output = conv(img_t.unsqueeze(0))
plt.imshow(output[0, 0].detach())
plt.show()<jupyter_output><empty_output><jupyter_text>Downsampling<jupyter_code># CNN combining convolutions and pooling
model = nn.Sequential(
    nn.Conv2d(3, 16, kernel_size=3, padding=1),
    nn.Tanh(),
    nn.MaxPool2d(2),
    nn.Conv2d(16, 8, kernel_size=3, padding=1),
    nn.Tanh(),
    nn.MaxPool2d(2),
    nn.Linear(8 * 8 * 8, 32),
    nn.Tanh(),
    nn.Linear(32, 10))
# how many parameters ?
# 18354 vs 3738506 of a Fully connected NN
numel_list = [p.numel() for p in model.parameters()]
sum(numel_list), numel_list
# if we apply a model to an image there was an error
# The layer that has a 512x32 is Linear, the 1sr after the last Convolution
# we must reshape from an 8 channels * 8 * 8 image to 512 1D vector
model(img_t.unsqueeze(0))<jupyter_output><empty_output><jupyter_text>So, we must reshape from an 8 channels * 8 * 8 image to 512 1D vector.This could be achieved by calling view on the output of the last nn.MaxPool2d, but unfortunately, we don’t have any explicit visibility of the output of each module when we use nn.Sequential. **A new Net subclass**When we want to build models that do more complex things than just applyingone layer after another, we need to leave nn.Sequential for something that gives us added flexibility. PyTorch allows us to use any computation in our model by subclassing nn.Module.<jupyter_code># The solution: make our own nn.module subclass

class Net(nn.Module):
  def __init__(self):
    super().__init__()
    self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
    self.act1 = nn.Tanh()
    self.pool1 = nn.MaxPool2d(2)
    self.conv2 = nn.Conv2d(16, 8, kernel_size=3, padding=1)
    self.act2 = nn.Tanh()
    self.pool2 = nn.MaxPool2d(2)
    self.fc1 = nn.Linear(8 * 8 * 8, 32)
    self.act3 = nn.Tanh()
    self.fc2 = nn.Linear(32, 10)

  # That takes the inputs to the module and returns the output
  def forward(self, x):
    out = self.pool1(self.act1(self.conv1(x)))
    out = self.pool2(self.act2(self.conv2(out)))
    out = out.view(-1, 8 * 8 * 8)  # the reshape to 1D 512 elements
    out = self.act3(self.fc1(out))
    out = self.fc2(out)
    return out
# we obtain 10 probabilities, 1 per class in the Cifar10 Dataset
model = Net()
model(img_t.unsqueeze(0))
# Refactor the Net subclass to use funct. Api for Activat.funct. and Pooling

import torch.nn.functional as F
class Net(nn.Module):
    def __init__(self):
      super().__init__()
      self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
      self.conv2 = nn.Conv2d(16, 8, kernel_size=3, padding=1)
      self.fc1 = nn.Linear(8 * 8 * 8, 32)
      self.fc2 = nn.Linear(32, 10)

    def forward(self, x):
      out = F.max_pool2d(torch.tanh(self.conv1(x)), 2)
      out = F.max_pool2d(torch.tanh(self.conv2(out)), 2)
      out = out.view(-1, 8 * 8 * 8)
      out = torch.tanh(self.fc1(out))
      out = self.fc2(out)
      return out<jupyter_output><empty_output><jupyter_text>We build a new dataset Cifar3 to have only 3 classes and down the training timesSelected classes:(0,1,8) ==> airplanes, automobiles and ships<jupyter_code>label_map = {0: 0, 1: 1, 2: 8}
class_names = ['airplane', 'automobile', 'ship']
cifar3 = [(img, label_map[label])
          for img, label in normalized_c10
          if label in [0, 1, 2]]
cifar3_val = [(img, label_map[label])
              for img, label in normalized_c10_val
              if label in [0, 1, 2]]
len(cifar3)<jupyter_output><empty_output><jupyter_text><jupyter_code># Training the Net CNN
import datetime

def training_net(n_epochs, optimizer, model, loss_fn, train_loader):
    for epoch in range(1, n_epochs + 1):
        loss_train = 0.0

        # The DataLoader (train_loader) create batches of 64 imgs (batch_size)
        for imgs, labels in train_loader:
            outputs = model(imgs)
            # compute the loss to minimize
            loss = loss_fn(outputs, labels)
            # put 0 the grads of the last round, compute new grads and update model
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            # sums the losses we saw over the epoch
            # item() to transform loss to a python number
            loss_train += loss.item()

        if epoch == 1 or epoch % 10 == 0:
            print('{} Epoch {}, Training loss {}'.format(
            datetime.datetime.now(), epoch,
            loss_train / len(train_loader)))
train_loader = torch.utils.data.DataLoader(cifar3, batch_size=64,
                shuffle=True)
model = Net() 
optimizer = optim.SGD(model.parameters(), lr=1e-2) 
loss_fn = nn.CrossEntropyLoss() 
training_net(
              n_epochs = 100,
              optimizer = optimizer,
              model = model,
              loss_fn = loss_fn,
              train_loader = train_loader,
              )
# build the data loaders for train and validation
train_loader = torch.utils.data.DataLoader(cifar3, batch_size=64,shuffle=False)
val_loader = torch.utils.data.DataLoader(cifar3_val, batch_size=64,shuffle=False)
# measure the accuracy using the validation dataset
def validate_net(model, train_loader, val_loader):
    for name, loader in [("train", train_loader), ("val", val_loader)]:
        correct = 0
        total = 0
        # don't want grads here
        with torch.no_grad():
          for imgs, labels in loader:
              outputs = model(imgs)
              # gives the index of the highest value of outputs
              _, predicted = torch.max(outputs, dim=1)
              # increased total with batch size
              total += labels.shape[0]
              # Comparing the predicted class that had the maximum probability 
              # with the correct label. The sum gives the number of items are agree
              correct += int((predicted == labels).sum())
        print(f"Accuracy {name}: {correct / total}")

validate_net(model, train_loader, val_loader)
# save model parameters (weights and biases)
torch.save(model.state_dict(), data_path + 'cifar3_train_params.pt')
# load model
loaded_model = Net()
loaded_model.load_state_dict(torch.load(data_path + 'cifar3_train_params.pt'))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11499157776530039, 'fraction_numerical': 0.04199887703537339, 'mean_word_length': 2.784955376115597, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '286373', 'license': 'CC0-1.0', 'n_tokens_mistral': 3159, 'n_tokens_neox': 2902, 'n_words': 816, 'path': 'nn_convolutional_world.ipynb', 'repo_name': 'jrercoli/nn_convolutional_world', 'size': '8905'}