데이터 세트

많은 기계 학습 모델, 특히 지도 학습의 경우 데이터 세트는 훈련 프로세스의 중요한 부분입니다. Swift for TensorFlow는 모델 저장소 의 데이터 세트 모듈 내에서 여러 공통 데이터 세트에 대한 래퍼를 제공합니다. 이러한 래퍼는 Swift 기반 모델과 함께 일반 데이터 세트를 쉽게 사용할 수 있도록 하며 Swift for TensorFlow의 일반화된 훈련 루프와 잘 통합됩니다.

제공된 데이터 세트 래퍼

다음은 모델 리포지토리 내에 현재 제공되는 데이터 세트 래퍼입니다.

Swift 프로젝트 내에서 이러한 데이터 세트 래퍼 중 하나를 사용하려면 Datasets Swift 대상에 대한 종속성으로 추가하고 모듈을 가져옵니다.

import Datasets

대부분의 데이터 세트 래퍼는 레이블이 지정된 데이터의 무작위 섞인 배치를 생성하도록 설계되었습니다. 예를 들어 CIFAR-10 데이터 세트를 사용하려면 먼저 원하는 배치 크기로 초기화해야 합니다.

let dataset = CIFAR10(batchSize: 100)

처음 사용 시 Swift for TensorFlow 데이터세트 래퍼는 원본 데이터세트를 자동으로 다운로드하고 모든 관련 아카이브를 추출 및 구문 분석한 다음 처리된 데이터세트를 사용자 로컬 캐시 디렉터리에 저장합니다. 이후에 동일한 데이터세트를 사용하면 로컬 캐시에서 직접 로드됩니다.

이 데이터세트와 관련된 수동 학습 루프를 설정하려면 다음과 같은 방법을 사용합니다.

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

위의 코드는 100개의 에포크( .prefix(100) )를 통해 반복자를 설정하고 현재 에포크의 숫자 인덱스와 해당 에포크를 구성하는 셔플된 배치에 대해 지연 매핑된 시퀀스를 반환합니다. 각 훈련 에포크 내에서 처리를 위해 배치가 반복되고 추출됩니다. CIFAR10 데이터 세트 래퍼의 경우 각 배치는 해당 배치의 모든 이미지가 포함된 Tensor<Float> 와 일치하는 레이블이 있는 Tensor<Int32> 제공하는 LabeledImage 입니다.

CIFAR-10의 경우 전체 데이터 세트가 작아서 한 번에 메모리에 로드할 수 있지만, 다른 대규모 데이터 세트의 경우 배치는 디스크에서 느리게 로드되어 각 배치를 얻은 지점에서 처리됩니다. 이렇게 하면 더 큰 데이터 세트로 인한 메모리 고갈을 방지할 수 있습니다.

에포크스 API

이러한 데이터 세트 래퍼의 대부분은 Epochs API 라고 하는 공유 인프라를 기반으로 구축되었습니다. Epochs는 텍스트에서 이미지 등에 이르기까지 다양한 데이터 세트 유형을 지원하기 위한 유연한 구성 요소를 제공합니다.

자신만의 Swift 데이터세트 래퍼를 생성하려면 Epochs API를 사용하는 것이 가장 좋습니다. 그러나 이미지 분류 데이터 세트와 같은 일반적인 경우에는 기존 데이터 세트 래퍼 중 하나를 기반으로 하는 템플릿에서 시작하여 특정 요구 사항에 맞게 수정하는 것이 좋습니다.

예를 들어 CIFAR-10 데이터 세트 래퍼와 작동 방식을 살펴보겠습니다. 학습 데이터 세트의 핵심은 여기에 정의되어 있습니다.

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)
  }
}

loadCIFARTrainingFiles() 함수의 결과는 훈련 데이터 세트의 각 이미지에 대한 (data: [UInt8], label: Int32) 튜플의 배열입니다. 그런 다음 이는 TrainingEpochs(samples:batchSize:entropy:) 에 제공되어 batchSize 가 있는 무한한 시대 시퀀스를 생성합니다. 결정적 일괄 처리 동작을 원하는 경우 자체 난수 생성기를 제공할 수 있지만 기본적으로 SystemRandomNumberGenerator 가 사용됩니다.

거기에서 배치에 대한 지연 맵은 makeBatch(samples:mean:standardDeviation:device:) 함수에서 정점에 이릅니다. 이는 CIFAR-10 데이터 세트에 대한 실제 이미지 처리 파이프라인이 위치한 사용자 정의 함수이므로 살펴보겠습니다.

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)
}

이 함수의 두 줄은 수신 BatchSamples 의 모든 data 바이트를 원시 CIFAR-10 데이터 세트 내 이미지의 바이트 레이아웃과 일치하는 Tensor<UInt8> 로 연결합니다. 다음으로, 이미지 채널은 표준 이미지 분류 모델에서 예상되는 것과 일치하도록 재정렬되고 이미지 데이터는 모델 사용을 위해 Tensor<Float> 로 다시 캐스팅됩니다.

선택적 정규화 매개변수를 제공하여 이미지 채널 값을 추가로 조정할 수 있습니다. 이는 많은 이미지 분류 모델을 훈련할 때 일반적인 프로세스입니다. 정규화 매개변수 Tensor 는 데이터 세트 초기화 시 한 번 생성된 다음 동일한 값을 가진 작은 임시 텐서가 반복적으로 생성되는 것을 방지하기 위한 최적화로 makeBatch() 에 전달됩니다.

마지막으로 정수 레이블은 Tensor<Int32> 에 배치되고 이미지/레이블 텐서 쌍은 LabeledImage 에 반환됩니다. LabeledImage 는 Eppch API의 Collatable 프로토콜을 준수하는 데이터와 레이블이 있는 구조체인 LabeledData 의 특정 사례입니다.

다양한 데이터 세트 유형의 Epochs API에 대한 더 많은 예를 보려면 모델 리포지토리 내의 다른 데이터 세트 래퍼를 검토할 수 있습니다.