VNCoreMLRequest — to podklasa VNRequest, która pozwala uruchamiać modele Core ML w potoku Vision, łącząc zalety gotowych detektorów Vision (twarze, tekst, obiekty) z niestandardowymi modelami ML do klasyfikacji i regresji. Według Apple Machine Learning Documentation (2024), VNCoreMLRequest automatycznie przetwarza wstępne przetwarzanie obrazu — skalowanie, przycinanie i konwersję przestrzeni kolorów — zgodnie z wymaganiami modelu Core ML.
Najważniejsze
VNCoreMLRequest — to podklasa VNRequest, dodana w iOS 11 wraz z Vision, która pozwala uruchamiać modele Core ML w kontekście Vision. Przejmuje całe wstępne przetwarzanie obrazu wymagane przez model Core ML: zmianę rozmiaru, przycinanie, normalizację i konwersję przestrzeni kolorów.
Bez VNCoreMLRequest programista musiałby ręcznie konwertować UIImage/CGImage na MultiArray (MLMultiArray) lub PixelBuffer (CVPixelBuffer) o odpowiednim rozmiarze. VNCoreMLRequest automatyzuje ten proces: przekazujesz CGImage przez VNImageRequestHandler, a VNCoreMLRequest sam skaluje go do rozmiaru wejściowego modelu.
VNCoreMLRequest dziedziczy wszystkie możliwości VNRequest: completion handler, regionOfInterest, możliwość wykonywania wielu żądań jednocześnie, obsługę VNImageRequestHandler i VNSequenceRequestHandler.
import Vision
import CoreML
// 1. Załaduj i opakuj model
guard let model = try VNCoreMLModel(
for: MobileNetV2().model)
else { return }
// 2. Utwórz 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. Wykonaj przez handler
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try handler.perform([request])
VNCoreMLRequest obsługuje modele z różnymi typami wejścia: obrazy (Image Feature), MultiArray i Double. Dla obrazów Vision automatycznie konwertuje CGImage na CVPixelBuffer o odpowiednim rozmiarze i przestrzeni kolorów. Dla innych typów danych wejściowych należy używać Core ML bezpośrednio bez Vision.
Według Apple WWDC 2023, VNCoreMLRequest jest używany w 40% wszystkich aplikacji iOS korzystających z Core ML do pracy z obrazami. To najpopularniejszy sposób integracji modeli ML w aplikacjach iOS.
VNCoreMLModel — to opakowanie, które adaptuje model Core ML (MLModel) do użycia w Vision. Konwertuje dane wejściowe i wyjściowe modelu na format zrozumiały dla Vision: obraz → CVPixelBuffer, wynik → VNObservation.
Inicjalizacja VNCoreMLModel sprawdza zgodność modelu z Vision: model musi przyjmować obraz na wejściu (Image Feature) i zwracać klasyfikację (MLMultiArray lub Dictionary). Jeśli model jest niezgodny, inicjalizator zgłasza błąd.
import Vision
import CoreML
// Opcja 1: Z .mlmodel (skompilowany w czasie budowania)
let model1 = try VNCoreMLModel(
for: MyVisionModel().model)
// Opcja 2: Z .mlmodelc (skompilowany na urządzeniu)
let compiledURL = Bundle.main.url(
forResource: "MyVisionModel",
withExtension: "mlmodelc")!
let model2 = try VNCoreMLModel(
for: MLModel(contentsOf: compiledURL))
VNCoreMLModel buforuje model w pamięci po pierwszym załadowaniu. Ponowne załadowanie tego samego modelu zwraca buforowaną instancję, co przyspiesza kolejne żądania. Jeśli jednak model waży więcej niż 100 MB, iOS może go wyładować z pamięci przy braku zasobów — w takim przypadku VNCoreMLModel przeładuje model automatycznie.
Dla modeli wytrenowanych przez Create ML, VNCoreMLModel działa bez dodatkowej konfiguracji. Create ML eksportuje modele z prawidłowymi metadanymi, które Vision rozpoznaje automatycznie — wystarczy przekazać model do VNCoreMLModel(model:).
imageCropAndScaleOption — kluczowa właściwość VNCoreMLRequest, która określa, jak Vision przekształca oryginalny obraz do rozmiaru wejściowego modelu Core ML. Wybór właściwej opcji bezpośrednio wpływa na dokładność klasyfikacji.
.centerCrop — przycina obraz centralnie do kwadratu, następnie skaluje do rozmiaru wejściowego modelu. Odpowiedni dla modeli wytrenowanych na wyśrodkowanych obiektach (większość klasyfikatorów ImageNet). .scaleFill — rozciąga obraz do rozmiaru wejściowego bez zachowania proporcji. Szybkie, ale zniekształca geometrię. .scaleFit — skaluje z zachowaniem proporcji, dodając letterbox (czarne pasy) na krawędziach.
import Vision
let request = VNCoreMLRequest(model: model)
// .centerCrop — dla wyśrodkowanych obiektów (domyślnie)
request.imageCropAndScaleOption = .centerCrop
// .scaleFill — dla jednolitych tekstur (bez zniekształceń)
request.imageCropAndScaleOption = .scaleFill
// .scaleFit — gdy proporcje obiektu mają znaczenie
request.imageCropAndScaleOption = .scaleFit
Zalecenia: dla większości modeli klasyfikacji używaj .centerCrop — zapewnia najlepszy stosunek dokładności do wydajności. Jeśli model został wytrenowany na obrazach z zachowaniem proporcji (np. detekcja anomalii na zdjęciach dokumentów), wybierz .scaleFit z letterbox.
Według Apple Developer Documentation 2024, nieprawidłowy wybór imageCropAndScaleOption może obniżyć dokładność modelu o 15–25%. Na przykład .scaleFill dla twarzy znajdującej się przy krawędzi kadru może przyciąć jej część przy .centerCrop lub zniekształcić proporcje przy .scaleFill.
Główna siła VNCoreMLRequest — możliwość łączenia go z innymi VNRequest w jednym wywołaniu perform(). Pozwala to budować potoki: najpierw znajdź twarze (VNDetectFaceRectanglesRequest), następnie sklasyfikuj każdą twarz przez niestandardowy model Core ML (VNCoreMLRequest).
VNCoreMLRequest obsługuje również regionOfInterest — jeśli ustawisz ten obszar, Vision przycina obraz do określonego prostokąta przed przekazaniem do modelu Core ML. Jest to krytyczne dla potoków: po wykryciu twarzy przekazujesz jej bounding box jako regionOfInterest dla VNCoreMLRequest.
import Vision
// 1. Wykrywanie twarzy
let faceRequest = VNDetectFaceRectanglesRequest()
// 2. Klasyfikacja emocji przez Core ML
guard let emotionModel = try VNCoreMLModel(
for: EmotionClassifier().model)
else { return }
let emotionRequest = VNCoreMLRequest(model: emotionModel)
try handler.perform([faceRequest, emotionRequest])
// 3. Ustaw regionOfInterest dla każdej twarzy
for face in faceRequest.results as? [VNFaceObservation] ?? [] {
emotionRequest.regionOfInterest = face.boundingBox
try handler.perform([emotionRequest])
// Przetwórz wynik klasyfikacji emocji
}
Ograniczenie: regionOfInterest dla VNCoreMLRequest ma sens, gdy model został wytrenowany na obrazach o tym samym rozmiarze i proporcjach. Jeśli model oczekuje ściśle kwadratowego wejścia (224x224), .centerCrop z regionOfInterest da najlepszy wynik.
Według Apple ML Research, potok „detekcja → klasyfikacja” przez regionOfInterest daje wzrost dokładności o 20–30% w porównaniu do klasyfikacji całego obrazu, ponieważ model ML otrzymuje na wejściu tylko odpowiedni obszar bez szumu tła.
| Typ potoku | Żadanie 1 (detekcja) | Żadanie 2 (ML) | Przykład |
|---|---|---|---|
| Twarz → emocja | VNDetectFaceRectanglesRequest | VNCoreMLRequest | Określanie nastroju |
| Obiekt → marka | VNDetectObjectAtPointRequest | VNCoreMLRequest | Rozpoznawanie logo |
| Tekst → język | VNRecognizeTextRequest | VNCoreMLRequest | Klasyfikacja języka tekstu |
| Scena → opis | VNClassifyImageRequest | VNCoreMLRequest | Generowanie tagów |
VNCoreMLRequest zwraca wyniki w postaci VNClassificationObservation (dla modeli klasyfikacyjnych) lub VNCoreMLFeatureValueObservation (dla regresyjnych i innych typów). Typ wyniku zależy od danych wyjściowych modelu Core ML.
VNClassificationObservation zawiera identifier (nazwa klasy) i confidence (pewność). Modele z wyjściem softmax zwracają tablicę takich obserwacji posortowanych malejąco według confidence. VNCoreMLFeatureValueObservation zawiera dowolną wartość MLFeatureValue — może to być MultiArray, Double, String lub Dictionary.
// Dla modeli klasyfikacyjnych
if let classificationResults = request.results
as? [VNClassificationObservation] {
for result in classificationResults
where result.confidence > 0.5 {
print("\\(result.identifier): \\(result.confidence)")
}
}
// Dla modeli regresyjnych (wartości cech)
if let featureResults = request.results
as? [VNCoreMLFeatureValueObservation] {
for result in featureResults {
let value = result.featureValue
print("\\(result.featureName): \\(value)")
}
}
Filtrowanie według confidence: Apple zaleca odrzucanie wyników z confidence < 0.3 dla ogólnych klasyfikatorów i < 0.7 dla krytycznych aplikacji. Dla modeli wytrenowanych na zbalansowanych zbiorach danych, confidence koreluje z prawdopodobieństwem poprawnej odpowiedzi, ale nie gwarantuje jej.
VNCoreMLFeatureValueObservation.featureName odpowiada nazwie warstwy wyjściowej modelu (np. „classLabel” lub „features”). Pozwala to przetwarzać modele z wieloma wyjściami — każde wyjście jest reprezentowane przez osobną obserwację z unikalnym featureName.
VNCoreMLRequest jest zoptymalizowany do pracy na Neural Engine (A12+), GPU i CPU. Vision automatycznie wybiera najlepsze urządzenie do wykonania modelu w zależności od jego typu i rozmiaru. Jednak wydajność można dodatkowo poprawić poprzez odpowiednią konfigurację.
Modele Core ML są ładowane do pamięci przy pierwszym VNCoreMLRequest i pozostają tam do wyładowania aplikacji. Dla modeli o rozmiarze powyżej 200 MB Apple zaleca ładowanie ich na żądanie i zwalnianie przez MLModel.release(). VNCoreMLModel sama zarządza buforowaniem, ale możesz to kontrolować przez autoreleasepool.
Do wsadowego przetwarzania obrazów utwórz jeden VNCoreMLRequest i używaj go ponownie z różnymi VNImageRequestHandler. Nie twórz nowego VNCoreMLRequest dla każdego obrazu — spowolni to przetwarzanie z powodu ponownego ładowania modelu. Ponowne użycie żądania daje wzrost wydajności do 40% przy przetwarzaniu 10+ obrazów.
// Prawidłowo: pojedyncze żądanie dla wszystkich obrazów
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 aktualizowane przy każdym wywołaniu
}
Wybór urządzenia: domyślnie Vision wybiera Neural Engine dla zgodnych modeli na urządzeniach A12+. Jeśli model nie obsługuje Neural Engine, Vision używa GPU lub CPU. Możesz wymusić określone urządzenie przez MLModelConfiguration.computeUnits, ale Apple zaleca pozostawienie automatycznego wyboru.
Według Apple Performance Benchmarks 2024, VNCoreMLRequest na Neural Engine (iPhone 15 Pro) przetwarza klasyfikację MobileNetV2 w 3–5 ms, na GPU — 8–12 ms, na CPU — 20–30 ms. Różnica staje się krytyczna dla aplikacji czasu rzeczywistego przetwarzających 30+ klatek na sekundę.
Często zadawane pytania
Tak, Core ML można używać bezpośrednio przez MLModel.prediction(), bez Vision. Jednak VNCoreMLRequest automatyzuje wstępne przetwarzanie obrazów (skalowanie, przycinanie, konwersję do CVPixelBuffer). Jeśli model przyjmuje nie obraz, a MultiArray lub Double — używaj Core ML bezpośrednio. VNCoreMLRequest tylko dla modeli z Image Feature na wejściu.
Utwórz nowy VNCoreMLModel ze zaktualizowanego MLModel i nowy VNCoreMLRequest. Stare żądanie będzie nadal używać starej wersji modelu. Do zdalnej aktualizacji modeli używaj MLModel.compileModel(at:) do kompilacji modelu na urządzeniu z pliku .mlmodelc pobranego z serwera.
VNCoreMLRequest obsługuje tylko modele z jednym wejściem Image Feature. Jeśli model ma wiele wejść (np. obraz + tekst), używaj Core ML bezpośrednio przez MLModel. Vision nie może przekazać dodatkowych parametrów poza obrazem.
Ograniczenie — 8192 x 8192 pikseli dla CGImage przekazywanego do VNImageRequestHandler. Jednak modele Core ML zwykle oczekują wejścia 224x224, 299x299 lub 512x512. Vision automatycznie skaluje duży obraz do rozmiaru wejściowego. Jeśli oryginalny obraz jest zbyt duży, wstępnie zmniejsz go przez CGImage, aby oszczędzić pamięć.
Tak, ustaw preferBackgroundProcessing = true na VNRequest. Pozwoli to Vision opóżnić wykonanie żądania, jeśli system jest w trybie zasobożernym (np. ładowanie treści). Koniecznie używaj DispatchQueue.global(qos: .background) do wywołania handler.perform().
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również