X10 का परिचय

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


TensorFlow.org पर देखें GitHub पर स्रोत देखें

डिफ़ॉल्ट रूप से, स्विफ्ट फॉर टेंसरफ्लो उत्सुक प्रेषण का उपयोग करके टेंसर संचालन करता है। यह तेजी से पुनरावृत्ति की अनुमति देता है, लेकिन मशीन लर्निंग मॉडल के प्रशिक्षण के लिए यह सबसे अच्छा विकल्प नहीं है।

X10 टेंसर लाइब्रेरी , टेंसर ट्रेसिंग और XLA कंपाइलर का लाभ उठाते हुए, TensorFlow के लिए स्विफ्ट में एक उच्च-प्रदर्शन बैकएंड जोड़ती है। यह ट्यूटोरियल X10 का परिचय देगा और आपको GPU या TPU पर चलाने के लिए एक प्रशिक्षण लूप को अपडेट करने की प्रक्रिया में मार्गदर्शन करेगा।

उत्सुक बनाम X10 टेंसर

TensorFlow के लिए स्विफ्ट में त्वरित गणनाएँ Tensor प्रकार के माध्यम से की जाती हैं। टेंसर विभिन्न प्रकार के संचालन में भाग ले सकते हैं, और मशीन लर्निंग मॉडल के मूलभूत निर्माण खंड हैं।

डिफ़ॉल्ट रूप से, एक टेन्सर ऑपरेशन-दर-ऑपरेशन आधार पर गणना करने के लिए उत्सुक निष्पादन का उपयोग करता है। प्रत्येक टेंसर में एक संबद्ध डिवाइस होता है जो बताता है कि यह किस हार्डवेयर से जुड़ा है और इसके लिए किस बैकएंड का उपयोग किया जाता है।

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)

यदि आप इस नोटबुक को GPU-सक्षम इंस्टेंस पर चला रहे हैं, तो आपको वह हार्डवेयर ऊपर डिवाइस विवरण में दिखाई देगा। उत्सुक रनटाइम में टीपीयू के लिए समर्थन नहीं है, इसलिए यदि आप उनमें से एक को त्वरक के रूप में उपयोग कर रहे हैं तो आप देखेंगे कि सीपीयू को हार्डवेयर लक्ष्य के रूप में उपयोग किया जा रहा है।

टेंसर बनाते समय, एक विकल्प निर्दिष्ट करके डिफ़ॉल्ट उत्सुक मोड डिवाइस को ओवरराइड किया जा सकता है। इस प्रकार आप X10 बैकएंड का उपयोग करके गणना करने का विकल्प चुनते हैं।

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)

यदि आप इसे GPU-सक्षम इंस्टेंस में चला रहे हैं, तो आपको X10 टेंसर डिवाइस में सूचीबद्ध एक्सेलेरेटर देखना चाहिए। उत्सुक निष्पादन के विपरीत, यदि आप इसे टीपीयू-सक्षम उदाहरण में चला रहे हैं, तो आपको अब देखना चाहिए कि गणना उस डिवाइस का उपयोग कर रही है। X10 यह है कि आप TensorFlow के लिए स्विफ्ट के भीतर TPU का लाभ कैसे उठाते हैं।

डिफ़ॉल्ट उत्सुक और X10 डिवाइस सिस्टम पर पहले त्वरक का उपयोग करने का प्रयास करेंगे। यदि आपके पास जीपीयू संलग्न है, तो पहले उपलब्ध जीपीयू का उपयोग किया जाएगा। यदि TPU मौजूद हैं, तो X10 डिफ़ॉल्ट रूप से पहले TPU कोर का उपयोग करेगा। यदि कोई त्वरक नहीं मिला या समर्थित नहीं है, तो डिफ़ॉल्ट डिवाइस सीपीयू पर वापस आ जाएगा।

डिफ़ॉल्ट उत्सुक और XLA डिवाइस से परे, आप डिवाइस में विशिष्ट हार्डवेयर और बैकएंड लक्ष्य प्रदान कर सकते हैं:

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

एक उत्सुक-मोड मॉडल का प्रशिक्षण

आइए देखें कि आप डिफ़ॉल्ट उत्सुक निष्पादन मोड का उपयोग करके मॉडल को कैसे सेट अप और प्रशिक्षित करेंगे। इस उदाहरण में, हम स्विफ्ट-मॉडल रिपॉजिटरी से सरल LeNet-5 मॉडल और MNIST हस्तलिखित अंक वर्गीकरण डेटासेट का उपयोग करेंगे।

सबसे पहले, हम MNIST डेटासेट सेट अप और डाउनलोड करेंगे।

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

इसके बाद, हम मॉडल और ऑप्टिमाइज़र को कॉन्फ़िगर करेंगे।

import ImageClassificationModels

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

अब, हम बुनियादी प्रगति ट्रैकिंग और रिपोर्टिंग लागू करेंगे। सभी मध्यवर्ती आँकड़ों को उसी डिवाइस पर टेंसर के रूप में रखा जाता है जहाँ प्रशिक्षण चलाया जाता है और केवल रिपोर्टिंग के दौरान scalarized() को कॉल किया जाता है। बाद में X10 का उपयोग करते समय यह विशेष रूप से महत्वपूर्ण होगा, क्योंकि यह आलसी टेंसरों के अनावश्यक भौतिकीकरण से बचाता है।

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

अंत में, हम मॉडल को पांच युगों के लिए एक प्रशिक्षण लूप के माध्यम से चलाएंगे।

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

जैसा कि आप देख सकते हैं, मॉडल को हमारी अपेक्षा के अनुरूप प्रशिक्षित किया गया, और सत्यापन सेट के विरुद्ध इसकी सटीकता प्रत्येक युग में बढ़ी। इस प्रकार TensorFlow मॉडल के लिए स्विफ्ट को परिभाषित किया जाता है और उत्सुक निष्पादन का उपयोग करके चलाया जाता है, अब देखते हैं कि X10 का लाभ उठाने के लिए क्या संशोधन करने की आवश्यकता है।

X10 मॉडल का प्रशिक्षण

डेटासेट, मॉडल और ऑप्टिमाइज़र में टेंसर होते हैं जिन्हें डिफ़ॉल्ट उत्सुक निष्पादन डिवाइस पर प्रारंभ किया जाता है। X10 के साथ काम करने के लिए, हमें इन टेंसरों को X10 डिवाइस में ले जाना होगा।

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

डेटासेट के लिए, हम उस बिंदु पर ऐसा करेंगे जहां बैचों को प्रशिक्षण लूप में संसाधित किया जाता है, ताकि हम उत्सुक निष्पादन मॉडल से डेटासेट का पुन: उपयोग कर सकें।

मॉडल और ऑप्टिमाइज़र के मामले में, हम उन्हें उत्सुक निष्पादन डिवाइस पर उनके आंतरिक टेंसर के साथ आरंभ करेंगे, फिर उन्हें X10 डिवाइस पर ले जाएंगे।

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

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

प्रशिक्षण लूप के लिए आवश्यक संशोधन कुछ विशिष्ट बिंदुओं पर आते हैं। सबसे पहले, हमें प्रशिक्षण डेटा के बैचों को X10 डिवाइस पर ले जाना होगा। जब प्रत्येक बैच पुनर्प्राप्त किया जाता है तो यह Tensor(copying:to:) के माध्यम से किया जाता है।

अगला परिवर्तन यह इंगित करना है कि प्रशिक्षण लूप के दौरान निशान कहाँ से काटे जाएँ। X10 आपके कोड में आवश्यक टेंसर गणनाओं का पता लगाकर और समय-समय पर उस ट्रेस के अनुकूलित प्रतिनिधित्व को संकलित करके काम करता है। प्रशिक्षण लूप के मामले में, आप एक ही ऑपरेशन को बार-बार दोहरा रहे हैं, जो ट्रेस करने, संकलित करने और पुन: उपयोग करने के लिए एक आदर्श अनुभाग है।

कोड की अनुपस्थिति में जो स्पष्ट रूप से टेंसर से एक मान का अनुरोध करता है (ये आमतौर पर .scalars या .scalarized() कॉल के रूप में सामने आते हैं), X10 सभी लूप पुनरावृत्तियों को एक साथ संकलित करने का प्रयास करेगा। इसे रोकने के लिए, और एक विशिष्ट बिंदु पर ट्रेस को काटने के लिए, ऑप्टिमाइज़र द्वारा मॉडल वेट को अपडेट करने और सत्यापन के दौरान हानि और सटीकता प्राप्त होने के बाद हम एक स्पष्ट LazyTensorBarrier() डालते हैं। यह दो पुन: उपयोग किए गए निशान बनाता है: प्रशिक्षण लूप में प्रत्येक चरण और सत्यापन के दौरान अनुमान का प्रत्येक बैच।

इन संशोधनों के परिणामस्वरूप निम्नलिखित प्रशिक्षण लूप प्राप्त होता है।

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

X10 बैकएंड का उपयोग करके मॉडल का प्रशिक्षण उसी तरीके से आगे बढ़ना चाहिए था जैसा कि उत्सुक निष्पादन मॉडल ने पहले किया था। आपने पहले बैच से पहले और पहले युग के अंत में उन बिंदुओं पर अनूठे निशानों के सही समय पर संकलन के कारण देरी देखी होगी। यदि आप इसे त्वरक के साथ चला रहे हैं, तो आपको उस बिंदु के बाद प्रशिक्षण को उत्सुक मोड की तुलना में तेजी से आगे बढ़ते हुए देखना चाहिए।

प्रारंभिक ट्रेस संकलन समय बनाम तेज़ थ्रूपुट का एक ट्रेडऑफ़ है, लेकिन अधिकांश मशीन लर्निंग मॉडल में दोहराया संचालन से थ्रूपुट में वृद्धि ऑफ़सेट संकलन ओवरहेड से अधिक होनी चाहिए। व्यवहार में, हमने कुछ प्रशिक्षण मामलों में X10 के साथ थ्रूपुट में 4 गुना से अधिक सुधार देखा है।

जैसा कि पहले कहा गया है, X10 का उपयोग अब TPUs के साथ काम करना न केवल संभव बल्कि आसान बनाता है, जिससे आपके स्विफ्ट फॉर टेन्सरफ्लो मॉडल के लिए त्वरक की पूरी श्रेणी को अनलॉक किया जा सकता है।