Section 2/171 menit
2. Masalah yang Dipecahkan
2. Masalah yang Dipecahkan
Bottleneck Konversi Data yang Tidak Terlihat
Cara paling naif menggunakan Core ML adalah langsung feed UIImage ke model. Tapi di balik layar, konversi format data bisa memakan waktu lebih lama dari inferensi itu sendiri.
swift
// ❌ TANPA optimasi: data conversion overhead tersembunyi
func classifyImage(_ image: UIImage) throws -> String {
// UIImage → CVPixelBuffer: konversi memori besar yang terjadi setiap kali
guard let pixelBuffer = image.pixelBuffer(width: 224, height: 224) else {
throw MLError.conversionFailed
}
// Di balik layar: UIImage → CGImage → CVPixelBuffer (3 konversi!)
let result = try model.prediction(input: pixelBuffer)
return result.label
}
// Total waktu: 5ms inferensi + 12ms konversi = 17ms per frame
// Untuk 60fps video: butuh <16ms total → mustahil dengan approach ini
swift
// ✓ DENGAN optimasi: reuse CVPixelBuffer dan hindari redundant conversion
final class OptimizedClassifier {
private let model: VNCoreMLModel
private let requestHandler: VNSequenceRequestHandler // stateful, reuse di video
init() throws {
let mlModel = try MyClassifier(configuration: .init()).model
self.model = try VNCoreMLModel(for: mlModel)
self.requestHandler = VNSequenceRequestHandler()
}
func classify(sampleBuffer: CMSampleBuffer) throws -> String {
// CMSampleBuffer langsung dari camera → tidak perlu konversi ke UIImage
let request = VNCoreMLRequest(model: model)
try requestHandler.perform([request], on: sampleBuffer, orientation: .up)
guard let results = request.results as? [VNClassificationObservation],
let top = results.first else { return "unknown" }
return top.identifier
// Total: ~5ms inferensi, konversi ~0ms (zero-copy dari camera buffer)
}
}
Memory Spike pada Batch Prediction yang Naif
swift
// ❌ TANPA optimasi: load semua data ke memori sebelum predict
func processAllImages(_ images: [UIImage]) throws -> [String] {
// Untuk 1000 gambar 224x224x3 Float32: ~600MB RAM sekaligus!
let inputs = try images.map { img -> MyModelInput in
let buffer = img.pixelBuffer(width: 224, height: 224)!
return MyModelInput(image: buffer)
}
let results = try model.predictions(inputs: inputs)
return results.map { $0.classLabel }
}
// ✓ DENGAN optimasi: streaming batch dengan bounded memory
func processImagesStreaming(_ images: [UIImage]) throws -> [String] {
var results: [String] = []
let batchSize = 16 // tuning berdasarkan available memory
for batch in images.chunks(ofSize: batchSize) {
let inputs = try batch.map { img -> MyModelInput in
MyModelInput(image: img.pixelBuffer(width: 224, height: 224)!)
}
let batchResults = try model.predictions(inputs: inputs)
results.append(contentsOf: batchResults.map { $0.classLabel })
// Setiap batch di-release setelah selesai
}
return results
}
Tidak Bisa Menggunakan Model yang Tidak Didukung Layer-nya
swift
// ❌ TANPA Custom Layers: model dengan operasi custom tidak bisa dijalankan
// Error: "Encountered unknown layer type: Custom (CustomAttention)"
// Model dari PyTorch dengan custom attention mechanism → tidak support native
// ✓ DENGAN MLCustomLayer: implementasikan layer yang hilang di Swift/Metal
class CustomAttentionLayer: NSObject, MLCustomLayer {
// Implementasi di Swift → dijalankan di CPU
// Atau Metal kernel → dijalankan di GPU
}