CoreML для Swift: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

CoreML для Swift: полное руководство с примерами

1. Что это и зачем нужна

CoreML — это фреймворк машинного обучения, разработанный компанией Apple для интеграции моделей машинного обучения непосредственно в приложения под iOS, macOS, watchOS и tvOS. В отличие от облачных решений, CoreML выполняет все вычисления на устройстве пользователя, что обеспечивает мгновенную обработку данных без задержек на передачу по сети и полную конфиденциальность пользовательских данных.

Зачем нужна CoreML? Во-первых, она позволяет разработчикам внедрять сложные функции ИИ (распознавание изображений, обработку естественного языка, анализ звука) без необходимости разбираться в тонкостях нейронных сетей. Во-вторых, работа на устройстве означает, что приложение может функционировать в офлайн-режиме, что критически важно для многих сценариев использования. В-третьих, Apple оптимизировала CoreML для работы с Neural Engine, GPU и CPU, что обеспечивает максимальную производительность на всех устройствах Apple.

CoreML поддерживает модели, созданные в популярных фреймворках (TensorFlow, PyTorch, scikit-learn), которые можно конвертировать в формат .mlmodel с помощью инструмента coremltools. Фреймворк также тесно интегрируется с Vision (для анализа изображений), Natural Language (для работы с текстом) и SoundAnalysis (для аудио).

2. Установка

CoreML является встроенным фреймворком Apple и не требует установки через менеджеры пакетов. Для его использования достаточно импортировать его в проект. Однако для конвертации моделей из других форматов вам понадобится Python-пакет coremltools.

Установка coremltools (Python):

pip install coremltools

Подключение CoreML в проекте Xcode:

import CoreML

Для работы с изображениями и видео дополнительно импортируйте Vision:

import Vision

3. Быстрый старт — минимальный рабочий пример

Создадим простое приложение, которое классифицирует изображение с помощью предобученной модели MobileNetV2. Сначала скачайте модель MobileNetV2.mlmodel из репозитория Apple или конвертируйте её с помощью coremltools.

Минимальный пример классификации изображения:

import CoreML
import Vision
import UIKit

// 1. Загружаем модель
guard let model = try? VNCoreMLModel(for: MobileNetV2().model) else {
    fatalError("Не удалось загрузить модель")
}

// 2. Создаем запрос
let request = VNCoreMLRequest(model: model) { request, error in
    guard let results = request.results as? [VNClassificationObservation],
          let topResult = results.first else {
        print("Не удалось классифицировать изображение")
        return
    }
    print("Распознано: \(topResult.identifier) с вероятностью \(topResult.confidence)")
}

// 3. Загружаем изображение
guard let image = UIImage(named: "cat.jpg"),
      let ciImage = CIImage(image: image) else {
    fatalError("Не удалось загрузить изображение")
}

// 4. Выполняем запрос
let handler = VNImageRequestHandler(ciImage: ciImage)
try? handler.perform([request])

4. Основные методы/функции/классы

4.1. MLModel

Сигнатура: class MLModel

Что делает: Основной класс, представляющий загруженную модель машинного обучения. Позволяет выполнять предсказания и получать информацию о модели.

Пример:

// Загрузка модели из файла
guard let modelURL = Bundle.main.url(forResource: "MyModel", withExtension: "mlmodelc"),
      let model = try? MLModel(contentsOf: modelURL) else {
    fatalError("Не удалось загрузить модель")
}

// Получение метаданных модели
let description = model.modelDescription
print("Входные данные: \(description.inputDescriptionsByName)")
print("Выходные данные: \(description.outputDescriptionsByName)")

4.2. MLFeatureProvider

Сигнатура: protocol MLFeatureProvider

Что делает: Протокол, который определяет интерфейс для предоставления входных данных модели. Обычно используется через конкретные реализации, такие как MLDictionaryFeatureProvider.

Пример:

// Создание входных данных для модели
let inputFeatures: [String: MLFeatureValue] = [
    "input": MLFeatureValue(double: 3.5),
    "other_input": MLFeatureValue(string: "test")
]

guard let provider = try? MLDictionaryFeatureProvider(dictionary: inputFeatures) else {
    fatalError("Не удалось создать provider")
}

// Использование с моделью
let output = try? model.prediction(from: provider)

4.3. MLMultiArray

Сигнатура: class MLMultiArray

Что делает: Представляет многомерный массив данных (тензор), который используется для передачи больших объемов числовых данных в модель и из нее.

Пример:

// Создание многомерного массива 3x3
let shape = [3, 3] as [NSNumber]
guard let array = try? MLMultiArray(shape: shape, dataType: .double) else {
    fatalError("Не удалось создать MLMultiArray")
}

// Заполнение данными
for i in 0..<3 {
    for j in 0..<3 {
        let index = [i, j] as [NSNumber]
        array[index] = NSNumber(value: Double(i * 3 + j))
    }
}

// Чтение данных
let value = array[[1, 2] as [NSNumber]].doubleValue
print("Значение [1,2]: \(value)")

4.4. VNCoreMLModel

Сигнатура: class VNCoreMLModel

Что делает: Обертка для модели CoreML, которая позволяет использовать её с фреймворком Vision для обработки изображений и видео.

Пример:

// Создание Vision-модели из CoreML модели
guard let visionModel = try? VNCoreMLModel(for: MyImageClassifier().model) else {
    fatalError("Не удалось создать VNCoreMLModel")
}

// Настройка запроса
let request = VNCoreMLRequest(model: visionModel) { request, error in
    // Обработка результатов
}
request.imageCropAndScaleOption = .centerCrop

4.5. MLModelConfiguration

Сигнатура: class MLModelConfiguration

Что делает: Позволяет настроить параметры выполнения модели, такие как использование конкретного вычислительного устройства (CPU, GPU, Neural Engine) и политику вычислений.

Пример:

// Создание конфигурации
let config = MLModelConfiguration()
config.computeUnits = .all // Использовать все доступные вычислительные блоки
config.allowLowPrecisionAccumulationOnGPU = true

// Загрузка модели с конфигурацией
guard let model = try? MyModel(configuration: config) else {
    fatalError("Не удалось загрузить модель с конфигурацией")
}

4.6. MLPredictionOptions

Сигнатура: class MLPredictionOptions

Что делает: Позволяет задать опции для выполнения предсказания, например, использовать ли кэширование результатов.

Пример:

// Создание опций предсказания
let options = MLPredictionOptions()
options.outputBackings = .default

// Выполнение предсказания с опциями
let output = try? model.prediction(from: inputProvider, options: options)

4.7. MLFeatureValue

Сигнатура: class MLFeatureValue

Что делает: Представляет одно значение признака (число, строка, изображение, многомерный массив и т.д.), которое может быть передано в модель.

Пример:

// Создание различных типов значений
let doubleValue = MLFeatureValue(double: 42.0)
let stringValue = MLFeatureValue(string: "hello")
let int64Value = MLFeatureValue(int64: 100)

// Создание значения из изображения
guard let image = UIImage(named: "photo"),
      let pixelBuffer = image.pixelBuffer() else {
    fatalError("Не удалось создать pixel buffer")
}
let imageValue = MLFeatureValue(pixelBuffer: pixelBuffer)

4.8. MLModelDescription

Сигнатура: class MLModelDescription

Что делает: Предоставляет метаданные о модели: имена и типы входных/выходных данных, информацию об авторе, версию и т.д.

Пример:

// Получение описания модели
let description = model.modelDescription

// Информация о входных данных
for (name, featureDescription) in description.inputDescriptionsByName {
    print("Вход: \(name), тип: \(featureDescription.type), многозначный: \(featureDescription.isMultiArray)")
}

// Информация о выходных данных
for (name, featureDescription) in description.outputDescriptionsByName {
    print("Выход: \(name), тип: \(featureDescription.type)")
}

// Метаданные
if let metadata = description.metadata {
    print("Автор: \(metadata.author ?? "неизвестен")")
    print("Описание: \(metadata.shortDescription ?? "нет описания")")
    print("Версия: \(metadata.versionString ?? "не указана")")
}

4.9. MLBatchProvider

Сигнатура: protocol MLBatchProvider

Что делает: Позволяет выполнять пакетные предсказания для нескольких входных данных одновременно, что значительно ускоряет обработку.

Пример:

// Создание пакета входных данных
let inputs: [MLFeatureProvider] = [input1, input2, input3]
let batch = MLArrayBatchProvider(array: inputs)

// Выполнение пакетного предсказания
guard let batchOutput = try? model.predictions(fromBatch: batch) else {
    fatalError("Ошибка пакетного предсказания")
}

// Обработка результатов
for i in 0..<batchOutput.count {
    let output = batchOutput.features(at: i)
    // Обработка каждого выхода
}

4.10. MLUpdateTask

Сигнатура: class MLUpdateTask

Что делает: Позволяет выполнять дообучение (fine-tuning) модели на устройстве с использованием новых данных. Доступно только для моделей, поддерживающих обновление.

Пример:

// Создание контекста для обновления
let updateContext = MLUpdateContext()
// Настройка параметров обучения

// Создание задачи обновления
let updateTask = try? MLUpdateTask(forModelAt: modelURL, 
                                    trainingData: trainingBatch,
                                    configuration: config,
                                    completionHandler: { context in
    // Сохранение обновленной модели
    try? context.model.write(to: updatedModelURL)
})

// Запуск обучения
updateTask?.resume()

5. Реальный практический пример (100-200 строк кода)

Создадим приложение для iOS, которое анализирует фотографии из библиотеки пользователя, определяет объекты на них и сохраняет результаты. Пример включает работу с CoreML, Vision и PhotoKit.

import UIKit
import CoreML
import Vision
import Photos

class PhotoAnalyzer {
    
    private let model: VNCoreMLModel
    private let request: VNCoreMLRequest
    
    init() {
        // Загружаем модель YOLOv3 для обнаружения объектов
        guard let mlModel = try? YOLOv3(configuration: MLModelConfiguration()).model,
              let visionModel = try? VNCoreMLModel(for: mlModel) else {
            fatalError("Не удалось загрузить модель YOLOv3")
        }
        
        self.model = visionModel
        
        // Создаем запрос с обработчиком результатов
        self.request = VNCoreMLRequest(model: visionModel) { request, error in
            guard let results = request.results as? [VNRecognizedObjectObservation] else {
                print("Ошибка анализа: \(error?.localizedDescription ?? "неизвестная ошибка")")
                return
            }
            
            // Обрабатываем результаты
            for observation in results {
                let labels = observation.labels
                for label in labels where label.confidence > 0.5 {
                    print("Обнаружен: \(label.identifier) с вероятностью \(label.confidence)")
                }
            }
        }
        
        // Настройка параметров запроса
        request.imageCropAndScaleOption = .scaleFill
        request.usesCPUOnly = false
    }
    
    // Анализ одного изображения
    func analyzeImage(_ image: UIImage, completion: @escaping ([String: Float]) -> Void) {
        guard let ciImage = CIImage(image: image) else {
            completion([:])
            return
        }
        
        let handler = VNImageRequestHandler(ciImage: ciImage, orientation: .up)
        
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                try handler.perform([self.request])
                
                guard let results = self.request.results as? [VNRecognizedObjectObservation] else {
                    DispatchQueue.main.async { completion([:]) }
                    return
                }
                
                var detectedObjects: [String: Float] = [:]
                for observation in results {
                    if let topLabel = observation.labels.first {
                        let currentConfidence = detectedObjects[topLabel.identifier] ?? 0
                        if topLabel.confidence > currentConfidence {
                            detectedObjects[topLabel.identifier] = topLabel.confidence
                        }
                    }
                }
                
                DispatchQueue.main.async {
                    completion(detectedObjects)
                }
                
            } catch {
                print("Ошибка выполнения запроса: \(error)")
                DispatchQueue.main.async { completion([:]) }
            }
        }
    }
    
    // Пакетный анализ изображений из библиотеки
    func analyzePhotoLibrary(limit: Int = 20) {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchOptions.fetchLimit = limit
        
        let fetchResult = PHAsset.fetchAssets(with                    

Рекомендаций