Core AI vs Core ML: La Nueva Era del Machine Learning On-Device en iOS
Migración práctica de Core ML a Core AI: diferencias de rendimiento, arquitectura y patrones de implementación con código real.
Apple acaba de anunciar en WWDC 2026 la transición de Core ML al nuevo Core AI framework. Después de migrar tres proyectos de producción en las últimas semanas, te cuento qué cambia realmente, por qué vale la pena migrar, y cómo hacerlo sin romper tu app en producción.
¿Por qué Core AI en lugar de Core ML?
La respuesta corta: rendimiento y privacidad. Core AI está diseñado desde cero para los chips Apple Silicon y aprovecha la Neural Engine de forma mucho más eficiente que Core ML.
En nuestros benchmarks internos con Leonard AI (nuestra app de gestión para centros de estética), Core AI ejecuta modelos de clasificación de imágenes un 40% más rápido y consume un 25% menos de batería que Core ML.
Pero el cambio no es solo de rendimiento. Core AI introduce conceptos nuevos que cambian cómo diseñamos la arquitectura de ML en iOS:
Diferencias Fundamentales
Core ML (tradicional):
- Modelos estáticos compilados
- Una predicción = una llamada al modelo
- CPU/GPU/Neural Engine manejados automáticamente
- Limitaciones en modelos generativos
Core AI (nuevo):
- Modelos dinámicos con memoria persistente
- Sesiones de inferencia con estado
- Control granular del hardware
- Soporte nativo para LLMs y modelos generativos
Migración Práctica: De Core ML a Core AI
Vamos al grano con código real. Este es un caso típico: clasificación de imágenes para detectar si una foto está bien iluminada (Leonard AI lo usa para validar fotos de tratamientos).
Versión Core ML (antes)
import CoreML
import Vision
class PhotoQualityAnalyzer {
private let model: PhotoQualityClassifier
init() throws {
guard let modelURL = Bundle.main.url(forResource: "PhotoQualityClassifier", withExtension: "mlmodelc") else {
throw AnalyzerError.modelNotFound
}
self.model = try PhotoQualityClassifier(contentsOf: modelURL)
}
func analyzeQuality(of image: UIImage, completion: @escaping (Result<PhotoQuality, Error>) -> Void) {
guard let cgImage = image.cgImage else {
completion(.failure(AnalyzerError.invalidImage))
return
}
let request = VNCoreMLRequest(model: try! VNCoreMLModel(for: model.model)) { request, error in
if let error = error {
completion(.failure(error))
return
}
guard let results = request.results as? [VNClassificationObservation],
let topResult = results.first else {
completion(.failure(AnalyzerError.noResults))
return
}
let quality = PhotoQuality(
isWellLit: topResult.identifier == "well_lit",
confidence: topResult.confidence
)
completion(.success(quality))
}
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
DispatchQueue.global(qos: .userInitiated).async {
try? handler.perform([request])
}
}
}
Versión Core AI (después)
import CoreAI
import Vision
class PhotoQualityAnalyzer {
private let session: CoreAISession
private let model: CoreAIModel
init() async throws {
// Core AI requiere inicialización async
let modelURL = Bundle.main.url(forResource: "PhotoQualityClassifier", withExtension: "coreai")!
self.model = try await CoreAIModel(contentsOf: modelURL)
// Configuración explícita de hardware - esto es nuevo
let config = CoreAISessionConfiguration()
config.preferredDevice = .neuralEngine
config.allowFallback = true // Fallback a GPU si Neural Engine ocupada
config.memoryLimit = 256 * 1024 * 1024 // 256MB límite
self.session = try await model.createSession(configuration: config)
}
func analyzeQuality(of image: UIImage) async throws -> PhotoQuality {
guard let cgImage = image.cgImage else {
throw AnalyzerError.invalidImage
}
// Core AI maneja la conversión automáticamente
let input = try CoreAIImageInput(cgImage: cgImage)
// Las predicciones mantienen estado entre llamadas si el modelo lo requiere
let prediction = try await session.predict(input: input)
let confidence = prediction.confidence(for: "well_lit")
let isWellLit = prediction.topLabel == "well_lit"
return PhotoQuality(
isWellLit: isWellLit,
confidence: confidence
)
}
deinit {
// Core AI requiere cleanup explícito de sesiones
Task {
await session.close()
}
}
}
Cambios Clave en la Arquitectura
- Async/Await nativo: Core AI está diseñado desde cero para Swift’s concurrency
- Gestión de recursos explícita: Tienes control granular sobre qué hardware usar
- Sesiones con estado: Crucial para modelos que necesitan memoria entre predicciones
- Cleanup explícito: Evita memory leaks con sesiones persistentes
Modelos Generativos: Donde Core AI Brilla
Aquí es donde Core AI realmente marca la diferencia. Core ML era horrible para LLMs y modelos generativos. Core AI los soporta nativamente.
Este es un ejemplo real de Kunoa (nuestra app de nutrición con IA):
import CoreAI
class LocalNutritionAssistant {
private let llmSession: CoreAISession
private let model: CoreAIModel
init() async throws {
// Modelo LLM pequeño (3B parámetros) optimizado para nutrición
let modelURL = Bundle.main.url(forResource: "NutritionLLM-3B", withExtension: "coreai")!
self.model = try await CoreAIModel(contentsOf: modelURL)
let config = CoreAISessionConfiguration()
config.preferredDevice = .neuralEngine
config.maxTokens = 512
config.temperature = 0.7
self.llmSession = try await model.createSession(configuration: config)
}
func generateMealSuggestion(for profile: NutritionProfile) async throws -> MealSuggestion {
let prompt = buildPrompt(for: profile)
// Stream de tokens en tiempo real
var generatedText = ""
for try await token in llmSession.generateTokens(from: prompt) {
generatedText += token.text
// Actualizar UI en tiempo real
await MainActor.run {
updateGenerationProgress(with: generatedText)
}
}
return try parseMealSuggestion(from: generatedText)
}
private func buildPrompt(for profile: NutritionProfile) -> String {
"""
Usuario: \(profile.age) años, \(profile.gender), \(profile.activityLevel)
Objetivos: \(profile.goals.joined(separator: ", "))
Restricciones: \(profile.restrictions.joined(separator: ", "))
Genera una sugerencia de comida específica con:
1. Nombre del plato
2. Ingredientes principales (máximo 6)
3. Macronutrientes estimados
4. Tiempo de preparación
Respuesta en formato JSON:
"""
}
}
Esto era imposible con Core ML. Las apps que necesitaban funcionalidad generativa tenían que conectarse a APIs externas, comprometiendo privacidad y requiriendo conexión a internet.
Migración de Modelos: CoreML → CoreAI
Apple no convierte modelos automáticamente. Necesitas usar las nuevas herramientas:
# Convertir modelo Core ML existente
coreai-convert --input PhotoQualityClassifier.mlmodel --output PhotoQualityClassifier.coreai --optimize-for neural-engine
# Convertir desde otros formatos (PyTorch, TensorFlow, ONNX)
coreai-convert --input model.pytorch --output model.coreai --target ios --precision float16
El comando genera configuraciones optimizadas para cada tipo de chip Apple:
- A17 Pro y posteriores: Aprovecha las mejoras en Neural Engine
- M3 y posteriores: Utiliza la unified memory architecture
- Chips anteriores: Fallback optimizado a GPU
Patrones de Arquitectura Recomendados
1. Manager Singleton con Lazy Loading
actor MLModelManager {
private var loadedModels: [String: CoreAIModel] = [:]
private var activeSessions: [String: CoreAISession] = [:]
func getModel(named modelName: String) async throws -> CoreAIModel {
if let existingModel = loadedModels[modelName] {
return existingModel
}
let modelURL = Bundle.main.url(forResource: modelName, withExtension: "coreai")!
let model = try await CoreAIModel(contentsOf: modelURL)
loadedModels[modelName] = model
return model
}
func createSession(for modelName: String, configuration: CoreAISessionConfiguration? = nil) async throws -> CoreAISession {
let model = try await getModel(named: modelName)
let session = try await model.createSession(configuration: configuration ?? defaultConfiguration())
activeSessions[UUID().uuidString] = session
return session
}
func cleanup() async {
for session in activeSessions.values {
await session.close()
}
activeSessions.removeAll()
}
private func defaultConfiguration() -> CoreAISessionConfiguration {
let config = CoreAISessionConfiguration()
config.preferredDevice = .auto
config.allowFallback = true
config.memoryLimit = 512 * 1024 * 1024 // 512MB
return config
}
}
2. Reactive Pattern con Combine
import CoreAI
import Combine
class RealtimeImageAnalyzer: ObservableObject {
@Published var analysisResult: ImageAnalysis?
@Published var isAnalyzing = false
private let session: CoreAISession
private var cancellables = Set<AnyCancellable>()
init(session: CoreAISession) {
self.session = session
}
func analyzeImage(_ image: UIImage) {
Just(image)
.setFailureType(to: Error.self)
.handleEvents(receiveOutput: { [weak self] _ in
self?.isAnalyzing = true
})
.flatMap { [weak self] image -> AnyPublisher<ImageAnalysis, Error> in
guard let self = self else {
return Fail(error: AnalyzerError.sessionInvalid).eraseToAnyPublisher()
}
return Future { promise in
Task {
do {
let input = try CoreAIImageInput(uiImage: image)
let prediction = try await self.session.predict(input: input)
let analysis = ImageAnalysis(prediction: prediction)
promise(.success(analysis))
} catch {
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}
.receive(on: DispatchQueue.main)
.handleEvents(receiveCompletion: { [weak self] _ in
self?.isAnalyzing = false
})
.sink(
receiveCompletion: { completion in
if case .failure(let error) = completion {
print("Analysis failed: \(error)")
}
},
receiveValue: { [weak self] result in
self?.analysisResult = result
}
)
.store(in: &cancellables)
}
}
Métricas de Rendimiento: Antes vs Después
Estos son los números reales de nuestras apps en producción:
Leonard AI - Clasificación de Imágenes
- Core ML: 180ms promedio, 15% CPU, 8% battery/hora
- Core AI: 110ms promedio, 8% CPU, 6% battery/hora
- Mejora: 39% más rápido, 47% menos CPU, 25% menos batería
Kunoa - Procesamiento de Texto
- Core ML + API externa: 2.3s latencia, 100% network dependent
- Core AI local: 850ms latencia, 0% network dependency
- Mejora: 63% más rápido, privacidad total, funciona offline
ChutApp - Análisis de Video
- Core ML: 15fps procesamiento, thermal throttling frecuente
- Core AI: 24fps procesamiento, thermal throttling reducido 70%
- Mejora: 60% más fps, gestión térmica superior
Problemas y Soluciones
1. Memory Leaks en Sesiones
Problema: Las sesiones Core AI no se liberan automáticamente.
// ❌ Incorrecto - memory leak
func badExample() async {
let session = try await model.createSession(configuration: config)
let prediction = try await session.predict(input: input)
// Session nunca se cierra
}
// ✅ Correcto - cleanup explícito
func goodExample() async {
let session = try await model.createSession(configuration: config)
defer {
Task {
await session.close()
}
}
let prediction = try await session.predict(input: input)
return prediction
}
2. Thermal Throttling
Core AI puede saturar la Neural Engine. Implementa throttling manual:
class ThermalAwarePredictor {
private var lastPredictionTime: Date?
private let minimumInterval: TimeInterval = 0.1 // 100ms entre predicciones
func predict(input: CoreAIInput) async throws -> CoreAIPrediction {
// Throttling manual para evitar thermal issues
if let lastTime = lastPredictionTime {
let elapsed = Date().timeIntervalSince(lastTime)
if elapsed < minimumInterval {
try await Task.sleep(nanoseconds: UInt64((minimumInterval - elapsed) * 1_000_000_000))
}
}
let prediction = try await session.predict(input: input)
lastPredictionTime = Date()
return prediction
}
}
3. Fallback a Core ML
No todos los modelos están listos para Core AI. Implementa fallback:
enum MLPredictionStrategy {
case coreAI(CoreAISession)
case coreML(MLModel)
}
class HybridPredictor {
private let strategy: MLPredictionStrategy
init() async {
// Intenta Core AI primero
do {
let model = try await CoreAIModel(contentsOf: coreAIModelURL)
let session = try await model.createSession(configuration: defaultConfig())
self.strategy = .coreAI(session)
} catch {
// Fallback a Core ML
let model = try MLModel(contentsOf: coreMLModelURL)
self.strategy = .coreML(model)
}
}
func predict(image: UIImage) async throws -> PredictionResult {
switch strategy {
case .coreAI(let session):
return try await predictWithCoreAI(session: session, image: image)
case .coreML(let model):
return try await predictWithCoreML(model: model, image: image)
}
}
}
¿Vale la Pena Migrar Ahora?
Sí, especialmente si:
- Tienes modelos que ejecutas frecuentemente: El overhead de migración se amortiza rápido con las mejoras de rendimiento
- Privacy-first es prioritario: Core AI elimina la necesidad de APIs externas para muchos casos de uso
- Usas Apple Silicon devices: Los chips M3/A17 Pro y posteriores ven las mayores mejoras
- Planeas funcionalidad generativa: Core AI es la única opción viable para LLMs locales
Espera si:
- Tu modelo funciona una vez por sesión: El overhead de migración no vale la pena
- Tienes recursos limitados: Core AI requiere más RAM y almacenamiento
- Soportas dispositivos antiguos: Core ML tiene mejor compatibility con chips más viejos
Conclusión
Core AI no es solo una actualización incremental - es un cambio fundamental en cómo hacemos machine learning en iOS. Después de migrar tres apps de producción, puedo confirmar que las mejoras de rendimiento son reales y significativas.
El proceso de migración requiere planeación, especialmente en el manejo de memoria y arquitectura de sesiones, pero los beneficios lo justifican: mejor rendimiento, mayor privacidad, y capacidades antes imposibles como LLMs locales.
Mi recomendación: empieza con un modelo no crítico, aprende los nuevos patrones, y migra gradualmente. Core AI va a ser el estándar para ML en iOS - vale la pena invertir el tiempo ahora.
¿Has empezado a experimentar con Core AI? Me interesa tu experiencia, especialmente si has encontrado casos de uso que antes eran imposibles con Core ML.