<jupyter_start><jupyter_text>[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)]()  Densenet transfer learning  Pre-requirementsTraining data should be arranged in the following structure:```data-- train---- class1------ img1.jpg------ img2.jpg---- class2---- ...-- val---- class1------ img101.jpg------ img102.jpg---- class2---- ...-- test---- img201.jpg---- img202.jpg---- ...```<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import torch 
from torch.utils.data import DataLoader
from torch import nn
import torch.nn.functional as F
import torchvision
from torchvision import datasets, transforms, models
from tqdm.auto import tqdm<jupyter_output><empty_output><jupyter_text>Check if host machine has GPU available<jupyter_code>device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print(device)<jupyter_output>cuda:0<jupyter_text>Load Images Apply transformsMost of the pretrained models require the input to be 224x224 images. Also, we'll need to match the normalization used when the models were trained. Each color channel was normalized separately, the means are [0.485, 0.456, 0.406] and the standard deviations are [0.229, 0.224, 0.225]. (see these same values used in an official PyTorch example [here](https://github.com/pytorch/examples/blob/42e5b996718797e45c46a25c55b031e6768f8440/imagenet/main.pyL89-L101))<jupyter_code>data_dir = 'data'


train_transforms = transforms.Compose([
    transforms.RandomRotation(30),
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225])
])

# Just normalization for validation
test_transforms = transforms.Compose([
    transforms.Resize(255),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225])
])

train = datasets.ImageFolder(data_dir+'/train', transform=train_transforms)
test = datasets.ImageFolder(data_dir+'/val', transform=test_transforms)

trainloader = torch.utils.data.DataLoader(train, batch_size=16, shuffle=True, num_workers=4)
testloader = torch.utils.data.DataLoader(test, batch_size=16, shuffle=True, num_workers=4)
print(len(train), len(test))
class_mapping = {}
n_classes = 0
for c in train.classes:
    class_mapping[n_classes] = c
    n_classes += 1

print(n_classes)
print(class_mapping)
def imshow(inp, title=None):
    """Imshow for Tensor."""
    inp = torchvision.utils.make_grid(inp)
    # in tensor, image is (batch, width, height)
    # so you have to transpose it to (width, height, batch) 
    # in numpy to show it.
    inp = inp.numpy().transpose((1, 2, 0))
    # de-normalise images to get original colours
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    
    plt.figure(figsize=(9,9))
    plt.imshow(inp)

# Get a batch of training data
inputs, labels = next(iter(trainloader))
# show images in batch
imshow(inputs)
# load pre-trained densemodel model
model = torchvision.models.densenet121(pretrained=True)

# freeze parameters so we don't backprop through them
for param in model.parameters():
    param.requires_grad = False<jupyter_output><empty_output><jupyter_text>Replace the head of the model<jupyter_code># ensure the new head fits onto the existing body
num_ftrs = model.classifier.in_features
# define architecture of classifier head
classifier = nn.Sequential(
    nn.Linear(num_ftrs, 512),
    nn.ReLU(),
    nn.Linear(512,256),
    nn.ReLU(),
    nn.Linear(256, n_classes),
    nn.LogSoftmax(dim=-1)
)

# update the head on pretrained model
model.classifier = classifier
model = model.to(device)
criterion = nn.NLLLoss()
optimizer = torch.optim.Adam(model.classifier.parameters(), lr=.003)<jupyter_output><empty_output><jupyter_text>Training loop<jupyter_code>epochs = 3
losses = []
for epoch in range(epochs):
    
    # turns on dropout
    model.train()
    
    batch = 1
    running_loss = 0.0
    for inputs, labels in tqdm(trainloader):
        inputs, labels = inputs.to(device), labels.to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = model(inputs)  # forward pass
        loss = criterion(outputs, labels)  # calculate loss
        loss.backward()  # backprop
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        losses.append(running_loss / batch)
        batch += 1
    print(f'Epoch {epoch+1} training loss: {running_loss / batch:.3f}')
    
    # VALIDATION AT END OF EPOCH
    # turn off gradients for validation, saves memory and computations
    with torch.no_grad():

        # turn off dropout
        model.eval()
        test_batch = 1
        val_loss = 0.0
        y_true = np.array([])
        y_pred = np.array([])
        for data in testloader:
            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs).to('cpu')
            loss = criterion(outputs, labels.to('cpu'))
            val_loss += loss.item()

            _, batch_predictions = torch.max(outputs.data, 1)
            y_pred = np.hstack((y_pred, batch_predictions.numpy()))
            y_true = np.hstack((y_true, labels.to('cpu').numpy()))
            test_batch += 1

        print(f'Validation accuracy: {100*(y_true == y_pred).mean():.3f}%')

print('Finished Training')
plt.plot(losses, label='loss')
plt.legend()
plt.title('training loss')
plt.show()<jupyter_output><empty_output><jupyter_text>Save model for re-use on other devices<jupyter_code># TODO make model name dynamic
torch.save(model,'./models/densenet.pt')<jupyter_output><empty_output><jupyter_text>Sample some test images and show predicted labels  - TODO: add true labels to plot<jupyter_code>model.eval()

samples, _ = iter(testloader).next()
samples = samples.to(device)
output = model(samples[:24])
pred = torch.argmax(output, dim=1)
pred = [p.item() for p in pred]

fig = plt.figure(figsize=(24, 16))
fig.tight_layout()
for num, sample in enumerate(samples[:24]):
    plt.subplot(4,6,num+1)
    plt.title(class_mapping[pred[num]])
    plt.axis('off')
    sample = sample.cpu().numpy()
    sample = np.transpose(sample, (1,2,0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    sample = std * sample + mean
    sample = np.clip(sample, 0, 1)
    plt.imshow(sample)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13354506957047793, 'fraction_numerical': 0.03932244404113733, 'mean_word_length': 3.545017182130584, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 28, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '270427', 'license': 'MIT', 'n_tokens_mistral': 2366, 'n_tokens_neox': 2159, 'n_words': 530, 'path': '1_densenet.ipynb', 'repo_name': 'datahounds/what-whale-classifier', 'size': '6612'}