<jupyter_start><jupyter_text>Utilizando un modelo pre-entrenado[`torchvision.models`](https://pytorch.org/vision/stable/models.html) ofrece una serie de modelos famosos de la literatura de *deep learning*Por defecto el modelo se carga con pesos aleatoriosSi indicamos `pretrained=True` se descarga un modelo entrenadoSe pueden escoger modelos para clasificar, localizar y segmentar Modelo para clasificar imágenestorchvision tiene una basta cantidad de modelos para clasificar incluyendo distintas versiones de VGG, ResNet, AlexNet, GoogLeNet, DenseNet, entre otrosCargaremos un modelo [resnet18](https://arxiv.org/pdf/1512.03385.pdf) [pre-entrenado](https://pytorch.org/docs/stable/torchvision/models.htmltorchvision.models.resnet18) en [ImageNet](http://image-net.org/)<jupyter_code>from torchvision import models

model = models.resnet18(pretrained=True, progress=True)
model.eval()<jupyter_output><empty_output><jupyter_text>Los modelos pre-entrenados esperan imágenes con- tres canales (RGB)- al menos 224x224 píxeles- píxeles entre 0 y 1 (float)- normalizadas con         normalize = torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],                                                     std=[0.229, 0.224, 0.225])<jupyter_code>img
from PIL import Image
import torch
from torchvision import transforms

img = Image.open("img/dog.jpg")

my_transform = transforms.Compose([transforms.Resize(256),
                                   transforms.CenterCrop(224),
                                   transforms.ToTensor(),
                                   transforms.Normalize(mean=(0.485, 0.456, 0.406), 
                                                        std=(0.229, 0.224, 0.225))])

# Las clases con probabilidad más alta son
probs = torch.nn.Softmax(dim=1)(model.forward(my_transform(img).unsqueeze(0)))

best = probs.argsort(descending=True)
display(best[0, :10], 
        probs[0, best[0, :10]])<jupyter_output><empty_output><jupyter_text>¿A qué corresponde estas clases?Clases de ImageNet: https://gist.github.com/ageitgey/4e1342c10a71981d0b491e1b8227328b  Modelo para detectar entidades en imágenesAdicional a los modelos de clasificación torchvision también tiene modelos para- Detectar entidades en una imagen: Faster RCNN- Hacer segmentación por instancia: Mask RCNN- Hacer segmentación semántica: FCC, DeepLab- Clasificación de video A continuación probaremos la [Faster RCNN](https://arxiv.org/abs/1506.01497) para hace detecciónEste modelo fue pre-entrenado en la base de datos [COCO](https://cocodataset.org/)El modelo retorna un diccionario con- 'boxes': Los bounding box de las entidades- 'labels': La etiqueta de la clase más probable de la entidad- 'score': La probabilidad de la etiqueta<jupyter_code>model = models.detection.fasterrcnn_resnet50_fpn(pretrained=True)
model.eval()

transform = transforms.ToTensor()
img = Image.open("img/pelea.jpg") # No require normalización de color
img_tensor = transform(img)

result = model(img_tensor.unsqueeze(0))[0]

def filter_results(result, threshold=0.9):
    mask = result['scores'] > threshold
    bbox = result['boxes'][mask].detach().cpu().numpy()
    lbls = result['labels'][mask].detach().cpu().numpy()
    return bbox, lbls
from PIL import ImageFont, ImageDraw
#fnt = ImageFont.truetype("arial.ttf", 20) 

label2name = {1: 'persona', 2: 'bicicleta', 3: 'auto', 4: 'moto', 
              8: 'camioneta', 18: 'perro'}

def draw_rectangles(img, bbox, lbls):
    draw = ImageDraw.Draw(img)
    for k in range(len(bbox)):
        if lbls[k] in label2name.keys():
            draw.rectangle(bbox[k], fill=None, outline='white', width=2)
            draw.text([int(d) for d in bbox[k][:2]], label2name[lbls[k]], fill='white')

bbox, lbls = filter_results(result)
img = Image.open("img/pelea.jpg")
draw_rectangles(img, bbox, lbls)
display(img)<jupyter_output><empty_output><jupyter_text>Transferencia de AprendizajeA continuación usaremos la técnicas de transferencia de aprendizaje para aprender un clasificador de imágenes para un fragmento de la base de datos food 5kEl objetivo es clasificar si la imagen corresponde a comida o noGuardamos las imagenes con la siguiente estructura de carpetas<jupyter_code>!ls img/food5k/
!ls img/food5k/train
!ls img/food5k/valid<jupyter_output><empty_output><jupyter_text>Con esto podemos usar `torchvision.datasets.ImageFolder` para crear los dataset de forma muy sencillaDado que usaremos un modelo preentrenado debemos transformar entregar las imágenes en tamaño 224x224 y con color normalizadoUsaremos también aumentación de datos en el conjunto de entrenamiento<jupyter_code>from torchvision import datasets

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])])

valid_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_dataset = datasets.ImageFolder('img/food5k/train', transform=train_transforms)
valid_dataset = datasets.ImageFolder('img/food5k/valid', transform=valid_transforms)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=32, shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid_dataset, batch_size=256, shuffle=False)

for image, label in train_loader:
    break
    
fig, ax = plt.subplots(1, 6, figsize=(9, 2), tight_layout=True)
for i in range(6):
    ax[i].imshow(image[i].permute(1,2,0).numpy())
    ax[i].axis('off')
    ax[i].set_title(label[i].numpy())<jupyter_output><empty_output><jupyter_text>Usaremos el modelo ResNet18<jupyter_code>model = models.resnet18(pretrained=True, progress=True)
# model = models.squeezenet1_1(pretrained=True, progress=True)
display(model)<jupyter_output><empty_output><jupyter_text>En este caso re-entrenaremos sólo la última capa: `fc`Las demás capas las congelaremosPara congelar una capa simplemente usamos `requires_grad=False` en sus parámetrosCuando llamemos `backward` no se calculará gradiente para estas capas<jupyter_code>#Congelamos todos los parámetros
for param in model.parameters(): 
    param.requires_grad = False

# La reemplazamos por una nueva capa de salida
model.fc = torch.nn.Linear(model.fc.in_features , 2)  # Para resnet
#model.classifier = torch.nn.Sequential(torch.nn.Dropout(p=0.5, inplace=False), 
#                                       torch.nn.Conv2d(512, 2, kernel_size=(1, 1), stride=(1, 1)),
#                                       torch.nn.ReLU(inplace=True),
#                                       torch.nn.AdaptiveAvgPool2d(output_size=(1, 1))) # Para Squeezenet

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(10):
    for x, y in train_loader:
        optimizer.zero_grad()
        yhat = model.forward(x)
        loss = criterion(yhat, y)
        loss.backward()
        optimizer.step()

    epoch_loss = 0.0
    for x, y in valid_loader:
        yhat = model.forward(x)
        loss = criterion(yhat, y)
        epoch_loss += loss.item()
    print(f"{epoch}, {epoch_loss:0.4f}, {torch.sum(yhat.argmax(dim=1) == y).item()/100}")
targets, predictions = [], []
for mbdata, label in valid_loader:
    logits = model.forward(mbdata)
    predictions.append(logits.argmax(dim=1).detach().numpy())
    targets.append(label.numpy())
predictions = np.concatenate(predictions)
targets = np.concatenate(targets)

from sklearn.metrics import confusion_matrix, classification_report

cm = confusion_matrix(targets, predictions)
display(cm)

print(classification_report(targets, predictions))<jupyter_output><empty_output><jupyter_text>¿Cómo se compara lo anterior a entrenar una arquitectura convolucional desde cero?A modo de ejemplo se adapta la arquitectura Lenet5 para aceptar imágenes a color de 224x224 ¿Cuánto desempeño se obtiene entrenando la misma cantidad de épocas?<jupyter_code>import torch.nn as nn
class Lenet5(nn.Module):
    
    def __init__(self):
        super(type(self), self).__init__()
        # La entrada son imágenes de 3x224x224
        self.features = nn.Sequential(nn.Conv2d(3, 6, 5),
                                      nn.ReLU(),
                                      nn.MaxPool2d(3),
                                      nn.Conv2d(6, 16, 5),
                                      nn.ReLU(),
                                      nn.MaxPool2d(3), 
                                      nn.Conv2d(16, 32, 5),
                                      nn.ReLU(),
                                      nn.MaxPool2d(3))
        
        self.classifier = nn.Sequential(nn.Linear(32*6*6, 120),
                                        nn.ReLU(),
                                        nn.Linear(120, 84),
                                        nn.ReLU(),
                                        nn.Linear(84, 2))

    def forward(self, x):
        z = self.features(x)
        #print(z.shape)
        # Esto es de tamaño Mx16x5x5
        z = z.view(-1, 32*6*6)
        # Esto es de tamaño Mx400
        return self.classifier(z)
    
model = Lenet5()
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

for epoch in range(10):
    for x, y in train_loader:
        optimizer.zero_grad()
        yhat = model.forward(x)
        loss = criterion(yhat, y)
        loss.backward()
        optimizer.step()

    epoch_loss = 0.0
    for x, y in valid_loader:
        yhat = model.forward(x)
        loss = criterion(yhat, y)
        epoch_loss += loss.item()
    print(f"{epoch}, {epoch_loss:0.4f}, {torch.sum(yhat.argmax(dim=1) == y).item()/100}")
targets, predictions = [], []
for mbdata, label in valid_loader:
    logits = model.forward(mbdata)
    predictions.append(logits.argmax(dim=1).detach().numpy())
    targets.append(label.numpy())
predictions = np.concatenate(predictions)
targets = np.concatenate(targets)

from sklearn.metrics import confusion_matrix, classification_report

cm = confusion_matrix(targets, predictions)
display(cm)

print(classification_report(targets, predictions))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10914231856738925, 'fraction_numerical': 0.03147973609802074, 'mean_word_length': 2.8740416210295727, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 34, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 33, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '8246', 'license': 'CC0-1.0', 'n_tokens_mistral': 3609, 'n_tokens_neox': 3372, 'n_words': 766, 'path': 'notebooks/clases/6_modelos_preentrenados.ipynb', 'repo_name': 'phuijse/INFO257', 'size': '10610'}