Bucle de entrenamiento

Al entrenar un modelo de aprendizaje automático, es común tener un bucle donde se ingieren (o generan) datos de entrenamiento, se ejecutan lotes a través de un modelo, se obtienen gradientes y el modelo se actualiza mediante un optimizador. Si bien puedes escribir tu propio bucle de entrenamiento para cada aplicación de entrenamiento, Swift para TensorFlow proporciona una abstracción del bucle de entrenamiento experimental que puede simplificar este proceso.

El módulo TrainingLoop dentro del repositorio de modelos contiene la versión actual de este bucle de entrenamiento generalizado experimental. Está estructurado de tal manera que se integra con contenedores de conjuntos de datos que se ajustan a la API de Epochs para facilitar la ingesta de datos y automatizar la interacción de modelos, conjuntos de datos y optimizadores con motores de aceleración para lograr un rendimiento óptimo. Se puede lograr una gran personalización del proceso de capacitación mediante el uso de devoluciones de llamada.

La mayoría de los ejemplos basados ​​en imágenes en el repositorio de modelos se han convertido para utilizar esta abstracción del bucle de entrenamiento, así como los ejemplos de entrenamiento del modelo de texto supervisado. Sin embargo, es posible que el circuito de entrenamiento no sea apropiado en su diseño actual para todos los modelos de aprendizaje automático.

La implementación de Swift para el ciclo de entrenamiento generalizado de TensorFlow está fuertemente influenciada por Learner de fastai . Para obtener más información sobre su diseño, consulte "fastai: una API en capas para el aprendizaje profundo" y la presentación de Sylvain Gugger "Fast.ai: un bucle de entrenamiento infinitamente personalizable" .

Uso

El ejemplo ResNet-CIFAR10 proporciona una buena demostración de cómo utilizar este bucle de formación en la práctica. Primero, importe el módulo:

import TrainingLoop

luego elija un motor de aceleración configurando un Device . En este caso, seleccionaremos el backend basado en X10 XLA y usaremos el primer acelerador disponible:

let device = Device.defaultXLA

El siguiente paso es configurar el conjunto de datos, el modelo y el optimizador para usarlos con su bucle de entrenamiento:

let dataset = CIFAR10(batchSize: 10, on: device)
var model = ResNet(classCount: 10, depth: .resNet56, downsamplingInFirstStage: false)
var optimizer = SGD(for: model, learningRate: 0.001)

y luego configurar el ciclo de entrenamiento:

var trainingLoop = TrainingLoop(
  training: dataset.training,
  validation: dataset.validation,
  optimizer: optimizer,
  lossFunction: softmaxCrossEntropy,
  metrics: [.accuracy])

El ciclo de entrenamiento supone que el conjunto de datos que está utilizando se ajusta a la API de Epochs y le permite especificar qué divisiones dentro del conjunto de datos usar para el entrenamiento y la validación. Cualquier función de pérdida se puede usar una vez colocada en un contenedor compatible, como softmaxCrossEntropy está aquí .

Las métricas actuales que se pueden capturar incluyen:

  • loss
  • accuracy
  • top5Accuracy
  • matthewsCorrelationCoefficient
  • perplexity

Finalmente, para realizar el entrenamiento, llamas a lo siguiente:

try! trainingLoop.fit(&model, epochs: 10, on: device)

Esto entrenará el modelo durante 10 épocas utilizando el motor del acelerador que especificamos. Las estadísticas se mostrarán durante el entrenamiento en la consola mediante un mensaje animado.

Devoluciones de llamada

La personalización de este ciclo de entrenamiento generalizado se produce mediante el uso de devoluciones de llamada. Estas devoluciones de llamada se pueden conectar a varios puntos dentro del bucle.

Varias devoluciones de llamada integradas brindan funciones que se pueden agregar a cualquier ciclo de capacitación. Éstas incluyen:

  • Registro de estadísticas en archivos de valores separados por comas (CSV)
  • Ajustar la tasa de aprendizaje según un horario personalizado
  • Monitorear y graficar el progreso del entrenamiento a través de TensorBoard

Además de esto, puede crear sus propias devoluciones de llamada personalizadas para agregar una variedad de funciones adicionales a un ciclo de entrenamiento estándar.

Registro CSV

La clase CSVLogger encapsula una devolución de llamada que escribirá estadísticas de entrenamiento en un formato de valores separados por comas en un archivo de su elección. Este archivo comenzará con columnas etiquetadas como epoch , batch y cualquier métrica que haya habilitado dentro de su ciclo de entrenamiento. Luego se escribirá una fila para cada lote, con los valores actuales de esas columnas.

Para agregar el registro CSV a su bucle de entrenamiento, agregue algo como lo siguiente a una serie de devoluciones de llamada proporcionadas a las callbacks: parámetro para su TrainingLoop :

try! CSVLogger(path: "file.csv").log

Como ejemplo, el ejemplo LeNet-MNIST utiliza esto dentro de su ciclo de entrenamiento.

Horarios de tasas de aprendizaje

Es común al entrenar un modelo cambiar la tasa de aprendizaje proporcionada a un optimizador durante el proceso de entrenamiento. Esto puede ser tan simple como una disminución lineal a lo largo del tiempo, o tan complejo como ciclos de calentamiento y descenso descritos por funciones complicadas.

La devolución de llamada learningRateScheduler proporciona los medios para describir programas de tasas de aprendizaje compuestos por diferentes segmentos, cada uno con su propia forma distintiva. Esto se logra definiendo un LearningRateSchedule compuesto por ScheduleSegment , cada uno de los cuales tiene una Shape definida por una función, una tasa de aprendizaje inicial y una tasa de aprendizaje final.

Por ejemplo, la muestra BERT-CoLA utiliza un aumento lineal en la tasa de aprendizaje durante un período de calentamiento y una disminución lineal después de eso. Para hacer esto, la devolución de llamada del programa de tasa de aprendizaje se define de la siguiente manera:

learningRateScheduler(
  schedule: makeSchedule(
    [
      ScheduleSegment(shape: linear, startRate: 0, endRate: peakLearningRate, stepCount: 10),
      ScheduleSegment(shape: linear, endRate: 0)
    ]
  )
)

Los dos ScheduleSegment definen una tasa de aprendizaje que comienza en 0 y aumenta linealmente hasta alcanzar peakLearningRate en una serie de 10 pasos discretos, luego comienza en la tasa de aprendizaje final del paso anterior y disminuye linealmente a 0 al final del proceso de capacitación.

Integración de TensorBoard

TensorBoard es una poderosa herramienta de visualización para monitorear el entrenamiento del modelo, analizar el entrenamiento cuando se completa o comparar ejecuciones de entrenamiento. Swift para TensorFlow admite la visualización de TensorBoard mediante el uso del módulo TensorBoard en el repositorio de modelos, que proporciona devoluciones de llamadas que registran métricas de entrenamiento.

El ejemplo GPT2-WikiText2 ilustra cómo agregar el registro de TensorBoard al entrenamiento de su modelo. Primero, importe el módulo TensorBoard . Entonces es tan simple como agregar tensorBoardStatisticsLogger() a las devoluciones de llamada de tu TrainingLoop callbacks: matriz.

De forma predeterminada, eso registrará cada ejecución de entrenamiento dentro de un directorio run/tensorboard/stats . Para ver esto dentro de Tensorboard, ejecute

tensorboard --logdir ./run/tensorboard/stats

y TensorBoard debería iniciar un servidor local donde puedas ver tus métricas de entrenamiento. Los resultados del entrenamiento y la validación deben mostrarse por separado y cada ejecución tiene una marca de tiempo única para permitir una fácil comparación entre múltiples ejecuciones del mismo modelo.

El diseño de Swift para la integración de TensorFlow TensorBoard se inspiró en tensorboardX . Las devoluciones de llamada de TensorBoard crean directamente los buffers de protocolo de resumen y eventos apropiados y los escriben en un archivo de registro durante el entrenamiento.

Devoluciones de llamada personalizadas

Además de las devoluciones de llamada integradas descritas anteriormente, tienes la posibilidad de personalizar la función de los bucles de entrenamiento creando tus propias devoluciones de llamada. Estas devoluciones de llamada son funciones que tienen una firma similar a la siguiente:

func customCallback<L: TrainingLoopProtocol>(_ loop: inout L, event: TrainingLoopEvent) throws
{
  if event == .updateStart {
    ...
  }
}

El bucle de entrenamiento y el estado asociado se pasan como primer parámetro. La parte actual del bucle a la que responde la devolución de llamada se proporciona mediante event . El evento del bucle de entrenamiento tiene uno de los siguientes estados, cada uno de los cuales corresponde a un punto diferente del ciclo de vida del bucle:

  • fitStart
  • fitEnd
  • epochStart
  • epochEnd
  • trainingStart
  • trainingEnd
  • validationStart
  • validationEnd
  • batchStart
  • batchEnd
  • updateStart
  • inferencePredictionEnd

Su función de devolución de llamada puede optar por activar su lógica en cualquier combinación de los estados anteriores, lo que permite extraer datos o controlar el ciclo de entrenamiento de muchas maneras.