<jupyter_start><jupyter_text>Reorganize image folders``` /train/    label1/       train_1.jpg       train_2.jpg    label2/       ... /test    /label1/       ...    /label2/``` IMAGE_PATH = "../input/plant-pathology-2020-fgvc7"OUTPUT_PATH = "../working/plant-pathology-2020-fgvc7"input_path = OUTPUT_PATH<jupyter_code>import os, csv, shutil

def move_files(root_path):
    image_class = {}
    classes = []
    class_pos = {}
    class_image = {}
    
    csv_name = 'train.csv'
    with open(f"{root_path}/{csv_name}", "rt") as f:
        first = True
        for line in csv.reader(f):
            if first:
                for i, c in enumerate(line):
                    if i > 0:
                        class_pos.setdefault(i - 1, c)
                        classes.append(c)
            else:
                for i, c in enumerate(line):
                    if i > 0 and int(c) == 1:
                        file_name = line[0]
                        image_class.setdefault(file_name, i - 1)
                        img_class_name = class_pos[i - 1]
                        class_image.setdefault(img_class_name, [])
                        class_image[img_class_name].append(file_name)
            first = False

    out_path = OUTPUT_PATH
    types = ["train", "test"]
    for t in types:
        dir_path = f"{out_path}/{t}"
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        for c in classes:
            sub_path = dir_path + "/" + c
            if not os.path.exists(sub_path):
                os.makedirs(sub_path)

    # files into test: 70 or 20%, whicheve is smaller
    for class_name in class_image:
        images = class_image[class_name]
        test_size = int(min(70, 0.2 * len(images)))
        sub_path = f"{out_path}/train/{class_name}"
        for fname in images[test_size : ]:
            src = f"{root_path}/images/{fname}.jpg"
            dst = f"{sub_path}/{fname}.jpg"
            shutil.copyfile(src, dst)
        sub_path = f"{out_path}/test/{class_name}"
        for fname in images[: test_size]:
            src = f"{root_path}/images/{fname}.jpg"
            dst = f"{sub_path}/{fname}.jpg"
            shutil.copyfile(src, dst)
move_files(IMAGE_PATH)<jupyter_output><empty_output><jupyter_text>Download Model<jupyter_code>device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
orig = Object()
orig.model = torchvision.models.resnet50(pretrained=True).to(device)
orig.model<jupyter_output><empty_output><jupyter_text>Replace the classifier<jupyter_code>orig.model.fc = nn.Sequential()
orig.model
_ = orig.model.requires_grad_(False)<jupyter_output><empty_output><jupyter_text>Load Images<jupyter_code>tr = Object()
tr.transforms = torchvision.transforms.Compose([
    torchvision.transforms.Resize((224, 224)),
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize(
        mean=[0.485, 0.456, 0.406], 
        std=[0.229, .224, .225])
])
tr.train = torchvision.datasets.ImageFolder(
    input_path + '/train', 
    transform=tr.transforms)
tr.valid = torchvision.datasets.ImageFolder(
    input_path + '/test', 
    transform=tr.transforms)
len(tr.train.samples)
tr.train_loader = torch.utils.data.DataLoader(
    tr.train, 
    batch_size=32,
    shuffle=True,
    num_workers=2,)
tr.valid_loader = torch.utils.data.DataLoader(
    tr.valid, 
    batch_size=32,
    shuffle=True,
    num_workers=2,)
tr.train_loader.dataset.classes
def build_embeddings(loader, model):
    train_embeddings = None
    train_y = None

    model.eval()

    batch_num = 1
    
    # The loop will read the set of images and corresponding
    # labels one batch at a time, or 32 images/labels at a time.
    # (see the batch_size value above for the actual size).
    for x, y in loader:
        if batch_num % 5 == 1:
            print('Batch:', batch_num)
        batch_num += 1;
        
        # Calculate outputs for all 16 images in a batch.
        # Rembember that outputs are the values produce by the
        # layer one before the last. Typically these values 
        # are called "embeddings".
        # It will be an array of 2048 real values per image.        
        batch_embeddings = model(x.to(device))

        # Concatenate new tensors to get one continuous array
        if train_embeddings is None:
            train_embeddings = batch_embeddings
        else:
            train_embeddings = torch.cat(
                (train_embeddings, batch_embeddings),
                0)

        if train_y is None:
            train_y = y.to(device)
        else:
            train_y = torch.cat(
                (train_y, y.to(device)),
                0)

    print(train_y.shape,
          train_embeddings.shape)

    return train_y, train_embeddings
%%time

tr.train_y, tr.train_embeddings = \
    build_embeddings(tr.train_loader, orig.model)
%%time

tr.test_y, tr.test_embeddings = \
    build_embeddings(tr.valid_loader, orig.model)
tr.train_embeddings.shape<jupyter_output><empty_output><jupyter_text>Validation<jupyter_code>def validate_model( model,
                    criterion,
                    x,
                    y):
    model.eval()
    outputs = model(x)
    loss = criterion(outputs, y)
    _, preds = torch.max(outputs, 1)
    epoch_loss = loss.item()
    epoch_acc = torch.sum(preds == y.data).double() / x.size(0)
    print(f'Validation loss: {epoch_loss:.4f}, accuracy: {epoch_acc:.4f}')<jupyter_output><empty_output><jupyter_text>Training<jupyter_code>tr.criterion = nn.CrossEntropyLoss()
def train_model_embeddings(
    model,
    epochs,
    batch_size, 
    train_emb,
    valid_emb,
    train_y,
    valid_y):
    
    criterion = tr.criterion
    opt = optim.SGD(model.parameters(), lr=1e-4, momentum=0.9, nesterov=True)
    
    report_every = min(3000, epochs / 10)
    for epoch in range(epochs):
        model.train()
        running_loss = 0
        running_correct = 0
        
        steps = int(train_emb.size(0) / batch_size + 1)
        
        for bi in range(steps):
            start = bi * batch_size
            end = (bi + 1) * batch_size
            
            x = train_emb[start : end]
            y = train_y[start : end]
            
            outputs = model(x)
            
            loss = criterion(outputs, y)
            
            opt.zero_grad()
            
            loss.backward()
            
            opt.step()
            
            _, preds = torch.max(outputs, 1)
            
            running_loss += loss.item() * x.size(0)
            running_correct += torch.sum(preds == y)
            
        if epoch % report_every == 1:
            epoch_loss = running_loss / train_emb.size(0)
            epoch_acc = running_correct.double() / train_emb.size(0)
            print(f'Epoch: {epoch}, Train Loss: {epoch_loss:.4f}',
                  f', accuracy: {epoch_acc:.4f}')
            
    epoch_loss = running_loss / train_emb.size(0)
    epoch_acc = running_correct.double() / train_emb.size(0)
    print(f'Batch Size: {batch_size}, epochs: {epochs}')
    print(f'Train Loss: {epoch_loss:.4f}, Train Acc: {epoch_acc:.4f}')
tr.fc = nn.Linear(2048, 4).to(device)
tr.fc
device
%%time

train_model_embeddings(
    tr.fc,
    1000,
    32,
    tr.train_embeddings,
    tr.test_embeddings,
    tr.train_y,
    tr.test_y)
validate_model(tr.fc, tr.criterion, tr.test_embeddings, tr.test_y)<jupyter_output><empty_output><jupyter_text>Make Predictions<jupyter_code>tr.test_images = [
    OUTPUT_PATH + '/test/healthy/Train_100.jpg',
    OUTPUT_PATH + '/test/multiple_diseases/Train_122.jpg',
    OUTPUT_PATH + '/test/rust/Train_10.jpg',
    OUTPUT_PATH + '/test/rust/Train_102.jpg',
    OUTPUT_PATH + '/test/scab/Train_11.jpg',
]
from PIL import Image
tr.img_list = [Image.open(img_path).convert("RGB") for img_path in tr.test_images]
tr.test_batch = torch.stack([
    tr.transforms(img).to(device) for img in tr.img_list])
tr.test_batch.shape
orig.model.eval()
tr.logits = orig.model(tr.test_batch)
tr.logits
tr.logits.shape
orig.model
orig.model.fc = tr.fc
orig.model.eval()
tr.logits = orig.model(tr.test_batch)
tr.logits
from torch.nn import functional as F
F.softmax(tr.logits, dim=1)
tr.train_loader.dataset.classes
tr.proba = F.softmax(tr.logits, dim=1).cpu().data.numpy()
tr.proba
import matplotlib.pyplot as plt
%matplotlib inline

tr.fig, tr.axs = plt.subplots(1, len(tr.img_list), figsize=(20, 5))
for i, img in enumerate(tr.img_list):
    ax = tr.axs[i]
    ax.axis('off')
    
    ax.set_title("{:.0f}% {}, {:.0f}% {}\n {:.0f}% {}, {:.0f}% {}"
                 .format(100 * tr.proba[i,0], tr.train_loader.dataset.classes[0],
                         100 * tr.proba[i,1], tr.train_loader.dataset.classes[1],
                         100 * tr.proba[i,2], tr.train_loader.dataset.classes[2],
                         100 * tr.proba[i,3], tr.train_loader.dataset.classes[3],
                  )
    )
    
    ax.imshow(img)
tr.test_images = [
    OUTPUT_PATH + '/test/healthy/Train_100.jpg',
    OUTPUT_PATH + '/test/multiple_diseases/Train_122.jpg',
    OUTPUT_PATH + '/test/rust/Train_10.jpg',
    OUTPUT_PATH + '/test/rust/Train_102.jpg',
    OUTPUT_PATH + '/test/scab/Train_11.jpg',
]<jupyter_output><empty_output><jupyter_text>Convert To Production<jupyter_code>orig.model_script = torch.jit.trace(orig.model, tr.test_batch)
_ = orig.model_script.cpu()
!pip install ml2rt
import ml2rt
ml2rt.save_torch(orig.model_script, 'plant.pt')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1247230136119025, 'fraction_numerical': 0.019098870950722803, 'mean_word_length': 2.336149243224217, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '237050', 'license': 'MIT', 'n_tokens_mistral': 3379, 'n_tokens_neox': 3201, 'n_words': 607, 'path': 'pytorch-redisai/live-coding/PyTorch Transfer Learning - Live Coding.ipynb', 'repo_name': 'alex-kalinin/pytorch-workshops', 'size': '9477'}