altasr 1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. altasr-1/PKG-INFO +428 -0
  2. altasr-1/PYPI_README.md +393 -0
  3. altasr-1/README.md +186 -0
  4. altasr-1/altasr/__init__.py +92 -0
  5. altasr-1/altasr/audio.py +208 -0
  6. altasr-1/altasr/benchmark.py +374 -0
  7. altasr-1/altasr/config.py +245 -0
  8. altasr-1/altasr/export.py +152 -0
  9. altasr-1/altasr/inference/__init__.py +44 -0
  10. altasr-1/altasr/inference/checkpoint.py +111 -0
  11. altasr-1/altasr/inference/decoding.py +304 -0
  12. altasr-1/altasr/inference/evaluate.py +194 -0
  13. altasr-1/altasr/inference/pool.py +159 -0
  14. altasr-1/altasr/inference/streaming.py +247 -0
  15. altasr-1/altasr/inference/transcribe.py +88 -0
  16. altasr-1/altasr/inference/transcriber.py +331 -0
  17. altasr-1/altasr/integrations/__init__.py +3 -0
  18. altasr-1/altasr/integrations/llm.py +231 -0
  19. altasr-1/altasr/lm.py +266 -0
  20. altasr-1/altasr/metrics.py +126 -0
  21. altasr-1/altasr/model/__init__.py +29 -0
  22. altasr-1/altasr/model/attention.py +104 -0
  23. altasr-1/altasr/model/block.py +66 -0
  24. altasr-1/altasr/model/convolution.py +59 -0
  25. altasr-1/altasr/model/ctc.py +104 -0
  26. altasr-1/altasr/model/encoder.py +109 -0
  27. altasr-1/altasr/model/feedforward.py +27 -0
  28. altasr-1/altasr/model/positional.py +75 -0
  29. altasr-1/altasr/model/subsampling.py +40 -0
  30. altasr-1/altasr/text.py +313 -0
  31. altasr-1/altasr/training/__init__.py +36 -0
  32. altasr-1/altasr/training/data.py +329 -0
  33. altasr-1/altasr/training/finetune.py +226 -0
  34. altasr-1/altasr/training/prepare.py +101 -0
  35. altasr-1/altasr/training/train.py +248 -0
  36. altasr-1/altasr/training/trainer.py +488 -0
  37. altasr-1/altasr/utils.py +154 -0
  38. altasr-1/altasr.egg-info/PKG-INFO +428 -0
  39. altasr-1/altasr.egg-info/SOURCES.txt +43 -0
  40. altasr-1/altasr.egg-info/dependency_links.txt +1 -0
  41. altasr-1/altasr.egg-info/entry_points.txt +9 -0
  42. altasr-1/altasr.egg-info/requires.txt +26 -0
  43. altasr-1/altasr.egg-info/top_level.txt +1 -0
  44. altasr-1/pyproject.toml +57 -0
  45. altasr-1/setup.cfg +4 -0
altasr-1/PKG-INFO ADDED
@@ -0,0 +1,428 @@
1
+ Metadata-Version: 2.4
2
+ Name: altasr
3
+ Version: 1
4
+ Summary: ALTASR: a scalable, streaming-ready Conformer-CTC speech recognition toolkit, built for Kinyarwanda and reusable for any language.
5
+ Author: YaliLabs / ALTA Project
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/yalilabs/altasr
8
+ Keywords: speech-recognition,asr,kinyarwanda,conformer,ctc,streaming,live-captioning,low-resource
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: torch>=2.3
15
+ Requires-Dist: torchaudio>=2.3
16
+ Requires-Dist: numpy>=1.24
17
+ Requires-Dist: soundfile>=0.12
18
+ Requires-Dist: av>=11.0
19
+ Requires-Dist: tqdm>=4.66
20
+ Provides-Extra: bpe
21
+ Requires-Dist: sentencepiece>=0.1.99; extra == "bpe"
22
+ Provides-Extra: onnx
23
+ Requires-Dist: onnx>=1.15; extra == "onnx"
24
+ Requires-Dist: onnxruntime>=1.17; extra == "onnx"
25
+ Provides-Extra: whisper
26
+ Requires-Dist: faster-whisper>=1.0; extra == "whisper"
27
+ Provides-Extra: mic
28
+ Requires-Dist: sounddevice>=0.4; extra == "mic"
29
+ Provides-Extra: all
30
+ Requires-Dist: sentencepiece>=0.1.99; extra == "all"
31
+ Requires-Dist: onnx>=1.15; extra == "all"
32
+ Requires-Dist: onnxruntime>=1.17; extra == "all"
33
+ Requires-Dist: faster-whisper>=1.0; extra == "all"
34
+ Requires-Dist: sounddevice>=0.4; extra == "all"
35
+
36
+ # ALTASR
37
+
38
+ **Scalable, streaming-ready speech recognition — built for Kinyarwanda, reusable for any language.**
39
+
40
+ ALTASR is a Conformer-CTC automatic speech recognition (ASR) toolkit by the
41
+ ALTA Project (YaliLabs). It covers the full lifecycle — data preparation,
42
+ training, fine-tuning, evaluation, benchmarking, streaming inference, and
43
+ edge deployment — behind a small, consistent API and a set of one-line CLIs.
44
+
45
+ ```bash
46
+ pip install altasr
47
+ ```
48
+
49
+ That single command installs everything needed for training and inference
50
+ (PyTorch, torchaudio, audio codecs). Optional features are extras:
51
+
52
+ ```bash
53
+ pip install altasr[bpe] # subword (BPE) tokenizer
54
+ pip install altasr[onnx] # ONNX export for edge / CPU runtimes
55
+ pip install altasr[whisper] # Whisper baselines for benchmarking
56
+ pip install altasr[mic] # live microphone streaming
57
+ pip install altasr[all] # everything
58
+ ```
59
+
60
+ ---
61
+
62
+ **Full reference** (dataset format, every CLI parameter, multi-GPU,
63
+ web integration): [docs/USAGE.md](https://github.com/yalilabs/altasr/blob/main/docs/USAGE.md)
64
+
65
+ ## Highlights
66
+
67
+ - **Modern Conformer-CTC encoder** with relative positional attention,
68
+ intermediate CTC regularization, and stochastic depth — accuracy keeps
69
+ improving as you scale data and model size.
70
+ - **True streaming**: chunked-attention models plus a `StreamingSession`
71
+ API and `altasr-stream` CLI for live captioning from files or microphone.
72
+ - **Punctuation-aware**: the tokenizer and normalization keep punctuation,
73
+ so transcripts come out readable (`ndashaka amazi, urakoze.`).
74
+ - **Smart on your domain, automatically**: training builds an n-gram LM
75
+ + word lexicon from *your transcripts* and ships them in the checkpoint;
76
+ beam decoding uses them so corpus terms ("DGX", names, places) come out
77
+ right with **no hardcoded lists**. Hotwords remain for never-seen terms.
78
+ - **Your choice of decoder**: `decoder="auto"|"greedy"|"beam"` everywhere —
79
+ auto picks the best default (beam for single files, greedy for bulk and
80
+ streaming); explicit choices always win.
81
+ - **LLM & agent ready**: structured results (confidence + word
82
+ timestamps), an OpenAI tool schema so agents can call ASR as a tool,
83
+ and optional LLM post-correction through any OpenAI-compatible
84
+ endpoint (`SmartTranscriber` runs the whole pipeline).
85
+ - **Any language**: build a tokenizer (char or BPE) directly from *your*
86
+ dataset; nothing is Kinyarwanda-specific except the released checkpoints.
87
+ - **Robust to imperfect data**: speed perturbation, SpecAugment, and
88
+ capacity-scaled regularization are on by default; unreadable/corrupt
89
+ files are skipped with a warning instead of crashing a training run.
90
+ - **Efficient everywhere**: int8 dynamic quantization for CPU, ONNX export
91
+ for edge devices, bucketed batching and AMP for GPU training.
92
+ - **Fine-tuning pipeline**: adapt a trained checkpoint to a new dataset (or
93
+ a new language) in one command, with tokenizer extension and layer freezing.
94
+ - **Professional benchmarking**: compare your checkpoints against Whisper
95
+ and generate a Markdown/HTML report with WER, CER, error breakdowns and RTF.
96
+
97
+ ---
98
+
99
+ ## Quickstart: transcribe an audio file
100
+
101
+ ```python
102
+ from altasr import Transcriber
103
+
104
+ asr = Transcriber.from_pretrained("path/to/checkpoint")
105
+ text = asr.transcribe("recording.mp3")
106
+ print(text)
107
+ ```
108
+
109
+ With error handling for real applications:
110
+
111
+ ```python
112
+ from pathlib import Path
113
+ from altasr import Transcriber
114
+
115
+ def safe_transcribe(checkpoint: str, audio_path: str) -> str | None:
116
+ try:
117
+ asr = Transcriber.from_pretrained(checkpoint)
118
+ except FileNotFoundError:
119
+ print(f"Checkpoint not found: {checkpoint}")
120
+ return None
121
+ except ImportError as e:
122
+ print(f"Missing dependency: {e}") # e.g. torch not installed
123
+ return None
124
+
125
+ if not Path(audio_path).exists():
126
+ print(f"Audio file not found: {audio_path}")
127
+ return None
128
+ try:
129
+ return asr.transcribe(audio_path)
130
+ except Exception as e: # unreadable / corrupt audio
131
+ print(f"Could not transcribe {audio_path}: {e}")
132
+ return None
133
+ ```
134
+
135
+ Command line:
136
+
137
+ ```bash
138
+ altasr-transcribe --checkpoint runs/my_model --audio recording.mp3
139
+ ```
140
+
141
+ ## Scenario: get domain terms right (hotwords) + structured output
142
+
143
+ ```python
144
+ text = asr.transcribe("meeting.mp3") # auto = beam + learned LM/lexicon
145
+ text = asr.transcribe("meeting.mp3", decoder="greedy") # fastest, explicit
146
+ text = asr.transcribe("meeting.mp3", # + hotwords for terms
147
+ hotwords=["NewClientName"]) # not in training data
148
+
149
+ res = asr.transcribe_detailed("meeting.mp3")
150
+ res.decoder # e.g. "beam+lm+lexfix" — shows what fired
151
+ res.text, res.confidence # "twaguze DGX nshya.", 0.94
152
+ res.words[0] # Word(word='twaguze', start=0.12, end=0.58, confidence=0.97)
153
+ ```
154
+
155
+ ```bash
156
+ altasr-transcribe --checkpoint CKPT --audio meeting.mp3 --hotwords "DGX,H200" --detailed
157
+ ```
158
+
159
+ ## Scenario: LLM pipelines and agents
160
+
161
+ ```python
162
+ from altasr import Transcriber, LLMCorrector, SmartTranscriber
163
+
164
+ asr = Transcriber.from_pretrained("runs/my_model/best")
165
+ smart = SmartTranscriber(
166
+ asr, hotwords=["DGX", "YaliLabs"], beam_size=8,
167
+ corrector=LLMCorrector(base_url="http://localhost:11434/v1",
168
+ model="llama3.1")) # any OpenAI-compatible API
169
+ result = smart.transcribe("meeting.mp3") # dict: text, asr_text,
170
+ # confidence, words, ...
171
+ ```
172
+
173
+ Let an LLM agent call ASR as a tool (OpenAI function calling):
174
+
175
+ ```python
176
+ from altasr import ASR_TOOL_SCHEMA, handle_tool_call
177
+ resp = client.chat.completions.create(model=..., messages=msgs,
178
+ tools=[ASR_TOOL_SCHEMA])
179
+ result = handle_tool_call(asr, resp.choices[0].message.tool_calls[0].function.arguments)
180
+ ```
181
+
182
+ Full agent loop, design notes, and streaming-agent guidance:
183
+ [docs/USAGE.md](https://github.com/yalilabs/altasr/blob/main/docs/USAGE.md#8-agentic-integration-asr-as-an-llm-tool).
184
+
185
+ ## Scenario: CPU / edge deployment
186
+
187
+ Quantize to int8 at load time (CPU only, ~4x smaller Linear layers, faster):
188
+
189
+ ```python
190
+ asr = Transcriber.from_pretrained("runs/my_model", device="cpu", quantize="int8")
191
+ print(asr.transcribe("recording.wav"))
192
+ ```
193
+
194
+ Or export to ONNX for onnxruntime / mobile:
195
+
196
+ ```bash
197
+ pip install altasr[onnx]
198
+ altasr-export runs/my_model --format onnx --out deploy/model.onnx
199
+ ```
200
+
201
+ The exporter writes `model.onnx`, a `model.json` metadata sidecar
202
+ (sample rate, mel settings, vocabulary), and copies `tokenizer.json` next to
203
+ it, so the deployment folder is self-contained.
204
+
205
+ ## Scenario: live captioning (streaming)
206
+
207
+ Train or download a `streaming` preset checkpoint, then:
208
+
209
+ ```bash
210
+ # From a file, paced in real time (add --fast to run as fast as possible)
211
+ altasr-stream --checkpoint runs/streaming_model --audio talk.wav
212
+
213
+ # From the microphone (pip install altasr[mic])
214
+ altasr-stream --checkpoint runs/streaming_model --mic --quantize int8
215
+ ```
216
+
217
+ In Python:
218
+
219
+ ```python
220
+ from altasr import Transcriber
221
+
222
+ asr = Transcriber.from_pretrained("runs/streaming_model")
223
+ session = asr.stream(chunk_seconds=0.6)
224
+
225
+ for chunk in audio_chunks: # your capture loop: 1-D float32 @ 16 kHz
226
+ new_text = session.push(chunk)
227
+ if new_text:
228
+ print(new_text, end="", flush=True)
229
+ print(session.finish())
230
+ ```
231
+
232
+ Offline (non-streaming) checkpoints also work with `altasr-stream`, but a
233
+ `streaming`-preset model is trained with chunked attention and causal
234
+ convolutions, so its live accuracy is much closer to its offline accuracy.
235
+
236
+ ## Scenario: train on your own dataset (any language)
237
+
238
+ Metadata is JSON/JSONL/CSV/TSV with an audio path and a transcript per row.
239
+
240
+ ```bash
241
+ altasr-train \
242
+ --preset medium \
243
+ --audio-root /data/my_corpus \
244
+ --train /data/my_corpus/train.json \
245
+ --val /data/my_corpus/dev.json \
246
+ --tokenizer bpe --bpe-vocab-size 1024 \
247
+ --out-dir runs/my_model
248
+ ```
249
+
250
+ - The tokenizer (char by default, BPE with `--tokenizer bpe`) is built
251
+ **from your training transcripts**, so any language works out of the box.
252
+ - Punctuation is kept by default; pass `--no-punctuation` for bare text.
253
+ - Presets: `small` (~7M params), `medium` (~23M), `large` (~118M),
254
+ `streaming` (medium-sized, chunked attention for live use).
255
+ - Training auto-resumes from the last checkpoint in `--out-dir`.
256
+
257
+ **Dataset format** — metadata is any of: a JSON list of
258
+ `{"audio_path": ..., "text": ...}` objects, a JSON dict keyed by utterance
259
+ id, JSON-lines, or CSV with a header. Common field names are auto-detected
260
+ (`audio_path`/`path`/`file`/`audio` for audio; `text`/`sentence`/
261
+ `transcription`/`transcript` for the transcript; optional `duration`).
262
+ Audio can be wav/mp3/flac/ogg/m4a/… at any sample rate — ALTASR resamples
263
+ internally. Validate your dataset in seconds with `--dry-run`. Full details
264
+ and a worked formatting example: [docs/USAGE.md](https://github.com/yalilabs/altasr/blob/main/docs/USAGE.md#1-dataset-format).
265
+
266
+ **Multi-GPU** — same command, launched with `torchrun` (one process per
267
+ GPU; gradients are averaged automatically; checkpoints/logs come from GPU 0
268
+ only). On a 2-GPU machine (e.g. 2×H200):
269
+
270
+ ```bash
271
+ torchrun --standalone --nproc_per_node=2 -m altasr.training.train \
272
+ --preset medium --audio-root /data/my_corpus \
273
+ --train /data/my_corpus/train.json --val /data/my_corpus/dev.json \
274
+ --out-dir runs/my_model_2gpu
275
+ ```
276
+
277
+ Works on any CUDA GPU generation PyTorch supports (Ampere, Hopper
278
+ H100/H200, Blackwell, ...): bf16 and TF32 are enabled by capability
279
+ detection, not hard-coded lists. `--batch-size` and `--num-workers` are
280
+ per GPU. See [docs/USAGE.md](https://github.com/yalilabs/altasr/blob/main/docs/USAGE.md#3-multi-gpu-training-2-gpus-h200-etc) for scaling guidance.
281
+
282
+ **Multi-GPU inference** works too — shard bulk transcription or
283
+ evaluation across GPUs:
284
+
285
+ ```python
286
+ from altasr import TranscriberPool
287
+ pool = TranscriberPool.from_pretrained("runs/my_model/best") # all GPUs
288
+ texts = pool.transcribe_batch(list_of_files) # ~N x faster on N GPUs
289
+ ```
290
+
291
+ ```bash
292
+ altasr-evaluate --checkpoint runs/my_model/best --device all-gpus \
293
+ --audio-root /data/test --metadata /data/test/test.json
294
+ ```
295
+
296
+ `TranscriberPool` is thread-safe (per-device scheduling), so it drops
297
+ straight into the web-server examples below in place of `Transcriber`.
298
+
299
+ For large corpora, precompute mel features once:
300
+
301
+ ```bash
302
+ altasr-prepare-data --audio-root /data/my_corpus \
303
+ --metadata /data/my_corpus/train.json --out-dir /data/mels
304
+ altasr-train ... --features mel --features-dir /data/mels
305
+ ```
306
+
307
+ ## Scenario: fine-tune an existing model on new data
308
+
309
+ ```bash
310
+ altasr-finetune \
311
+ --checkpoint runs/my_model \
312
+ --audio-root /data/new_domain \
313
+ --train /data/new_domain/train.json \
314
+ --val /data/new_domain/dev.json \
315
+ --out-dir runs/my_model_medical \
316
+ --freeze-layers 8
317
+ ```
318
+
319
+ - The tokenizer is **extended** with characters found in the new data
320
+ (old token ids stay stable; the CTC head is resized automatically).
321
+ Disable with `--no-extend-tokenizer`.
322
+ - `--freeze-layers N` freezes the feature extractor and the first N encoder
323
+ blocks — fast, stable adaptation on small datasets.
324
+ - Sensible defaults: `--lr 1e-4`, `--epochs 5`, short warmup.
325
+
326
+ ## Scenario: evaluate and benchmark
327
+
328
+ ```bash
329
+ # Rich evaluation of one checkpoint
330
+ altasr-evaluate --checkpoint runs/my_model \
331
+ --audio-root /data/test --metadata /data/test/test.json --json eval.json
332
+ ```
333
+
334
+ Reported metrics: WER, CER, WER without punctuation, substitution /
335
+ deletion / insertion rates, and real-time factor (RTF).
336
+
337
+ ```bash
338
+ # Professional comparison report (Markdown + optional HTML + JSON)
339
+ pip install altasr[whisper]
340
+ altasr-benchmark \
341
+ --checkpoint "ALTASR medium=runs/my_model" \
342
+ --checkpoint "ALTASR small=runs/small" \
343
+ --whisper small --whisper large-v3 \
344
+ --audio-root /data/test --metadata /data/test/test.json \
345
+ --out benchmark/report.md --html
346
+ ```
347
+
348
+ The report includes a methodology section, a results table (params, WER,
349
+ CER, error breakdown, RTF), per-system notes, and the hardest utterances —
350
+ ready to share with stakeholders.
351
+
352
+ ## Scenario: serve ALTASR from a web app (FastAPI / Flask / Django)
353
+
354
+ Load the model **once at startup**, guard it with a lock, transcribe in a
355
+ worker thread. Minimal FastAPI service:
356
+
357
+ ```python
358
+ import asyncio, os, tempfile
359
+ from fastapi import FastAPI, File, HTTPException, UploadFile
360
+ from altasr import Transcriber
361
+
362
+ app = FastAPI()
363
+ asr = Transcriber.from_pretrained("runs/my_model/best")
364
+ lock = asyncio.Lock()
365
+
366
+ @app.post("/transcribe")
367
+ async def transcribe(file: UploadFile = File(...)):
368
+ suffix = os.path.splitext(file.filename or "a")[1] or ".wav"
369
+ with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
370
+ tmp.write(await file.read()); path = tmp.name
371
+ try:
372
+ async with lock:
373
+ return {"text": await asyncio.to_thread(asr.transcribe, path)}
374
+ except Exception as exc:
375
+ raise HTTPException(422, f"could not transcribe: {exc}")
376
+ finally:
377
+ os.unlink(path)
378
+ ```
379
+
380
+ ```bash
381
+ pip install fastapi uvicorn python-multipart
382
+ uvicorn app:app --port 8000
383
+ curl -F "file=@recording.mp3" http://localhost:8000/transcribe
384
+ ```
385
+
386
+ Complete examples — including a **WebSocket live-captioning endpoint**,
387
+ Flask, Django, and production sizing notes — are in
388
+ [docs/USAGE.md](https://github.com/yalilabs/altasr/blob/main/docs/USAGE.md#5-connecting-altasr-to-a-web-application).
389
+
390
+ ## Checkpoint format
391
+
392
+ A checkpoint is a plain folder — easy to version, copy, and deploy:
393
+
394
+ ```
395
+ runs/my_model/
396
+ ├── config.json # full architecture + training config
397
+ ├── tokenizer.json # vocabulary (char or BPE), normalization settings
398
+ └── model.pt # weights
399
+ ```
400
+
401
+ `Transcriber.from_pretrained(folder)` is all an application needs.
402
+
403
+ ## Requirements
404
+
405
+ - Python ≥ 3.9
406
+ - PyTorch ≥ 2.3 (installed automatically; for GPU wheels see pytorch.org)
407
+ - FFmpeg is **not** required — audio decoding uses soundfile / PyAV.
408
+
409
+ ## Troubleshooting
410
+
411
+ | Symptom | Fix |
412
+ | --- | --- |
413
+ | `ImportError: ... requires PyTorch` | `pip install torch torchaudio` (or reinstall `altasr`) |
414
+ | `BPETokenizer requires sentencepiece` | `pip install altasr[bpe]` |
415
+ | `--mic` fails to start | `pip install altasr[mic]`; check OS microphone permissions |
416
+ | Whisper baseline skipped in benchmark | `pip install altasr[whisper]` |
417
+ | CUDA out of memory during training | `--grad-checkpoint` (large models); lower `--max-batch-seconds`; then lower `--batch-size` + raise `--grad-accum` |
418
+ | Slow CPU inference | `quantize="int8"`, or export to ONNX |
419
+ | CLI clears my terminal / logs | set `ALTASR_NO_CLEAR=1` (clearing is skipped automatically when output is piped) |
420
+ | A term like "DGX" comes out wrong | if it's in your training data: use beam decoding (the default) — the learned LM/lexicon handles it; if never seen: `--hotwords "DGX"`; for many terms, fine-tune |
421
+ | Only one of my GPUs is used | training: launch with `torchrun --standalone --nproc_per_node=<N> -m altasr.training.train ...`; inference: `--device all-gpus` or `TranscriberPool` |
422
+ | `Could not load libtorchcodec` / `libavutil.so` errors | `pip install av soundfile` (ALTASR's own decoders); no system FFmpeg needed — since v0.2.2 such files are skipped, not fatal |
423
+ | `[data] SKIPPING unreadable item` warnings | normal for a few bad files in web corpora; 10+ consecutive = wrong `--audio-root`/`--features-dir` |
424
+
425
+ ## License
426
+
427
+ Apache-2.0 — © YaliLabs / ALTA Project.
428
+ Source, issues and documentation: <https://github.com/yalilabs/altasr>