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
Update model card for granite-qwen-frozen-4
Browse files
README.md
CHANGED
|
@@ -3,23 +3,33 @@ license: mit
|
|
| 3 |
language:
|
| 4 |
- en
|
| 5 |
datasets:
|
| 6 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
base_model:
|
| 8 |
-
-
|
| 9 |
-
- Qwen/Qwen3
|
| 10 |
pipeline_tag: automatic-speech-recognition
|
| 11 |
tags:
|
| 12 |
- asr
|
| 13 |
- speech-recognition
|
| 14 |
- audio
|
| 15 |
- qwen
|
| 16 |
-
-
|
|
|
|
| 17 |
library_name: transformers
|
| 18 |
---
|
| 19 |
|
| 20 |
# Tiny Audio
|
| 21 |
|
| 22 |
-
|
|
|
|
|
|
|
| 23 |
|
| 24 |
## Quick Start
|
| 25 |
|
|
@@ -29,6 +39,7 @@ from transformers import pipeline
|
|
| 29 |
pipe = pipeline("automatic-speech-recognition", model="mazesmazes/tiny-audio", trust_remote_code=True)
|
| 30 |
result = pipe("audio.wav")
|
| 31 |
print(result["text"])
|
|
|
|
| 32 |
```
|
| 33 |
|
| 34 |
## Usage Examples
|
|
@@ -56,7 +67,6 @@ result = pipe(audio)
|
|
| 56 |
### Batch Processing
|
| 57 |
|
| 58 |
```python
|
| 59 |
-
# Process multiple files
|
| 60 |
files = ["audio1.wav", "audio2.wav", "audio3.wav"]
|
| 61 |
results = pipe(files, batch_size=4)
|
| 62 |
for r in results:
|
|
@@ -65,67 +75,25 @@ for r in results:
|
|
| 65 |
|
| 66 |
### Word-Level Timestamps
|
| 67 |
|
| 68 |
-
``
|
| 69 |
-
result = pipe("audio.wav", return_timestamps="word")
|
| 70 |
-
# Returns:
|
| 71 |
-
# {
|
| 72 |
-
# "text": "hello world",
|
| 73 |
-
# "chunks": [
|
| 74 |
-
# {"text": "hello", "timestamp": (0.0, 0.5)},
|
| 75 |
-
# {"text": "world", "timestamp": (0.6, 1.0)}
|
| 76 |
-
# ]
|
| 77 |
-
# }
|
| 78 |
-
```
|
| 79 |
-
|
| 80 |
-
### Streaming Inference
|
| 81 |
|
| 82 |
```python
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
processor = ASRProcessor.from_pretrained("mazesmazes/tiny-audio")
|
| 88 |
-
|
| 89 |
-
# Load and process audio
|
| 90 |
-
import librosa
|
| 91 |
-
audio, sr = librosa.load("audio.wav", sr=16000)
|
| 92 |
-
inputs = processor(audio, sampling_rate=16000, return_tensors="pt")
|
| 93 |
-
|
| 94 |
-
# Stream tokens
|
| 95 |
-
for token in model.generate_streaming(inputs["input_features"]):
|
| 96 |
-
print(token, end="", flush=True)
|
| 97 |
```
|
| 98 |
|
| 99 |
-
###
|
| 100 |
|
| 101 |
```python
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
# Load model and processor
|
| 107 |
-
model = ASRModel.from_pretrained("mazesmazes/tiny-audio")
|
| 108 |
-
processor = ASRProcessor.from_pretrained("mazesmazes/tiny-audio")
|
| 109 |
-
|
| 110 |
-
# Load audio (16kHz)
|
| 111 |
-
audio, sr = librosa.load("audio.wav", sr=16000)
|
| 112 |
-
|
| 113 |
-
# Process
|
| 114 |
-
inputs = processor(audio, sampling_rate=16000, return_tensors="pt")
|
| 115 |
-
|
| 116 |
-
# Generate
|
| 117 |
-
with torch.no_grad():
|
| 118 |
-
output = model.generate(
|
| 119 |
-
input_features=inputs["input_features"],
|
| 120 |
-
attention_mask=inputs["attention_mask"],
|
| 121 |
-
max_new_tokens=256
|
| 122 |
-
)
|
| 123 |
-
|
| 124 |
-
# Decode
|
| 125 |
-
text = processor.batch_decode(output, skip_special_tokens=True)[0]
|
| 126 |
-
print(text)
|
| 127 |
```
|
| 128 |
|
|
|
|
|
|
|
| 129 |
### GPU Inference
|
| 130 |
|
| 131 |
```python
|
|
@@ -135,106 +103,107 @@ pipe = pipeline(
|
|
| 135 |
"automatic-speech-recognition",
|
| 136 |
model="mazesmazes/tiny-audio",
|
| 137 |
trust_remote_code=True,
|
| 138 |
-
device="cuda"
|
|
|
|
| 139 |
)
|
| 140 |
```
|
| 141 |
|
| 142 |
-
##
|
| 143 |
|
| 144 |
-
``
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
|
| 154 |
## Architecture
|
| 155 |
|
| 156 |
```
|
| 157 |
-
Audio (16kHz) →
|
| 158 |
```
|
| 159 |
|
| 160 |
-
Only the projector is trained (~12M params). The encoder and decoder remain frozen, leveraging their pretrained knowledge.
|
| 161 |
-
|
| 162 |
| Component | Model | Parameters | Status |
|
| 163 |
|-----------|-------|------------|--------|
|
| 164 |
-
| Audio Encoder |
|
| 165 |
-
| Projector | 2-layer MLP |
|
| 166 |
-
| Language Model | Qwen3-
|
|
|
|
| 167 |
|
| 168 |
### How It Works
|
| 169 |
|
| 170 |
-
1. **Audio
|
| 171 |
-
2. **Projector**: A 2-layer MLP
|
| 172 |
-
3. **Language
|
| 173 |
|
| 174 |
-
|
| 175 |
|
| 176 |
## Model Specifications
|
| 177 |
|
| 178 |
| Specification | Value |
|
| 179 |
|---------------|-------|
|
| 180 |
| Input | Audio (16kHz mono) |
|
| 181 |
-
| Output |
|
| 182 |
-
| Max Audio Length | ~30 seconds
|
| 183 |
-
| Vocabulary | Qwen3 tokenizer |
|
| 184 |
| Languages | English only |
|
| 185 |
-
| Generation | Greedy decoding (num_beams=1, do_sample=False) |
|
| 186 |
|
| 187 |
## Training Details
|
| 188 |
|
| 189 |
| | |
|
| 190 |
|---|---|
|
| 191 |
-
| **
|
| 192 |
-
| **Hardware** | Single NVIDIA
|
| 193 |
-
| **
|
| 194 |
-
| **
|
| 195 |
-
| **Optimizer** | AdamW |
|
| 196 |
-
| **Learning Rate** | 1e-4 |
|
| 197 |
-
| **
|
| 198 |
-
|
|
|
|
| 199 |
|
| 200 |
## Limitations
|
| 201 |
|
| 202 |
-
- **English only**: Not trained on other languages
|
| 203 |
-
- **Sample rate**: Expects 16kHz audio (other rates resampled automatically)
|
| 204 |
-
- **Audio length**: Best for clips under 30 seconds
|
| 205 |
- **Accuracy**: May degrade on:
|
| 206 |
-
-
|
| 207 |
- Noisy or low-quality audio
|
| 208 |
-
-
|
| 209 |
-
- Overlapping speakers
|
| 210 |
-
- **No punctuation**: Output is lowercase without punctuation by default
|
| 211 |
-
|
| 212 |
-
## Requirements
|
| 213 |
-
|
| 214 |
-
```
|
| 215 |
-
transformers>=4.40.0
|
| 216 |
-
torch>=2.0.0
|
| 217 |
-
torchaudio>=2.0.0
|
| 218 |
-
```
|
| 219 |
-
|
| 220 |
-
Optional for streaming:
|
| 221 |
-
```
|
| 222 |
-
librosa
|
| 223 |
-
soundfile
|
| 224 |
-
```
|
| 225 |
|
| 226 |
## Files
|
| 227 |
|
| 228 |
| File | Description |
|
| 229 |
|------|-------------|
|
| 230 |
| `config.json` | Model configuration |
|
| 231 |
-
| `model.safetensors` | Projector weights (~
|
|
|
|
| 232 |
| `preprocessor_config.json` | Audio preprocessing config |
|
| 233 |
-
| `tokenizer.json` | Tokenizer |
|
| 234 |
-
| `
|
| 235 |
-
| `special_tokens_map.json` | Special tokens |
|
| 236 |
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
|
| 239 |
## Citation
|
| 240 |
|
|
@@ -258,9 +227,9 @@ If you use this model, please cite:
|
|
| 258 |
|
| 259 |
## Acknowledgments
|
| 260 |
|
| 261 |
-
- [
|
| 262 |
-
- [Qwen3](https://huggingface.co/Qwen/Qwen3
|
| 263 |
-
-
|
| 264 |
|
| 265 |
## License
|
| 266 |
|
|
|
|
| 3 |
language:
|
| 4 |
- en
|
| 5 |
datasets:
|
| 6 |
+
- mythicinfinity/libriheavy
|
| 7 |
+
- MLCommons/peoples_speech
|
| 8 |
+
- fixie-ai/common_voice_17_0
|
| 9 |
+
- speechcolab/gigaspeech
|
| 10 |
+
- kensho/spgispeech
|
| 11 |
+
- facebook/voxpopuli
|
| 12 |
+
- edinburghcstr/ami
|
| 13 |
+
- sanchit-gandhi/tedlium-data
|
| 14 |
base_model:
|
| 15 |
+
- ibm-granite/granite-speech-5.0-470m-turboctc
|
| 16 |
+
- Qwen/Qwen3.5-2B
|
| 17 |
pipeline_tag: automatic-speech-recognition
|
| 18 |
tags:
|
| 19 |
- asr
|
| 20 |
- speech-recognition
|
| 21 |
- audio
|
| 22 |
- qwen
|
| 23 |
+
- granite-speech
|
| 24 |
+
- lora
|
| 25 |
library_name: transformers
|
| 26 |
---
|
| 27 |
|
| 28 |
# Tiny Audio
|
| 29 |
|
| 30 |
+
An English speech recognition model that outputs punctuated, capitalized, formatted text. Built with [Tiny Audio](https://github.com/alexkroman/tiny-audio)—a minimal, hackable ASR framework.
|
| 31 |
+
|
| 32 |
+
A frozen Granite Speech encoder is connected to a frozen Qwen3.5-2B decoder through a trained MLP projector, with LoRA adapters on the decoder. Only ~80M parameters are trained.
|
| 33 |
|
| 34 |
## Quick Start
|
| 35 |
|
|
|
|
| 39 |
pipe = pipeline("automatic-speech-recognition", model="mazesmazes/tiny-audio", trust_remote_code=True)
|
| 40 |
result = pipe("audio.wav")
|
| 41 |
print(result["text"])
|
| 42 |
+
# The quarterly revenue grew by 12% according to Dr. Smith.
|
| 43 |
```
|
| 44 |
|
| 45 |
## Usage Examples
|
|
|
|
| 67 |
### Batch Processing
|
| 68 |
|
| 69 |
```python
|
|
|
|
| 70 |
files = ["audio1.wav", "audio2.wav", "audio3.wav"]
|
| 71 |
results = pipe(files, batch_size=4)
|
| 72 |
for r in results:
|
|
|
|
| 75 |
|
| 76 |
### Word-Level Timestamps
|
| 77 |
|
| 78 |
+
Timestamps come from forced alignment of the transcript against the audio and are returned under a `words` key:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
```python
|
| 81 |
+
result = pipe("audio.wav", return_timestamps=True)
|
| 82 |
+
print(result["text"])
|
| 83 |
+
for word in result["words"]:
|
| 84 |
+
print(word)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
```
|
| 86 |
|
| 87 |
+
### Speaker Labels
|
| 88 |
|
| 89 |
```python
|
| 90 |
+
result = pipe("meeting.wav", return_speakers=True, num_speakers=2)
|
| 91 |
+
for word in result["words"]:
|
| 92 |
+
print(word)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
```
|
| 94 |
|
| 95 |
+
`return_speakers=True` implies `return_timestamps=True`. You can pass `num_speakers`, or `min_speakers` / `max_speakers`, when the speaker count is known.
|
| 96 |
+
|
| 97 |
### GPU Inference
|
| 98 |
|
| 99 |
```python
|
|
|
|
| 103 |
"automatic-speech-recognition",
|
| 104 |
model="mazesmazes/tiny-audio",
|
| 105 |
trust_remote_code=True,
|
| 106 |
+
device="cuda",
|
| 107 |
+
torch_dtype=torch.bfloat16,
|
| 108 |
)
|
| 109 |
```
|
| 110 |
|
| 111 |
+
## Benchmarks
|
| 112 |
|
| 113 |
+
Word error rate (%, lower is better) on 1,000 samples per dataset, scored with the [Tiny Audio eval harness](https://github.com/alexkroman/tiny-audio/tree/main/scripts/eval) (`ta eval`) after text normalization.
|
| 114 |
+
|
| 115 |
+
| Dataset | WER |
|
| 116 |
+
|---------|----:|
|
| 117 |
+
| LibriSpeech test-clean | 1.80 |
|
| 118 |
+
| SPGISpeech | 2.24 |
|
| 119 |
+
| LibriSpeech test-other | 3.09 |
|
| 120 |
+
| TED-LIUM | 3.74 |
|
| 121 |
+
| LoquaciousSet † | 6.10 |
|
| 122 |
+
| Common Voice | 6.62 |
|
| 123 |
+
| VoxPopuli | 6.96 |
|
| 124 |
+
| AMI (IHM) | 8.88 |
|
| 125 |
+
| GigaSpeech | 9.07 |
|
| 126 |
+
| Earnings22 † | 10.54 |
|
| 127 |
+
| People's Speech | 17.69 |
|
| 128 |
+
| AMI (SDM) | 23.59 |
|
| 129 |
+
| **Mean (12 sets)** | **8.36** |
|
| 130 |
+
|
| 131 |
+
† Held out: no data from this source was used in training.
|
| 132 |
|
| 133 |
## Architecture
|
| 134 |
|
| 135 |
```
|
| 136 |
+
Audio (16kHz) → Granite Speech encoder (frozen) → MLP projector (trained) → Qwen3.5-2B + LoRA (trained adapters) → Text
|
| 137 |
```
|
| 138 |
|
|
|
|
|
|
|
| 139 |
| Component | Model | Parameters | Status |
|
| 140 |
|-----------|-------|------------|--------|
|
| 141 |
+
| Audio Encoder | [granite-speech-5.0-470m-turboctc](https://huggingface.co/ibm-granite/granite-speech-5.0-470m-turboctc) | ~470M | Frozen |
|
| 142 |
+
| Projector | 2-layer MLP (hidden 4096) | 12.6M | Trained |
|
| 143 |
+
| Language Model | [Qwen3.5-2B](https://huggingface.co/Qwen/Qwen3.5-2B) | ~2B | Frozen |
|
| 144 |
+
| LoRA adapters | r=64, alpha=64, all linear layers | 67.3M | Trained |
|
| 145 |
|
| 146 |
### How It Works
|
| 147 |
|
| 148 |
+
1. **Audio encoder**: Granite Speech turns 16kHz audio into frame-level embeddings.
|
| 149 |
+
2. **Projector**: A 2-layer MLP maps those embeddings into the decoder's embedding space. Each projected frame replaces an `<audio>` placeholder token in the prompt.
|
| 150 |
+
3. **Language model**: Qwen3.5-2B, adapted with LoRA, generates the transcript conditioned on the projected audio and the prompt *"Transcribe the speech with proper punctuation and capitalization"*.
|
| 151 |
|
| 152 |
+
At inference, 0.25s of silence is prepended to each clip (`inference_lead_in_seconds`). This keeps the first word from being dropped on clips that start mid-speech.
|
| 153 |
|
| 154 |
## Model Specifications
|
| 155 |
|
| 156 |
| Specification | Value |
|
| 157 |
|---------------|-------|
|
| 158 |
| Input | Audio (16kHz mono) |
|
| 159 |
+
| Output | Punctuated, capitalized text with formatted numbers |
|
| 160 |
+
| Max Audio Length | ~30 seconds per call |
|
| 161 |
+
| Vocabulary | Qwen3.5 tokenizer |
|
| 162 |
| Languages | English only |
|
| 163 |
+
| Generation | Greedy decoding (num_beams=1, do_sample=False), max 256 new tokens |
|
| 164 |
|
| 165 |
## Training Details
|
| 166 |
|
| 167 |
| | |
|
| 168 |
|---|---|
|
| 169 |
+
| **Data** | LibriHeavy (medium), People's Speech (clean), Common Voice 17, GigaSpeech (M), SPGISpeech (M), VoxPopuli, AMI (IHM + SDM), TED-LIUM: one pass, ~3.8M utterances |
|
| 170 |
+
| **Hardware** | Single NVIDIA H100 80GB |
|
| 171 |
+
| **Steps** | 58,861 |
|
| 172 |
+
| **Batch Size** | 64 |
|
| 173 |
+
| **Optimizer** | AdamW (fused), cosine schedule, 1,000 warmup steps, no weight decay |
|
| 174 |
+
| **Learning Rate** | 1e-3 (projector), 1e-4 (LoRA) |
|
| 175 |
+
| **Precision** | bf16 (projector held in fp32) |
|
| 176 |
+
|
| 177 |
+
The training recipe is [`configs/experiments/granite_qwen_frozen.yaml`](https://github.com/alexkroman/tiny-audio/blob/main/configs/experiments/granite_qwen_frozen.yaml).
|
| 178 |
|
| 179 |
## Limitations
|
| 180 |
|
| 181 |
+
- **English only**: Not trained on other languages.
|
| 182 |
+
- **Sample rate**: Expects 16kHz audio (other rates are resampled automatically).
|
| 183 |
+
- **Audio length**: Best for clips under 30 seconds. Chunk longer audio.
|
| 184 |
- **Accuracy**: May degrade on:
|
| 185 |
+
- Far-field and overlapping speech (see AMI SDM)
|
| 186 |
- Noisy or low-quality audio
|
| 187 |
+
- Rare names and domain-specific terminology
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
## Files
|
| 190 |
|
| 191 |
| File | Description |
|
| 192 |
|------|-------------|
|
| 193 |
| `config.json` | Model configuration |
|
| 194 |
+
| `model.safetensors` | Projector weights (~50MB) |
|
| 195 |
+
| `adapter_config.json` / `adapter_model.safetensors` | LoRA adapters for the decoder (~270MB) |
|
| 196 |
| `preprocessor_config.json` | Audio preprocessing config |
|
| 197 |
+
| `tokenizer.json` / `tokenizer_config.json` / `chat_template.jinja` | Tokenizer |
|
| 198 |
+
| `asr_*.py`, `projectors.py`, `alignment.py`, `diarization.py` | Custom model code (loaded with `trust_remote_code=True`) |
|
|
|
|
| 199 |
|
| 200 |
+
Only the projector and LoRA weights are stored here. The encoder (Granite Speech) and decoder (Qwen3.5-2B) are downloaded from their own Hugging Face repos.
|
| 201 |
+
|
| 202 |
+
The previous GLM-ASR + Qwen3-0.6B model is still available at revision `glm-asr-qwen3-0.6b`:
|
| 203 |
+
|
| 204 |
+
```python
|
| 205 |
+
pipe = pipeline("automatic-speech-recognition", model="mazesmazes/tiny-audio", revision="glm-asr-qwen3-0.6b", trust_remote_code=True)
|
| 206 |
+
```
|
| 207 |
|
| 208 |
## Citation
|
| 209 |
|
|
|
|
| 227 |
|
| 228 |
## Acknowledgments
|
| 229 |
|
| 230 |
+
- [Granite Speech](https://huggingface.co/ibm-granite/granite-speech-5.0-470m-turboctc) for the audio encoder
|
| 231 |
+
- [Qwen3.5](https://huggingface.co/Qwen/Qwen3.5-2B) for the language model
|
| 232 |
+
- The LibriHeavy, People's Speech, Common Voice, GigaSpeech, SPGISpeech, VoxPopuli, AMI, and TED-LIUM teams for training data
|
| 233 |
|
| 234 |
## License
|
| 235 |
|