Section 11/161 menit
11. On-Device Model Personalization
11. On-Device Model Personalization
Core ML mendukung on-device fine-tuning — model bisa di-update dengan data pengguna tanpa data meninggalkan device.
Updatable Model dengan Core ML
swift
import CoreML
class PersonalizedClassifier {
private var modelURL: URL
private var updatableModel: MLUpdateTask?
init() throws {
// Model harus di-mark sebagai "updatable" di coremltools
guard let url = Bundle.main.url(forResource: "UpdatableClassifier", withExtension: "mlmodelc") else {
throw ModelError.notFound
}
// Copy ke Documents karena Bundle tidak writable
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
modelURL = documentsURL.appendingPathComponent("UpdatableClassifier.mlmodelc")
if !FileManager.default.fileExists(atPath: modelURL.path) {
try FileManager.default.copyItem(at: url, to: modelURL)
}
}
// Fine-tune dengan data baru dari pengguna
func train(withSamples samples: [(image: CGImage, label: String)]) async throws {
var trainingData: [String: [MLFeatureValue]] = [
"image": [],
"label": []
]
for sample in samples {
let buffer = try MLFeatureValue(cgImage: sample.image, pixelsWide: 224, pixelsHigh: 224,
pixelFormatType: kCVPixelFormatType_32BGRA, options: nil)
trainingData["image"]!.append(buffer)
trainingData["label"]!.append(MLFeatureValue(string: sample.label))
}
let trainingBatch = try MLArrayBatchProvider(
dictionary: trainingData.mapValues { $0 }
)
let updateConfig = MLUpdateProgressHandlers(
forEvents: [.trainingBegin, .epochEnd, .trainingEnd],
progressHandler: { context in
if context.event == .epochEnd {
print("Epoch \(context.metrics[.epochIndex]!): loss = \(context.metrics[.lossValue]!)")
}
},
completionHandler: { [weak self] context in
if context.task.error == nil {
// Simpan model yang sudah di-update
try? context.model.write(to: self!.modelURL)
print("Model updated successfully")
}
}
)
let updateTask = try MLUpdateTask(
forModelAt: modelURL,
trainingData: trainingBatch,
configuration: nil,
progressHandlers: updateConfig
)
updateTask.resume()
}
}