ডিফারেনশিয়াবল সুইফটে ধারালো প্রান্ত

ডিফারেনশিয়াবল সুইফট ব্যবহারযোগ্যতার দিক থেকে অনেক দূর এগিয়েছে। এখানে সেই অংশগুলি সম্পর্কে একটি হেড-আপ রয়েছে যা এখনও কিছুটা অস্পষ্ট। অগ্রগতির সাথে সাথে, এই নির্দেশিকাটি আরও ছোট হতে থাকবে এবং আপনি বিশেষ সিনট্যাক্সের প্রয়োজন ছাড়াই পার্থক্যযোগ্য কোড লিখতে সক্ষম হবেন।

লুপস

লুপগুলি পার্থক্যযোগ্য, শুধুমাত্র একটি বিস্তারিত জানার আছে। যখন আপনি লুপ লেখেন, তখন বিটটি মুড়ে দিন যেখানে আপনি 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

অতীন্দ্রিয় এবং অন্যান্য ফাংশন (sin, cos, abs, max)

Float এবং Double জন্য অনেকগুলি ট্রান্সসেন্ডেন্টাল এবং অন্যান্য সাধারণ বিল্ট-ইন ফাংশন ইতিমধ্যেই আলাদা করা হয়েছে। Float চেয়ে Double জন্য কম আছে। কিছু হয় জন্য উপলব্ধ নয়. তাই এখানে কিছু ম্যানুয়াল ডেরিভেটিভ সংজ্ঞা দেওয়া হল যাতে আপনি যা প্রয়োজন তা কীভাবে তৈরি করবেন সে সম্পর্কে ধারণা দিতে, যদি এটি ইতিমধ্যেই সরবরাহ করা না থাকে:

pow (ডেরিভেটিভ ব্যাখ্যার জন্য লিঙ্ক দেখুন)

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

abs

@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() ফাংশনের সাথে খুব সহজভাবে কাজ করে।