Python
0
15 commits
updated May 13, 2026
A production-grade pipeline for generating synthetic speech training data targeting fine-tuning of Speech-to-Text (STT) models on Egyptian Arabic (عامية مصرية).
The pipeline runs two parallel data tracks that merge into a unified review and export stage.
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ TRACK A — Synthetic Data │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ STAGE 1 │ │ STAGE 2 │ │
│ │ Text Generation │────▶│ TTS Synthesis │──────────────────────┐ │
│ │ │ │ │ │ │
│ │ Nile-Chat-4B → │ │ NAMAA-Egyptian-TTS │ │ │
│ │ Egyptian prompts │ │ → .wav audio files │ │ │
│ └─────────────────────┘ └─────────────────────┘ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ stage1_prompts.json stage2_manifest.json │ │
│ stage1_checkpoint.json stage2_progress.json │ │
└────────────────────────────────────────────────────────────────────────────┼────────────┘
│
┌────────────────────────────────────────────────────────────────────────────┼────────────┐
│ TRACK B — Real Speech │ │
│ │ │
│ ┌──────────────────────────────────────────────┐ │ │
│ │ STAGE 1B — Real Speech Harvesting │ │ │
│ │ │ │ │
│ │ yt-dlp downloads YouTube audio (1) │ │ │
│ │ WhisperX transcribes + force-aligns (2) │ │ │
│ │ pydub splits into sentence segments (3) │──────────────────────────┘ │
│ │ WER scores each segment (4) │ │
│ │ → data/audio/real/*.wav │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ stage1b_manifest.json │
│ stage1b_download_progress.json │
│ stage1b_alignment_{video_id}.json │
│ stage1b_segment_progress.json │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│
┌───────────────────▼───────────────────┐
│ STAGE 3 — Unified Review & Score │
│ │
│ Merges Track A + Track B samples │
│ WER auto-labeling (same thresholds) │
│ Streamlit UI with 🤖/🎙️ source badge │
└───────────────────┬───────────────────┘
│
▼
stage3_scores.json
stage3_scores_progress.json
│
┌───────────────────▼───────────────────┐
│ STAGE 4 — Dataset Export │
│ │
│ HuggingFace Dataset + CSV │
│ source field: "synthetic" / "real" │
└───────────────────────────────────────┘
Every stage writes a manifest. Any stage can be re-run independently. All stages are fully resumable via checkpoints — a crash at sample 150 restarts from 151, not from zero.
config.yaml. Only Creative Commons licensed content should be used to avoid copyright issues..wav segments using the WhisperX timestamps. Each segment is resampled to 22050 Hz mono to match NAMAA TTS output format for consistency across the dataset.config.yaml (Colab API or Groq Whisper fallback) and scored against the WhisperX transcript. Same thresholds as Track A apply.nemo_toolkit[asr], one of the heaviest ML installs in Python (PyTorch Lightning, Hydra, Apex, etc.). Known to have installation issues on Mac M1/ARM systems. The pipeline solves this via a Remote GPU Server (see below).asr_backend: auto in config.yaml (default). The pipeline tries NeMo first; if the import fails, it automatically falls back to Whisper and logs which backend is active.The primary ASR model (EgypTalk-ASR-v2) requires NVIDIA's nemo_toolkit, which cannot compile natively on Apple Silicon. To bypass this, the pipeline includes a Remote GPU Inference Server.
nemagpu.py runs on any machine with an NVIDIA GPU (e.g., Google Colab, a Linux server, cloud VM)/transcribe HTTP endpoint via Ngroknemagpu.py to Google Colab or open the linked notebookconfig.yaml → colab_api_urlasr_backend: "colab_api" in config.yaml# Install dependencies
pip install nemo_toolkit[asr] flask pyngrok huggingface_hub
# Set your Ngrok auth token (get one at https://ngrok.com)
export NGROK_AUTHTOKEN="your_token_here"
# Start the server
python nemagpu.py
Note: This script requires CUDA (NVIDIA GPU). It will not work on Mac M1/M2/M3 (Apple Silicon) because NeMo depends on CUDA for model inference. The whole purpose of this server is to offload GPU-bound work to a CUDA-capable machine while your Mac runs the rest of the pipeline.
Egyptian Arabic (عامية مصرية) differs substantially from Modern Standard Arabic (فصحى) in vocabulary, grammar, and phonology. Nile-Chat-4B was specifically trained on Egyptian dialect data, and the system prompt explicitly enforces dialectal output and prohibits MSA.
Egyptian Arabic has no standardized orthography. The same word may be written multiple ways (e.g., عاوز vs عايز). The WER scorer normalizes common variants (alef forms, taa marbuta) to reduce false mismatches.
Even dialect-specialized models may occasionally mix English or MSA into generated text. The system prompt strictly prohibits this, but generated data should be spot-checked. Nile-Chat-4B is less prone to this than general-purpose models since it was trained predominantly on Egyptian Arabic data.
Egyptian Arabic is low-resource compared to MSA. This pipeline exists precisely to address this gap through controlled synthetic data generation.
Every synthesized sample is scored by Word Error Rate (WER): how well the TTS output can be transcribed back to the original text. Low WER = the speech clearly represents the intended text.
| Condition | Label |
|---|---|
| WER < 15% | auto_approved |
| WER > 40% | auto_rejected |
| Everything else | needs_review |
TTS naturalness: Synthetic speech lacks the prosodic variation, hesitations, and emotional range of natural recordings. Models fine-tuned on this data should be supplemented with real speech data — which is exactly what Track B provides.
Domain bias: Nile-Chat-4B generates text based on its training data (3.3B tokens of Egyptian web text). The generated text may over-represent certain patterns or vocabulary common in the training corpus, while missing niche or regional expressions.
NeMo complexity: The full NeMo toolkit install pulls ~2GB of dependencies and requires CUDA. On Mac M1 systems, use the Colab API bridge or Whisper fallback.
WER on synthetic speech: WER is computed by transcribing TTS output back through ASR. This creates a circular dependency — both models may share similar failure modes, potentially over-approving certain error patterns.
Track B source dependency: Real speech quality depends entirely on the YouTube source. Background music, poor microphone quality, or heavy code-switching in the source video will produce low-quality segments. WER filtering catches most of these but manual spot-checking is still recommended.
Track B copyright: Only Creative Commons licensed YouTube content should be used. The pipeline does not automatically verify CC licensing — this is the operator's responsibility when populating youtube_urls in config.yaml.
No manual setup required. Just run main.py with any system Python 3.9+:
cd ssdp/
python main.py --all
On first run, main.py will automatically:
.venv/ virtual environment inside the project directoryrequirements.txt into .venv (never globally)Subsequent runs detect the existing .venv and skip straight to execution.
Optional heavy dependency (install manually inside the venv if needed):
source .venv/bin/activate pip install nemo_toolkit[asr] # Primary ASR (may fail on Mac M1)
The defaults in config.yaml are pre-set for Mac M1 (MPS GPU):
text_gen_device: "mps" # Use "mps" on Mac M1, "cuda" on NVIDIA, "cpu" otherwise
tts_device: "mps" # Same device options for TTS
asr_backend: "colab_api" # "colab_api" for remote GPU, "auto" tries NeMo → Whisper
# Track B — Real Speech Harvesting
track_b_enabled: true
youtube_urls:
- "https://www.youtube.com/watch?v=YOUR_CC_URL" # CC-licensed Egyptian Arabic content only
min_segment_duration: 2.0 # seconds — discard clips shorter than this
max_segment_duration: 15.0 # seconds — discard clips longer than this
max_non_arabic_ratio: 0.30 # discard segments with >30% non-Arabic characters
num_real_samples: 100 # stop after harvesting this many approved segments
whisperx_model: "medium" # "medium" recommended for CPU; "large-v3" for GPU
whisperx_device: "cpu" # cpu / cuda / mps
Important: Only add YouTube URLs with Creative Commons licenses to
youtube_urls. Filter for CC content atyoutube.com/results?search_query=مصري&sp=EgQIAxAB.
# Run individual stages (venv auto-created on first run)
python main.py --stage 1 # Generate text prompts (Track A)
python main.py --stage 2 # Synthesize audio (Track A)
python main.py --stage 1b # Harvest real speech from YouTube (Track B)
python main.py --stage 3 # Auto-score + launch review UI (both tracks)
python main.py --stage 4 # Export approved samples (both tracks)
# Run Track B only
python main.py --track-b
# Run everything sequentially (Track A → Track B → Review → Export)
python main.py --all
| Issue | Solution |
|---|---|
| NeMo install fails on Mac M1 | Use asr_backend: "colab_api" or "whisper" in config.yaml |
| Nile-Chat-4B OOM | Use text_gen_device: "cpu" (slower but lower memory) |
| TTS fails on some samples | Pipeline retries 3x with exponential backoff, marks failures |
| Streamlit won't launch | Run directly: streamlit run stages/stage3_review.py |
Colab API returns Hypothesis(...) | Pipeline auto-cleans NeMo Hypothesis objects via regex |
| WhisperX install fails | Run pip install faster-whisper ctranslate2 first, then pip install whisperx |
| yt-dlp download fails | Check YouTube URL is valid and CC-licensed. Run yt-dlp --update to update |
| WhisperX very slow on CPU | Set whisperx_model: "medium" in config.yaml (~1.5GB vs 3GB, still good Arabic) |
| Track B produces no segments | Check min_segment_duration and max_non_arabic_ratio thresholds in config.yaml |
| Stage 1b skips all URLs | Set track_b_enabled: true in config.yaml |
ssdp/
├── config.yaml # All externalized config (includes Track B settings)
├── main.py # CLI entrypoint (--stage 1/2/1b/3/4, --all, --track-b)
├── nemagpu.py # Remote ASR server (run on NVIDIA GPU machine)
├── stages/
│ ├── stage1_generate.py # Track A: text generation via Nile-Chat-4B
│ ├── stage2_synthesize.py # Track A: TTS synthesis via NAMAA
│ ├── stage1b_harvest.py # Track B: YouTube download → align → segment → WER score
│ ├── stage3_review.py # Unified review: merges Track A + B, WER scoring, Streamlit UI
│ └── stage4_export.py # HuggingFace dataset export (source field: synthetic/real)
├── utils/
│ ├── checkpoint.py # Resume/checkpoint logic (atomic writes)
│ ├── wer.py # WER scoring with Arabic normalization
│ └── logger.py # Structured dual-output logging
├── data/
│ ├── manifests/ # JSON manifests per stage
│ │ ├── stage1_prompts.json
│ │ ├── stage2_manifest.json
│ │ ├── stage1b_manifest.json # Track B output manifest
│ │ ├── stage1b_download_progress.json # Track B download checkpoint
│ │ ├── stage1b_alignment_{id}.json # Track B alignment cache per video
│ │ ├── stage1b_segment_progress.json # Track B segment scoring checkpoint
│ │ └── stage3_scores_progress.json
│ ├── audio/
│ │ ├── *.wav # Track A synthesized audio
│ │ └── real/
│ │ ├── raw/ # Full downloaded YouTube audio (pre-segmentation)
│ │ └── *.wav # Track B sentence segments
│ └── reviewed/
├── logs/
│ └── pipeline.log # Structured log with timestamps
├── requirements.txt
└── README.md
This pipeline is provided as-is for research and educational purposes. The generated data inherits the licenses of the underlying models (Nile-Chat-4B, NAMAA TTS, EgypTalk ASR, Whisper).
Python
0
15 commits
updated May 13, 2026
A production-grade pipeline for generating synthetic speech training data targeting fine-tuning of Speech-to-Text (STT) models on Egyptian Arabic (عامية مصرية).
The pipeline runs two parallel data tracks that merge into a unified review and export stage.
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ TRACK A — Synthetic Data │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ STAGE 1 │ │ STAGE 2 │ │
│ │ Text Generation │────▶│ TTS Synthesis │──────────────────────┐ │
│ │ │ │ │ │ │
│ │ Nile-Chat-4B → │ │ NAMAA-Egyptian-TTS │ │ │
│ │ Egyptian prompts │ │ → .wav audio files │ │ │
│ └─────────────────────┘ └─────────────────────┘ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ stage1_prompts.json stage2_manifest.json │ │
│ stage1_checkpoint.json stage2_progress.json │ │
└────────────────────────────────────────────────────────────────────────────┼────────────┘
│
┌────────────────────────────────────────────────────────────────────────────┼────────────┐
│ TRACK B — Real Speech │ │
│ │ │
│ ┌──────────────────────────────────────────────┐ │ │
│ │ STAGE 1B — Real Speech Harvesting │ │ │
│ │ │ │ │
│ │ yt-dlp downloads YouTube audio (1) │ │ │
│ │ WhisperX transcribes + force-aligns (2) │ │ │
│ │ pydub splits into sentence segments (3) │──────────────────────────┘ │
│ │ WER scores each segment (4) │ │
│ │ → data/audio/real/*.wav │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ stage1b_manifest.json │
│ stage1b_download_progress.json │
│ stage1b_alignment_{video_id}.json │
│ stage1b_segment_progress.json │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│
┌───────────────────▼───────────────────┐
│ STAGE 3 — Unified Review & Score │
│ │
│ Merges Track A + Track B samples │
│ WER auto-labeling (same thresholds) │
│ Streamlit UI with 🤖/🎙️ source badge │
└───────────────────┬───────────────────┘
│
▼
stage3_scores.json
stage3_scores_progress.json
│
┌───────────────────▼───────────────────┐
│ STAGE 4 — Dataset Export │
│ │
│ HuggingFace Dataset + CSV │
│ source field: "synthetic" / "real" │
└───────────────────────────────────────┘
Every stage writes a manifest. Any stage can be re-run independently. All stages are fully resumable via checkpoints — a crash at sample 150 restarts from 151, not from zero.
config.yaml. Only Creative Commons licensed content should be used to avoid copyright issues..wav segments using the WhisperX timestamps. Each segment is resampled to 22050 Hz mono to match NAMAA TTS output format for consistency across the dataset.config.yaml (Colab API or Groq Whisper fallback) and scored against the WhisperX transcript. Same thresholds as Track A apply.nemo_toolkit[asr], one of the heaviest ML installs in Python (PyTorch Lightning, Hydra, Apex, etc.). Known to have installation issues on Mac M1/ARM systems. The pipeline solves this via a Remote GPU Server (see below).asr_backend: auto in config.yaml (default). The pipeline tries NeMo first; if the import fails, it automatically falls back to Whisper and logs which backend is active.The primary ASR model (EgypTalk-ASR-v2) requires NVIDIA's nemo_toolkit, which cannot compile natively on Apple Silicon. To bypass this, the pipeline includes a Remote GPU Inference Server.
nemagpu.py runs on any machine with an NVIDIA GPU (e.g., Google Colab, a Linux server, cloud VM)/transcribe HTTP endpoint via Ngroknemagpu.py to Google Colab or open the linked notebookconfig.yaml → colab_api_urlasr_backend: "colab_api" in config.yaml# Install dependencies
pip install nemo_toolkit[asr] flask pyngrok huggingface_hub
# Set your Ngrok auth token (get one at https://ngrok.com)
export NGROK_AUTHTOKEN="your_token_here"
# Start the server
python nemagpu.py
Note: This script requires CUDA (NVIDIA GPU). It will not work on Mac M1/M2/M3 (Apple Silicon) because NeMo depends on CUDA for model inference. The whole purpose of this server is to offload GPU-bound work to a CUDA-capable machine while your Mac runs the rest of the pipeline.
Egyptian Arabic (عامية مصرية) differs substantially from Modern Standard Arabic (فصحى) in vocabulary, grammar, and phonology. Nile-Chat-4B was specifically trained on Egyptian dialect data, and the system prompt explicitly enforces dialectal output and prohibits MSA.
Egyptian Arabic has no standardized orthography. The same word may be written multiple ways (e.g., عاوز vs عايز). The WER scorer normalizes common variants (alef forms, taa marbuta) to reduce false mismatches.
Even dialect-specialized models may occasionally mix English or MSA into generated text. The system prompt strictly prohibits this, but generated data should be spot-checked. Nile-Chat-4B is less prone to this than general-purpose models since it was trained predominantly on Egyptian Arabic data.
Egyptian Arabic is low-resource compared to MSA. This pipeline exists precisely to address this gap through controlled synthetic data generation.
Every synthesized sample is scored by Word Error Rate (WER): how well the TTS output can be transcribed back to the original text. Low WER = the speech clearly represents the intended text.
| Condition | Label |
|---|---|
| WER < 15% | auto_approved |
| WER > 40% | auto_rejected |
| Everything else | needs_review |
TTS naturalness: Synthetic speech lacks the prosodic variation, hesitations, and emotional range of natural recordings. Models fine-tuned on this data should be supplemented with real speech data — which is exactly what Track B provides.
Domain bias: Nile-Chat-4B generates text based on its training data (3.3B tokens of Egyptian web text). The generated text may over-represent certain patterns or vocabulary common in the training corpus, while missing niche or regional expressions.
NeMo complexity: The full NeMo toolkit install pulls ~2GB of dependencies and requires CUDA. On Mac M1 systems, use the Colab API bridge or Whisper fallback.
WER on synthetic speech: WER is computed by transcribing TTS output back through ASR. This creates a circular dependency — both models may share similar failure modes, potentially over-approving certain error patterns.
Track B source dependency: Real speech quality depends entirely on the YouTube source. Background music, poor microphone quality, or heavy code-switching in the source video will produce low-quality segments. WER filtering catches most of these but manual spot-checking is still recommended.
Track B copyright: Only Creative Commons licensed YouTube content should be used. The pipeline does not automatically verify CC licensing — this is the operator's responsibility when populating youtube_urls in config.yaml.
No manual setup required. Just run main.py with any system Python 3.9+:
cd ssdp/
python main.py --all
On first run, main.py will automatically:
.venv/ virtual environment inside the project directoryrequirements.txt into .venv (never globally)Subsequent runs detect the existing .venv and skip straight to execution.
Optional heavy dependency (install manually inside the venv if needed):
source .venv/bin/activate pip install nemo_toolkit[asr] # Primary ASR (may fail on Mac M1)
The defaults in config.yaml are pre-set for Mac M1 (MPS GPU):
text_gen_device: "mps" # Use "mps" on Mac M1, "cuda" on NVIDIA, "cpu" otherwise
tts_device: "mps" # Same device options for TTS
asr_backend: "colab_api" # "colab_api" for remote GPU, "auto" tries NeMo → Whisper
# Track B — Real Speech Harvesting
track_b_enabled: true
youtube_urls:
- "https://www.youtube.com/watch?v=YOUR_CC_URL" # CC-licensed Egyptian Arabic content only
min_segment_duration: 2.0 # seconds — discard clips shorter than this
max_segment_duration: 15.0 # seconds — discard clips longer than this
max_non_arabic_ratio: 0.30 # discard segments with >30% non-Arabic characters
num_real_samples: 100 # stop after harvesting this many approved segments
whisperx_model: "medium" # "medium" recommended for CPU; "large-v3" for GPU
whisperx_device: "cpu" # cpu / cuda / mps
Important: Only add YouTube URLs with Creative Commons licenses to
youtube_urls. Filter for CC content atyoutube.com/results?search_query=مصري&sp=EgQIAxAB.
# Run individual stages (venv auto-created on first run)
python main.py --stage 1 # Generate text prompts (Track A)
python main.py --stage 2 # Synthesize audio (Track A)
python main.py --stage 1b # Harvest real speech from YouTube (Track B)
python main.py --stage 3 # Auto-score + launch review UI (both tracks)
python main.py --stage 4 # Export approved samples (both tracks)
# Run Track B only
python main.py --track-b
# Run everything sequentially (Track A → Track B → Review → Export)
python main.py --all
| Issue | Solution |
|---|---|
| NeMo install fails on Mac M1 | Use asr_backend: "colab_api" or "whisper" in config.yaml |
| Nile-Chat-4B OOM | Use text_gen_device: "cpu" (slower but lower memory) |
| TTS fails on some samples | Pipeline retries 3x with exponential backoff, marks failures |
| Streamlit won't launch | Run directly: streamlit run stages/stage3_review.py |
Colab API returns Hypothesis(...) | Pipeline auto-cleans NeMo Hypothesis objects via regex |
| WhisperX install fails | Run pip install faster-whisper ctranslate2 first, then pip install whisperx |
| yt-dlp download fails | Check YouTube URL is valid and CC-licensed. Run yt-dlp --update to update |
| WhisperX very slow on CPU | Set whisperx_model: "medium" in config.yaml (~1.5GB vs 3GB, still good Arabic) |
| Track B produces no segments | Check min_segment_duration and max_non_arabic_ratio thresholds in config.yaml |
| Stage 1b skips all URLs | Set track_b_enabled: true in config.yaml |
ssdp/
├── config.yaml # All externalized config (includes Track B settings)
├── main.py # CLI entrypoint (--stage 1/2/1b/3/4, --all, --track-b)
├── nemagpu.py # Remote ASR server (run on NVIDIA GPU machine)
├── stages/
│ ├── stage1_generate.py # Track A: text generation via Nile-Chat-4B
│ ├── stage2_synthesize.py # Track A: TTS synthesis via NAMAA
│ ├── stage1b_harvest.py # Track B: YouTube download → align → segment → WER score
│ ├── stage3_review.py # Unified review: merges Track A + B, WER scoring, Streamlit UI
│ └── stage4_export.py # HuggingFace dataset export (source field: synthetic/real)
├── utils/
│ ├── checkpoint.py # Resume/checkpoint logic (atomic writes)
│ ├── wer.py # WER scoring with Arabic normalization
│ └── logger.py # Structured dual-output logging
├── data/
│ ├── manifests/ # JSON manifests per stage
│ │ ├── stage1_prompts.json
│ │ ├── stage2_manifest.json
│ │ ├── stage1b_manifest.json # Track B output manifest
│ │ ├── stage1b_download_progress.json # Track B download checkpoint
│ │ ├── stage1b_alignment_{id}.json # Track B alignment cache per video
│ │ ├── stage1b_segment_progress.json # Track B segment scoring checkpoint
│ │ └── stage3_scores_progress.json
│ ├── audio/
│ │ ├── *.wav # Track A synthesized audio
│ │ └── real/
│ │ ├── raw/ # Full downloaded YouTube audio (pre-segmentation)
│ │ └── *.wav # Track B sentence segments
│ └── reviewed/
├── logs/
│ └── pipeline.log # Structured log with timestamps
├── requirements.txt
└── README.md
This pipeline is provided as-is for research and educational purposes. The generated data inherits the licenses of the underlying models (Nile-Chat-4B, NAMAA TTS, EgypTalk ASR, Whisper).