Las redes neuronales son un paradigma de programación de inspiración biológica en el que se basa el aprendizaje profundo. Python proporciona varias bibliotecas con las que puede crear y entrenar redes neuronales sobre datos dados. PyTorch es una de esas bibliotecas que nos brinda varias utilidades para construir y entrenar redes neuronales fácilmente. Cuando se trata de redes neuronales, se vuelve esencial establecer una arquitectura óptima e hiperparámetros. Mientras se entrena una red neuronal, la pérdida de entrenamiento siempre se reduce siempre que la tasa de aprendizaje sea óptima. Pero es importante que nuestra red funcione mejor no solo con los datos en los que está entrenada, sino también con los datos que nunca antes había visto. Una forma de medir esto es mediante la introducción de un conjunto de validación para realizar un seguimiento de la precisión de las pruebas de la red neuronal.
Instalación de PyTorch
La instalación de PyTorch es bastante similar a cualquier otra biblioteca de Python. Podemos usar pip o conda para instalar PyTorch: –
pip install torch torchvision
Este comando instalará PyTorch junto con torchvision, que proporciona varios conjuntos de datos, modelos y transformaciones para la visión artificial. Para instalar usando conda puede usar el siguiente comando:-
conda install pytorch torchvision torchaudio cudatoolkit=10.2 -c pytorch
Cargando datos
Para este tutorial, vamos a utilizar el conjunto de datos MNIST que se proporciona en la biblioteca de torchvision. En Deep Learning, a menudo entrenamos nuestras redes neuronales en lotes de cierto tamaño, DataLoader es una utilidad de carga de datos en PyTorch que crea una iteración sobre estos lotes del conjunto de datos. Empecemos cargando nuestros datos:-
from torchvision import datasets, transforms from torch.utils.data import DataLoader, random_split transforms = transforms.Compose([ transforms.ToTensor() ])
En el código anterior, declaramos una variable llamada transform que esencialmente nos ayuda a transformar los datos sin procesar en el formato definido. Aquí nuestra transformación es simplemente tomar los datos sin procesar y convertirlos en un tensor. Un tensor es una forma elegante de decir una array n-dimensional.
train = datasets.MNIST('', train = True, transform = transforms, download = True) train, valid = random_split(train,[50000,10000])
Ahora estamos descargando nuestros datos sin procesar y aplicamos la transformación sobre ellos para convertirlos en tensores, el entrenamiento indica si los datos que se están cargando son datos de entrenamiento o datos de prueba. Al final, dividimos el tensor de tren en 2 tensores de 50000 y 10000 puntos de datos que se convierten en nuestro tren y tensores válidos.
trainloader = DataLoader(train, batch_size=32) validloader = DataLoader(valid, batch_size=32)
Ahora acabamos de crear nuestros cargadores de datos de los tensores anteriores de 32 tamaños de lote. Ahora que tenemos los datos, comencemos por crear nuestra red neuronal.
Construyendo nuestro modelo
Hay 2 formas en que podemos crear redes neuronales en PyTorch, es decir, usando el método Sequential() o usando el método de clase. Usaremos el método de clase para crear nuestra red neuronal, ya que brinda más control sobre el flujo de datos. El formato para crear una red neuronal usando el método de clase es el siguiente:-
from torch import nn class model(nn.Module): def __init__(self): # Define Model Here def forward(self, x): # Define Forward Pass Here
Entonces, en el método __init__() definimos nuestras capas y otras variables y en el método forward() definimos nuestro paso hacia adelante, es decir, cómo fluyen los datos a través de las capas.
import torch from torch import nn import torch.nn.functional as F class Network(nn.Module): def __init__(self): super(Network,self).__init__() self.fc1 = nn.Linear(28*28, 256) self.fc2 = nn.Linear(256, 128) self.fc3 = nn.Linear(128, 10) def forward(self, x): x = x.view(1,-1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x model = Network() if torch.cuda.is_available(): model = model.cuda()
En el código anterior, definimos una red neuronal con la siguiente arquitectura:
- Capa de entrada: 784 Nodes, las imágenes MNIST tienen una dimensión de 28*28 que tienen 784 píxeles, por lo que cuando se aplanan se convertirán en la entrada de la red neuronal con 784 Nodes de entrada.
- Capa oculta 1: 256 Nodes
- Capa oculta 2: 128 Nodes
- Capa de salida: 10 Nodes, para 10 clases, es decir, números 0-9
nn.Linear() o Linear Layer se utiliza para aplicar una transformación lineal a los datos entrantes. Si está familiarizado con TensorFlow, es muy parecido a la capa densa.
En el método forward() comenzamos aplanando la imagen y pasándola a través de cada capa y aplicando la función de activación para la misma. Después de eso, creamos nuestra instancia de red neuronal y, por último, solo estamos verificando si la máquina tiene una GPU y, si la tiene, transferiremos nuestro modelo allí para un cálculo más rápido.
Definición de criterio y optimizador
Los optimizadores definen cómo se actualizarán los pesos de la red neuronal, en este tutorial usaremos SGD Optimizer o Stochastic Gradient Descent Optimizer. Los optimizadores toman los parámetros del modelo y la tasa de aprendizaje como argumentos de entrada. Hay varios optimizadores que puede probar como Adam, Adagrad, etc.
El criterio es la pérdida que desea minimizar, que en este caso es CrossEntropyLoss(), que es la combinación de log_softmax() y NLLLoss().
criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr = 0.01)
Red neuronal de entrenamiento con validación
El paso de entrenamiento en PyTorch es casi idéntico casi cada vez que lo entrenas. Pero antes de implementar eso, aprendamos sobre 2 modos del objeto modelo: –
- Modo de entrenamiento: establecido por model.train() , le dice a su modelo que está entrenando al modelo. Entonces, las capas como el abandono, etc., que se comportan de manera diferente durante el entrenamiento y las pruebas, pueden comportarse en consecuencia.
- Modo de evaluación: establecido por model.eval() , le dice a su modelo que está probando el modelo.
A pesar de que no lo necesita aquí, es mejor saber acerca de ellos. Ahora que lo tenemos claro, entendamos los pasos de entrenamiento:
- Mover datos a GPU (Opcional)
- Borre los gradientes usandoOptimizer.zero_grad()
- Hacer un pase hacia adelante
- Calcular la pérdida
- Realice un pase hacia atrás usando loss.backward() para calcular los gradientes
- Tome el paso del optimizador usandoOptimizer.step() para actualizar los pesos
Los pasos de validación y prueba también son similares, pero solo hace un pase hacia adelante y calcula la pérdida. Un bucle de entrenamiento simple sin validación se escribe de la siguiente manera:
epochs = 5 for e in range(epochs): train_loss = 0.0 for data, labels in tqdm(trainloader): # Transfer Data to GPU if available if torch.cuda.is_available(): data, labels = data.cuda(), labels.cuda() # Clear the gradients optimizer.zero_grad() # Forward Pass target = model(data) # Find the Loss loss = criterion(target,labels) # Calculate gradients loss.backward() # Update Weights optimizer.step() # Calculate Loss train_loss += loss.item() print(f'Epoch {e+1} \t\t Training Loss: {train_loss / len(trainloader)}')
Si agrega el ciclo de validación, será lo mismo, pero solo con el cálculo de pérdida y pase hacia adelante. Pero puede suceder que su última iteración no sea la que le dio la menor pérdida de validación. Para abordar esto, podemos establecer una pérdida válida máxima que puede ser np.inf y, si la pérdida válida actual es menor, podemos guardar el diccionario de estado del modelo que podemos cargar más tarde, como un punto de control. state_dict es un objeto OrderedDict que asigna cada capa a su tensor de parámetros.
import numpy as np epochs = 5 min_valid_loss = np.inf for e in range(epochs): train_loss = 0.0 model.train() # Optional when not using Model Specific layer for data, labels in trainloader: if torch.cuda.is_available(): data, labels = data.cuda(), labels.cuda() optimizer.zero_grad() target = model(data) loss = criterion(target,labels) loss.backward() optimizer.step() train_loss += loss.item() valid_loss = 0.0 model.eval() # Optional when not using Model Specific layer for data, labels in validloader: if torch.cuda.is_available(): data, labels = data.cuda(), labels.cuda() target = model(data) loss = criterion(target,labels) valid_loss = loss.item() * data.size(0) print(f'Epoch {e+1} \t\t Training Loss: {train_loss / len(trainloader)} \t\t Validation Loss: {valid_loss / len(validloader)}') if min_valid_loss > valid_loss: print(f'Validation Loss Decreased({min_valid_loss:.6f}--->{valid_loss:.6f}) \t Saving The Model') min_valid_loss = valid_loss # Saving State Dict torch.save(model.state_dict(), 'saved_model.pth')
Después de ejecutar el código anterior, debería obtener el siguiente resultado, aunque su pérdida puede variar:
Código
Python3
import torch from torch import nn import torch.nn.functional as F from torchvision import datasets, transforms from torch.utils.data import DataLoader, random_split import numpy as np #Declare transform to convert raw data to tensor transforms = transforms.Compose([ transforms.ToTensor() ]) # Loading Data and splitting it into train and validation data train = datasets.MNIST('', train = True, transform = transforms, download = True) train, valid = random_split(train,[50000,10000]) # Create Dataloader of the above tensor with batch size = 32 trainloader = DataLoader(train, batch_size=32) validloader = DataLoader(valid, batch_size=32) # Building Our Mode class Network(nn.Module): # Declaring the Architecture def __init__(self): super(Network,self).__init__() self.fc1 = nn.Linear(28*28, 256) self.fc2 = nn.Linear(256, 128) self.fc3 = nn.Linear(128, 10) # Forward Pass def forward(self, x): x = x.view(x.shape[0],-1) # Flatten the images x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x model = Network() if torch.cuda.is_available(): model = model.cuda() # Declaring Criterion and Optimizer criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr = 0.01) # Training with Validation epochs = 5 min_valid_loss = np.inf for e in range(epochs): train_loss = 0.0 for data, labels in trainloader: # Transfer Data to GPU if available if torch.cuda.is_available(): data, labels = data.cuda(), labels.cuda() # Clear the gradients optimizer.zero_grad() # Forward Pass target = model(data) # Find the Loss loss = criterion(target,labels) # Calculate gradients loss.backward() # Update Weights optimizer.step() # Calculate Loss train_loss += loss.item() valid_loss = 0.0 model.eval() # Optional when not using Model Specific layer for data, labels in validloader: # Transfer Data to GPU if available if torch.cuda.is_available(): data, labels = data.cuda(), labels.cuda() # Forward Pass target = model(data) # Find the Loss loss = criterion(target,labels) # Calculate Loss valid_loss += loss.item() print(f'Epoch {e+1} \t\t Training Loss: {\ train_loss / len(trainloader)} \t\t Validation Loss: {\ valid_loss / len(validloader)}') if min_valid_loss > valid_loss: print(f'Validation Loss Decreased({min_valid_loss:.6f\ }--->{valid_loss:.6f}) \t Saving The Model') min_valid_loss = valid_loss # Saving State Dict torch.save(model.state_dict(), 'saved_model.pth')
Publicación traducida automáticamente
Artículo escrito por herumbshandilya y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA