Ajuste de la tasa de aprendizaje de una red neuronal en PyTorch

La tasa de aprendizaje es un hiperparámetro importante en Gradient Descent. Su valor determina qué tan rápido convergería la red neuronal a los mínimos. Por lo general, elegimos una tasa de aprendizaje y, según los resultados, cambiamos su valor para obtener el valor óptimo para LR. Si la tasa de aprendizaje es demasiado baja para la red neuronal, el proceso de convergencia sería muy lento y si es demasiado alta, la convergencia sería rápida, pero existe la posibilidad de que la pérdida se exceda. Por lo tanto, generalmente ajustamos nuestros parámetros para encontrar el mejor valor para la tasa de aprendizaje. Pero, ¿hay alguna manera de que podamos mejorar este proceso?

¿Por qué ajustar la tasa de aprendizaje?

En lugar de tomar una tasa de aprendizaje constante, podemos comenzar con un valor más alto de LR y luego seguir disminuyendo su valor periódicamente después de ciertas iteraciones. De esta forma, inicialmente podemos tener una convergencia más rápida mientras reducimos las posibilidades de sobrepasar la pérdida. Para implementar esto, podemos usar varios programadores en la biblioteca optim en PyTorch. El formato de un ciclo de entrenamiento es el siguiente:-

epochs = 10
scheduler = <Any scheduler>

for epoch in range(epochs):
    # Training Steps
     
    # Validation Steps
    
    scheduler.step()

Programadores de uso común en torch.optim.lr_scheduler

PyTorch proporciona varios métodos para ajustar la tasa de aprendizaje en función del número de épocas. Echemos un vistazo a algunos de ellos:

  • StepLR:   multiplica la tasa de aprendizaje con gamma cada época de tamaño de paso . Por ejemplo, si lr = 0.1, gamma = 0.1 y step_size = 10, luego de 10 épocas lr cambia a lr*step_size en este caso 0.01 y luego de otras 10 épocas se convierte en 0.001.
# Code format:-
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = StepLR(optimizer, step_size=10, gamma=0.1)

# Procedure:-
lr = 0.1, gamma = 0.1 and step_size = 10
lr = 0.1               for epoch < 10
lr = 0.01              for epoch >= 10 and epoch < 20
lr = 0.001             for epoch >= 20 and epoch < 30
... and so on
  • MultiStepLR: esta es una versión más personalizada de StepLR en la que el lr se cambia después de que alcanza una de sus épocas. Aquí proporcionamos hitos que son épocas en las que queremos actualizar nuestra tasa de aprendizaje.
# Code format:-
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = MultiStepLR(optimizer, milestones=[10,30], gamma=0.1)

# Procedure:-
lr = 0.1, gamma = 0.1 and milestones=[10,30]
lr = 0.1               for epoch < 10
lr = 0.01              for epoch >= 10 and epoch < 30
lr = 0.001             for epoch >= 30
  • ExponentialLR: esta es una versión agresiva de StepLR en LR que se cambia después de cada época. Puede pensar en ello como StepLR con step_size = 1.
# Code format:-
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = ExponentialLR(optimizer, gamma=0.1)

# Procedure:-
lr = 0.1, gamma = 0.1
lr = 0.1               for epoch = 1
lr = 0.01              for epoch = 2
lr = 0.001             for epoch = 3
... and so on
  • ReduceLROnPlateau: reduce la tasa de aprendizaje cuando una métrica deja de mejorar. Los modelos a menudo se benefician al reducir la tasa de aprendizaje en un factor de 2 a 10 una vez que el aprendizaje se estanca. Este planificador lee una cantidad de métricas y, si no se observa ninguna mejora durante un número de épocas de paciencia , la tasa de aprendizaje se reduce.
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = ReduceLROnPlateau(optimizer, 'min', patience = 5)

# In min mode, lr will be reduced when the metric has stopped decreasing. 
# In max mode, lr will be reduced when the metric has stopped increasing. 

Entrenamiento de redes neuronales usando programadores

Para este tutorial, usaremos el conjunto de datos MNIST, por lo que comenzaremos cargando nuestros datos y luego definiendo el modelo. Se recomienda que sepa cómo crear y entrenar una red neuronal en PyTorch. Comencemos cargando nuestros datos.

from torchvision import datasets,transforms
from torch.utils.data import DataLoader

transform = transforms.Compose([
    transforms.ToTensor()
])

train = datasets.MNIST('',train = True, download = True, transform=transform)
valid = datasets.MNIST('',train = False, download = True, transform=transform)

trainloader = DataLoader(train, batch_size= 32, shuffle=True)
validloader = DataLoader(test, batch_size= 32, shuffle=True)

Ahora que tenemos nuestro cargador de datos listo, podemos proceder a crear nuestro modelo. El modelo PyTorch sigue el siguiente formato:-

from torch import nn

class model(nn.Module):
    def __init__(self):
        # Define Model Here
        
    def forward(self, x):
        # Define Forward Pass Here

Con eso claro, definamos nuestro modelo: –

import torch
from torch import nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.fc1 = nn.Linear(28*28,256)
        self.fc2 = nn.Linear(256,128)
        self.out = nn.Linear(128,10)
        self.lr = 0.01
        self.loss = nn.CrossEntropyLoss()
    
    def forward(self,x):
        batch_size, _, _, _ = x.size()
        x = x.view(batch_size,-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.out(x)

model = Net()

# Send the model to GPU if available
if torch.cuda.is_available():
    model = model.cuda()

Ahora que tenemos nuestro modelo, podemos especificar nuestro optimizador, la función de pérdida y nuestro lr_scheduler . Usaremos el optimizador SGD, CrossEntropyLoss para la función de pérdida y ReduceLROnPlateau para el programador lr.

from torch.optim import SGD
from torch.optim.lr_scheduler import ReduceLROnPlateau

optimizer = SGD(model.parameters(), lr = 0.1)
loss = nn.CrossEntropyLoss()
scheduler = ReduceLROnPlateau(optimizer, 'min', patience = 5)

Definamos el ciclo de entrenamiento. El ciclo de entrenamiento es más o menos el mismo que antes, excepto que esta vez llamaremos a nuestro método de pasos del planificador al final del ciclo.

from tqdm.notebook import trange

epoch = 25
for e in trange(epoch):
    train_loss, valid_loss = 0.0, 0.0
    
    # Set model to training mode
    model.train()
    for data, label in trainloader:
        if torch.cuda.is_available():
            data, label = data.cuda(), label.cuda()

        optimizer.zero_grad()
        target = model(data)
        train_step_loss = loss(target, label)
        train_step_loss.backward()
        optimizer.step()

        train_loss += train_step_loss.item() * data.size(0)

    # Set model to Evaluation mode
    model.eval()
    for data, label in validloader:
        if torch.cuda.is_available():
            data, label = data.cuda(), label.cuda()

        target = model(data)
        valid_step_loss = loss(target, label)

        valid_loss += valid_step_loss.item() * data.size(0)
    
    curr_lr = optimizer.param_groups[0]['lr']

    print(f'Epoch {e}\t \
            Training Loss: {train_loss/len(trainloader)}\t \
            Validation Loss:{valid_loss/len(validloader)}\t \
            LR:{curr_lr}')
    scheduler.step(valid_loss/len(validloader))

Como puede ver, el programador siguió ajustando lr cuando la pérdida de validación dejó de disminuir.

Código:

import torch
from torch import nn
import torch.nn.functional as F
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
from torch.optim import SGD
from torch.optim.lr_scheduler import ReduceLROnPlateau
from tqdm.notebook import trange
  
# LOADING DATA
transform = transforms.Compose([
    transforms.ToTensor()
])
  
train = datasets.MNIST('',train = True, download = True, transform=transform)
valid = datasets.MNIST('',train = False, download = True, transform=transform)
  
trainloader = DataLoader(train, batch_size= 32, shuffle=True)
validloader = DataLoader(test, batch_size= 32, shuffle=True)
  
# CREATING OUR MODEL
class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.fc1 = nn.Linear(28*28,64)
        self.fc2 = nn.Linear(64,32)
        self.out = nn.Linear(32,10)
        self.lr = 0.01
        self.loss = nn.CrossEntropyLoss()
      
    def forward(self,x):
        batch_size, _, _, _ = x.size()
        x = x.view(batch_size,-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.out(x)
  
model = Net()
  
# Send the model to GPU if available
if torch.cuda.is_available():
    model = model.cuda()
  
# SETTING OPTIMIZER, LOSS AND SCHEDULER
optimizer = SGD(model.parameters(), lr = 0.1)
loss = nn.CrossEntropyLoss()
scheduler = ReduceLROnPlateau(optimizer, 'min', patience = 5)
  
# TRAINING THE NEURAL NETWORK
epoch = 25
for e in trange(epoch):
    train_loss, valid_loss = 0.0, 0.0
      
    # Set model to training mode
    model.train()
    for data, label in trainloader:
        if torch.cuda.is_available():
            data, label = data.cuda(), label.cuda()
  
        optimizer.zero_grad()
        target = model(data)
        train_step_loss = loss(target, label)
        train_step_loss.backward()
        optimizer.step()
  
        train_loss += train_step_loss.item() * data.size(0)
  
    # Set model to Evaluation mode
    model.eval()
    for data, label in validloader:
        if torch.cuda.is_available():
            data, label = data.cuda(), label.cuda()
  
        target = model(data)
        valid_step_loss = loss(target, label)
  
        valid_loss += valid_step_loss.item() * data.size(0)
      
    curr_lr = optimizer.param_groups[0]['lr']
  
    print(f'Epoch {e}\t \
            Training Loss: {train_loss/len(trainloader)}\t \
            Validation Loss:{valid_loss/len(validloader)}\t \
            LR:{curr_lr}')
    scheduler.step(valid_loss/len(validloader))

Publicación traducida automáticamente

Artículo escrito por herumbshandilya y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *