Red Convolucional en PyTorch (CNN)

Modificación al ejemplo del Perceptron Multicapa (MLP)

Mariano Rivera

agosto 2020

import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torch.autograd import Variable
import torchvision.datasets as datasets

torch.__version__
'1.3.1'

Aqui sólo definimos la clase de la red convolucional para ser usada en vez del Perceptrón Multi-Capa (MLP).

Clase CONVnet

Como alternativa definimos la red neuronal convolucional:

class CONVnet(nn.Module):
    def __init__(self, num_filters):
        super(CONVnet, self).__init__()
        '''
        El método __init__ define las capas de las cuales constará el modelo, 
        aunque no la forma en que se interconectan
        '''
        self.conv1  = nn.Conv2d(in_channels  =1, 
                                out_channels =num_filters[0], 
                                kernel_size  =3,
                                padding      =1,) 
        self.activ1 = nn.ReLU()
        self.max1   = nn.MaxPool2d(2)
        
        self.conv2  = nn.Conv2d(in_channels  =num_filters[0], 
                                out_channels =num_filters[1], 
                                kernel_size  =3,
                                padding      =1,)
        self.activ2 = nn.ReLU()
        self.max2   = nn.MaxPool2d(2)
        
        self.conv3  = nn.Conv2d(in_channels  =num_filters[1], 
                                out_channels =num_filters[2], 
                                kernel_size  =3,
                                padding      =1,)
        self.activ3 = nn.ReLU()
        self.max3   = nn.MaxPool2d(2)
        
        self.fc1    = nn.Linear(num_filters[2]*7*7, 10)
        self.num_filters = num_filters

    def forward(self, x):
        '''
        el método forward implementa la arquitecura de la red: define la 
        interconección de las capas, el orden en que se realiza el procesamiento
        '''
        x1 = self.conv1 (x)
        x1 = self.activ1(x1)
        x1 = self.max1  (x1)
        
        x2 = self.conv2 (x1)
        x2 = self.activ2(x2)
        x1 = self.max2  (x2)
        
        x3 = self.conv3 (x2)
        x3 = self.activ3(x3)
        x3 = self.max3  (x3)
        
        x4 = x3.view(-1, self.num_filters[2]*7*7)  # aqui convertimos la imagen a un vector unidimensional
        out = self.fc1(x4)
        return out
        
    def name(self):
        '''
        Regresa el nombre de la Clase de red
        '''
        return "CONV"

El modelo se construye en este caso con

model = CONVnet(num_filters = [10, 20, 40])

Note que de una capa convolucional a la siguiente se decrementan las dimensiones Alto y Ancho del tensor y se incrementa el número de canales (de acuerdo al número de filtros en la capa que antecede.

print(model)
CONVnet(
  (conv1): Conv2d(1, 10, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (activ1): ReLU()
  (max1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(10, 20, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (activ2): ReLU()
  (max2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv3): Conv2d(20, 40, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (activ3): ReLU()
  (max3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (fc1): Linear(in_features=1960, out_features=10, bias=True)
)