mazesmazes commited on
Commit
a07485c
·
verified ·
1 Parent(s): c54008e

Update custom model files, README, and requirements

Browse files
Files changed (5) hide show
  1. .gitattributes +2 -35
  2. README.md +247 -57
  3. asr_config.py +13 -22
  4. handler.py +73 -0
  5. requirements.txt +5 -0
.gitattributes CHANGED
@@ -1,36 +1,3 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- tokenizer.json filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.safetensors filter=lfs diff=lfs merge=lfs -text
2
+ *.bin filter=lfs diff=lfs merge=lfs -text
3
+ tokenizer_config.json -filter -diff -merge text
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,77 +1,267 @@
1
  ---
2
- library_name: transformers
 
 
 
 
 
 
 
 
3
  tags:
4
- - generated_from_trainer
5
- model-index:
6
- - name: tiny-audio-omni
7
- results: []
 
 
8
  ---
9
 
10
- <!-- This model card has been generated automatically according to the information the Trainer had access to. You
11
- should probably proofread and complete it, then remove this comment. -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- # tiny-audio-omni
 
 
 
 
 
 
 
 
14
 
15
- This model is a fine-tuned version of [](https://huggingface.co/) on the None dataset.
16
- It achieves the following results on the evaluation set:
17
- - Loss: 0.4575
18
 
19
- ## Model description
 
 
 
 
20
 
21
- More information needed
 
 
 
 
22
 
23
- ## Intended uses & limitations
24
 
25
- More information needed
 
 
 
 
 
 
 
26
 
27
- ## Training and evaluation data
28
 
29
- More information needed
30
 
31
- ## Training procedure
32
 
33
- ### Training hyperparameters
 
 
 
 
 
 
 
 
34
 
35
- The following hyperparameters were used during training:
36
- - learning_rate: 0.001
37
- - train_batch_size: 14
38
- - eval_batch_size: 14
39
- - seed: 42
40
- - gradient_accumulation_steps: 4
41
- - total_train_batch_size: 56
42
- - optimizer: Use OptimizerNames.ADAMW_TORCH_FUSED with betas=(0.9,0.999) and epsilon=1e-08 and optimizer_args=No additional optimizer arguments
43
- - lr_scheduler_type: polynomial
44
- - lr_scheduler_warmup_steps: 1000
45
- - num_epochs: 1
46
- - label_smoothing_factor: 0.1
47
 
48
- ### Training results
 
 
49
 
50
- | Training Loss | Epoch | Step | Validation Loss |
51
- |:-------------:|:------:|:-----:|:---------------:|
52
- | 2.1239 | 0.0534 | 1000 | 0.4662 |
53
- | 2.0815 | 0.1069 | 2000 | 0.4654 |
54
- | 2.0997 | 0.1603 | 3000 | 0.4644 |
55
- | 2.0654 | 0.2137 | 4000 | 0.4634 |
56
- | 2.0897 | 0.2672 | 5000 | 0.4625 |
57
- | 2.0523 | 0.3206 | 6000 | 0.4618 |
58
- | 2.0583 | 0.3740 | 7000 | 0.4616 |
59
- | 2.0573 | 0.4274 | 8000 | 0.4608 |
60
- | 2.0345 | 0.4809 | 9000 | 0.4603 |
61
- | 2.0328 | 0.5343 | 10000 | 0.4598 |
62
- | 2.0610 | 0.5877 | 11000 | 0.4593 |
63
- | 2.0336 | 0.6412 | 12000 | 0.4592 |
64
- | 2.0445 | 0.6946 | 13000 | 0.4588 |
65
- | 2.0572 | 0.7480 | 14000 | 0.4582 |
66
- | 2.0349 | 0.8015 | 15000 | 0.4582 |
67
- | 2.0164 | 0.8549 | 16000 | 0.4579 |
68
- | 2.0246 | 0.9083 | 17000 | 0.4576 |
69
- | 2.0219 | 0.9617 | 18000 | 0.4575 |
70
 
 
 
 
71
 
72
- ### Framework versions
73
 
74
- - Transformers 5.0.0
75
- - Pytorch 2.8.0+cu128
76
- - Datasets 3.6.0
77
- - Tokenizers 0.22.2
 
1
  ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ datasets:
6
+ - speechbrain/LoquaciousSet
7
+ base_model:
8
+ - zai-org/GLM-ASR-Nano-2512
9
+ - Qwen/Qwen3-0.6B
10
+ pipeline_tag: automatic-speech-recognition
11
  tags:
12
+ - asr
13
+ - speech-recognition
14
+ - audio
15
+ - qwen
16
+ - glm-asr
17
+ library_name: transformers
18
  ---
19
 
20
+ # Tiny Audio
21
+
22
+ A speech recognition model trained in 24 hours on a single GPU for ~$12. Built with [Tiny Audio](https://github.com/alexkroman/tiny-audio)—a minimal, hackable ASR framework.
23
+
24
+ ## Quick Start
25
+
26
+ ```python
27
+ from transformers import pipeline
28
+
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
35
+
36
+ ### Basic Transcription
37
+
38
+ ```python
39
+ from transformers import pipeline
40
+
41
+ pipe = pipeline("automatic-speech-recognition", model="mazesmazes/tiny-audio", trust_remote_code=True)
42
+
43
+ # From file
44
+ result = pipe("audio.wav")
45
+ print(result["text"])
46
+
47
+ # From URL
48
+ result = pipe("https://example.com/audio.mp3")
49
+
50
+ # From numpy array (must be 16kHz)
51
+ import numpy as np
52
+ audio = np.random.randn(16000).astype(np.float32) # 1 second
53
+ result = pipe(audio)
54
+ ```
55
+
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:
63
+ print(r["text"])
64
+ ```
65
+
66
+ ### Word-Level Timestamps
67
+
68
+ ```python
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
+ from tiny_audio import ASRModel, ASRProcessor
84
+ import torch
85
+
86
+ model = ASRModel.from_pretrained("mazesmazes/tiny-audio")
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
+ ### Using with torch directly
100
+
101
+ ```python
102
+ from tiny_audio import ASRModel, ASRProcessor
103
+ import torch
104
+ import librosa
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
132
+ import torch
133
+
134
+ pipe = pipeline(
135
+ "automatic-speech-recognition",
136
+ model="mazesmazes/tiny-audio",
137
+ trust_remote_code=True,
138
+ device="cuda" # or device=0
139
+ )
140
+ ```
141
+
142
+ ### Half Precision
143
+
144
+ ```python
145
+ pipe = pipeline(
146
+ "automatic-speech-recognition",
147
+ model="mazesmazes/tiny-audio",
148
+ trust_remote_code=True,
149
+ torch_dtype=torch.float16,
150
+ device="cuda"
151
+ )
152
+ ```
153
+
154
+ ## Architecture
155
+
156
+ ```
157
+ Audio (16kHz) → GLM-ASR Encoder (frozen) → MLP Projector (trained) → Qwen3 (frozen) → Text
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 | GLM-ASR-Nano-2512 | ~600M | Frozen |
165
+ | Projector | 2-layer MLP | ~12M | Trained |
166
+ | Language Model | Qwen3-0.6B | ~600M | Frozen |
167
+
168
+ ### How It Works
169
+
170
+ 1. **Audio Encoder**: GLM-ASR converts 16kHz audio into frame-level embeddings (768-dim)
171
+ 2. **Projector**: A 2-layer MLP with frame stacking bridges the audio and text embedding spaces
172
+ 3. **Language Model**: Qwen3 generates text autoregressively, conditioned on the projected audio
173
+
174
+ The projector reduces sequence length via frame stacking: `output_len = (input_len - 5) // 5 + 1`
175
+
176
+ ## Model Specifications
177
+
178
+ | Specification | Value |
179
+ |---------------|-------|
180
+ | Input | Audio (16kHz mono) |
181
+ | Output | Text transcription |
182
+ | Max Audio Length | ~30 seconds (limited by encoder) |
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
+ | **Dataset** | LoquaciousSet (25,000 hours) |
192
+ | **Hardware** | Single NVIDIA A40 |
193
+ | **Time** | ~24 hours |
194
+ | **Cost** | ~$12 |
195
+ | **Optimizer** | AdamW |
196
+ | **Learning Rate** | 1e-4 |
197
+ | **Batch Size** | 4 |
198
+ | **Steps** | 50,000 |
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
+ - Heavily accented speech
207
+ - Noisy or low-quality audio
208
+ - Domain-specific terminology
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 (~48MB) |
232
+ | `preprocessor_config.json` | Audio preprocessing config |
233
+ | `tokenizer.json` | Tokenizer |
234
+ | `tokenizer_config.json` | Tokenizer config |
235
+ | `special_tokens_map.json` | Special tokens |
236
 
237
+ Note: Only the projector weights are stored. The encoder (GLM-ASR) and decoder (Qwen3) are loaded from their respective HuggingFace repos.
238
 
239
+ ## Citation
240
 
241
+ If you use this model, please cite:
242
 
243
+ ```bibtex
244
+ @misc{tinyaudio2024,
245
+ author = {Alex Kroman},
246
+ title = {Tiny Audio: Minimal ASR Training},
247
+ year = {2024},
248
+ publisher = {GitHub},
249
+ url = {https://github.com/alexkroman/tiny-audio}
250
+ }
251
+ ```
252
 
253
+ ## Links
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ - [GitHub Repository](https://github.com/alexkroman/tiny-audio) - Train your own model
256
+ - [Free 3.5-hour Course](https://github.com/alexkroman/tiny-audio/blob/main/docs/course/0-course-overview.md) - Learn ASR from scratch
257
+ - [Live Demo](https://huggingface.co/spaces/mazesmazes/tiny-audio) - Try it in your browser
258
 
259
+ ## Acknowledgments
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
+ - [GLM-ASR](https://huggingface.co/zai-org/GLM-ASR-Nano-2512) for the audio encoder
262
+ - [Qwen3](https://huggingface.co/Qwen/Qwen3-0.6B) for the language model
263
+ - [LoquaciousSet](https://huggingface.co/datasets/speechbrain/LoquaciousSet) for training data
264
 
265
+ ## License
266
 
267
+ MIT
 
 
 
asr_config.py CHANGED
@@ -152,28 +152,19 @@ class ASRConfig(transformers.PretrainedConfig):
152
  ]
153
  self.freeze_projector = freeze_projector
154
 
155
- # Generation parameters (use explicit value if provided, else use default)
156
- self.num_beams = num_beams if num_beams is not None else generation_defaults["num_beams"]
157
- self.max_new_tokens = (
158
- max_new_tokens if max_new_tokens is not None else generation_defaults["max_new_tokens"]
159
- )
160
- self.min_new_tokens = (
161
- min_new_tokens if min_new_tokens is not None else generation_defaults["min_new_tokens"]
162
- )
163
- self.repetition_penalty = (
164
- repetition_penalty
165
- if repetition_penalty is not None
166
- else generation_defaults["repetition_penalty"]
167
- )
168
- self.length_penalty = (
169
- length_penalty if length_penalty is not None else generation_defaults["length_penalty"]
170
- )
171
- self.no_repeat_ngram_size = (
172
- no_repeat_ngram_size
173
- if no_repeat_ngram_size is not None
174
- else generation_defaults["no_repeat_ngram_size"]
175
- )
176
- self.use_cache = use_cache if use_cache is not None else generation_defaults["use_cache"]
177
  self.do_sample = do_sample
178
  self.enable_thinking = enable_thinking
179
  self.temperature = temperature
 
152
  ]
153
  self.freeze_projector = freeze_projector
154
 
155
+ # Generation parameters: check named param first, then kwargs (from config.json), then default
156
+ def get_gen_param(name, named_value):
157
+ if named_value is not None:
158
+ return named_value
159
+ return kwargs.get(name, generation_defaults[name])
160
+
161
+ self.num_beams = get_gen_param("num_beams", num_beams)
162
+ self.max_new_tokens = get_gen_param("max_new_tokens", max_new_tokens)
163
+ self.min_new_tokens = get_gen_param("min_new_tokens", min_new_tokens)
164
+ self.repetition_penalty = get_gen_param("repetition_penalty", repetition_penalty)
165
+ self.length_penalty = get_gen_param("length_penalty", length_penalty)
166
+ self.no_repeat_ngram_size = get_gen_param("no_repeat_ngram_size", no_repeat_ngram_size)
167
+ self.use_cache = get_gen_param("use_cache", use_cache)
 
 
 
 
 
 
 
 
 
168
  self.do_sample = do_sample
169
  self.enable_thinking = enable_thinking
170
  self.temperature = temperature
handler.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Custom inference handler for HuggingFace Inference Endpoints."""
2
+
3
+ from typing import Any, Dict, List, Union
4
+
5
+ try:
6
+ # For remote execution, imports are relative
7
+ from .asr_modeling import ASRModel
8
+ from .asr_pipeline import ASRPipeline
9
+ except ImportError:
10
+ # For local execution, imports are not relative
11
+ from asr_modeling import ASRModel # type: ignore[no-redef]
12
+ from asr_pipeline import ASRPipeline # type: ignore[no-redef]
13
+
14
+
15
+ class EndpointHandler:
16
+ """HuggingFace Inference Endpoints handler for ASR model.
17
+
18
+ Handles model loading, warmup, and inference requests for deployment
19
+ on HuggingFace Inference Endpoints or similar services.
20
+ """
21
+
22
+ def __init__(self, path: str = ""):
23
+ """Initialize the endpoint handler.
24
+
25
+ Args:
26
+ path: Path to model directory or HuggingFace model ID
27
+ """
28
+ import os
29
+
30
+ import nltk
31
+
32
+ nltk.download("punkt_tab", quiet=True)
33
+
34
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
35
+
36
+ # Prepare model kwargs - let transformers handle device placement
37
+ model_kwargs = {
38
+ "device_map": "auto",
39
+ "torch_dtype": "auto",
40
+ "low_cpu_mem_usage": True,
41
+ }
42
+
43
+ # Load model (this loads the model, tokenizer, and feature extractor)
44
+ self.model = ASRModel.from_pretrained(path, **model_kwargs)
45
+
46
+ # Get device from model for pipeline
47
+ self.device = next(self.model.parameters()).device
48
+
49
+ # Instantiate custom pipeline - it will get feature_extractor and tokenizer from model
50
+ self.pipe = ASRPipeline(
51
+ model=self.model,
52
+ feature_extractor=self.model.feature_extractor,
53
+ tokenizer=self.model.tokenizer,
54
+ device=self.device,
55
+ )
56
+
57
+ def __call__(self, data: Dict[str, Any]) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
58
+ """Process an inference request.
59
+
60
+ Args:
61
+ data: Request data containing 'inputs' (audio path/bytes) and optional 'parameters'
62
+
63
+ Returns:
64
+ Transcription result with 'text' key
65
+ """
66
+ inputs = data.get("inputs")
67
+ if inputs is None:
68
+ raise ValueError("Missing 'inputs' in request data")
69
+
70
+ # Pass through any parameters from request, let model config provide defaults
71
+ params = data.get("parameters", {})
72
+
73
+ return self.pipe(inputs, **params)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Core dependencies for tiny-audio model inference
2
+ # This file is pushed to HuggingFace for model repository
3
+
4
+ # Transformers - main library for model loading and inference
5
+ transformers>=4.57.0