डिफरेंशियल स्विफ्ट में तेज धारें

प्रयोज्यता के मामले में डिफरेंशियल स्विफ्ट ने एक लंबा सफर तय किया है। यहां उन हिस्सों के बारे में जानकारी दी गई है जो अभी भी थोड़े अस्पष्ट हैं। जैसे-जैसे प्रगति जारी रहेगी, यह मार्गदर्शिका छोटी होती जाएगी, और आप विशेष सिंटैक्स की आवश्यकता के बिना अलग-अलग कोड लिखने में सक्षम होंगे।

छोरों

लूप अलग-अलग होते हैं, उनके बारे में जानने के लिए बस एक ही विवरण है। जब आप लूप लिखते हैं, तो उस बिट को लपेटें जहां आप निर्दिष्ट करते हैं कि आप withoutDerivative(at:) में क्या लूप कर रहे हैं

var a: [Float] = [1,2,3]

उदाहरण के लिए:

for _ in a.indices 
{}

बन जाता है

for _ in withoutDerivative(at: a.indices) 
{}

या:

for _ in 0..<a.count 
{}

बन जाता है

for _ in 0..<withoutDerivative(at: a.count) 
{}

यह आवश्यक है क्योंकि Array.count सदस्य सरणी के संबंध में व्युत्पन्न में योगदान नहीं करता है। सरणी में केवल वास्तविक तत्व ही व्युत्पन्न में योगदान करते हैं।

यदि आपके पास एक लूप है जहां आप मैन्युअल रूप से एक पूर्णांक को ऊपरी सीमा के रूप में उपयोग करते हैं, तो withoutDerivative(at:) उपयोग करने की कोई आवश्यकता नहीं है:

let iterations: Int = 10
for _ in 0..<iterations {} //this is fine as-is.

मानचित्र और कम करें

map और reduce के विशेष भिन्न-भिन्न संस्करण हैं जो बिल्कुल वैसे ही काम करते हैं जैसे आप करते थे:

a = [1,2,3]
let aPlusOne = a.differentiableMap {$0 + 1}
let aSum = a.differentiableReduce(0, +)
print("aPlusOne", aPlusOne)
print("aSum", aSum)
aPlusOne [2.0, 3.0, 4.0]
aSum 6.0

ऐरे सबस्क्रिप्ट सेट

ऐरे सबस्क्रिप्ट सेट ( array[0] = 0 ) बॉक्स के बाहर भिन्न नहीं हैं, लेकिन आप इस एक्सटेंशन को पेस्ट कर सकते हैं:

extension Array where Element: Differentiable {
    @differentiable(where Element: Differentiable)
    mutating func updated(at index: Int, with newValue: Element) {
        self[index] = newValue
    }

    @derivative(of: updated)
    mutating func vjpUpdated(at index: Int, with newValue: Element)
      -> (value: Void, pullback: (inout TangentVector) -> (Element.TangentVector))
    {
        self.updated(at: index, with: newValue)
        return ((), { v in
            let dElement = v[index]
            v.base[index] = .zero
            return dElement
        })
    }
}

और फिर वर्कअराउंड सिंटैक्स इस प्रकार है:

var b: [Float] = [1,2,3]

इसके अलावा:

b[0] = 17

इसे लिखें:

b.updated(at: 0, with: 17)

आइए सुनिश्चित करें कि यह काम करता है:

func plusOne(array: [Float]) -> Float{
  var array = array
  array.updated(at: 0, with: array[0] + 1)
  return array[0]
}

let plusOneValAndGrad = valueWithGradient(at: [2], in: plusOne)
print(plusOneValAndGrad)
(value: 3.0, gradient: [1.0])

इस समाधान के बिना आपको जो त्रुटि मिलेगी वह यह है कि Differentiation of coroutine calls is not yet supported । इस समाधान को अनावश्यक बनाने की प्रगति देखने के लिए यहां लिंक दिया गया है: https://bugs.swift.org/browse/TF-1277 (यह Array.subscript._modify के बारे में बात करता है, जब आप कोई सरणी बनाते हैं तो इसे पर्दे के पीछे कहा जाता है) सबस्क्रिप्ट सेट)।

Float <-> Double रूपांतरण

यदि आप Float और Double के बीच स्विच कर रहे हैं, तो उनके कंस्ट्रक्टर पहले से ही अलग-अलग नहीं हैं। यहां एक फ़ंक्शन है जो आपको Float से Double तक अलग-अलग तरीके से जाने देगा।

(नीचे दिए गए कोड में Float और Double स्विच करें, और आपको एक फ़ंक्शन मिलेगा जो Double से Float में कनवर्ट करता है।)

आप किसी अन्य वास्तविक संख्यात्मक प्रकार के लिए समान कनवर्टर्स बना सकते हैं।

@differentiable
func convertToDouble(_ a: Float) -> Double {
    return Double(a)
}

@derivative(of: convertToDouble)
func convertToDoubleVJP(_ a: Float) -> (value: Double, pullback: (Double) -> Float) {
    func pullback(_ v: Double) -> Float{
        return Float(v)
    }
    return (value: Double(a), pullback: pullback)
}

यहाँ एक उदाहरण उपयोग है:

@differentiable
func timesTwo(a: Float) -> Double {
  return convertToDouble(a * 2)
}
let input: Float = 3
let valAndGrad = valueWithGradient(at: input, in: timesTwo)
print("grad", valAndGrad.gradient)
print("type of input:", type(of: input))
print("type of output:", type(of: valAndGrad.value))
print("type of gradient:", type(of: valAndGrad.gradient))
grad 2.0
type of input: Float
type of output: Double
type of gradient: Float

ट्रान्सेंडैंटल और अन्य कार्य (पाप, कॉस, पेट, अधिकतम)

Float और Double के लिए बहुत सारे ट्रान्सेंडैंटल और अन्य सामान्य अंतर्निहित फ़ंक्शंस को पहले से ही अलग किया जा चुका है। Float तुलना में Double के लिए कम हैं। कुछ किसी के लिए भी उपलब्ध नहीं हैं। तो यहां कुछ मैनुअल व्युत्पन्न परिभाषाएं दी गई हैं, जिससे आपको यह पता चलेगा कि आपको जो चाहिए वह कैसे बनाया जाए, यदि यह पहले से उपलब्ध नहीं है:

पाउ (व्युत्पन्न स्पष्टीकरण के लिए लिंक देखें)

import Foundation

@usableFromInline
@derivative(of: pow) 
func powVJP(_ base: Double, _ exponent: Double) -> (value: Double, pullback: (Double) -> (Double, Double)) {
    let output: Double = pow(base, exponent)
    func pullback(_ vector: Double) -> (Double, Double) {
        let baseDerivative = vector * (exponent * pow(base, exponent - 1))
        let exponentDerivative = vector * output * log(base)
        return (baseDerivative, exponentDerivative)
    }

    return (value: output, pullback: pullback)
}

अधिकतम

@usableFromInline
@derivative(of: max)
func maxVJP<T: Comparable & Differentiable>(_ x: T, _ y: T) -> (value: T, pullback: (T.TangentVector)
  -> (T.TangentVector, T.TangentVector))
{
    func pullback(_ v: T.TangentVector) -> (T.TangentVector, T.TangentVector) {
        if x < y {
            return (.zero, v)
        } else {
            return (v, .zero)
        }
    }
    return (value: max(x, y), pullback: pullback)
}

पेट

@usableFromInline
@derivative(of: abs)
func absVJP<T: Comparable & SignedNumeric & Differentiable>(_ x: T)
  -> (value: T, pullback: (T.TangentVector) -> T.TangentVector)
{
    func pullback(_ v: T.TangentVector) -> T.TangentVector{
        if x < 0 {
            return .zero - v
        }
        else {
            return v
        }
    }
    return (value: abs(x), pullback: pullback)
}

sqrt (व्युत्पन्न स्पष्टीकरण के लिए लिंक देखें)

@usableFromInline
@derivative(of: sqrt) 
func sqrtVJP(_ x: Double) -> (value: Double, pullback: (Double) -> Double) {
    let output = sqrt(x)
    func pullback(_ v: Double) -> Double {
        return v / (2 * output)
    }
    return (value: output, pullback: pullback)
}

आइए देखें कि ये काम करते हैं:

let powGrad = gradient(at: 2, 2, in: pow)
print("pow gradient: ", powGrad, "which is", powGrad == (4.0, 2.772588722239781) ? "correct" : "incorrect")

let maxGrad = gradient(at: 1, 2, in: max)
print("max gradient: ", maxGrad, "which is", maxGrad == (0.0, 1.0) ? "correct" : "incorrect")

let absGrad = gradient(at: 2, in: abs)
print("abs gradient: ", absGrad, "which is", absGrad == 1.0 ? "correct" : "incorrect")

let sqrtGrad = gradient(at: 4, in: sqrt)
print("sqrt gradient: ", sqrtGrad, "which is", sqrtGrad == 0.25 ? "correct" : "incorrect")
pow gradient:  (4.0, 2.772588722239781) which is correct
max gradient:  (0.0, 1.0) which is correct
abs gradient:  1.0 which is correct
sqrt gradient:  0.25 which is correct

कंपाइलर त्रुटि जो आपको कुछ इस तरह की आवश्यकता के प्रति सचेत करती है वह है: Expression is not differentiable. Cannot differentiate functions that have not been marked '@differentiable' and that are defined in other files

KeyPath सबस्क्रिप्टिंग

KeyPath सबस्क्रिप्टिंग (प्राप्त या सेट) बॉक्स से बाहर काम नहीं करती है, लेकिन एक बार फिर, कुछ एक्सटेंशन हैं जिन्हें आप जोड़ सकते हैं, और फिर वर्कअराउंड सिंटैक्स का उपयोग कर सकते हैं। यह रहा:

https://github.com/tensorflow/swift/issues/530#issuecomment-687400701

यह समाधान दूसरों की तुलना में थोड़ा बदसूरत है। यह केवल कस्टम ऑब्जेक्ट के लिए काम करता है, जो डिफरेंशियल और एडिटिवअरिथमेटिक के अनुरूप होना चाहिए। आपको एक .tmp सदस्य और एक .read() फ़ंक्शन जोड़ना होगा, और KeyPath सबस्क्रिप्ट प्राप्त करते समय आप .tmp सदस्य को इंटरमीडिएट स्टोरेज के रूप में उपयोग करते हैं (लिंक किए गए कोड में एक उदाहरण है)। KeyPath सबस्क्रिप्ट सेट .write() फ़ंक्शन के साथ बहुत सरलता से काम करते हैं।