conjuntos de datos

En muchos modelos de aprendizaje automático, especialmente para el aprendizaje supervisado, los conjuntos de datos son una parte vital del proceso de capacitación. Swift para TensorFlow proporciona contenedores para varios conjuntos de datos comunes dentro del módulo Conjuntos de datos en el repositorio de modelos . Estos contenedores facilitan el uso de conjuntos de datos comunes con modelos basados ​​en Swift y se integran bien con el bucle de entrenamiento generalizado de Swift para TensorFlow.

Envoltorios de conjuntos de datos proporcionados

Estos son los contenedores de conjuntos de datos proporcionados actualmente dentro del repositorio de modelos:

Para usar uno de estos envoltorios de conjuntos de datos dentro de un proyecto Swift, agregue Datasets como una dependencia a su destino Swift e importe el módulo:

import Datasets

La mayoría de los contenedores de conjuntos de datos están diseñados para producir lotes de datos etiquetados mezclados aleatoriamente. Por ejemplo, para utilizar el conjunto de datos CIFAR-10, primero lo inicializa con el tamaño de lote deseado:

let dataset = CIFAR10(batchSize: 100)

En el primer uso, los contenedores de conjuntos de datos de Swift para TensorFlow descargarán automáticamente el conjunto de datos original, extraerán y analizarán todos los archivos relevantes y luego almacenarán el conjunto de datos procesado en un directorio de caché local del usuario. Los usos posteriores del mismo conjunto de datos se cargarán directamente desde la memoria caché local.

Para configurar un ciclo de entrenamiento manual que involucre este conjunto de datos, usaría algo como lo siguiente:

for (epoch, epochBatches) in dataset.training.prefix(100).enumerated() {
  Context.local.learningPhase = .training
  ...
  for batch in epochBatches {
    let (images, labels) = (batch.data, batch.label)
    ...
  }
}

Lo anterior configura un iterador a través de 100 épocas ( .prefix(100) ) y devuelve el índice numérico de la época actual y una secuencia asignada de forma diferida sobre lotes mezclados que componen esa época. Dentro de cada época de entrenamiento, los lotes se iteran y se extraen para su procesamiento. En el caso del contenedor del conjunto de datos CIFAR10 , cada lote es LabeledImage , que proporciona un Tensor<Float> que contiene todas las imágenes de ese lote y un Tensor<Int32> con sus etiquetas coincidentes.

En el caso de CIFAR-10, todo el conjunto de datos es pequeño y se puede cargar en la memoria al mismo tiempo, pero para otros conjuntos de datos más grandes, los lotes se cargan lentamente desde el disco y se procesan en el punto donde se obtiene cada lote. Esto evita el agotamiento de la memoria con esos conjuntos de datos más grandes.

La API de épocas

La mayoría de estos contenedores de conjuntos de datos se basan en una infraestructura compartida que hemos llamado Epochs API . Epochs proporciona componentes flexibles destinados a admitir una amplia variedad de tipos de conjuntos de datos, desde texto hasta imágenes y más.

Si desea crear su propio contenedor de conjunto de datos Swift, lo más probable es que desee utilizar la API de Epochs para hacerlo. Sin embargo, para casos comunes, como conjuntos de datos de clasificación de imágenes, recomendamos encarecidamente comenzar a partir de una plantilla basada en uno de los contenedores de conjuntos de datos existentes y modificarla para satisfacer sus necesidades específicas.

Como ejemplo, examinemos el contenedor del conjunto de datos CIFAR-10 y cómo funciona. El núcleo del conjunto de datos de entrenamiento se define aquí:

let trainingSamples = loadCIFARTrainingFiles(in: localStorageDirectory)
training = TrainingEpochs(samples: trainingSamples, batchSize: batchSize, entropy: entropy)
  .lazy.map { (batches: Batches) -> LazyMapSequence<Batches, LabeledImage> in
    return batches.lazy.map{
      makeBatch(samples: $0, mean: mean, standardDeviation: standardDeviation, device: device)
  }
}

El resultado de la función loadCIFARTrainingFiles() es una matriz de (data: [UInt8], label: Int32) tuplas para cada imagen en el conjunto de datos de entrenamiento. Luego, esto se proporciona a TrainingEpochs(samples:batchSize:entropy:) para crear una secuencia infinita de épocas con lotes de batchSize . Puede proporcionar su propio generador de números aleatorios en los casos en los que desee un comportamiento de procesamiento por lotes determinista, pero de forma predeterminada se utiliza SystemRandomNumberGenerator .

A partir de ahí, los mapas diferidos sobre los lotes culminan en la función makeBatch(samples:mean:standardDeviation:device:) . Esta es una función personalizada donde se encuentra el proceso de procesamiento de imágenes real para el conjunto de datos CIFAR-10, así que echemos un vistazo a eso:

fileprivate func makeBatch<BatchSamples: Collection>(
  samples: BatchSamples, mean: Tensor<Float>?, standardDeviation: Tensor<Float>?, device: Device
) -> LabeledImage where BatchSamples.Element == (data: [UInt8], label: Int32) {
  let bytes = samples.lazy.map(\.data).reduce(into: [], +=)
  let images = Tensor<UInt8>(shape: [samples.count, 3, 32, 32], scalars: bytes, on: device)

  var imageTensor = Tensor<Float>(images.transposed(permutation: [0, 2, 3, 1]))
  imageTensor /= 255.0
  if let mean = mean, let standardDeviation = standardDeviation {
    imageTensor = (imageTensor - mean) / standardDeviation
  }

  let labels = Tensor<Int32>(samples.map(\.label), on: device)
  return LabeledImage(data: imageTensor, label: labels)
}

Las dos líneas de esta función concatenan todos los bytes data de los BatchSamples entrantes en un Tensor<UInt8> que coincide con el diseño de bytes de las imágenes dentro del conjunto de datos CIFAR-10 sin procesar. A continuación, los canales de imágenes se reordenan para que coincidan con los esperados en nuestros modelos de clasificación de imágenes estándar y los datos de la imagen se vuelven a convertir en un Tensor<Float> para el consumo del modelo.

Se pueden proporcionar parámetros de normalización opcionales para ajustar aún más los valores del canal de imagen, un proceso que es común cuando se entrenan muchos modelos de clasificación de imágenes. El parámetro de normalización Tensor s se crea una vez en la inicialización del conjunto de datos y luego se pasa a makeBatch() como una optimización para evitar la creación repetida de pequeños tensores temporales con los mismos valores.

Finalmente, las etiquetas de los enteros se colocan en un Tensor<Int32> y el par tensor imagen/etiqueta se devuelve en un LabeledImage . Un LabeledImage es un caso específico de LabeledData , una estructura con datos y etiquetas que se ajustan al protocolo Collatable de la API de Eppch.

Para obtener más ejemplos de la API de Epochs en diferentes tipos de conjuntos de datos, puede examinar los otros contenedores de conjuntos de datos dentro del repositorio de modelos.