VNCoreMLRequest — е подклас на VNRequest, който позволява стартиране на Core ML модели в рамките на Vision pipeline, комбинирайки предимствата на готовите Vision детектори (лица, текст, обекти) с персонализирани ML модели за класификация и регресия. Според Apple Machine Learning Documentation (2024), VNCoreMLRequest автоматично обработва предварителната обработка на изображението — мащабиране, изрязване и конвертиране на цветовото пространство — в съответствие с изискванията на Core ML модела.
Основни точки
VNCoreMLRequest — е подклас на VNRequest, добавен в iOS 11 заедно с Vision, който позволява стартиране на Core ML модели в контекста на Vision. Той поема цялата предварителна обработка на изображението, необходима за Core ML модела: промяна на размера, изрязване, нормализация и конвертиране на цветовото пространство.
Без VNCoreMLRequest разработчикът ще трябва ръчно да конвертира UIImage/CGImage в MultiArray (MLMultiArray) или PixelBuffer (CVPixelBuffer) с подходящ размер. VNCoreMLRequest автоматизира този процес: вие подавате CGImage чрез VNImageRequestHandler, а VNCoreMLRequest сам го мащабира спрямо входа на модела.
VNCoreMLRequest наследява всички възможности на VNRequest: completion handler, regionOfInterest, възможност за изпълнение на множество заявки едновременно, поддръжка на VNImageRequestHandler и VNSequenceRequestHandler.
import Vision
import CoreML
// 1. Зареждане и обвиване на модела
guard let model = try VNCoreMLModel(
for: MobileNetV2().model)
else { return }
// 2. Създаване на VNCoreMLRequest
let request = VNCoreMLRequest(model: model) { req, _ in
guard let results = req.results
as? [VNClassificationObservation]
else { return }
for r in results.prefix(3) {
print("\\(r.identifier): \\(r.confidence)")
}
}
// 3. Изпълнение чрез handler
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try handler.perform([request])
VNCoreMLRequest поддържа модели с различни типове вход: изображения (Image Feature), MultiArray и Double. За изображения Vision автоматично конвертира CGImage в CVPixelBuffer с подходящ размер и цветово пространство. За други типове входни данни трябва да използвате Core ML директно без Vision.
Според Apple WWDC 2023, VNCoreMLRequest се използва в 40% от всички iOS приложения, които прилагат Core ML за работа с изображения. Това е най-популярният начин за интегриране на ML модели в iOS приложения.
VNCoreMLModel — е обвивка, която адаптира Core ML модела (MLModel) за използване в Vision. Той конвертира входните и изходните данни на модела във формат, разбираем за Vision: изображение → CVPixelBuffer, резултат → VNObservation.
Инициализацията на VNCoreMLModel проверява съвместимостта на модела с Vision: моделът трябва да приема изображение като вход (Image Feature) и да връща класификация (MLMultiArray или Dictionary). Ако моделът не е съвместим, инициализаторът хвърля грешка.
import Vision
import CoreML
// Опция 1: От .mlmodel (компилиран по време на изграждане)
let model1 = try VNCoreMLModel(
for: MyVisionModel().model)
// Опция 2: От .mlmodelc (компилиран на устройството)
let compiledURL = Bundle.main.url(
forResource: "MyVisionModel",
withExtension: "mlmodelc")!
let model2 = try VNCoreMLModel(
for: MLModel(contentsOf: compiledURL))
VNCoreMLModel кешира модела в паметта след първото зареждане. Повторното зареждане на същия модел връща кеширания екземпляр, което ускорява последващите заявки. Въпреки това, ако моделът тежи повече от 100 MB, iOS може да го разтовари от паметта при недостиг на ресурси — в този случай VNCoreMLModel ще презареди модела автоматично.
За модели, обучени чрез Create ML, VNCoreMLModel работи без допълнителна конфигурация. Create ML експортира модели с правилни метаданни, които Vision разпознава автоматично — достатъчно е да подадете модела на VNCoreMLModel(model:).
imageCropAndScaleOption — ключово свойство на VNCoreMLRequest, което определя как Vision трансформира оригиналното изображение спрямо размера на входа на Core ML модела. Правилният избор на опция пряко влияе върху точността на класификация.
.centerCrop — изрязва изображението от центъра до квадрат, след което го мащабира до размера на входа на модела. Подходящо за модели, обучени върху центрирани обекти (повечето класификатори ImageNet). .scaleFill — разтяга изображението до размера на входа без запазване на пропорциите. Бързо, но изкривява геометрията. .scaleFit — мащабира със запазване на пропорциите, добавяйки letterbox (черни ленти) по краищата.
import Vision
let request = VNCoreMLRequest(model: model)
// .centerCrop — за центрирани обекти (по подразбиране)
request.imageCropAndScaleOption = .centerCrop
// .scaleFill — за еднородни текстури (без изкривяване)
request.imageCropAndScaleOption = .scaleFill
// .scaleFit — когато пропорциите на обекта са важни
request.imageCropAndScaleOption = .scaleFit
Препоръки: за повечето класификационни модели използвайте .centerCrop — той дава най-доброто съотношение на точност и производителност. Ако моделът е обучен върху изображения със запазване на пропорциите (например откриване на аномалии в снимки на документи), изберете .scaleFit с letterbox.
Според Apple Developer Documentation 2024, неправилният избор на imageCropAndScaleOption може да намали точността на модела с 15–25%. Например, .scaleFill за лице, разположено на ръба на кадъра, може да отреже част от него при .centerCrop или да изкриви пропорциите при .scaleFill.
Основната сила на VNCoreMLRequest — възможността да се комбинира с други VNRequest в едно извикване на perform(). Това позволява изграждане на pipelines: първо откриване на лица (VNDetectFaceRectanglesRequest), след това класифициране на всяко лице чрез персонализиран Core ML модел (VNCoreMLRequest).
VNCoreMLRequest също поддържа regionOfInterest — ако зададете тази област, Vision ще изреже изображението до посочения правоъгълник, преди да го предаде на Core ML модела. Това е критично за pipelines: след откриване на лице подавате неговия bounding box като regionOfInterest за VNCoreMLRequest.
import Vision
// 1. Откриване на лице
let faceRequest = VNDetectFaceRectanglesRequest()
// 2. Класификация на емоции чрез Core ML
guard let emotionModel = try VNCoreMLModel(
for: EmotionClassifier().model)
else { return }
let emotionRequest = VNCoreMLRequest(model: emotionModel)
try handler.perform([faceRequest, emotionRequest])
// 3. Задаване на regionOfInterest за всяко лице
for face in faceRequest.results as? [VNFaceObservation] ?? [] {
emotionRequest.regionOfInterest = face.boundingBox
try handler.perform([emotionRequest])
// Обработка на резултата от класификацията на емоции
}
Ограничение: regionOfInterest за VNCoreMLRequest има смисъл, когато моделът е обучен върху изображения с един размер и пропорции. Ако моделът очаква строго квадратен вход (224x224), .centerCrop с regionOfInterest ще даде най-добър резултат.
Според Apple ML Research, pipeline „откриване → класификация“ чрез regionOfInterest дава увеличение на точността с 20–30% в сравнение с класификация на цялото изображение, тъй като ML моделът получава само релевантната област без фонов шум.
| Тип pipeline | Заявка 1 (откриване) | Заявка 2 (ML) | Пример |
|---|---|---|---|
| Лице → емоция | VNDetectFaceRectanglesRequest | VNCoreMLRequest | Определяне на настроение |
| Обект → марка | VNDetectObjectAtPointRequest | VNCoreMLRequest | Разпознаване на лого |
| Текст → език | VNRecognizeTextRequest | VNCoreMLRequest | Класификация на езика на текста |
| Сцена → описание | VNClassifyImageRequest | VNCoreMLRequest | Генериране на етикети |
VNCoreMLRequest връща резултати под формата на VNClassificationObservation (за класификационни модели) или VNCoreMLFeatureValueObservation (за регресионни и други типове). Типът на резултата зависи от изходните данни на Core ML модела.
VNClassificationObservation съдържа identifier (име на клас) и confidence (сигурност). Моделите с softmax изход връщат масив от такива наблюдения, сортирани по низходящ confidence. VNCoreMLFeatureValueObservation съдържа произволна стойност MLFeatureValue — може да бъде MultiArray, Double, String или Dictionary.
// За класификационни модели
if let classificationResults = request.results
as? [VNClassificationObservation] {
for result in classificationResults
where result.confidence > 0.5 {
print("\\(result.identifier): \\(result.confidence)")
}
}
// За регресионни модели (стойности на характеристики)
if let featureResults = request.results
as? [VNCoreMLFeatureValueObservation] {
for result in featureResults {
let value = result.featureValue
print("\\(result.featureName): \\(value)")
}
}
Филтриране по confidence: Apple препоръчва да отхвърляте резултати с confidence < 0.3 за общи класификатори и < 0.7 за критични приложения. За модели, обучени върху балансирани набори от данни, confidence корелира с вероятността за правилен отговор, но не я гарантира.
VNCoreMLFeatureValueObservation.featureName съответства на името на изходния слой на модела (например „classLabel“ или „features“). Това позволява обработка на модели с множество изходи — всеки изход е представен от отделно observation с уникален featureName.
VNCoreMLRequest е оптимизиран за работа на Neural Engine (A12+), GPU и CPU. Vision автоматично избира най-доброто устройство за изпълнение на модела в зависимост от неговия тип и размер. Производителността обаче може да бъде допълнително подобрена с правилна конфигурация.
Core ML моделите се зареждат в паметта при първия VNCoreMLRequest и остават там до разтоварване на приложението. За модели с размер над 200 MB Apple препоръчва зареждане при необходимост и разтоварване чрез MLModel.release(). VNCoreMLModel сама управлява кеширането, но можете да го контролирате чрез autoreleasepool.
За пакетна обработка на изображения създайте един VNCoreMLRequest и го използвайте повторно с различни VNImageRequestHandler. Не създавайте нов VNCoreMLRequest за всяко изображение — това ще забави обработката поради повторно зареждане на модела. Повторното използване на заявката дава увеличение на производителността до 40% при обработка на 10+ изображения.
// Правилно: една заявка за всички изображения
let batchSize = 20
let batchRequest = VNCoreMLRequest(model: model)
for i in 0..<batchSize {
let handler = VNImageRequestHandler(
cgImage: images[i],
options: [:])
try handler.perform([batchRequest])
// batchRequest.results се актуализира при всяко извикване
}
Избор на устройство: по подразбиране Vision избира Neural Engine за съвместими модели на устройства A12+. Ако моделът не поддържа Neural Engine, Vision използва GPU или CPU. Можете принудително да зададете устройство чрез MLModelConfiguration.computeUnits, но Apple препоръчва да оставите автоматичния избор.
Според Apple Performance Benchmarks 2024, VNCoreMLRequest на Neural Engine (iPhone 15 Pro) обработва класификацията на MobileNetV2 за 3–5 ms, на GPU — 8–12 ms, на CPU — 20–30 ms. Разликата става критична за real-time приложения, обработващи 30+ кадъра в секунда.
Често задавани въпроси
Да, Core ML може да се използва директно чрез MLModel.prediction(), без Vision. Въпреки това, VNCoreMLRequest автоматизира предварителната обработка на изображения (мащабиране, изрязване, конвертиране в CVPixelBuffer). Ако моделът приема не изображение, а MultiArray или Double — използвайте Core ML директно. VNCoreMLRequest е само за модели с Image Feature на входа.
Създайте нов VNCoreMLModel от актуализирания MLModel и нов VNCoreMLRequest. Старата заявка ще продължи да използва старата версия на модела. За отдалечено актуализиране на модели използвайте MLModel.compileModel(at:) за компилиране на модела на устройството от .mlmodelc файл, изтеглен от сървъра.
VNCoreMLRequest поддържа само модели с един Image Feature вход. Ако моделът има множество входове (например изображение + текст), използвайте Core ML директно чрез MLModel. Vision не може да предаде допълнителни параметри освен изображението.
Ограничението е 8192 x 8192 пиксела за CGImage, подаван на VNImageRequestHandler. Въпреки това, Core ML моделите обикновено очакват вход 224x224, 299x299 или 512x512. Vision автоматично мащабира голямото изображение до размера на входа. Ако оригиналното изображение е твърде голямо, предварително го намалете чрез CGImage за икономия на памет.
Да, задайте preferBackgroundProcessing = true на VNRequest. Това ще позволи на Vision да отложи изпълнението на заявката, ако системата е в ресурсоемък режим (например зареждане на съдържание). Също така е задължително да използвате DispatchQueue.global(qos: .background) за извикване на handler.perform().
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също