Section 13/171 menit
13. Testing Model ML
13. Testing Model ML
Unit Test untuk Prediksi
swift
import Testing
import CoreML
// MARK: - Model Tests
@Suite("Core ML Model Tests")
struct CoreMLTests {
// Model loaded sekali untuk semua tests (mahal untuk di-load ulang)
let model: MyClassifier
init() throws {
model = try MyClassifier(configuration: MLModelConfiguration())
}
@Test("Prediksi label untuk gambar kucing")
func testCatClassification() throws {
let testImage = loadTestImage(named: "cat_test")!
let pixelBuffer = testImage.pixelBuffer(width: 224, height: 224)!
let result = try model.prediction(image: pixelBuffer)
#expect(result.classLabel == "cat")
#expect(result.classLabelProbs["cat"]! > 0.9)
}
@Test("Confidence threshold untuk ambiguous input")
func testLowConfidenceHandling() throws {
let ambiguousImage = loadTestImage(named: "ambiguous_animal")!
let pixelBuffer = ambiguousImage.pixelBuffer(width: 224, height: 224)!
let result = try model.prediction(image: pixelBuffer)
// Untuk input ambigu, tidak ada class yang > 90% confident
let maxConfidence = result.classLabelProbs.values.max() ?? 0
#expect(maxConfidence < 0.9, "Ambiguous input harus menghasilkan low confidence")
}
@Test("Latency di bawah threshold", .timeLimit(.milliseconds(50)))
func testInferenceLatency() throws {
let image = loadTestImage(named: "benchmark_image")!
let pixelBuffer = image.pixelBuffer(width: 224, height: 224)!
// Test ini gagal jika inferensi > 50ms
_ = try model.prediction(image: pixelBuffer)
}
@Test("Konsistensi prediksi untuk input yang sama")
func testPredictionConsistency() throws {
let image = loadTestImage(named: "consistent_test")!
let pixelBuffer = image.pixelBuffer(width: 224, height: 224)!
let predictions = try (0..<5).map { _ in
try model.prediction(image: pixelBuffer).classLabel
}
// Semua prediksi harus sama untuk input yang sama
#expect(Set(predictions).count == 1, "Prediksi harus deterministic")
}
private func loadTestImage(named name: String) -> UIImage? {
UIImage(named: name, in: Bundle(for: type(of: self) as! AnyClass), compatibleWith: nil)
}
}
Regression Testing untuk Model Update
swift
// Test untuk memastikan model baru tidak regresi dibanding model lama
struct ModelRegressionTests {
func compareModels(
newModelURL: URL,
baselineModelURL: URL,
testInputs: [MLFeatureProvider]
) throws -> RegressionReport {
let newModel = try MLModel(contentsOf: newModelURL)
let baselineModel = try MLModel(contentsOf: baselineModelURL)
var agreements = 0
var disagreements: [(Int, String, String)] = []
for (index, input) in testInputs.enumerated() {
let newOutput = try newModel.prediction(from: input)
let baselineOutput = try baselineModel.prediction(from: input)
let newLabel = newOutput.featureValue(for: "classLabel")?.stringValue ?? ""
let baselineLabel = baselineOutput.featureValue(for: "classLabel")?.stringValue ?? ""
if newLabel == baselineLabel {
agreements += 1
} else {
disagreements.append((index, baselineLabel, newLabel))
}
}
let agreementRate = Double(agreements) / Double(testInputs.count)
return RegressionReport(
agreementRate: agreementRate,
disagreements: disagreements,
passed: agreementRate >= 0.95 // 95% agreement threshold
)
}
struct RegressionReport {
let agreementRate: Double
let disagreements: [(index: Int, baseline: String, new: String)]
let passed: Bool
}
}