Una guía para el ajuste iterativo y la serialización

Foto por Shio Yang en desempaquetar

Entonces, recientemente descubriste abrazando la cara y la gran cantidad de modelos de código abierto como BERT, Llama, BART y una gran cantidad de modelos de lenguaje generativo de Mistral AI, Facebook, fuerza de ventas y otras empresas. Ahora desea experimentar ajustando algunos modelos de lenguaje grandes para sus proyectos paralelos. Las cosas empiezan muy bien, pero luego descubres lo codiciosos que son computacionalmente y no tienes un procesador GPU a mano.

colaboración de google Te ofrece generosamente una manera de acceder a computación gratuita para que puedas solucionar este problema. La desventaja es que debe hacerlo todo dentro de un entorno transitorio basado en un navegador. Para empeorar las cosas, todo esto tiene un tiempo limitado, por lo que parece que no importa lo que hagas, vas a perder tu precioso modelo ajustado y todos los resultados cuando el kernel finalmente se apague y el entorno sea destruido.

Nunca temas. Hay una manera de evitar esto: hacer uso de Google Drive para guardar cualquiera de sus resultados intermedios o parámetros del modelo. Esto le permitirá continuar con la experimentación en una etapa posterior o tomar y utilizar un modelo entrenado para realizar inferencias en otros lugares.

Para hacer esto, necesitará una cuenta de Google que tenga suficiente espacio en Google Drive tanto para sus datos de entrenamiento como para sus puntos de control del modelo. Supongo que ha creado una carpeta llamada data en Google Drive que contiene su conjunto de datos. Entonces otro llamó checkpoints eso está vacío.

Dentro de su Google Colab Notebook, luego monta su Drive usando el siguiente comando:

from google.colab import drive
drive.mount('/content/drive')

Ahora enumera el contenido de sus directorios de datos y puntos de control con los dos comandos siguientes en una nueva celda:

!ls /content/drive/MyDrive/data
!ls /content/drive/MyDrive/checkpoint

Si estos comandos funcionan, ahora tendrá acceso a estos directorios dentro de su computadora portátil. Si los comandos no funcionan, es posible que se haya perdido el paso de autorización. El drive.mount El comando anterior debería haber generado una ventana emergente que requiere que haga clic y autorice el acceso. Es posible que se haya perdido la ventana emergente o que no haya seleccionado todos los derechos de acceso requeridos. Intente volver a ejecutar la celda y verificar.

Una vez que haya ordenado ese acceso, podrá escribir sus scripts de modo que los modelos y los resultados se serialicen en los directorios de Google Drive para que persistan durante las sesiones. En un mundo ideal, codificaría su trabajo de entrenamiento para que cualquier script que tarde demasiado en ejecutarse pueda cargar modelos parcialmente entrenados de la sesión anterior y continuar entrenando desde ese punto.

Una forma sencilla de lograrlo es crear una función de guardar y cargar que utilicen sus scripts de entrenamiento. El proceso de formación siempre debe comprobar si hay un modelo parcialmente entrenado, antes de inicializar uno nuevo. Aquí hay un ejemplo de función de guardar:

def save_checkpoint(epoch, model, optimizer, scheduler, loss, model_name, overwrite=True):
checkpoint = {
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'scheduler_state_dict': scheduler.state_dict(),
'loss': loss
}
direc = get_checkpoint_dir(model_name)
if overwrite:
file_path = direc + '/checkpoint.pth'
else:
file_path = direc + '/epoch_'+str(epoch) + '_checkpoint.pth'
if not os.path.isdir(direc):
try:
os.mkdir(direc)
except:
print("Error: directory does not exist and cannot be created")
file_path = direc +'_epoch_'+str(epoch) + '_checkpoint.pth'
torch.save(checkpoint, file_path)
print(f"Checkpoint saved at epoch {epoch}")

En este caso, estamos guardando el estado del modelo junto con algunos metadatos (épocas y pérdida) dentro de una estructura de diccionario. Incluimos una opción para sobrescribir un único archivo de punto de control o crear un archivo nuevo para cada época. Estamos utilizando la función de guardar antorcha, pero en principio se podrían utilizar otros métodos de serialización. La idea clave es que su programa abra el archivo y determine cuántas épocas de entrenamiento se utilizaron para el archivo existente. Esto permite que el programa decida si continúa entrenando o sigue adelante.

De manera similar, en la función de carga pasamos una referencia a un modelo que deseamos usar. Si ya existe un modelo serializado, cargamos los parámetros en nuestro modelo y devolvemos la cantidad de épocas para las que fue entrenado. Este valor de época determinará cuántas épocas adicionales se requieren. Si no hay ningún modelo, obtenemos el valor predeterminado de cero épocas y sabemos que el modelo todavía tiene los parámetros con los que se inicializó.

def load_checkpoint(model_name, model, optimizer, scheduler):
direc = get_checkpoint_dir(model_name)
if os.path.exists(direc):
file_path = get_path_with_max_epochs(direc)
checkpoint = torch.load(file_path, map_location=torch.device('cpu'))
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
print(f"Checkpoint loaded from {epoch} epoch")
return epoch, loss
else:
print(f"No checkpoint found, starting from epoch 1.")
return 0, None

Estas dos funciones deberán llamarse dentro de su ciclo de entrenamiento y deberá asegurarse de que el valor devuelto para el valor de las épocas se utilice para actualizar el valor de las épocas en sus iteraciones de entrenamiento. El resultado es que ahora tiene un proceso de entrenamiento que puede reiniciarse cuando un núcleo muere, y continuará desde donde lo dejó.

Ese ciclo de entrenamiento básico podría verse así:


EPOCHS = 10
for exp in experiments:
model, optimizer, scheduler = initialise_model_components(exp)
train_loader, val_loader = generate_data_loaders(exp)
start_epoch, prev_loss = load_checkpoint(exp, model, optimizer, scheduler)
for epoch in range(start_epoch, EPOCHS):
print(f'Epoch {epoch + 1}/{EPOCHS}')
# ALL YOUR TRAINING CODE HERE
save_checkpoint(epoch + 1, model, optimizer, scheduler, train_loss, exp)

Nota: En este ejemplo, estoy experimentando con el entrenamiento de múltiples configuraciones de modelos diferentes (en una lista llamada experiments), potencialmente utilizando diferentes conjuntos de datos de entrenamiento. Las funciones de apoyo initialise_model_components y generate_data_loaders Se están encargando de garantizar que obtenga el modelo y los datos correctos para cada experimento.

El ciclo de entrenamiento central anterior nos permite reutilizar la estructura de código general que entrena y serializa estos modelos, asegurando que cada modelo alcance la cantidad deseada de épocas de entrenamiento. Si reiniciamos el proceso, volverá a recorrer la lista de experimentos, pero abandonará cualquier experimento que ya haya alcanzado el número máximo de épocas.

Con suerte, puede utilizar este código repetitivo para configurar su propio proceso y experimentar con el entrenamiento de algunos modelos de lenguaje de aprendizaje profundo dentro de Google Colab. Por favor comente y déjeme saber qué está creando y cómo utiliza este código.