Automatic Speech Recognition
Transformers
Safetensors
English
asr_model
feature-extraction
asr
speech-recognition
speech-to-text
audio
speech-llm
word-timestamps
speaker-diarization
qwen
granite-speech
custom_code
Instructions to use mazesmazes/tiny-audio with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mazesmazes/tiny-audio with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("automatic-speech-recognition", model="mazesmazes/tiny-audio", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("mazesmazes/tiny-audio", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download asr_attention.py from mazesmazes/tiny-audio: direct link, hf CLI and curl.
- Browser
- Download file 9.86 kB
-
https://huggingface.co/mazesmazes/tiny-audio/resolve/main/asr_attention.py
- Command line
-
hf download hf://mazesmazes/tiny-audio/asr_attention.py
-
curl -L -o asr_attention.py https://huggingface.co/mazesmazes/tiny-audio/resolve/main/asr_attention.py
9.86 kB
| """Attention-implementation selection for ASRModel's encoder and decoder loaders. | |
| Decides which `attn_implementation` each sub-model is loaded with: FA2 only | |
| where CUDA and a working flash-attn are present and every head fits its | |
| kernels, eager on MPS for models that build a sliding-window mask, and the | |
| requested implementation otherwise. | |
| """ | |
| import functools | |
| import importlib | |
| import logging | |
| from typing import Any | |
| import torch | |
| from transformers import AutoConfig, PretrainedConfig | |
| logger: logging.Logger = logging.getLogger(__name__) | |
| # FlashAttention's kernels are compiled for head dimensions up to 256. | |
| FLASH_ATTENTION_MAX_HEAD_DIM = 256 | |
| def active_attn_implementation(config: PretrainedConfig) -> object: | |
| """The attention kernel a loaded model runs (e.g. "sdpa"), read from its config. | |
| transformers exposes it only as the private `_attn_implementation` field, | |
| so this is the one place that reads it. | |
| """ | |
| return config._attn_implementation # noqa: SLF001 # pyright: ignore[reportPrivateUsage] | |
| def _max_attention_head_dim(text_config: object) -> int | None: | |
| """Largest attention head dim across layers, or None if undeterminable. | |
| Has to cope with heterogeneous configs: Gemma 4 varies head_dim per layer, | |
| and reading `config.head_dim` on one of those raises | |
| AmbiguousGlobalPerLayerAttributeError instead of returning a number, so the | |
| per-layer list must be consulted explicitly. | |
| """ | |
| per_layer = getattr(text_config, "per_layer_config", None) | |
| dims: list[object] = [] | |
| if per_layer: | |
| dims = [getattr(layer, "head_dim", None) for layer in per_layer] | |
| else: | |
| try: | |
| dims = [getattr(text_config, "head_dim", None)] | |
| except RuntimeError: | |
| # transformers' AmbiguousGlobalPerLayerAttributeError (a RuntimeError) | |
| # from the heterogeneity guard; treated as "unknown". | |
| dims = [] | |
| usable = [d for d in dims if isinstance(d, int) and d > 0] | |
| return max(usable) if usable else None | |
| def _text_config(model_id: str) -> Any: | |
| """`model_id`'s decoder config: the text sub-config of a composite config, else itself.""" | |
| probe = AutoConfig.from_pretrained(model_id, trust_remote_code=True) | |
| return probe.get_text_config() if hasattr(probe, "get_text_config") else probe | |
| def _has_sliding_window_attention(model_id: str) -> bool: | |
| """Whether `model_id`'s text stack has any sliding-window attention layer. | |
| Read from the config rather than the loaded module so the answer is | |
| available before `from_pretrained` picks an attn implementation. | |
| Two independent spellings, because transformers has both. Gemma 4 declares | |
| `sliding_window: 512` AND `layer_types: [full_attention, | |
| sliding_attention]` (verified against google/gemma-4-E2B-it); Qwen2-style | |
| configs carry a non-null `sliding_window` that is inert unless | |
| `use_sliding_window` is true, so that flag has to veto the window. | |
| Returns True when the config can't be read, so an unknown architecture | |
| keeps the conservative eager path on MPS. | |
| """ | |
| try: | |
| text_config = _text_config(model_id) | |
| except Exception: | |
| logger.warning( | |
| "Could not read the config for %s to check for sliding-window " | |
| "attention; assuming it has some and using eager attention on MPS.", | |
| model_id, | |
| exc_info=True, | |
| ) | |
| return True | |
| layer_types: list[object] = getattr(text_config, "layer_types", None) or [] | |
| if any("sliding" in str(layer_type) for layer_type in layer_types): | |
| return True | |
| if getattr(text_config, "use_sliding_window", None) is False: | |
| return False | |
| return bool(getattr(text_config, "sliding_window", None)) | |
| def resolve_attn_implementation(requested: str | None, model_id: str | None = None) -> str | None: | |
| """Coerce flash_attention_2 to sdpa when CUDA isn't available, and avoid sdpa on MPS. | |
| FA2 is CUDA-only. On MPS/CPU, requesting it either errors at load or | |
| silently falls back to a slower path; either way the user pays the FA2 | |
| install + import cost for no win. Coerce here so a saved config that | |
| pins flash_attention_2 still loads on Mac / CPU-only Linux boxes. | |
| MPS needs eager for SOME models. PyTorch's Metal sdpa kernel returns wrong | |
| results for cached single-token decode against a sliding-window mask, which | |
| is exactly Gemma 4's layout (`sliding_window=512`, four sliding layers per | |
| full-attention layer). A full-sequence forward is fine, so the damage shows | |
| up only during generation: greedy decode with no cache transcribed | |
| correctly while the identical cached decode produced "Mr. and a". | |
| The mask is the trigger, so the coercion is scoped to models that actually | |
| build one. `model_id` opts into that check; without it every MPS load falls | |
| back to eager, which is what this function used to do unconditionally. That | |
| blanket version taxed the models it was never protecting: Qwen3.5-2B | |
| declares `sliding_window: None` and cannot hit the bug, and eager cost it | |
| 41% of decode throughput on an M4 Max (19.9 -> 28.1 tok/s, bf16, 64 tokens | |
| from a 200-token prompt). Audio encoders are likewise unaffected -- they run | |
| full-sequence forwards with no cache at all, and the Granite branch below | |
| has been pinning sdpa on MPS for every granite_qwen run to date. | |
| SECOND, SEPARATE MPS sdpa HAZARD, and the reason `generate` carries a guard: | |
| Metal's sdpa returns NaN for a row containing any fully-masked position, so | |
| a LEFT-PADDED batch comes back as NaN logits, argmax 0, and a transcript of | |
| "!!!!!!". One pad token is enough. Measured on bare transformers with no | |
| tiny_audio code involved (Qwen3.5-2B, bf16): the identical forward is exact | |
| to 0.000 on CPU/fp32, and eager is correct on MPS at any padding. Batch 1 | |
| pads nothing, which is why the eval harness is safe under sdpa -- but this | |
| is what the blanket eager was accidentally also protecting, so anything | |
| that batches ragged audio on a Mac must ask for eager explicitly. See | |
| `_assert_sdpa_safe_on_mps`. | |
| """ | |
| if ( | |
| torch.backends.mps.is_available() | |
| and requested in (None, "sdpa", "flash_attention_2") | |
| and (model_id is None or _has_sliding_window_attention(model_id)) | |
| ): | |
| return "eager" | |
| # Otherwise fall through: on MPS, sdpa and None (let transformers pick, | |
| # which is sdpa where available) are both safe for a model with no | |
| # sliding-window layer, and a config pinning flash_attention_2 is still | |
| # downgraded to sdpa below. | |
| if requested != "flash_attention_2": | |
| return requested | |
| if not torch.cuda.is_available(): | |
| return "sdpa" | |
| # CUDA alone isn't enough -- the flash_attn package must actually be | |
| # importable, and new enough for transformers. It is a source build that | |
| # routinely fails on RunPod images (its metadata hook imports torch, so any | |
| # broken torch install takes it down with it). Without this check a CUDA | |
| # box with no flash-attn raises at from_pretrained instead of quietly | |
| # using sdpa, which is the same numerics at lower throughput. | |
| # | |
| # Looked up on `transformers.utils` at call time rather than bound at | |
| # import, so patching it there (as the tests do) reaches this probe. | |
| if not importlib.import_module("transformers.utils").is_flash_attn_2_available(): | |
| logger.warning( | |
| "flash_attention_2 requested but flash_attn is not installed or too old; " | |
| "falling back to sdpa." | |
| ) | |
| return "sdpa" | |
| if not _flash_attention_runs(): | |
| return "sdpa" | |
| return requested | |
| def _flash_attention_runs() -> bool: | |
| """Whether flash-attn's kernels actually run, forward and backward, on this GPU. | |
| Importing proves only that the package is installed: its kernels are | |
| compiled per GPU architecture, so a build without this card's (e.g. the | |
| Blackwell RTX PRO 6000, sm_120) imports cleanly and fails at the first | |
| attention call, mid-load or mid-step. Checked once per process. | |
| """ | |
| try: | |
| flash_attn = importlib.import_module("flash_attn") | |
| q = torch.randn(1, 8, 2, 64, device="cuda", dtype=torch.bfloat16, requires_grad=True) | |
| flash_attn.flash_attn_func(q, q, q, causal=True).sum().backward() | |
| # AssertionError: torch's own "not compiled with CUDA enabled". | |
| except (RuntimeError, ImportError, AttributeError, AssertionError) as exc: | |
| logger.warning("flash_attn is installed but its kernels fail here (%s); using sdpa", exc) | |
| return False | |
| return True | |
| def resolve_decoder_attn_implementation(requested: str | None, model_id: str) -> str | None: | |
| """`resolve_attn_implementation` for the decoder, plus FlashAttention's head-dim limit. | |
| FlashAttention only has kernels for head_dim <= 256, and it fails at | |
| the first forward rather than at load. Gemma 4 E2B trips this: 7 of | |
| its 35 layers use head_dim=512 (the other 28 use 256), so FA2 can | |
| never run this architecture regardless of flash-attn version. Detect | |
| it from the config and fall back instead of dying a step into | |
| training. | |
| """ | |
| attn_implementation = resolve_attn_implementation(requested, model_id) | |
| if attn_implementation == "flash_attention_2": | |
| head_dim = _max_attention_head_dim(_text_config(model_id)) | |
| if head_dim is not None and head_dim > FLASH_ATTENTION_MAX_HEAD_DIM: | |
| logger.warning( | |
| "%s has max head_dim=%d, above FlashAttention's limit of %d; " | |
| "using sdpa for the decoder.", | |
| model_id, | |
| head_dim, | |
| FLASH_ATTENTION_MAX_HEAD_DIM, | |
| ) | |
| attn_implementation = "sdpa" | |
| return attn_implementation | |