SlideShare a Scribd company logo
S4TF save/export
Not a priority but a nice feature for the edge
ProtoBuf / FlatBuf file format
Inferencing
Personalization
Federated Learning
Runtime
S4TF Trivial data, model, hack
let SAMPLE_SIZE = 100
let a: Float = 2.0
let b: Float = 1.5
let x = Tensor<Float>(rangeFrom: 0, to: 1, stride: 1.0 / Float(SAMPLE_SIZE))
let noise = (Tensor<Float>(randomNormal: [SAMPLE_SIZE]) - 0.5) * 0.1
let y = (a * x + b) + noise
struct LinearRegression: Layer {
var layer1 = Dense<Float>(inputSize: 1, outputSize: 1, activation: identity)
@differentiable
func callAsFunction(_ input: Tensor<Float>) -> Tensor<Float> {
return layer1(input)
}
}
var regression = LinearRegression()
let optimizer = SGD(for: regression, learningRate: 0.03)
Context.local.learningPhase = .training
for _ in 0..<100 { //1000
let 𝛁model = regression.gradient { r -> Tensor<Float> in
let ŷ = r(X)
let loss = meanSquaredError(predicted: ŷ, expected: Y)
print("Loss: (loss)")
return loss
}
optimizer.update(&regression, along: 𝛁model)
}
Hack Swift CoreML ProtoBuf format
Apple provide Python CoreMLTools package and Swift API for iOS/macOS/.. for
model usage but no Swift API for CoreML model creation

1. Install ProtoBuf Compiler - https://github.com/protocolbuffers/protobuf

2. Install Swift ProtoBuf Plugin - https://github.com/apple/swift-protobuf

3. Download CoreML ProtoBuf source file - https://github.com/apple/
coremltools/tree/master/mlmodel/format

4. Compile and generate Swift CoreML data structures - protoc --
swift_out=[PATH TO FOLDER FOR GENERATED SWIFT FILES] [PATH TO
COREMLTOOS FOLDER]/mlmodel/format/*.proto
Swift CoreML ProtoBuf data
1: 4
2 {
1 {
1: "dense_input"
3 {
5 {
1: "001"
2: 65600
}
}
}
10 {
1: "output"
3 {
5 {
1: "001"
2: 65600
}
}
}
50 {
1: "dense_input"
3 {
5 {
1: "001"
2: 65600
protoc --decode_raw < model.mlmodel
syntax = “proto3”;
message Model {
int32 specificationVersion = 1;
ModelDescription description = 2;
bool isUpdatable = 10;
oneof Type {
...
NeuralNetwork neuralNetwork = 500;
...
}
...
}
message ModelDescription {
repeated FeatureDescription input = 1;
repeated FeatureDescription output = 10;
string predictedFeatureName = 11;
string predictedProbabilitiesName = 12;
repeated FeatureDescription trainingInput = 50;
Metadata metadata = 100;
}
message FeatureDescription {
string name = 1;
string shortDescription = 2;
FeatureType type = 3;
}
message NeuralNetwork {
repeated NeuralNetworkLayer layers = 1;
repeated NeuralNetworkPreprocessing preprocessing =
2;
NeuralNetworkMultiArrayShapeMapping
arrayInputShapeMapping = 5;
NeuralNetworkImageShapeMapping
imageInputShapeMapping = 6;
NetworkUpdateParameters updateParams = 10;
}
…
Model.proto Model.pb.swift
import Foundation
import SwiftProtobuf
struct CoreML_Specification_Model {
var specificationVersion: Int32 {
get {return _storage._specificationVersion}
set {_uniqueStorage()._specificationVersion = newValue}
}
var description_p: CoreML_Specification_ModelDescription
{
get {return _storage._description_p ??
CoreML_Specification_ModelDescription()}
set {_uniqueStorage()._description_p = newValue}
}
var hasDescription_p: Bool {return
_storage._description_p != nil}
var isUpdatable: Bool {
get {return _storage._isUpdatable}
set {_uniqueStorage()._isUpdatable = newValue}
}
var type: OneOf_Type? {
get {return _storage._type}
set {_uniqueStorage()._type = newValue}
}
...
/// generic models start at 500
var neuralNetwork: CoreML_Specification_NeuralNetwork {
get {
if case .neuralNetwork(let v)? = _storage._type
{return v}
return CoreML_Specification_NeuralNetwork()
}
set {_uniqueStorage()._type = .neuralNetwork(newValue)}
}
...
}
...
Export S4TF to CoreML in Swift
let coreModel = CoreML_Specification_Model.with {
$0.specificationVersion = 4
$0.description_p = CoreML_Specification_ModelDescription.with {
$0.input = [CoreML_Specification_FeatureDescription.with {
$0.name = "dense_input"
$0.type = CoreML_Specification_FeatureType.with {
$0.multiArrayType =
CoreML_Specification_ArrayFeatureType.with {
$0.shape = [1]
$0.dataType =
CoreML_Specification_ArrayFeatureType.ArrayDataType.double
}
}
}]
$0.output = [CoreML_Specification_FeatureDescription.with {
$0.name = "output"
$0.type = CoreML_Specification_FeatureType.with {
$0.multiArrayType = CoreML_Specification_ArrayFeatureType.with {
$0.shape = [1]
$0.dataType =
CoreML_Specification_ArrayFeatureType.ArrayDataType.double
}
}
}]
$0.trainingInput = [CoreML_Specification_FeatureDescription.with {
$0.name = "dense_input"
$0.type = CoreML_Specification_FeatureType.with {
$0.multiArrayType = CoreML_Specification_ArrayFeatureType.with {
$0.shape = [1]
$0.dataType =
CoreML_Specification_ArrayFeatureType.ArrayDataType.double
}
}
}, CoreML_Specification_FeatureDescription.with {
$0.name = "output_true"
$0.type = CoreML_Specification_FeatureType.with {
$0.multiArrayType = CoreML_Specification_ArrayFeatureType.with {
$0.shape = [1]
$0.dataType =
CoreML_Specification_ArrayFeatureType.ArrayDataType.double
}
}
}]
struct LinearRegression: Layer {
var layer1 = Dense<Float>(inputSize: 1, outputSize: 1, activation: identity)
...
}
var regression = LinearRegression()
let weight = Float(regression.layer1.weight[0][0])!
let bias = Float(regression.layer1.bias[0])!
let binaryModelData: Data = try coreModel.serializedData()
binaryModelData.write(to: URL(fileURLWithPath: "./s4tf_model_personalization.mlmodel"))
$0.isUpdatable = true
$0.neuralNetwork = CoreML_Specification_NeuralNetwork.with {
$0.layers = [CoreML_Specification_NeuralNetworkLayer.with {
$0.name = "dense_1"
$0.input = ["dense_input"]
$0.output = ["output"]
$0.isUpdatable = true
$0.innerProduct =
CoreML_Specification_InnerProductLayerParams.with {
$0.inputChannels = 1
$0.outputChannels = 1
$0.hasBias_p = true
$0.weights = CoreML_Specification_WeightParams.with {
$0.floatValue = [weight]
$0.isUpdatable = true
}
$0.bias = CoreML_Specification_WeightParams.with {
$0.floatValue = [bias]
$0.isUpdatable = true
}
}
}]
$0.updateParams =
CoreML_Specification_NetworkUpdateParameters.with {
$0.lossLayers = [CoreML_Specification_LossLayer.with {
$0.name = "lossLayer"
$0.meanSquaredErrorLossLayer =
CoreML_Specification_MeanSquaredErrorLossLayer.with {
$0.input = "output"
$0.target = "output_true"
}
}]
$0.optimizer = CoreML_Specification_Optimizer.with {
$0.sgdOptimizer =
CoreML_Specification_SGDOptimizer.with {
$0.learningRate =
CoreML_Specification_DoubleParameter.with {
$0.defaultValue = 0.03
$0.range =
CoreML_Specification_DoubleRange.with {
$0.maxValue = 1.0
}
}
$0.miniBatchSize =
CoreML_Specification_Int64Parameter.with {
$0.defaultValue = 1
$0.set = CoreML_Specification_Int64Set.with
$0.values = [1]
}
}
$0.momentum =
CoreML_Specification_DoubleParameter.with {
$0.defaultValue = 0
$0.range =
CoreML_Specification_DoubleRange.with {
$0.maxValue = 1.0
}
}
}
}
$0.epochs = CoreML_Specification_Int64Parameter.with {
$0.defaultValue = 100
$0.set = CoreML_Specification_Int64Set.with {
$0.values = [100]
}
}
$0.shuffle = CoreML_Specification_BoolParameter.with {
$0.defaultValue = true
}
}
}
}
CoreML Compile and Inference
func compileCoreML(path: String) -> (MLModel, URL) {
let modelUrl = URL(fileURLWithPath: path)
let compiledUrl = try! MLModel.compileModel(at: modelUrl)
return try! (MLModel(contentsOf: compiledUrl), compiledUrl)
}
func inferenceCoreML(model: MLModel, x: Float) -> Float {
let multiArr = try! MLMultiArray(shape: [1], dataType: .double)
multiArr[0] = NSNumber(value: x)
let inputValue = MLFeatureValue(multiArray: multiArr)
let dataPointFeatures: [String: MLFeatureValue] = [inputName: "dense_input"]
let provider = try! MLDictionaryFeatureProvider(dictionary: dataPointFeatures)
let prediction = try! model.prediction(from: provider)
return Float(prediction.featureValue(for: "output")!.multiArrayValue![0].doubleValue)
}
let (coreModel, compiledModelUrl) = compileCoreML(path: coreMLFilePath)
let prediction = inferenceCoreML(model: coreModel, x: 1.0)
Xcode has fantastic drag&drop integration of CoreML model into project with Swift wrapper
code generation but models can also be loaded, compiled and used dynamically
CoreML Personalization / Training
func generateData(sampleSize: Int = 100) -> ([Float], [Float]) {
let a: Float = 2.0
let b: Float = 1.5
var X = [Float]()
var Y = [Float]()
for i in 0..<sampleSize {
let x: Float = Float(i) / Float(sampleSize)
let noise: Float = (Float.random(in: 0..<1) - 0.5) * 0.1
let y: Float = (a * x + b) + noise
X.append(x)
Y.append(y)
}
return (X, Y)
}
func prepareTrainingBatch() -> MLBatchProvider {
var featureProviders = [MLFeatureProvider]()
let inputName = "dense_input"
let outputName = "output_true"
let (X, Y) = generateData()
for (x,y) in zip(X, Y) {
let multiArr = try! MLMultiArray(shape: [1], dataType: .double)
multiArr[0] = NSNumber(value: x)
let inputValue = MLFeatureValue(multiArray: multiArr)
multiArr[0] = NSNumber(value: y)
let outputValue = MLFeatureValue(multiArray: multiArr)
let dataPointFeatures: [String: MLFeatureValue] = [inputName: inputValue,
outputName: outputValue]
if let provider = try? MLDictionaryFeatureProvider(dictionary: dataPointFeatures) {
featureProviders.append(provider)
}
}
return MLArrayBatchProvider(array: featureProviders)
}
func train(url: URL) {
let configuration = MLModelConfiguration()
configuration.computeUnits = .all
configuration.parameters = [.epochs : 100]
let progressHandler = { (context: MLUpdateContext) in
switch context.event {
case .trainingBegin: ...
case .miniBatchEnd: ...
case .epochEnd: ...
}
}
let completionHandler = { (context: MLUpdateContext) in
guard context.task.state == .completed else { return }
let trainLoss = context.metrics[.lossValue] as! Double
let updatedModel = context.model
let updatedModelURL = URL(fileURLWithPath: retrainedCoreMLFilePath)
try! updatedModel.write(to: updatedModelURL)
}
let handlers = MLUpdateProgressHandlers(
forEvents: [.trainingBegin, .miniBatchEnd, .epochEnd],
progressHandler: progressHandler,
completionHandler: completionHandler)
let updateTask = try! MLUpdateTask(forModelAt: url,
trainingData: prepareTrainingBatch(),
configuration: configuration,
progressHandlers: handlers)
updateTask.resume()
}
Prepare Batch Data Training
train(url: compiledModelUrl)
// Wait for completition of the asyncronous training task
let retrainedModel = try! MLModel(contentsOf: URL(fileURLWithPath: retrainedCoreMLFilePath))
let prediction = inferenceCoreML(model: retrainedModel, x: 1.0)
S4TF How Automate Model Export
?
• Extend Layer, Sequencial/sequenced(), DSL function builder ???

• What about Training parameters (Cost Functions, Optimizations, …) ??

+ Model Optimizations:

• Other then Quantization and Pruning

• i.e. Microsoft ONNX BERT condensed layers (17x inference acceleration)

• i.e. CoreML Custom Layers and/or Custom Activation Functions

• https://github.com/JacopoMangiavacchi/S4TF_CoreML_Test

• https://medium.com/@JMangia/swift-loves-tensorflow-and-coreml-2a11da25d44

More Related Content

What's hot

Clojure: The Art of Abstraction
Clojure: The Art of AbstractionClojure: The Art of Abstraction
Clojure: The Art of Abstraction
Alex Miller
 
Advanced Python, Part 2
Advanced Python, Part 2Advanced Python, Part 2
Advanced Python, Part 2
Zaar Hai
 
Design patterns in javascript
Design patterns in javascriptDesign patterns in javascript
Design patterns in javascriptMiao Siyu
 
Coding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean CodeCoding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean Code
Ganesh Samarthyam
 
Java VS Python
Java VS PythonJava VS Python
Java VS Python
Simone Federici
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
SeniorDevOnly
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Intro
thnetos
 
Python Puzzlers
Python PuzzlersPython Puzzlers
Python Puzzlers
Tendayi Mawushe
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
Fabernovel
 
Python programming : Classes objects
Python programming : Classes objectsPython programming : Classes objects
Python programming : Classes objects
Emertxe Information Technologies Pvt Ltd
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
andyrobinson8
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
Christian Baranowski
 
The Macronomicon
The MacronomiconThe Macronomicon
The Macronomicon
Mike Fogus
 
The Ring programming language version 1.8 book - Part 36 of 202
The Ring programming language version 1.8 book - Part 36 of 202The Ring programming language version 1.8 book - Part 36 of 202
The Ring programming language version 1.8 book - Part 36 of 202
Mahmoud Samir Fayed
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
Arturo Herrero
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
JavaScript Arrays
JavaScript Arrays JavaScript Arrays
JavaScript Arrays
Reem Alattas
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Codestasimus
 

What's hot (20)

Clojure: The Art of Abstraction
Clojure: The Art of AbstractionClojure: The Art of Abstraction
Clojure: The Art of Abstraction
 
Core C#
Core C#Core C#
Core C#
 
Advanced Python, Part 2
Advanced Python, Part 2Advanced Python, Part 2
Advanced Python, Part 2
 
Clojure class
Clojure classClojure class
Clojure class
 
Design patterns in javascript
Design patterns in javascriptDesign patterns in javascript
Design patterns in javascript
 
Coding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean CodeCoding Guidelines - Crafting Clean Code
Coding Guidelines - Crafting Clean Code
 
Java VS Python
Java VS PythonJava VS Python
Java VS Python
 
From Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risksFrom Java to Scala - advantages and possible risks
From Java to Scala - advantages and possible risks
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Intro
 
Python Puzzlers
Python PuzzlersPython Puzzlers
Python Puzzlers
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
 
Python programming : Classes objects
Python programming : Classes objectsPython programming : Classes objects
Python programming : Classes objects
 
Scala in practice
Scala in practiceScala in practice
Scala in practice
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
The Macronomicon
The MacronomiconThe Macronomicon
The Macronomicon
 
The Ring programming language version 1.8 book - Part 36 of 202
The Ring programming language version 1.8 book - Part 36 of 202The Ring programming language version 1.8 book - Part 36 of 202
The Ring programming language version 1.8 book - Part 36 of 202
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 
JavaScript Arrays
JavaScript Arrays JavaScript Arrays
JavaScript Arrays
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
 

Similar to Swift for TensorFlow - CoreML Personalization

A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingGarth Gilmour
 
Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick touraztack
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
Stuart Roebuck
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with ClojureDmitry Buzdin
 
Scaling Deep Learning with MXNet
Scaling Deep Learning with MXNetScaling Deep Learning with MXNet
Scaling Deep Learning with MXNet
AI Frontiers
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with Python
Han Lee
 
T3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmerT3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmer
David Muñoz Díaz
 
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
Matthew Tovbin
 
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
Databricks
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
Laurence Svekis ✔
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
Tools for Making Machine Learning more Reactive
Tools for Making Machine Learning more ReactiveTools for Making Machine Learning more Reactive
Tools for Making Machine Learning more Reactive
Jeff Smith
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
scalaconfjp
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
Java fundamentals
Java fundamentalsJava fundamentals
Java fundamentalsHCMUTE
 
用Tornado开发RESTful API运用
用Tornado开发RESTful API运用用Tornado开发RESTful API运用
用Tornado开发RESTful API运用Felinx Lee
 

Similar to Swift for TensorFlow - CoreML Personalization (20)

A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional Programming
 
Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick tour
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 
Scala
ScalaScala
Scala
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
Scaling Deep Learning with MXNet
Scaling Deep Learning with MXNetScaling Deep Learning with MXNet
Scaling Deep Learning with MXNet
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with Python
 
T3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmerT3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmer
 
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
The Rule of 10,000 Spark Jobs - Learning from Exceptions and Serializing Your...
 
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
The Rule of 10,000 Spark Jobs: Learning From Exceptions and Serializing Your ...
 
JavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your codeJavaScript Advanced - Useful methods to power up your code
JavaScript Advanced - Useful methods to power up your code
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
Tools for Making Machine Learning more Reactive
Tools for Making Machine Learning more ReactiveTools for Making Machine Learning more Reactive
Tools for Making Machine Learning more Reactive
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Java fundamentals
Java fundamentalsJava fundamentals
Java fundamentals
 
用Tornado开发RESTful API运用
用Tornado开发RESTful API运用用Tornado开发RESTful API运用
用Tornado开发RESTful API运用
 

Recently uploaded

Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Aggregage
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
UiPathCommunity
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Enhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZEnhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZ
Globus
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
RinaMondal9
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
Peter Spielvogel
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
ControlCase
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
Jen Stirrup
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
Vlad Stirbu
 

Recently uploaded (20)

Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
Le nuove frontiere dell'AI nell'RPA con UiPath Autopilot™
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Enhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZEnhancing Performance with Globus and the Science DMZ
Enhancing Performance with Globus and the Science DMZ
 
Free Complete Python - A step towards Data Science
Free Complete Python - A step towards Data ScienceFree Complete Python - A step towards Data Science
Free Complete Python - A step towards Data Science
 
Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
 
PCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase TeamPCI PIN Basics Webinar from the Controlcase Team
PCI PIN Basics Webinar from the Controlcase Team
 
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...The Metaverse and AI: how can decision-makers harness the Metaverse for their...
The Metaverse and AI: how can decision-makers harness the Metaverse for their...
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
 

Swift for TensorFlow - CoreML Personalization

  • 1. S4TF save/export Not a priority but a nice feature for the edge ProtoBuf / FlatBuf file format Inferencing Personalization Federated Learning Runtime
  • 2. S4TF Trivial data, model, hack let SAMPLE_SIZE = 100 let a: Float = 2.0 let b: Float = 1.5 let x = Tensor<Float>(rangeFrom: 0, to: 1, stride: 1.0 / Float(SAMPLE_SIZE)) let noise = (Tensor<Float>(randomNormal: [SAMPLE_SIZE]) - 0.5) * 0.1 let y = (a * x + b) + noise struct LinearRegression: Layer { var layer1 = Dense<Float>(inputSize: 1, outputSize: 1, activation: identity) @differentiable func callAsFunction(_ input: Tensor<Float>) -> Tensor<Float> { return layer1(input) } } var regression = LinearRegression() let optimizer = SGD(for: regression, learningRate: 0.03) Context.local.learningPhase = .training for _ in 0..<100 { //1000 let 𝛁model = regression.gradient { r -> Tensor<Float> in let ŷ = r(X) let loss = meanSquaredError(predicted: ŷ, expected: Y) print("Loss: (loss)") return loss } optimizer.update(&regression, along: 𝛁model) }
  • 3. Hack Swift CoreML ProtoBuf format Apple provide Python CoreMLTools package and Swift API for iOS/macOS/.. for model usage but no Swift API for CoreML model creation 1. Install ProtoBuf Compiler - https://github.com/protocolbuffers/protobuf 2. Install Swift ProtoBuf Plugin - https://github.com/apple/swift-protobuf 3. Download CoreML ProtoBuf source file - https://github.com/apple/ coremltools/tree/master/mlmodel/format 4. Compile and generate Swift CoreML data structures - protoc -- swift_out=[PATH TO FOLDER FOR GENERATED SWIFT FILES] [PATH TO COREMLTOOS FOLDER]/mlmodel/format/*.proto
  • 4. Swift CoreML ProtoBuf data 1: 4 2 { 1 { 1: "dense_input" 3 { 5 { 1: "001" 2: 65600 } } } 10 { 1: "output" 3 { 5 { 1: "001" 2: 65600 } } } 50 { 1: "dense_input" 3 { 5 { 1: "001" 2: 65600 protoc --decode_raw < model.mlmodel syntax = “proto3”; message Model { int32 specificationVersion = 1; ModelDescription description = 2; bool isUpdatable = 10; oneof Type { ... NeuralNetwork neuralNetwork = 500; ... } ... } message ModelDescription { repeated FeatureDescription input = 1; repeated FeatureDescription output = 10; string predictedFeatureName = 11; string predictedProbabilitiesName = 12; repeated FeatureDescription trainingInput = 50; Metadata metadata = 100; } message FeatureDescription { string name = 1; string shortDescription = 2; FeatureType type = 3; } message NeuralNetwork { repeated NeuralNetworkLayer layers = 1; repeated NeuralNetworkPreprocessing preprocessing = 2; NeuralNetworkMultiArrayShapeMapping arrayInputShapeMapping = 5; NeuralNetworkImageShapeMapping imageInputShapeMapping = 6; NetworkUpdateParameters updateParams = 10; } … Model.proto Model.pb.swift import Foundation import SwiftProtobuf struct CoreML_Specification_Model { var specificationVersion: Int32 { get {return _storage._specificationVersion} set {_uniqueStorage()._specificationVersion = newValue} } var description_p: CoreML_Specification_ModelDescription { get {return _storage._description_p ?? CoreML_Specification_ModelDescription()} set {_uniqueStorage()._description_p = newValue} } var hasDescription_p: Bool {return _storage._description_p != nil} var isUpdatable: Bool { get {return _storage._isUpdatable} set {_uniqueStorage()._isUpdatable = newValue} } var type: OneOf_Type? { get {return _storage._type} set {_uniqueStorage()._type = newValue} } ... /// generic models start at 500 var neuralNetwork: CoreML_Specification_NeuralNetwork { get { if case .neuralNetwork(let v)? = _storage._type {return v} return CoreML_Specification_NeuralNetwork() } set {_uniqueStorage()._type = .neuralNetwork(newValue)} } ... } ...
  • 5. Export S4TF to CoreML in Swift let coreModel = CoreML_Specification_Model.with { $0.specificationVersion = 4 $0.description_p = CoreML_Specification_ModelDescription.with { $0.input = [CoreML_Specification_FeatureDescription.with { $0.name = "dense_input" $0.type = CoreML_Specification_FeatureType.with { $0.multiArrayType = CoreML_Specification_ArrayFeatureType.with { $0.shape = [1] $0.dataType = CoreML_Specification_ArrayFeatureType.ArrayDataType.double } } }] $0.output = [CoreML_Specification_FeatureDescription.with { $0.name = "output" $0.type = CoreML_Specification_FeatureType.with { $0.multiArrayType = CoreML_Specification_ArrayFeatureType.with { $0.shape = [1] $0.dataType = CoreML_Specification_ArrayFeatureType.ArrayDataType.double } } }] $0.trainingInput = [CoreML_Specification_FeatureDescription.with { $0.name = "dense_input" $0.type = CoreML_Specification_FeatureType.with { $0.multiArrayType = CoreML_Specification_ArrayFeatureType.with { $0.shape = [1] $0.dataType = CoreML_Specification_ArrayFeatureType.ArrayDataType.double } } }, CoreML_Specification_FeatureDescription.with { $0.name = "output_true" $0.type = CoreML_Specification_FeatureType.with { $0.multiArrayType = CoreML_Specification_ArrayFeatureType.with { $0.shape = [1] $0.dataType = CoreML_Specification_ArrayFeatureType.ArrayDataType.double } } }] struct LinearRegression: Layer { var layer1 = Dense<Float>(inputSize: 1, outputSize: 1, activation: identity) ... } var regression = LinearRegression() let weight = Float(regression.layer1.weight[0][0])! let bias = Float(regression.layer1.bias[0])! let binaryModelData: Data = try coreModel.serializedData() binaryModelData.write(to: URL(fileURLWithPath: "./s4tf_model_personalization.mlmodel")) $0.isUpdatable = true $0.neuralNetwork = CoreML_Specification_NeuralNetwork.with { $0.layers = [CoreML_Specification_NeuralNetworkLayer.with { $0.name = "dense_1" $0.input = ["dense_input"] $0.output = ["output"] $0.isUpdatable = true $0.innerProduct = CoreML_Specification_InnerProductLayerParams.with { $0.inputChannels = 1 $0.outputChannels = 1 $0.hasBias_p = true $0.weights = CoreML_Specification_WeightParams.with { $0.floatValue = [weight] $0.isUpdatable = true } $0.bias = CoreML_Specification_WeightParams.with { $0.floatValue = [bias] $0.isUpdatable = true } } }] $0.updateParams = CoreML_Specification_NetworkUpdateParameters.with { $0.lossLayers = [CoreML_Specification_LossLayer.with { $0.name = "lossLayer" $0.meanSquaredErrorLossLayer = CoreML_Specification_MeanSquaredErrorLossLayer.with { $0.input = "output" $0.target = "output_true" } }] $0.optimizer = CoreML_Specification_Optimizer.with { $0.sgdOptimizer = CoreML_Specification_SGDOptimizer.with { $0.learningRate = CoreML_Specification_DoubleParameter.with { $0.defaultValue = 0.03 $0.range = CoreML_Specification_DoubleRange.with { $0.maxValue = 1.0 } } $0.miniBatchSize = CoreML_Specification_Int64Parameter.with { $0.defaultValue = 1 $0.set = CoreML_Specification_Int64Set.with $0.values = [1] } } $0.momentum = CoreML_Specification_DoubleParameter.with { $0.defaultValue = 0 $0.range = CoreML_Specification_DoubleRange.with { $0.maxValue = 1.0 } } } } $0.epochs = CoreML_Specification_Int64Parameter.with { $0.defaultValue = 100 $0.set = CoreML_Specification_Int64Set.with { $0.values = [100] } } $0.shuffle = CoreML_Specification_BoolParameter.with { $0.defaultValue = true } } } }
  • 6. CoreML Compile and Inference func compileCoreML(path: String) -> (MLModel, URL) { let modelUrl = URL(fileURLWithPath: path) let compiledUrl = try! MLModel.compileModel(at: modelUrl) return try! (MLModel(contentsOf: compiledUrl), compiledUrl) } func inferenceCoreML(model: MLModel, x: Float) -> Float { let multiArr = try! MLMultiArray(shape: [1], dataType: .double) multiArr[0] = NSNumber(value: x) let inputValue = MLFeatureValue(multiArray: multiArr) let dataPointFeatures: [String: MLFeatureValue] = [inputName: "dense_input"] let provider = try! MLDictionaryFeatureProvider(dictionary: dataPointFeatures) let prediction = try! model.prediction(from: provider) return Float(prediction.featureValue(for: "output")!.multiArrayValue![0].doubleValue) } let (coreModel, compiledModelUrl) = compileCoreML(path: coreMLFilePath) let prediction = inferenceCoreML(model: coreModel, x: 1.0) Xcode has fantastic drag&drop integration of CoreML model into project with Swift wrapper code generation but models can also be loaded, compiled and used dynamically
  • 7. CoreML Personalization / Training func generateData(sampleSize: Int = 100) -> ([Float], [Float]) { let a: Float = 2.0 let b: Float = 1.5 var X = [Float]() var Y = [Float]() for i in 0..<sampleSize { let x: Float = Float(i) / Float(sampleSize) let noise: Float = (Float.random(in: 0..<1) - 0.5) * 0.1 let y: Float = (a * x + b) + noise X.append(x) Y.append(y) } return (X, Y) } func prepareTrainingBatch() -> MLBatchProvider { var featureProviders = [MLFeatureProvider]() let inputName = "dense_input" let outputName = "output_true" let (X, Y) = generateData() for (x,y) in zip(X, Y) { let multiArr = try! MLMultiArray(shape: [1], dataType: .double) multiArr[0] = NSNumber(value: x) let inputValue = MLFeatureValue(multiArray: multiArr) multiArr[0] = NSNumber(value: y) let outputValue = MLFeatureValue(multiArray: multiArr) let dataPointFeatures: [String: MLFeatureValue] = [inputName: inputValue, outputName: outputValue] if let provider = try? MLDictionaryFeatureProvider(dictionary: dataPointFeatures) { featureProviders.append(provider) } } return MLArrayBatchProvider(array: featureProviders) } func train(url: URL) { let configuration = MLModelConfiguration() configuration.computeUnits = .all configuration.parameters = [.epochs : 100] let progressHandler = { (context: MLUpdateContext) in switch context.event { case .trainingBegin: ... case .miniBatchEnd: ... case .epochEnd: ... } } let completionHandler = { (context: MLUpdateContext) in guard context.task.state == .completed else { return } let trainLoss = context.metrics[.lossValue] as! Double let updatedModel = context.model let updatedModelURL = URL(fileURLWithPath: retrainedCoreMLFilePath) try! updatedModel.write(to: updatedModelURL) } let handlers = MLUpdateProgressHandlers( forEvents: [.trainingBegin, .miniBatchEnd, .epochEnd], progressHandler: progressHandler, completionHandler: completionHandler) let updateTask = try! MLUpdateTask(forModelAt: url, trainingData: prepareTrainingBatch(), configuration: configuration, progressHandlers: handlers) updateTask.resume() } Prepare Batch Data Training train(url: compiledModelUrl) // Wait for completition of the asyncronous training task let retrainedModel = try! MLModel(contentsOf: URL(fileURLWithPath: retrainedCoreMLFilePath)) let prediction = inferenceCoreML(model: retrainedModel, x: 1.0)
  • 8. S4TF How Automate Model Export ? • Extend Layer, Sequencial/sequenced(), DSL function builder ??? • What about Training parameters (Cost Functions, Optimizations, …) ?? + Model Optimizations: • Other then Quantization and Pruning • i.e. Microsoft ONNX BERT condensed layers (17x inference acceleration) • i.e. CoreML Custom Layers and/or Custom Activation Functions • https://github.com/JacopoMangiavacchi/S4TF_CoreML_Test • https://medium.com/@JMangia/swift-loves-tensorflow-and-coreml-2a11da25d44