File size: 5,798 Bytes
efd84b3 27d3d58 efd84b3 27d3d58 efd84b3 27d3d58 efd84b3 27d3d58 efd84b3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | ---
license: other
license_name: mixed-provenance
license_link: https://huggingface.co/Scicom-intl/CallEnhancer-Lite#licence
language: [en, ms, zh]
tags: [speech-enhancement, speech-restoration, call-centre, telephony, distilhubert, realtime]
base_model: ntu-spml/distilhubert
pipeline_tag: audio-to-audio
---
# CallEnhancer-Lite β realtime call-centre speech restoration
A realtime-oriented sibling of [`Scicom-intl/CallEnhancer`](https://huggingface.co/Scicom-intl/CallEnhancer)
(enhancer-small) and [`Scicom-intl/CallEnhancer-Base`](https://huggingface.co/Scicom-intl/CallEnhancer-Base)
(enhancer-base). Same two-stage recipe β a feature predictor followed by a DAC vocoder β
but the 580M w2v-BERT 2.0 encoder is replaced by **DistilHuBERT (2 transformer layers)**
plus a small predictor head, and the target degradation is narrowed to three jobs:
**call-centre channel**, **background-noise removal**, and **cross-talk**.
| | encoder | decoder | total |
|---|---|---|---|
| enhancer-small | w2v-BERT 24L + LoRA β 580M | DAC 3072ch @48 kHz β 188M | 768M |
| enhancer-base | w2v-BERT 24L + LoRA β 580M | DAC 3072ch +extra_res β 381M | 961M |
| **enhancer-lite** | **DistilHuBERT 2L + head β 37M** | DAC 1536ch @24 kHz β 52M | **89M** |
## β οΈ Status: stage 1 only β this is not yet a usable enhancer
**This repo currently contains the ENCODER only.** It maps degraded 16 kHz audio to
1024-d SSL features; it does **not** produce audio. The matching 24 kHz decoder is still
training and will be added as `decoder_lite/`. Until then these weights are useful for
resuming training or for feature-level work, not for restoring calls.
## What the encoder does
```
degraded 16 kHz --> [DistilHuBERT 2L + PredictorHead] --> features [T, 1024] @ 50 Hz
```
It is distilled against a **frozen w2v-BERT 2.0 (24L) teacher running on the clean
audio**, so it predicts the same 1024-d feature space the existing CallEnhancer decoders
consume. Both stacks emit 50 Hz features (DistilHuBERT's conv frontend strides to 320
samples @16 kHz), which is exactly the DAC decoder's frame rate.
## Results
Feature-space error against the clean teacher, as `nmse = MSE / var(target) = 1 β RΒ²`
(scale-free; 1.0 = no better than predicting the mean, 0.0 = exact):
| model | encoder params | nmse |
|---|---|---|
| enhancer-small FE (w2v-BERT 24L + LoRA) | 580M | ~0.147 |
| **enhancer-lite FE @964k (this checkpoint)** | **37M** | **0.1403** |
The 15Γ smaller encoder matches the large one on this metric. The LR schedule was the
whole story: constant 1e-4 to 200k (plateau at 0.1652), then cosine decay with SGDR-style
warm restarts β 0.1652 β 0.1505 @300k β 0.1458 @500k β 0.1403 @964k.
Caveats for honest comparison: the two runs use different degradation menus, and ~40% of
the loss is measured on zero-padding in both (median training file is 6.7 s against a
12 s window). The quoted figure is a rolling mean over 10k steps; per-step values swing
by roughly Β±0.007 around it.
## Speed
Measured on 16 CPU threads, 2 s chunk, batch 1, fp32:
| stack | encoder | decoder | total | xRT |
|---|---|---|---|---|
| enhancer-small | 137.1 ms | 468.2 ms | 605.2 ms | 3.3 |
| **enhancer-lite** (37M + 52M @24 kHz) | **18.7 ms** | **114.4 ms** | **133.1 ms** | **15.0** |
The encoder is 6.7Γ faster; the decoder shrink is what delivers the rest. On GPU both
stacks are far past realtime, so the encoder swap's value there is sequential depth
(24 attention blocks β 2), weight traffic (1.2 GB β 74 MB bf16) and no O(TΒ²) attention β
i.e. small-chunk streaming latency and per-stream memory.
Algorithmic lookahead: 80 ms (2 head blocks Γ kernel 5 at 50 Hz).
## Files
| file | what |
|---|---|
| `fe_lite/fe_only.pt` | encoder weights (~148 MB) β for inference |
| `fe_lite/last.pt` | full train state incl. optimizer (~443 MB) β resumable |
## Usage
```python
import torch, fe_distilhubert as fedh # runpod/fe_distilhubert.py in the Sidon repo
fe = fedh.load_fe("fe_lite/fe_only.pt", torch.device("cuda"))
proc = fedh.make_processor()
batch = fedh.collate_wavs([wav_16k_float32], proc) # raw waveform in
feats = fe(**{k: v.cuda() for k, v in batch.items()}).last_hidden_state # [1, T, 1024]
```
No `peft` needed β unlike the LoRA-based enhancer-small FE, this is a full finetune.
## Training
* **Degradation** (all applied online, never precomputed): telephony channel 85%
(200β350 Hz high-pass β random 2.8β4.2 kHz ceiling via an 8/11.025/12/16 kHz bottleneck
β GSM / G.711 Β΅-law / none β 16β40 kbps MP3 β line noise β VoIP dropouts); caller-side
background noise 85% at 0β22 dB SNR from a real corpus; **cross-talk 50%** (1β2
interferer speakers, each active in a random 30β80% sub-segment with raised-cosine
fades, optionally passed through their own phone channel, SIR 2β15 dB, plus half-duplex
ducking); babble 20%. Reverb, echo, AGC, hum, codec tandeming, packet-loss concealment
and handset EQ are deliberately **off** β the small model spends its capacity on three
problems rather than ten.
* Cross-talk and babble are mixed into the **input only**; the target remains the clean
dominant speaker, so "restore the loudest talker" is well defined.
* 964k steps, effective batch 32, 12 s windows, bf16, 2Γ H20.
* Teacher pool: ~2,700 h of β₯44.1 kHz speech, DNSMOS-filtered, spanning read, audiobook,
podcast and conversational registers across many languages.
## Licence
`other` β provenance is mixed and not uniformly permissive. The base model
(`ntu-spml/distilhubert`) is Apache-2.0, but the noise corpus used during training
includes CC BY-NC and share-alike material, and parts of the speech pool carry
non-commercial terms. Review the upstream sources before any commercial use.
|