Section 11/201 menit
11. On-Device Training & Personalisasi
11. On-Device Training & Personalisasi
MLUpdateTask untuk Personalisasi Model
swift
// Core ML: on-device fine-tuning dengan MLUpdateTask (iOS 13+)
import CoreML
final class PersonalizedClassifier {
private var modelURL: URL
private var updatedModelURL: URL
init(baseModelURL: URL, userModelURL: URL) {
self.modelURL = baseModelURL
self.updatedModelURL = userModelURL
}
// Train ulang model dengan data baru dari user
func updateModel(with newSamples: [(image: UIImage, label: String)]) async throws {
var batchProvider: [MLFeatureProvider] = []
for (image, label) in newSamples {
guard let cgImage = image.cgImage else { continue }
// Resize gambar ke ukuran yang diharapkan model (misal 224x224)
let resizedImage = resizeCGImage(cgImage, to: CGSize(width: 224, height: 224))
guard let pixelBuffer = resizedImage?.toPixelBuffer() else { continue }
let imageFeature = try MLFeatureValue(pixelBuffer: pixelBuffer)
let labelFeature = MLFeatureValue(string: label)
let provider = try MLDictionaryFeatureProvider(dictionary: [
"image": imageFeature,
"label": labelFeature
])
batchProvider.append(provider)
}
let trainingData = MLArrayBatchProvider(array: batchProvider)
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// Output ke file terpisah — jangan overwrite model original
let updateTask = try MLUpdateTask(
forModelAt: modelURL,
trainingData: trainingData,
configuration: config,
progressHandlers: MLUpdateProgressHandlers(
forEvents: [.trainingBegin, .epochEnd],
progressHandler: { context in
if case .epochEnd = context.event {
print("Epoch \(context.metrics[.epochIndex] ?? 0): loss = \(context.metrics[.lossValue] ?? 0)")
}
},
completionHandler: { [weak self] context in
if context.task.error == nil {
do {
try context.model.write(to: self!.updatedModelURL)
print("Model updated successfully")
} catch {
print("Failed to save updated model: \(error)")
}
}
}
)
)
updateTask.resume()
}
private func resizeCGImage(_ image: CGImage, to size: CGSize) -> CGImage? {
let context = CGContext(
data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: image.bitsPerComponent,
bytesPerRow: 0,
space: image.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
bitmapInfo: image.bitmapInfo.rawValue
)
context?.draw(image, in: CGRect(origin: .zero, size: size))
return context?.makeImage()
}
}