Section 8/171 menit
8. Custom Layers: MLCustomLayer dan MLCustomModel
8. Custom Layers: MLCustomLayer dan MLCustomModel
Ketika model mengandung operasi yang tidak didukung Core ML secara native (Flash Attention, RoPE embedding, custom activation), implementasikan sebagai custom layer.
MLCustomLayer untuk Operasi CPU
swift
// Custom layer: Sigmoid Linear Unit (SiLU) activation
// SiLU(x) = x * sigmoid(x) — tidak ada di Core ML native sebelum iOS 17
@objc(SiLULayer)
class SiLULayer: NSObject, MLCustomLayer {
// Dipanggil saat model di-load
required init(parameters: [String: Any]) throws {
super.init()
// Parsing parameter dari model spec jika ada
}
// Deskripsi output shape berdasarkan input
func setInputShapes(_ inputShapes: [[NSNumber]]) throws -> [[NSNumber]] {
// SiLU tidak mengubah shape
return inputShapes
}
// Evaluasi forward pass (CPU)
func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws {
let input = inputs[0]
let output = outputs[0]
let count = input.count
let srcPtr = input.dataPointer.assumingMemoryBound(to: Float.self)
let dstPtr = output.dataPointer.assumingMemoryBound(to: Float.self)
// SiLU: x * (1 / (1 + exp(-x)))
// Gunakan Accelerate untuk SIMD-optimized computation
var negated = [Float](repeating: 0, count: count)
vDSP_vneg(srcPtr, 1, &negated, 1, vDSP_Length(count))
var sigmoid = [Float](repeating: 0, count: count)
var countInt = Int32(count)
vvexpf(&sigmoid, negated, &countInt) // exp(-x)
var ones = [Float](repeating: 1, count: count)
vDSP_vadd(sigmoid, 1, ones, 1, &sigmoid, 1, vDSP_Length(count)) // 1 + exp(-x)
vDSP_vdiv(sigmoid, 1, ones, 1, &sigmoid, 1, vDSP_Length(count)) // 1/(1+exp(-x))
// x * sigmoid(x)
vDSP_vmul(srcPtr, 1, sigmoid, 1, dstPtr, 1, vDSP_Length(count))
}
// Optional: Metal GPU implementation untuk performa lebih baik
func encodeToCommandBuffer(
_ commandBuffer: MTLCommandBuffer,
inputs: [MTLTexture],
outputs: [MTLTexture]
) throws {
// Implementasi Metal shader jika diperlukan
// Jika tidak diimplementasikan, fallback ke CPU evaluate()
}
}
Registrasi Custom Layer di Model
Setelah implementasi, daftarkan ke Core ML saat model di-load:
swift
// Registrasi custom layer sebelum load model
let config = MLModelConfiguration()
config.computeUnits = .cpuAndGPU
// Custom layer harus di-register sebelum model load
// Ini dilakukan melalui coremltools saat convert model:
// coremltools.convert(..., custom_conversion_functions={"SiLU": convert_silu})
// Di Swift: model yang sudah di-package dengan custom layer spec
// akan otomatis menggunakan implementasi yang di-register via @objc class name
let model = try MLModel(contentsOf: compiledModelURL, configuration: config)
MLCustomModel untuk Kontrol Penuh
MLCustomModel memberikan kontrol total atas seluruh inferensi — berguna untuk model yang sama sekali tidak bisa dikonversi:
swift
// Custom model: implementasi seluruh inferensi secara manual
@objc(TFLiteWrapperModel)
class TFLiteWrapperModel: NSObject, MLCustomModel {
private var interpreter: AnyObject? // TensorFlow Lite interpreter
required init(modelDescription: MLModelDescription, parameters: [String: Any]) throws {
super.init()
// Inisialisasi TFLite interpreter atau custom runtime
// Berguna untuk mengintegrasikan model dari framework lain
}
func prediction(from input: MLFeatureProvider, options: MLPredictionOptions) throws -> MLFeatureProvider {
// Jalankan inferensi menggunakan custom runtime
// Konversi output ke MLFeatureProvider
return SimpleFeatureProvider(features: [:])
}
}
class SimpleFeatureProvider: MLFeatureProvider {
let features: [String: MLFeatureValue]
var featureNames: Set<String> { Set(features.keys) }
init(features: [String: MLFeatureValue]) { self.features = features }
func featureValue(for featureName: String) -> MLFeatureValue? { features[featureName] }
}