Base de Conocimiento ANE
EN ES

Capítulos · 12

Comparación práctica: pipelines de exportación WhisperKit/Core ML vs Core AI

Parte de la ANE Knowledge Base. Ambos pipelines se ejecutaron realmente en la misma máquina (Apple M4 Pro, macOS 26.5.1) con el mismo modelo (openai/whisper-tiny), el 2026-07-12. Experimentos: experiments/whisperkit-conversion (doc 11) y experiments/coreai-whisper-export. Evidencia registrada bajo cada results/. Este es el complemento empírico de la comparación conceptual en doc 10.

1. Las dos ejecuciones, lado a lado#

Dimensión WhisperKit → Core ML (era 2024) coreai-torch → Core AI (era WWDC26)
Entorno Python 3.12, torch 2.5.0, transformers 4.53, coremltools 9.0, whisperkit 0.4.2 + argmaxtools 0.1.23 Python 3.12, torch 2.11.0, transformers 4.57.3, coreai-core 1.0.0b2, coreai-torch 0.4.1
Origen del modelo Whisper reescrito a mano (Argmax): atención Conv2d/BC1S, KV cache como I/O, 3 componentes AutoModelForSpeechSeq2Seq de serie de Hugging Face, sin modificar
API de exportación torch.jit.trace por forma fija torch.export con Dim("dec_seq_len", 1, 448) — longitud de decoder dinámica en un solo grafo
Código del lado del autor whisperkittools + argmaxtools (miles de líneas: atención personalizada, hooks, arnés de pruebas) script de ~100 líneas (adaptado de la receta de ~180 líneas de Apple)
Artefactos TextDecoder.mlmodelc (57 MB) + AudioEncoder.mlmodelc (16 MB) + MelSpectrogram.mlmodelc (372 KB) ≈ 73 MB (fp16) Un único whisper-tiny_float16.aimodel = 72 MB (fp32: 144 MB); contenido: metadata.json + main.mlirb (binario MLIR) + main.hash
Elección de arquitectura 3 grafos, decoder de un solo token con KV cache — óptimo para inferencia 1 grafo, sin KV cache, forward completo por llamada — simple como la receta
Tiempo de reloj de conversión ~48 s de suites de pruebas (más trace/convert dentro) 67 s fp32 (6 s para fp16 con pesos en caché)
Verificación integrada suite de PSNR (42.5–69.4 dB), decodificación de paridad de 447 tokens, planes de cómputo por op Manual pero trivial: paridad de logits max|diff| = 1.2e-4, coincidencia de argmax
Despacho a ANE medido 99–100% en decoder/encoder; MelSpectrogram todo en CPU (JSONs del plan de cómputo) No medible en este SO — véase §3
Latencia medida Encoder 6.15 ms (ANE, 3.59× vs CPU); paso de decoder 1.62 ms ~1,400 ms enc+dec completo en el runtime de CPU incluido en el paquete (fp16 ≈ fp32)
¿Se ejecuta en macOS 26.5? Completamente (conversión + ejecución en ANE) Exportación ✅; inferencia solo vía runtime de CPU incluido; los delegados ANE/GPU necesitan macOS 27

2. Qué significan los números (no compares 1.4 s con 8 ms ingenuamente)#

Las dos cifras de latencia miden cosas distintas:

  • El pipeline de Core ML se ejecutó en la ANE con una arquitectura con KV cache: encoder una vez (6.15 ms) + 1.62 ms por token.
  • La ejecución de Core AI usó el runtime de CPU de respaldo incluido (sin delegados en macOS 26.5) con un grafo sin caché que recalcula el encoder completo + decoder de 4 tokens en cada llamada. En la misma máquina, solo el encoder de Core ML en CPU_ONLY tardó 22 ms — así que ~1.4 s para un forward completo sin caché en fp32/fp16 sobre un runtime de CPU no optimizado no sorprende, y no dice nada sobre el rendimiento de Core AI-en-ANE.

Las conclusiones honestas que esta comparación respalda:

  1. El esfuerzo de autoría se redujo ~50× (un script vs un toolkit) para la misma familia de modelos — porque el trabajo de reescritura se trasladó a las tablas de decomp de Apple y a las primitivas de plataforma (doc 09 §3).
  2. La arquitectura de KV cache sigue siendo decisión del autor. La receta simple de Whisper de Apple la omite; sus recetas de LLM (coreai-models/models/qwen3 + primitives/ios/cache.py) sí la implementan. Un Whisper óptimo para inferencia sobre Core AI necesitaría la misma división encoder/decoder + estados de caché que WhisperKit fue pionero en usar — los conceptos de doc 06 se transfieren intactos.
  3. Los tamaños de artefacto convergen (72 vs 73 MB en fp16) — dominan los pesos; los formatos no.
  4. La verificación es más fácil pero más superficial por defecto en la ruta de la receta: whisperkittools incluye umbrales de PSNR y planes de cómputo; con Core AI obtienes una comprobación de paridad limpia en 10 líneas, pero debes solicitarla (o usar el Debugger/save_intermediates en macOS 27).

2.5 Por qué "HF de serie" y "reescrito a mano" son equivalentes: adónde se trasladó la cirugía#

Ambos pipelines calculan la misma función con los mismos pesos; difieren en quién realiza la reexpresión compatible con ANE y cuándo.

Era de Core ML — el convertidor era una fotocopiadora fiel. torch.jit.trace registra las ops exactamente como las ejecuta el código fuente (HF Whisper: nn.Linear sobre (B,S,C), divisiones de cabezas con view/transpose, un softmax fusionado), y coremltools tradujo ese grafo op por op. Nada lo rearquitecturó, así que el compilador de ANE recibió formas hostiles (último eje pequeño → relleno de 64 bytes, tensores de atención que fallan en caché, transposes que inducen copias) y recurría a fallback o corría lento — la propia línea base de Apple en 2022: 10× más lento, 14× más memoria. La única palanca disponible era reescribir el código fuente del modelo para que el trace ya tuviera la forma correcta: eso es whisperkittools/argmaxtools. Los hooks de linear_to_conv2d_map son la prueba de que esto es pura reexpresión: el mismo peso Linear (out, in), expandido a (out, in, 1, 1), computa de forma idéntica que una conv 1×1 sobre BC1S.

Core AI — la cirugía se convirtió en un paso del compilador. Tres cambios:

  1. torch.export produce un grafo funcional tipado y con formas simbólicas — una IR de compilador real, no una fotografía.
  2. run_decompositions(get_decomp_table()) usa una tabla diseñada para preservar ops compuestas: el F.scaled_dot_product_attention de HF sobrevive como un único nodo SDPA (coreai_torch/composite_ops/_sdpa.py), igual que RMSNorm/RoPE — en lugar de fragmentarse en matmuls que el compilador no podría reconocer.
  3. En la especialización, el compilador hace pattern-matching de esos nodos y emite el lowering por hardware: kernels fusionados en GPU; en ANE, ese mismo esquema per-head / channels-first / alineado al último eje que antes se escribía a mano. La forma de referencia de ese lowering es visible en el propio coreai-models/primitives/ios/sdpa.py de Apple — el mismo algoritmo que ml-ane-transformers (2022) y argmaxtools (2023), ahora propiedad de la plataforma. La asignación de layout y la planificación de memoria (la vieja disciplina de "minimizar copias") ocurren dentro de .optimize() + especialización sobre el MLIR del .aimodel (main.mlirb).

La equivalencia siempre fue mecánica — Linear(W) sobre (B,S,C)Conv2d1×1(W[:,:,None,None]) sobre (B,C,1,S); un softmax grande ≡ N softmaxes per-head — y este experimento la verifica de tres maneras: tamaños de artefacto fp16 idénticos (72 vs 73 MB — los mismos pesos), paridad de logits 1.2e-4 (la misma función), y la atención escrita a mano en 2022 reapareciendo literalmente en las primitivas de plataforma de 2026 (el mismo lowering).

La frontera: el compilador absorbió la optimización a nivel de op, no la optimización arquitectónica. Los KV caches, las divisiones encoder/decoder y el prefill de contexto siguen siendo decisiones del autor (§2, conclusión 2) — las matemáticas se fueron al compilador; la arquitectura de inferencia se quedó contigo.

3. La cuestión de la unidad de cómputo, respondida empíricamente#

¿Permite Core AI fijar unidades de cómputo como el .cpuAndNeuralEngine de Core ML? Sí — con una API aún más rica — pero la ubicación se decide en tiempo de especialización, no en tiempo de carga:

from coreai.runtime import AIModel, ComputeUnitKind, SpecializationOptions

ComputeUnitKind            # .cpu | .gpu | .neural_engine | .available_kinds()
opts = SpecializationOptions.from_preferred_compute_unit_kind(ComputeUnitKind.neural_engine)
# also: SpecializationOptions.allowed_compute_unit_kinds  <- the true CPU_AND_NE analogue (allowed *set*)
#       SpecializationOptions.cpu_only(), .default, .with_debug
model = await AIModel.load("model.aimodel", specialization_options=opts)   # baked at specialization
Core ML Core AI
Mecanismo MLModelConfiguration.computeUnits en carga SpecializationOptions en especialización (AIModel.load / AIModel.specialize de Swift)
Semántica Conjunto permitido (.all, .cpuOnly, .cpuAndGPU, .cpuAndNeuralEngine) Tanto unidad preferida (from_preferred_compute_unit_kind) como conjunto permitido (allowed_compute_unit_kinds)
Introspección de dispositivos MLModel.availableComputeDevices ComputeUnitKind.available_kinds() (devolvió 3 kinds en el M4 Pro)
Verificado en este Mac ✅ usado a lo largo de doc 11 API presente; delegados no disponibles: SpecializationOptions.is_supported() == False, y USE_OS_COREAI=1"Core AI Framework is not available for this version of macOS"

La sonda (evidencia: experiments/coreai-whisper-export/results/specialization-probe.txt) también reveló la arquitectura del runtime: coreai-core incluye un runtime portátil dentro del paquete (CPU; se ejecuta en cualquier lugar — así funcionó nuestra inferencia en macOS 26.5) mientras que los delegados de hardware (ANE/GPU) viven en el framework Core AI del SO (macOS 27+ / iOS 27+, habilitado desde Python con USE_OS_COREAI=1). Core ML no tiene tal división — es un framework del SO, razón por la cual se ejecuta en todas partes pero no puede instalarse con pip.

4. Resumen de restricciones de plataforma para esta máquina (macOS 26.5 + Xcode beta < 27)#

Capacidad ¿Disponible?
Core ML: conversión, compilación, ejecución en ANE, planes de cómputo ✅ todo ello
Core AI: cadena de herramientas torch.export.aimodel
Core AI: inferencia con runtime incluido (CPU) + comprobaciones de paridad
Core AI: especialización de delegados ANE/GPU ❌ necesita macOS 27
Core AI: compilación AOT con xcrun coreai-build ❌ no en esta beta de Xcode

Experimento de seguimiento cuando macOS 27 llegue a este Mac: volver a ejecutar run_aimodel.py con USE_OS_COREAI=1 y preferred_compute_unit_kind=neural_engine, y comparar el .aimodel fp16 con el encoder de Core ML de 6.15 ms de doc 11 — la última celda sin medir de esta comparación.

5. Conclusión#

Mismo modelo, misma máquina, dos generaciones de herramientas: Core ML + WhisperKit exigían experiencia pero entregaron una ejecución en ANE del 99–100% medida hoy; Core AI entregó una ruta de autoría 50× más simple y un runtime portátil, con su historia de hardware supeditada a la actualización del SO. El conocimiento de optimización no desapareció — se trasladó: de tu código (docs 02–07) a las primitivas y el compilador de Apple, con las decisiones de KV cache/arquitectura todavía firmemente en tus manos.

Generado desde el markdown de la base de conocimiento — cada afirmación traza a una fuente citada.