X10'la tanışın

%install '.package(url: "https://github.com/tensorflow/swift-models", .branch("tensorflow-0.12"))' Datasets ImageClassificationModels
print("\u{001B}[2J")


TensorFlow.org'da görüntüleyin Kaynağı GitHub'da görüntüle

Varsayılan olarak Swift For TensorFlow, istekli gönderimi kullanarak tensör işlemlerini gerçekleştirir. Bu, hızlı yinelemeye izin verir, ancak makine öğrenimi modellerinin eğitimi için en performanslı seçenek değildir.

X10 tensör kitaplığı, TensorFlow için Swift'e yüksek performanslı bir arka uç ekleyerek tensör izleme ve XLA derleyicisinden yararlanır. Bu eğitim X10'u tanıtacak ve bir eğitim döngüsünü GPU'lar veya TPU'lar üzerinde çalışacak şekilde güncelleme sürecinde size rehberlik edecektir.

Eager ve X10 tensörleri

Swift'de TensorFlow için hızlandırılmış hesaplamalar Tensor türü aracılığıyla gerçekleştirilir. Tensörler çok çeşitli işlemlere katılabilir ve makine öğrenimi modellerinin temel yapı taşlarıdır.

Varsayılan olarak bir Tensor, hesaplamaları işlem bazında gerçekleştirmek için istekli yürütmeyi kullanır. Her Tensörün, hangi donanıma bağlı olduğunu ve bunun için hangi arka ucun kullanıldığını açıklayan ilişkili bir Aygıtı vardır.

import TensorFlow
import Foundation
let eagerTensor1 = Tensor([0.0, 1.0, 2.0])
let eagerTensor2 = Tensor([1.5, 2.5, 3.5])
let eagerTensorSum = eagerTensor1 + eagerTensor2
print(eagerTensorSum)
[1.5, 3.5, 5.5]

print(eagerTensor1.device)
Device(kind: .CPU, ordinal: 0, backend: .TF_EAGER)

Bu dizüstü bilgisayarı GPU'nun etkin olduğu bir örnekte çalıştırıyorsanız, yukarıdaki cihaz açıklamasında bu donanımın yansıtıldığını görmelisiniz. İstekli çalışma zamanının TPU desteği yoktur, bu nedenle bunlardan birini hızlandırıcı olarak kullanıyorsanız CPU'nun donanım hedefi olarak kullanıldığını göreceksiniz.

Bir Tensör oluştururken, varsayılan istekli mod cihazı bir alternatif belirtilerek geçersiz kılınabilir. X10 arka ucunu kullanarak hesaplama yapmayı bu şekilde seçebilirsiniz.

let x10Tensor1 = Tensor([0.0, 1.0, 2.0], on: Device.defaultXLA)
let x10Tensor2 = Tensor([1.5, 2.5, 3.5], on: Device.defaultXLA)
let x10TensorSum = x10Tensor1 + x10Tensor2
print(x10TensorSum)
[1.5, 3.5, 5.5]

print(x10Tensor1.device)
Device(kind: .CPU, ordinal: 0, backend: .XLA)

Bunu GPU özellikli bir örnekte çalıştırıyorsanız, X10 tensörünün cihazında bu hızlandırıcının listelendiğini görmelisiniz. İstekli yürütmenin aksine, bunu TPU'nun etkin olduğu bir örnekte çalıştırıyorsanız artık hesaplamaların o cihazı kullandığını görmelisiniz. X10, TensorFlow için Swift içindeki TPU'lardan nasıl yararlanacağınızdır.

Varsayılan istekli ve X10 cihazları sistemdeki ilk hızlandırıcıyı kullanmaya çalışacaktır. Bağlı GPU'larınız varsa, mevcut ilk GPU'yu kullanır. TPU'lar mevcutsa X10 varsayılan olarak ilk TPU çekirdeğini kullanacaktır. Hızlandırıcı bulunamazsa veya desteklenmezse, varsayılan aygıt CPU'ya geri dönecektir.

Varsayılan istekli ve XLA cihazlarının ötesinde, bir Cihazda belirli donanım ve arka uç hedefleri sağlayabilirsiniz:

// let tpu1 = Device(kind: .TPU, ordinal: 1, backend: .XLA)
// let tpuTensor1 = Tensor([0.0, 1.0, 2.0], on: tpu1)

İstekli mod modelini eğitme

Varsayılan istekli yürütme modunu kullanarak bir modeli nasıl kurup eğiteceğinize bir göz atalım. Bu örnekte, Swift-models deposundaki basit LeNet-5 modelini ve MNIST el yazısı rakam sınıflandırma veri kümesini kullanacağız.

Öncelikle MNIST veri setini kurup indireceğiz.

import Datasets

let epochCount = 5
let batchSize = 128
let dataset = MNIST(batchSize: batchSize)
Loading resource: train-images-idx3-ubyte
File does not exist locally at expected path: /home/kbuilder/.cache/swift-models/datasets/MNIST/train-images-idx3-ubyte and must be fetched
Fetching URL: https://storage.googleapis.com/cvdf-datasets/mnist/train-images-idx3-ubyte.gz...
Archive saved to: /home/kbuilder/.cache/swift-models/datasets/MNIST
Loading resource: train-labels-idx1-ubyte
File does not exist locally at expected path: /home/kbuilder/.cache/swift-models/datasets/MNIST/train-labels-idx1-ubyte and must be fetched
Fetching URL: https://storage.googleapis.com/cvdf-datasets/mnist/train-labels-idx1-ubyte.gz...
Archive saved to: /home/kbuilder/.cache/swift-models/datasets/MNIST
Loading resource: t10k-images-idx3-ubyte
File does not exist locally at expected path: /home/kbuilder/.cache/swift-models/datasets/MNIST/t10k-images-idx3-ubyte and must be fetched
Fetching URL: https://storage.googleapis.com/cvdf-datasets/mnist/t10k-images-idx3-ubyte.gz...
Archive saved to: /home/kbuilder/.cache/swift-models/datasets/MNIST
Loading resource: t10k-labels-idx1-ubyte
File does not exist locally at expected path: /home/kbuilder/.cache/swift-models/datasets/MNIST/t10k-labels-idx1-ubyte and must be fetched
Fetching URL: https://storage.googleapis.com/cvdf-datasets/mnist/t10k-labels-idx1-ubyte.gz...
Archive saved to: /home/kbuilder/.cache/swift-models/datasets/MNIST

Daha sonra modeli ve optimize ediciyi yapılandıracağız.

import ImageClassificationModels

var eagerModel = LeNet()
var eagerOptimizer = SGD(for: eagerModel, learningRate: 0.1)

Artık temel ilerleme takibi ve raporlamasını uygulayacağız. Tüm ara istatistikler, eğitimin çalıştırıldığı ve scalarized() işlevinin yalnızca raporlama sırasında çağrıldığı aynı cihazda tensörler olarak tutulur. Bu özellikle daha sonra X10 kullanıldığında önemli olacaktır çünkü tembel tensörlerin gereksiz şekilde gerçekleşmesini önler.

struct Statistics {
    var correctGuessCount = Tensor<Int32>(0, on: Device.default)
    var totalGuessCount = Tensor<Int32>(0, on: Device.default)
    var totalLoss = Tensor<Float>(0, on: Device.default)
    var batches: Int = 0
    var accuracy: Float { 
        Float(correctGuessCount.scalarized()) / Float(totalGuessCount.scalarized()) * 100 
    } 
    var averageLoss: Float { totalLoss.scalarized() / Float(batches) }

    init(on device: Device = Device.default) {
        correctGuessCount = Tensor<Int32>(0, on: device)
        totalGuessCount = Tensor<Int32>(0, on: device)
        totalLoss = Tensor<Float>(0, on: device)
    }

    mutating func update(logits: Tensor<Float>, labels: Tensor<Int32>, loss: Tensor<Float>) {
        let correct = logits.argmax(squeezingAxis: 1) .== labels
        correctGuessCount += Tensor<Int32>(correct).sum()
        totalGuessCount += Int32(labels.shape[0])
        totalLoss += loss
        batches += 1
    }
}

Son olarak modeli beş dönem boyunca bir eğitim döngüsünden geçireceğiz.

print("Beginning training...")

for (epoch, batches) in dataset.training.prefix(epochCount).enumerated() {
    let start = Date()
    var trainStats = Statistics()
    var testStats = Statistics()

    Context.local.learningPhase = .training
    for batch in batches {
        let (images, labels) = (batch.data, batch.label)
        let 𝛁model = TensorFlow.gradient(at: eagerModel) { eagerModel -> Tensor<Float> in
            let ŷ = eagerModel(images)
            let loss = softmaxCrossEntropy(logits: ŷ, labels: labels)
            trainStats.update(logits: ŷ, labels: labels, loss: loss)
            return loss
        }
        eagerOptimizer.update(&eagerModel, along: 𝛁model)
    }

    Context.local.learningPhase = .inference
    for batch in dataset.validation {
        let (images, labels) = (batch.data, batch.label)
        let ŷ = eagerModel(images)
        let loss = softmaxCrossEntropy(logits: ŷ, labels: labels)
        testStats.update(logits: ŷ, labels: labels, loss: loss)
    }

    print(
        """
        [Epoch \(epoch)] \
        Training Loss: \(String(format: "%.3f", trainStats.averageLoss)), \
        Training Accuracy: \(trainStats.correctGuessCount)/\(trainStats.totalGuessCount) \
        (\(String(format: "%.1f", trainStats.accuracy))%), \
        Test Loss: \(String(format: "%.3f", testStats.averageLoss)), \
        Test Accuracy: \(testStats.correctGuessCount)/\(testStats.totalGuessCount) \
        (\(String(format: "%.1f", testStats.accuracy))%) \
        seconds per epoch: \(String(format: "%.1f", Date().timeIntervalSince(start)))
        """)
}
Beginning training...
[Epoch 0] Training Loss: 0.528, Training Accuracy: 50154/59904 (83.7%), Test Loss: 0.168, Test Accuracy: 9468/10000 (94.7%) seconds per epoch: 11.9
[Epoch 1] Training Loss: 0.133, Training Accuracy: 57488/59904 (96.0%), Test Loss: 0.107, Test Accuracy: 9659/10000 (96.6%) seconds per epoch: 11.7
[Epoch 2] Training Loss: 0.092, Training Accuracy: 58193/59904 (97.1%), Test Loss: 0.069, Test Accuracy: 9782/10000 (97.8%) seconds per epoch: 11.8
[Epoch 3] Training Loss: 0.071, Training Accuracy: 58577/59904 (97.8%), Test Loss: 0.066, Test Accuracy: 9794/10000 (97.9%) seconds per epoch: 11.8
[Epoch 4] Training Loss: 0.059, Training Accuracy: 58800/59904 (98.2%), Test Loss: 0.064, Test Accuracy: 9800/10000 (98.0%) seconds per epoch: 11.8

Gördüğünüz gibi model beklediğimiz gibi eğitildi ve doğrulama kümesine karşı doğruluğu her dönemde arttı. TensorFlow modelleri için Swift bu şekilde tanımlanır ve hevesli yürütme kullanılarak çalıştırılır, şimdi X10'dan yararlanmak için hangi değişikliklerin yapılması gerektiğini görelim.

X10 modelinin eğitimi

Veri kümeleri, modeller ve optimize ediciler, varsayılan istekli yürütme cihazında başlatılan tensörler içerir. X10 ile çalışmak için bu tensörleri bir X10 cihazına taşımamız gerekecek.

let device = Device.defaultXLA
print(device)
Device(kind: .CPU, ordinal: 0, backend: .XLA)

Veri kümeleri için bunu, eğitim döngüsünde toplu işlerin işlendiği noktada yapacağız, böylece istekli yürütme modelindeki veri kümesini yeniden kullanabiliriz.

Model ve optimizer durumunda, onları istekli yürütme cihazındaki dahili tensörleriyle başlatacağız, ardından onları X10 cihazına taşıyacağız.

var x10Model = LeNet()
x10Model.move(to: device)

var x10Optimizer = SGD(for: x10Model, learningRate: 0.1)
x10Optimizer = SGD(copying: x10Optimizer, to: device)

Eğitim döngüsü için gereken değişiklikler birkaç belirli noktada gerçekleşir. Öncelikle eğitim verisi gruplarını X10 cihazına taşımamız gerekecek. Bu, her parti alındığında Tensor(copying:to:) aracılığıyla yapılır.

Bir sonraki değişiklik, eğitim döngüsü sırasında izlerin nereden kesileceğini belirtmektir. X10, kodunuzda ihtiyaç duyulan tensör hesaplamalarının izini sürerek ve bu izin optimize edilmiş bir temsilini tam zamanında derleyerek çalışır. Eğitim döngüsü durumunda, aynı işlemi defalarca tekrarlıyorsunuz; bu, izlemek, derlemek ve yeniden kullanmak için ideal bir bölüm.

Bir Tensor'dan açıkça bir değer talep eden kodun yokluğunda (bunlar genellikle .scalars veya .scalarized() çağrıları olarak öne çıkar), X10 tüm döngü yinelemelerini birlikte derlemeye çalışacaktır. Bunu önlemek ve izi belirli bir noktada kesmek için, optimizer model ağırlıklarını güncelledikten ve doğrulama sırasında kayıp ve doğruluk elde edildikten sonra açık bir LazyTensorBarrier() yerleştiririz. Bu, yeniden kullanılan iki iz oluşturur: eğitim döngüsündeki her adım ve doğrulama sırasındaki her çıkarım grubu.

Bu değişiklikler aşağıdaki eğitim döngüsüyle sonuçlanır.

print("Beginning training...")

for (epoch, batches) in dataset.training.prefix(epochCount).enumerated() {
    let start = Date()
    var trainStats = Statistics(on: device)
    var testStats = Statistics(on: device)

    Context.local.learningPhase = .training
    for batch in batches {
        let (eagerImages, eagerLabels) = (batch.data, batch.label)
        let images = Tensor(copying: eagerImages, to: device)
        let labels = Tensor(copying: eagerLabels, to: device)
        let 𝛁model = TensorFlow.gradient(at: x10Model) { x10Model -> Tensor<Float> in
            let ŷ = x10Model(images)
            let loss = softmaxCrossEntropy(logits: ŷ, labels: labels)
            trainStats.update(logits: ŷ, labels: labels, loss: loss)
            return loss
        }
        x10Optimizer.update(&x10Model, along: 𝛁model)
        LazyTensorBarrier()
    }

    Context.local.learningPhase = .inference
    for batch in dataset.validation {
        let (eagerImages, eagerLabels) = (batch.data, batch.label)
        let images = Tensor(copying: eagerImages, to: device)
        let labels = Tensor(copying: eagerLabels, to: device)
        let ŷ = x10Model(images)
        let loss = softmaxCrossEntropy(logits: ŷ, labels: labels)
        LazyTensorBarrier()
        testStats.update(logits: ŷ, labels: labels, loss: loss)
    }

    print(
        """
        [Epoch \(epoch)] \
        Training Loss: \(String(format: "%.3f", trainStats.averageLoss)), \
        Training Accuracy: \(trainStats.correctGuessCount)/\(trainStats.totalGuessCount) \
        (\(String(format: "%.1f", trainStats.accuracy))%), \
        Test Loss: \(String(format: "%.3f", testStats.averageLoss)), \
        Test Accuracy: \(testStats.correctGuessCount)/\(testStats.totalGuessCount) \
        (\(String(format: "%.1f", testStats.accuracy))%) \
        seconds per epoch: \(String(format: "%.1f", Date().timeIntervalSince(start)))
        """)
}
Beginning training...
[Epoch 0] Training Loss: 0.421, Training Accuracy: 51888/59904 (86.6%), Test Loss: 0.134, Test Accuracy: 9557/10000 (95.6%) seconds per epoch: 18.6
[Epoch 1] Training Loss: 0.117, Training Accuracy: 57733/59904 (96.4%), Test Loss: 0.085, Test Accuracy: 9735/10000 (97.3%) seconds per epoch: 14.9
[Epoch 2] Training Loss: 0.080, Training Accuracy: 58400/59904 (97.5%), Test Loss: 0.068, Test Accuracy: 9791/10000 (97.9%) seconds per epoch: 13.1
[Epoch 3] Training Loss: 0.064, Training Accuracy: 58684/59904 (98.0%), Test Loss: 0.056, Test Accuracy: 9804/10000 (98.0%) seconds per epoch: 13.5
[Epoch 4] Training Loss: 0.053, Training Accuracy: 58909/59904 (98.3%), Test Loss: 0.063, Test Accuracy: 9779/10000 (97.8%) seconds per epoch: 13.4

Modelin X10 arka ucunu kullanarak eğitimi, daha önce hevesli yürütme modelinin yaptığı gibi ilerlemeliydi. Bu noktalardaki benzersiz izlerin tam zamanında derlenmesi nedeniyle, ilk partiden önce ve ilk dönemin sonunda bir gecikme fark etmiş olabilirsiniz. Bunu bir hızlandırıcı takılıyken çalıştırıyorsanız, bu noktadan sonra eğitimin istekli moda göre daha hızlı ilerlediğini görmüş olmalısınız.

Başlangıçtaki izleme derleme süresi ile daha hızlı aktarım arasında bir denge vardır, ancak çoğu makine öğrenimi modelinde, tekrarlanan işlemlerden elde edilen aktarım hızındaki artış, derleme yükünü dengelemekten daha fazla olmalıdır. Uygulamada, bazı eğitim örneklerinde X10 ile verimde 4 kattan fazla artış gördük.

Daha önce de belirtildiği gibi, X10'u kullanmak artık TPU'larla çalışmayı sadece mümkün kılmakla kalmıyor, aynı zamanda kolaylaştırıyor ve Swift for TensorFlow modelleriniz için tüm hızlandırıcı sınıfının kilidini açıyor.