Base de Conocimiento ANE
EN ES

Capítulos · 11

Práctica: reproduciendo el pipeline de conversión de WhisperKit

Parte de la Base de Conocimiento de ANE. Este documento registra una ejecución real de extremo a extremo del pipeline estudiado en doc 06, llevada a cabo el 2026-07-12. Scripts, entorno fijado y evidencia capturada: experiments/whisperkit-conversion/.

1. Configuración#

Hardware Apple M4 Pro (macOS 26.5.1)
Toolchain Python 3.12.8 vía uv venv; coremlcompiler de Xcode beta
Paquete whisperkit 0.4.2 instalado desde nuestro submódulo fijado (references/whisperkittools, commit 84f77a8)
Dependencias clave (resueltas) argmaxtools 0.1.23 (idéntico a nuestro snapshot vendorizado), coremltools 9.0, torch 2.5.0, transformers 4.53.0
Modelo openai/whisper-tiny (39M params — el ejercitador de pipeline completo más pequeño)
uv venv --python 3.12 .venv                                    # torch 2.5.0 needs Python <= 3.12
VIRTUAL_ENV=$PWD/.venv uv pip install ../../references/whisperkittools
export HF_HOME="$PWD/hf-cache"                                 # self-contained HF cache
whisperkit-generate-model --model-version openai/whisper-tiny --output-dir ./output

Notas de la ejecución:

  • Una instalación no editable mantiene el submódulo intacto (la editable dejaría caer un egg-info dentro de él).
  • La CLI funciona porque setup.py usa find_packages(), que también incluye tests/ y scripts/whisperkit-generate-model es literalmente un ejecutor de suites de unittest (el "verificación como CI" de doc 06 §8 hecho tangible).
  • La primera instalación de dependencias es pesada (~1 GB: torch, scipy, wandb vía argmaxtools); un aviso cosmético scikit-learn 1.9 not supported de coremltools es inofensivo aquí.

2. Lo que el pipeline hizo realmente#

De results/conversion-log.txt (198 líneas, trace completo confirmado):

  1. TextDecoder — instanció el WhisperTextDecoder de Argmax (SDPA = Cat, según doc 06 §3), cargó los pesos de HF a través de los hooks linear_to_conv2d, ejecutó una decodificación de paridad autorregresiva completa de 447 tokens contra Hugging Face, hizo el trace (888 ops de PyTorch → MIL), convirtió, cargó en CPU_AND_NE, probó con PSNR, compiló con coremlcompiler, y extrajo el compute plan.
  2. AudioEncoder — el mismo flujo con SDPA = SplitHeadsQ.
  3. MelSpectrogram — el módulo de DSP torch.stft, convertido y con compute plan.

Tiempo total de pruebas: ~48 s (32.7 s de la suite del decodificador + 15.6 s de la suite del codificador), todas las pruebas OK. Los intermedios .mlpackage se eliminan tras la compilación — los artefactos desplegables son los bundles .mlmodelc.

3. Resultados medidos#

Corrección (PSNR, umbral 35 dB)#

Comprobación Resultado
torch2torch decoder (reimpl de Argmax vs HF, decodificación de 447 tokens) PSNR 136, precisión de argmax(logits) 100%
torch2torch encoder PSNR 143
torch2coreml TextDecoder PSNR 42.5
torch2coreml AudioEncoder PSNR 68.9
torch2coreml MelSpectrogram PSNR 69.4

La brecha entre torch2torch (~140) y torch2coreml (~42–69) es el coste de la conversión a FP16 — los 42.5 dB del decodificador son los más bajos porque un tensor completo de logits con softmax sobre un vocabulario de 51k amplifica el ruido de FP16, pero aun así supera cómodamente el listón de 35 dB.

Despacho a la ANE (de MLComputePlan, JSONs por op en results/)#

Componente Ops Soportadas por ANE Despachadas a ANE Primera carga (especialización) Tamaño
TextDecoder 205 203 (99.0%) 203 (99.0%) 1.07 s 57 MB
AudioEncoder 883 883 (100%) 883 (100%) 3.64 s 16 MB
MelSpectrogram 25 22 (88%) 0 (0%) — todo en CPU 0.39 s 372 KB

El resultado de MelSpectrogram es la lección de manual, ahora medida: sus dos ops más pesadas (la STFT expresada como convoluciones agrupadas, 21.3% del coste cada una) son solo de CPU ('supported': ['CPU']), así que Core ML mantiene el grafo entero de 25 ops en la CPU en lugar de rebotar datos entre unidades de cómputo — la misma economía que las 4 ops de embedding de DistilBERT en la CPU (doc 03 §5), a escala de grafo completo.

Nótense las 883 ops del codificador para un modelo de 4 capas — eso es el chunking del Principio 2 (SplitHeadsQ: explosión de ops por cabeza × por chunk) visible en el grafo, canjeado por residencia en caché.

Latencia: ANE vs CPU (nuestro benchmark; whisperkittools incluye pruebas de velocidad deshabilitadas)#

Los tests/test_*.py codifican a mano TEST_SKIP_SPEED_TESTS = True, así que reprodujimos la medición con bench_ane_vs_cpu.py (mediana de 50 predicciones sobre los modelos compilados, results/benchmark.txt):

Componente CPU_AND_NE CPU_ONLY Aceleración
AudioEncoder (secuencia de 1500 tokens) 6.15 ms 22.04 ms 3.59×
TextDecoder (1 paso de token con KV cache) 1.62 ms 2.47 ms 1.52×

Ambas predicciones de la KB confirmadas en hardware real:

  • El trabajo de secuencia larga, intensivo en cómputo (codificador) es donde la ANE brilla → 3.6×.
  • Un único paso de decodificación de token es diminuto y está limitado por ancho de banda (Principio 4) → solo 1.5×, que es exactamente por qué generate_model.py relaja su aserción de aceleración a 0.3× (doc 06 §8) y por qué la compresión (doc 07) importa para los decodificadores. A 1.62 ms/token ≈ un límite superior de 600 tokens/s, la decodificación de whisper-tiny está muy lejos de estar limitada por la ANE.

4. Escollos de reproducción (lo que los docs no te cuentan)#

  1. Python ≤ 3.12 — el pin torch==2.5.0 no tiene wheels para 3.13; uv venv --python 3.12 lo resuelve limpiamente.
  2. Instala en modo no editable desde el submódulo para mantenerlo limpio; los console scripts siguen obteniendo todo lo que necesitan (find_packages()).
  3. Las pruebas de velocidad están desactivadas por defectoTEST_SKIP_SPEED_TESTS = True en ambos archivos de prueba; PSNR + compute plan es lo que obtienes del pipeline estándar. Trae tu propio banco de latencia (el nuestro tiene ~40 líneas con ct.models.CompiledMLModel).
  4. Los dtypes de entrada del decodificador importan: input_ids/cache_length son escalares int32 por batch; las máscaras son float16 aditivas (convención -1e4, doc 02 §P3).
  5. Las descargas pesadas dominan el tiempo de reloj (~1 GB de pip + ~150 MB de pesos de HF); la conversión en sí lleva menos de un minuto para tiny.
  6. coremltools 9.0 manejó todo lo que necesitaba el código de la era de doc 06 (la API MLComputePlan usada por argmaxtools requiere ≥ 8.1).

5. Veredicto#

El estudio de doc 06 reproducido de extremo a extremo, sin modificar, dos años después de su publicación: 99–100% de despacho a la ANE en los troncos transformer, PSNR cómodamente por encima del umbral, y comportamiento de latencia exactamente como predicen los cuatro principios. La propia estructura del pipeline (mixins de unittest → activos guardados solo al pasar → JSON de compute plan como artefacto) sigue siendo un modelo de cómo desplegar conversiones para la ANE con evidencia adjunta.

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