I transformed a toy Pytorch regression mannequin to CoreML mlmodel utilizing coremltools and set it to be updatable with mean_squared_error_loss. However when testing the coaching, the context.metrics[.lossValue]
may give unfavourable worth as proven within the screenshot beneath. I used to be questioning if I used a improper option to extract the coaching loss? Does context.metrics[.lossValue]
actually give MSE if I used coremltools operate set_mean_squared_error_loss
to set the loss? Any suggestion is appreciated.
I’m utilizing coremltools==7.0
, xcode==15.0.1
let progressHandlers = MLUpdateProgressHandlers(forEvents: [.trainingBegin, .epochEnd],
progressHandler: { context in
change context.occasion {
case .trainingBegin:
print("Coaching started.")
case .epochEnd:
let loss = context.metrics[.lossValue] as! Double
lossValues.append(loss)
let validationLoss = computeValidationLoss(mannequin: context.mannequin, validationData: validationData)
validationLossValues.append(validationLoss)
print("Epoch (context.metrics[.epochIndex]!) ended. Coaching Loss: (loss), Validation Loss: (validationLoss)")
print("(context.metrics[.lossValue]!)")
default:
break
}
},
completionHandler: { context in
if let error = context.activity.error {
print("Replace activity failed with error: (error)")
} else {
print("Replace activity accomplished")
}
}
)
Right here is my code to transform Pytorch mannequin to updatable CoreML mannequin:
import torch
import torch.optim as optim
import torch.nn as nn
import coremltools as ct
# Outline a easy neural community with two layers
class SimpleRegressionModel(nn.Module):
def __init__(self):
tremendous(SimpleRegressionModel, self).__init__()
self.layer1 = nn.Linear(2, 5) # 2 inputs, 5 outputs
self.layer2 = nn.Linear(5, 1) # 5 inputs, 1 output
def ahead(self, x):
x = torch.relu(self.layer1(x))
x = self.layer2(x)
return x
# Create the mannequin
mannequin = SimpleRegressionModel()
# Create a pattern enter tensor
sample_input = torch.rand(1, 2) # Modify the form based on your mannequin's enter
# Hint the mannequin with a pattern enter
traced_model = torch.jit.hint(mannequin, sample_input)
# Convert the traced mannequin to Core ML format
input_features = [ct.TensorType(shape=(1, 2))]
output_features = ["output"]
mlmodel = ct.convert(
traced_model,
inputs=input_features,
convert_to="neuralnetwork"
)
mlmodel.save("regression.mlmodel")
import coremltools
from coremltools.fashions.neural_network import NeuralNetworkBuilder, SgdParams, AdamParams
from coremltools.fashions import datatypes
# Load the mannequin specification
spec = coremltools.utils.load_spec('regression.mlmodel')
builder = NeuralNetworkBuilder(spec=spec)
builder.inspect_output_features() # Identify: linear_1
# Make layers updatable
builder.make_updatable(['linear_0', 'linear_1'])
# Manually add a imply squared error loss layer
characteristic = ('linear_1', datatypes.Array(1))
builder.set_mean_squared_error_loss(identify="lossLayer", input_feature=characteristic)
# Outline the optimizer (SGD on this instance)
# sgd_params = SgdParams(lr=0.001, batch=16) # Modify studying price and batch measurement as wanted
# builder.set_sgd_optimizer(sgd_params)
# outline the optimizer (Adam on this instance)
adam_params = AdamParams(lr=0.01, beta1=0.9, beta2=0.999, eps=1e-8, batch=16)
builder.set_adam_optimizer(adam_params)
# Set the variety of epochs
builder.set_epochs(100)
# Optionally, set descriptions in your coaching inputs
spec.description.trainingInput[0].shortDescription = 'Enter information'
spec.description.trainingInput[1].shortDescription = 'Goal output information'
# Save the up to date mannequin
updated_model = coremltools.fashions.MLModel(spec)
updated_model.save('updatable_regression30.mlmodel')
Right here is the code I exploit to attempt to replace the saved updatable_regression30.mlmodel
:
import CoreML
import GameKit
func generateSampleData(numSamples: Int, seed: UInt64) -> ([MLMultiArray], [MLMultiArray]) {
var inputArray = [MLMultiArray]()
var outputArray = [MLMultiArray]()
// Create a random quantity generator with a hard and fast seed
let randomSource = GKLinearCongruentialRandomSource(seed: seed)
let randomDistribution = GKRandomDistribution(randomSource: randomSource, lowestValue: 0, highestValue: 1000)
for _ in 0..<numSamples {
do {
let enter = strive MLMultiArray(form: [1, 2], dataType: .float32)
let output = strive MLMultiArray(form: [1], dataType: .float32)
var sumInput: Float = 0
for i in 0..<enter.form[1].intValue {
// Generate random worth utilizing the mounted seed generator
let inputValue = Float(randomDistribution.nextInt()) / 1000.0
enter[[0, i] as [NSNumber]] = NSNumber(worth: inputValue)
sumInput += inputValue
}
output[0] = NSNumber(worth: 10.0 * sumInput + 1.0)
inputArray.append(enter)
outputArray.append(output)
} catch {
print("Error occurred whereas creating MLMultiArrays: (error)")
}
}
return (inputArray, outputArray)
}
func computeValidationLoss(mannequin: MLModel, validationData: ([MLMultiArray], [MLMultiArray])) -> Double {
let (inputData, outputData) = validationData
var totalLoss: Double = 0
for (index, enter) in inputData.enumerated() {
let output = outputData[index]
// Utilizing optionally available binding to securely unwrap the prediction
if let prediction = strive? mannequin.prediction(from: MLDictionaryFeatureProvider(dictionary: ["x": MLFeatureValue(multiArray: input)])),
let predictedOutput = prediction.featureValue(for: "linear_1")?.multiArrayValue {
// Now you'll be able to safely use predictedOutput
let loss = (output[0].doubleValue - predictedOutput[0].doubleValue)
totalLoss += loss * loss // Imply squared error
}
}
return totalLoss / Double(inputData.depend) // Calculating the imply of squared errors
}
func trainModel() {
// Load the updatable mannequin
guard let updatableModelURL = Bundle.principal.url(forResource: "updatable_regression30", withExtension: "mlmodelc") else {
print("Didn't load the updatable mannequin")
return
}
// Generate pattern information
let (inputData, outputData) = generateSampleData(numSamples: 200, seed: 8)
let validationData = generateSampleData(numSamples: 100, seed:18)
// Create an MLArrayBatchProvider from the pattern information
var featureProviders = [MLFeatureProvider]()
for (index, enter) in inputData.enumerated() {
let output = outputData[index]
let dataPointFeatures: [String: MLFeatureValue] = [
"x": MLFeatureValue(multiArray: input),
"linear_1_true": MLFeatureValue(multiArray: output)
]
if let supplier = strive? MLDictionaryFeatureProvider(dictionary: dataPointFeatures) {
featureProviders.append(supplier)
}
}
let batchProvider = MLArrayBatchProvider(array: featureProviders)
// Outline progress handlers
var lossValues: [Double] = []
var validationLossValues: [Double] = []
let progressHandlers = MLUpdateProgressHandlers(forEvents: [.trainingBegin, .epochEnd],
progressHandler: { context in
change context.occasion {
case .trainingBegin:
print("Coaching started.")
case .epochEnd:
let loss = context.metrics[.lossValue] as! Double
lossValues.append(loss)
let validationLoss = computeValidationLoss(mannequin: context.mannequin, validationData: validationData)
validationLossValues.append(validationLoss)
print("Epoch (context.metrics[.epochIndex]!) ended. Coaching Loss: (loss), Validation Loss: (validationLoss)")
print("(context.metrics[.lossValue]!)")
default:
break
}
},
completionHandler: { context in
if let error = context.activity.error {
print("Replace activity failed with error: (error)")
} else {
let updatedModel = context.mannequin
do {
let fileManager = FileManager.default
let documentDirectory = strive fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:true)
let fileURL = documentDirectory.appendingPathComponent("CatDog5.mlmodelc")
strive updatedModel.write(to: fileURL)
print("Mannequin up to date and saved efficiently to (fileURL)")
} catch {
print("Failed to save lots of the up to date mannequin: (error)")
}
}
}
)
// Create an replace activity with progress handlers
let updateTask = strive! MLUpdateTask(forModelAt: updatableModelURL,
trainingData: batchProvider,
configuration: nil,
progressHandlers: progressHandlers)
// Begin the replace activity
updateTask.resume()
}
trainModel()