Hay una parte del mundo que no vemos, pero que lo explica casi todo: el sonido.
El sonido captura presencia, contexto, fricción, emoción, movimiento, entorno y cambio. Una máquina puede no ver una puerta que se abre, pero puede oírla. Puede no ver el cansancio en una llamada, pero puede encontrarlo en la señal de voz. Puede no ver una anomalía mecánica en una instalación, pero puede detectarla como patrón acústico antes de que se convierta en avería. El audio tiene una densidad de información enorme, pero para muchos equipos sigue siendo un territorio difícil de convertir en modelos útiles.
Hoy en Valendra damos un paso que nos ilusiona especialmente: compartimos ASTW.
ASTW significa Audio Shapes The World. Es una librería pequeña de clasificación de audio que convierte una colección de archivos y etiquetas en un artefacto de modelo portable. La idea de uso es deliberadamente simple: pasas audios y etiquetas, llamas a fit, guardas el resultado con save, y obtienes una carpeta autocontenida con model.safetensors, config.json, preprocessor_config.json, labels.json, metrics.json y un README.md listo para subir a Hugging Face.
El repositorio está publicado en GitHub: valendra-tech/valendra-labs-astw.
Por qué construir otra librería de audio
La clasificación de audio suele quedar atrapada entre dos extremos. En un extremo están los modelos grandes y preentrenados, que son potentes, pero arrastran tamaño, dependencia externa, coste operativo y una superficie de decisión difícil de inspeccionar. En el otro extremo están los scripts de investigación, que pueden ser útiles para un paper o un experimento local, pero no siempre producen un artefacto fácil de guardar, cargar, versionar y compartir.
ASTW nace entre esos dos mundos.
La intuición inicial fue muy concreta: partir de enfoques como Whisper y preguntarnos qué pasaría si, en lugar de quedarnos ahí, construyéramos una ruta directa para clasificación. Whisper popularizó una forma práctica de trabajar con audio mediante representaciones log-mel y modelos secuenciales. Pero si el problema no es transcribir, sino clasificar, muchas veces no necesitas cargar una arquitectura pensada para reconocimiento de voz completo. Necesitas convertir señal acústica en una representación estable y entrenar una cabeza de clasificación que resuelva una tarea clara.
Esa pregunta nos llevó a construir ASTW desde cero, sin pesos de Whisper y sin backbones de audio externos. No porque lo preentrenado no tenga valor, sino porque a veces el valor está precisamente en reducir dependencias, tamaño y opacidad. Hay escenarios donde un modelo compacto, entrenado con tus clases y exportado como artefacto portable, es más fácil de gobernar que una pieza grande que hereda supuestos que no controlas.
La pregunta central de ASTW es sencilla: ¿cuánto podemos acercar el modelado de audio a desarrolladores, investigadores y equipos de producto sin obligarles a montar una plataforma entera antes de validar una idea?
El flujo de trabajo que queríamos
ASTW intenta que el camino feliz sea corto y explícito:
from astw import ASTW
audios = [
"audio/cat_01.wav",
"audio/cat_02.wav",
"audio/dog_01.wav",
"audio/dog_02.wav",
]
labels = ["cat", "cat", "dog", "dog"]
model = ASTW()
model.fit(audios, labels, epochs=8, batch_size=4)
model.save("exports/my-audio-model")
Después, ese modelo puede volver a cargarse desde disco local o desde un repositorio de Hugging Face:
from astw import ASTW
local = ASTW.load("exports/my-audio-model")
print(local.predict("dog.wav"))
remote = ASTW.from_pretrained("your-user/your-audio-model")
print(remote.predict("clip.wav", top_k=3))
También hay una interfaz de línea de comandos para predicción puntual:
astw predict path/to/audio.wav --model exports/my-audio-model
astw predict path/to/audio.wav --model your-user/your-audio-model --top-k 5
Esta API puede parecer pequeña, y esa es parte de la intención. Queríamos una librería que se pudiera entender de extremo a extremo. Entrenar, exportar, cargar y predecir no deberían exigir leer diez módulos antes de producir el primer modelo.
Qué ocurre dentro de ASTW
El modelo por defecto es un clasificador de audio compacto basado en un encoder Transformer. La entrada no es texto ni tokens de lenguaje. La entrada es una representación log-mel local de la señal de audio.
La extracción de características usa:
- audio mono remuestreado a 16 kHz cuando hace falta
- 80 bins log-mel
n_fft=400hop_length=160- ventanas Hann
- normalización estilo Whisper sobre el espectrograma log-mel
- duración de chunk por defecto de 30 segundos
La librería implementa su propio extractor local, astw-local-log-mel-80. Eso evita descargar un extractor externo para entrenar modelos nuevos. El resultado son tensores de features que alimentan el clasificador.
El clasificador tiene esta estructura por defecto:
- dos capas
Conv1dcomo stem temporal - primera convolución de 80 canales a
d_model - segunda convolución con
stride=2para reducir la longitud temporal - embedding posicional aprendido
- pila de bloques encoder con self-attention multi-cabeza
- normalización final
- pooling sobre frames válidos
- cabeza lineal de clasificación
La configuración por defecto es:
d_model: 384
num_heads: 6
num_layers: 8
ff_mult: 4
dropout: 0.1
pooling: mean
sample_rate: 16000
El pooling por defecto es mean, con máscara para no mezclar padding con señal real. También existe attentive_stats, que calcula media y desviación estándar ponderadas por atención. Para activarlo:
from astw import ASTW
model = ASTW(pooling="attentive_stats")
En tareas pequeñas o con datasets limitados, esta diferencia importa. El pooling medio es más simple y estable. El pooling atento puede capturar mejor variación temporal cuando la señal distintiva aparece en zonas concretas del clip, aunque también introduce más capacidad y, por tanto, más necesidad de validación.
Entrenar desde cero no significa entrenar a ciegas
ASTW no carga pesos preentrenados. Entrena desde cero. Eso obliga a que la receta de entrenamiento sea razonable, porque no hay un modelo gigante compensando malas decisiones.
La librería combina dos optimizadores:
- Muon para parámetros con forma de matriz, como pesos de capas lineales y convoluciones
- AdamW para el resto de parámetros, como biases y embeddings posicionales
La configuración de entrenamiento por defecto incluye:
muon_lr: 0.01
adam_lr: 3e-4
weight_decay: 0.01
label_smoothing: 0.1
warmup_ratio: 0.1
patience: 30
random_state: 42
El scheduler aplica warmup y después una caída coseno. La métrica de selección es macro-F1 sobre validación, no solo accuracy. Esa decisión es importante en clasificación de audio porque muchas tareas tienen clases desbalanceadas o clases que se confunden de forma asimétrica. Accuracy puede ocultar que el modelo ignora una clase minoritaria. Macro-F1 fuerza a mirar el rendimiento clase a clase.
También se aplica SpecAugment durante entrenamiento. En la implementación actual se hacen dos máscaras de frecuencia y dos máscaras temporales por batch, con límites acotados para no destruir la señal:
- frecuencia: hasta 8 bins
- tiempo: hasta 20 frames válidos
El objetivo no es hacer magia. Es introducir variación suficiente para que el modelo no memorice patrones demasiado frágiles del dataset de entrenamiento.
El entrenamiento admite validación explícita (val_audios, val_labels) o split interno (val_split=0.2). Cuando hay una validación explícita, ASTW la respeta. Cuando no la hay, crea un split reproducible, estratificado si los datos lo permiten.
Qué acepta como entrada
Una decisión importante de API fue no limitar la librería a rutas de fichero. fit y predict aceptan varios formatos:
- ruta a fichero de audio
numpy.ndarray- tupla
(array, sample_rate) - diccionario con
arrayysample_rate
Esto permite usar ASTW tanto con datasets en disco como con pipelines que ya tienen audio cargado en memoria. Si el sample rate no coincide con los 16 kHz esperados, la librería remuestrea con librosa. Si el audio llega en más de un canal, se convierte a mono.
Ese detalle parece menor, pero reduce fricción en pruebas reales. En muchos proyectos de audio, los datos llegan mezclados: archivos WAV locales, arrays extraídos de un dataset, clips descargados de un Hub, fragmentos capturados por un servicio. La librería no debería obligarte a normalizar todo manualmente antes de empezar.
El artefacto exportado como contrato
Uno de los puntos centrales de ASTW es la exportación. Después de entrenar, save produce una carpeta con:
model.safetensors
config.json
preprocessor_config.json
labels.json
metrics.json
README.md
model.safetensors guarda los pesos de forma segura y portable. config.json describe el formato, el tipo de modelo, el sample rate, las etiquetas, la configuración del encoder, las métricas y los metadatos. preprocessor_config.json documenta cómo se extraen las características. labels.json deja explícita la relación entre etiquetas e ids. metrics.json conserva la información de validación. El README.md se genera con metadatos compatibles con Hugging Face, incluyendo pipeline_tag: audio-classification.
Esto convierte el modelo en algo más parecido a un paquete que a un fichero suelto. Y esa diferencia importa. Un peso sin configuración es una deuda futura. Un artefacto autocontenido se puede inspeccionar, versionar, subir, descargar y reproducir con menos conocimiento tribal.
La carga remota usa huggingface_hub.snapshot_download y restringe los ficheros necesarios:
README.md
config.json
labels.json
metrics.json
model.safetensors
preprocessor_config.json
Eso mantiene el contrato pequeño y claro.
Ejemplo 1: animales en ESC-50
El primer ejemplo incluido entrena un clasificador de 10 clases animales sobre ESC-50:
- cat
- cow
- crow
- dog
- frog
- hen
- insects
- pig
- rooster
- sheep
El flujo está dividido en dos scripts:
python examples/esc50_animals/0_train.py --epochs 30 --batch-size 8 --test-fold 1
python examples/esc50_animals/1_test.py --test-fold 1
El entrenamiento usa el fold 1 como split de selección por defecto, aunque también se puede pedir un split interno con:
python examples/esc50_animals/0_train.py \
--validation-source train-split \
--val-split 0.2 \
--epochs 30 \
--batch-size 8 \
--test-fold 1
El script de test carga el artefacto exportado y escribe dos salidas:
examples/esc50_animals/confusion_matrix.png
examples/esc50_animals/metrics.json
La matriz de confusión es especialmente útil aquí porque muchas clases animales comparten textura acústica. No basta con saber si el modelo acierta en global. Conviene ver si confunde rooster con hen, si separa bien dog de cow, o si insects se comporta como una clase más difusa.

Ejemplo 2: transporte en ESC-50
El segundo ejemplo usa el mismo dataset, pero cambia el dominio a cinco clases de transporte:
- airplane
- car_horn
- engine
- helicopter
- train
El flujo básico es:
python examples/esc50_transport/0_train.py --epochs 24 --batch-size 8 --test-fold 1
python examples/esc50_transport/1_test.py --test-fold 1
Este ejemplo permite probar attentive_stats:
python examples/esc50_transport/0_train.py --pooling attentive_stats
Es un buen caso para comparar pooling porque algunas señales de transporte son más continuas, como engine, mientras otras pueden ser eventos localizados, como car_horn. Un pooling que trate todos los frames por igual puede ser suficiente en algunos casos. En otros, conviene evaluar si una agregación atenta captura mejor los segmentos que realmente explican la clase.

Ejemplo 3: emoción en voz con RAVDESS
El tercer ejemplo trabaja con emoción en voz usando RAVDESS, concretamente el dataset xbgoose/ravdess. RAVDESS contiene 1440 clips cortos, de unos 3 a 4 segundos, grabados por 24 actores profesionales, 12 hombres y 12 mujeres, y equilibrados sobre 8 emociones.
El ejemplo de ASTW filtra el problema a cuatro clases:
- neutral
- calm
- angry
- surprised
Las otras emociones (happy, sad, fearful, disgust) se dejan fuera para mantener el ejemplo enfocado. Están más cerca entre sí acústicamente y normalmente requieren preentrenamiento, más datos o un modelo mayor para separarse de forma fiable.
La parte más importante del ejemplo no es solo la lista de clases, sino el split. Por defecto, los actores 21, 22, 23 y 24 forman el test holdout. El modelo entrena y valida sobre actores distintos. Esto evita una evaluación engañosa donde el clasificador aprende rasgos del hablante en lugar de emoción.
El flujo es:
python examples/speech_emotion/0_train.py --epochs 24 --batch-size 16
python examples/speech_emotion/1_test.py
También se puede cambiar el conjunto de actores de test:
python examples/speech_emotion/0_train.py --test-actors 19 20 21 22 23 24
Este ejemplo representa bien el tipo de problema donde el audio es potente y delicado a la vez. La voz contiene emoción, pero también contiene identidad, acento, microfonía, sala, estilo de interpretación y variabilidad de intensidad. Un split independiente por hablante no resuelve todo, pero sí evita una de las trampas más frecuentes en audio ML.

Qué problemas puede resolver
ASTW no pretende ser una plataforma universal de audio. Es una pieza compacta para clasificación supervisada. Esa acotación la hace útil en problemas donde las clases están definidas y el equipo necesita validar rápido:
- clasificación de eventos acústicos
- categorización de sonidos ambientales
- detección de tipos de maquinaria o transporte
- clasificación básica de emoción o intención en clips de voz controlados
- etiquetado interno de datasets de audio
- prototipos de audio ML que necesitan exportación portable
La clave es no confundir clasificación con entendimiento general. ASTW no transcribe, no separa fuentes, no hace diarización y no pretende sustituir modelos de audio grandes cuando el caso de uso exige representación generalista. Su valor está en otro sitio: reducir la distancia entre datos etiquetados y un clasificador exportable.
Límites claros
Publicar código abierto también implica publicar límites.
ASTW entrena desde cero. Eso da control, pero también exige datos suficientes por clase y una evaluación honesta. Si el dataset es pequeño, ruidoso o sesgado, el modelo lo reflejará. Si las clases no son acústicamente separables, la arquitectura no hará milagros. Si el split mezcla hablantes, dispositivos o entornos de forma incorrecta, las métricas pueden parecer mejores de lo que serán en producción.
Tampoco es un sistema de serving completo. El artefacto se puede cargar y usar, pero producción requiere las piezas habituales: control de versiones, trazabilidad de dataset, pruebas de regresión, latencia medida, monitorización de drift y validación con datos reales. ASTW baja la barrera de entrada al modelado de audio. No elimina las responsabilidades de MLOps.
Esa distinción es importante para nosotros. En Valendra preferimos publicar artefactos con límites explícitos antes que prometer soluciones cerradas. Una herramienta pequeña y bien delimitada suele ser más útil que una promesa grande y ambigua.
Por qué open source
Incluimos ejemplos completos porque el código abierto no es solo publicar. También es acompañar.
Un repositorio que solo muestra una clase y una API mínima puede ser correcto, pero deja demasiado trabajo al lector. ASTW incluye ejemplos de animales, transporte y emoción en voz porque muestran tres patrones distintos: clases ambientales, eventos más ligados a máquinas y una tarea de voz donde el split importa. También generan métricas y matrices de confusión, porque evaluar no debería ser un apéndice opcional.
Desde Valendra queremos estar del lado de la investigación aplicada: construir, publicar, exponer supuestos y permitir que otras personas reproduzcan o discutan el resultado. No todo experimento tiene que convertirse en producto. Algunos experimentos existen para abrir camino, reducir fricción y dejar una pieza reutilizable para quien viene después.
ASTW es un experimento en esa dirección.
Cómo empezar
Instalación desde PyPI:
pip install astw
Instalación local para desarrollo:
python -m pip install -e .
Instalación con dependencias de ejemplos:
python -m pip install -e '.[examples]'
Predicción por CLI:
astw predict path/to/audio.wav --model exports/my-audio-model --top-k 5
Construcción de wheel:
python -m pip install build
python -m build








