データセット

多くの機械学習モデル、特に教師あり学習では、データセットはトレーニング プロセスの重要な部分です。 Swift for TensorFlow は、モデル リポジトリの Datasets モジュール内でいくつかの一般的なデータセットのラッパーを提供します。これらのラッパーは、Swift ベースのモデルで一般的なデータセットの使用を容易にし、TensorFlow の一般化されたトレーニング ループの Swift と適切に統合します。

提供されたデータセット ラッパー

以下は、モデル リポジトリ内で現在提供されているデータセット ラッパーです。

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データセット ラッパーの場合、各バッチはLabeledImageであり、そのバッチのすべての画像を含むTensor<Float>と、それらに一致するラベルを持つTensor<Int32>を提供します。

CIFAR-10 の場合、データセット全体は小さいため、一度にメモリにロードできますが、他の大きなデータセットの場合、バッチはディスクから遅延してロードされ、各バッチが取得された時点で処理されます。これにより、これらの大きなデータセットによるメモリの枯渇が防止されます。

エポックAPI

これらのデータセット ラッパーのほとんどは、 Epochs APIと呼ばれる共有インフラストラクチャ上に構築されています。 Epochs は、テキストから画像などに至るまで、さまざまな種類のデータセットをサポートすることを目的とした柔軟なコンポーネントを提供します。

独自の Swift データセット ラッパーを作成したい場合は、Epochs API を使用することになるでしょう。ただし、画像分類データセットなどの一般的なケースでは、既存のデータセット ラッパーの 1 つに基づくテンプレートから開始し、特定のニーズに合わせてそれを変更することを強くお勧めします。

例として、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)
}

この関数の 2 行は、受信BatchSamplesからのすべてのdataバイトを、生の CIFAR-10 データセット内の画像のバイト レイアウトと一致するTensor<UInt8>に連結します。次に、標準画像分類モデルで期待されるものと一致するように画像チャネルが並べ替えられ、画像データがモデル使用のためにTensor<Float>に再キャストされます。

オプションの正規化パラメータを提供して、画像チャネル値をさらに調整することができます。これは、多くの画像分類モデルをトレーニングするときに一般的なプロセスです。正規化パラメータTensorは、データセットの初期化時に一度作成され、同じ値を持つ小さな一時的なテンソルが繰り返し作成されるのを防ぐための最適化としてmakeBatch()に渡されます。

最後に、整数ラベルがTensor<Int32>に配置され、画像とラベルのテンソル ペアがLabeledImageで返されます。 LabeledImageLabeledDataの特殊なケースであり、Eppch API のCollatableプロトコルに準拠するデータとラベルを含む構造体です。

さまざまなデータセット タイプの Epochs API のその他の例については、モデル リポジトリ内の他のデータセット ラッパーを調べることができます。