A real-time ASL word-level recognition system that captures live webcam video, processes hand/body movements frame-by-frame using MediaPipe, and predicts the signed word using deep learning models trained on the WLASL dataset.
- Architecture — Line 61
- Quick Start — Line 93
- 1. Environment Setup — Line 95
- Installing PyTorch with CUDA support — Line 133
- 2. Download the Dataset — Line 169
- Option A: Kaggle (Recommended) — Line 171
- Option B: Official WLASL scripts — Line 236
- Validate downloaded videos — Line 247
- End-to-end quick start with Kaggle — Line 264
- Choosing a wlasl_variant with Kaggle — Line 305
- Device-specific configuration after Kaggle download — Line 320
- 3. Preprocess Data — Line 383
- Working with multiple variants — Line 409
- 4. Train a Model — Line 464
- 5. Evaluate — Line 499
- 6. Run the Live Demo — Line 525
- 7. Single Video Prediction — Line 555
- 8. Export to ONNX — Line 607
- 9. Run Tests — Line 635
- 1. Environment Setup — Line 95
- Project Structure — Line 678
- Configuration Guide — Line 745
- Auto-Configure for Your Hardware — Line 759
- Approach Details — Line 881
- Approach A: Pose/Keypoint Transformer — Line 883
- Approach B: RGB Video Classifier — Line 903
- Approach C: Hybrid Fusion — Line 911
- Troubleshooting — Line 919
- HTML files masquerading as videos — Line 921
- MediaPipe installation issues — Line 924
- CUDA out of memory — Line 936
- Webcam not detected — Line 942
- Low accuracy — Line 948
- Diagnosing partial data — Line 953
- wlasl_variant / num_classes mismatch — Line 1000
- Recommended Configurations — Line 1006
- WLASL100 (recommended starting point) — Line 1010
- WLASL300 — Line 1043
- WLASL1000 / WLASL2000 — Line 1065
- Video Classifier (Approach B) — Line 1089
- Fusion (Approach C) — Line 1105
- Tips & Best Practices — Line 1124
- Hardware-Specific Setup — Line 1126
- Training with Limited Data — Line 1149
- Improving Accuracy — Line 1161
- What to Expect — Line 1172
- Common Pitfalls — Line 1184
- Recommended Library & CUDA Versions — Line 1195
- PyTorch ↔ CUDA Compatibility — Line 1199
- MediaPipe — Line 1218
- Other Key Libraries — Line 1231
- Citation — Line 1248
- License — Line 1260
APPROACH A (Pose/Keypoint)
+-------------------------------+
| MediaPipe Transformer/ |
Webcam --> Frame --+ Holistic --> LSTM Encoder --+--> Predicted
Feed Buffer | Keypoints (T, 543*6) | Gloss +
(OpenCV) (T=64) | + Velocity | Confidence
+-------------------------------+
| |
| APPROACH B (RGB Video) |
| R(2+1)D / SlowFast / R3D |
| (B, 3, T, 224, 224) |
+-------------------------------+
| |
| APPROACH C (Hybrid Fusion) |
| Concat / Cross-Attention |
| of Pose + Video features |
+-------------------------------+
Three approaches are implemented:
| Approach | Model | Input | WLASL-100 Top-1 (expected) |
|---|---|---|---|
| A - Pose Transformer | Transformer Encoder / BiLSTM | MediaPipe keypoints (T, 543, 6) with velocity | 60–70% |
| B - Video Classifier | R(2+1)D-18, R3D-18, SlowFast | RGB frames (3, T, 224, 224) | 65–75% |
| C - Hybrid Fusion | Concat / Cross-Attention fusion of A+B | Both streams | 70–78% |
Linux/macOS:
git clone <this-repo-url>
cd "Live American Sign Language Recognition using WLASL"
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtWindows (PowerShell):
git clone <this-repo-url>
cd "Live American Sign Language Recognition using WLASL"
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txtWindows (Command Prompt):
git clone <this-repo-url>
cd "Live American Sign Language Recognition using WLASL"
python -m venv .venv
.venv\Scripts\activate.bat
pip install -r requirements.txtThe default pip install -r requirements.txt installs the CPU-only version of PyTorch. If you have an NVIDIA GPU, install the CUDA-enabled version before running pip install -r requirements.txt (or after, to overwrite the CPU version):
# CUDA 12.4 (recommended for modern GPUs — RTX 30xx/40xx/50xx)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
# CUDA 12.1
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# CUDA 11.8 (for older GPUs or driver versions)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# CPU only (default — no flag needed, but explicit if you want to be sure)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpuTo check which CUDA version your driver supports:
nvidia-smi # look for "CUDA Version" in the top-right cornerAfter installing, verify CUDA is available:
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}, version: {torch.version.cuda}')"These commands work identically on Linux, macOS, and Windows.
Windows note: All
pythoncommands in this README work on both platforms. When you see\at the end of a line (bash line continuation), replace it with`(backtick) in PowerShell or^in Command Prompt. Platform-specific shell commands (file operations, venv activation) show both variants where they differ.
The full WLASL video archive (~12,000 videos, ~5 GB) is available on Kaggle. This is the fastest way to get the data since it downloads as a single archive.
One-time Kaggle API setup:
Linux/macOS:
# kaggle is already included in requirements.txt, so if you ran
# pip install -r requirements.txt, it's already installed.
# Otherwise: pip install kaggle
# Get your API token from https://www.kaggle.com/settings → "Create New Token"
# Move the downloaded kaggle.json to ~/.kaggle/
mkdir -p ~/.kaggle
mv ~/Downloads/kaggle.json ~/.kaggle/kaggle.json
chmod 600 ~/.kaggle/kaggle.jsonWindows (PowerShell):
# Get your API token from https://www.kaggle.com/settings → "Create New Token"
# Move the downloaded kaggle.json to %USERPROFILE%\.kaggle\
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.kaggle"
Move-Item "$env:USERPROFILE\Downloads\kaggle.json" "$env:USERPROFILE\.kaggle\kaggle.json"
# No chmod needed on WindowsWindows (Command Prompt):
mkdir "%USERPROFILE%\.kaggle"
move "%USERPROFILE%\Downloads\kaggle.json" "%USERPROFILE%\.kaggle\kaggle.json"Download:
python scripts/download_kaggle.py --subset WLASL100This downloads all ~12K videos to data/raw/ (the full archive, regardless of --subset), fetches the annotation JSON, and prints a summary of how many videos match the chosen subset. The --subset flag only controls the summary output — the download itself always fetches the full dataset.
You can also use the Kaggle CLI directly:
Linux/macOS:
kaggle datasets download -d risangbaskoro/wlasl-processed -p data/_kaggle_download --unzip
mv data/_kaggle_download/videos/*.mp4 data/raw/
rm -rf data/_kaggle_downloadWindows (PowerShell):
kaggle datasets download -d risangbaskoro/wlasl-processed -p data\_kaggle_download --unzip
Move-Item data\_kaggle_download\videos\*.mp4 data\raw\
Remove-Item -Recurse -Force data\_kaggle_downloadNote: The Kaggle archive contains all ~12K videos for all WLASL variants (100–2000). You only download once — the preprocessing step (Step 3) filters to your chosen subset.
# Download the WLASL annotation JSON and print video download instructions
python scripts/download_wlasl.py --subset WLASL100This creates data/annotations/WLASL_v0.3.json and the directory structure under data/. Follow the printed instructions to download the actual video files from the official WLASL repo or a community mirror, then place them in data/raw/.
Note: Many original WLASL URLs have expired. The Kaggle option (Option A) typically provides significantly more videos.
Many WLASL URLs have expired. When a URL is dead, servers often return an HTML redirect page (saved as .mp4) instead of a 404. This applies to both Kaggle and URL-based downloads. Run the validator before preprocessing to remove these fake files:
# Report how many invalid files exist
python scripts/validate_videos.py --video-dir data/raw
# Delete invalid files (HTML redirects and empty files)
python scripts/validate_videos.py --video-dir data/raw --delete
# Delete and save a list of valid video IDs for reference
python scripts/validate_videos.py --video-dir data/raw --delete --save-valid data/valid_ids.txtThe validator checks the first 256 bytes of each file for HTML signatures (<!DOCTYPE html>, <html>, etc.) and reports counts of valid / HTML / empty files. The preprocessing pipeline also skips unreadable files automatically, but cleaning them up first saves processing time.
If you want the fastest path from zero to training, run these commands in order (all python commands are cross-platform):
# 1. Setup
pip install -r requirements.txt
# 2. Configure Kaggle API (one-time — see "One-time Kaggle API setup" above)
# 3. Download all videos from Kaggle (~5 GB, ~12K videos)
# --subset only controls the annotation summary printed after download;
# the full archive is always downloaded regardless of the variant chosen.
python scripts/download_kaggle.py --subset WLASL100
# 4. Validate and clean up bad files
python scripts/validate_videos.py --video-dir data/raw --delete
# 5. Extract keypoints
python -m src.data.preprocess --data-dir data --subset WLASL100 --mode keypoints
# 6. Train (see device-specific configs below)
python -m src.training.train --config configs/pose_transformer.yaml
# 7. Evaluate
# Linux/macOS uses \ for line continuation; Windows PowerShell uses `
python -m src.training.evaluate \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt \
--split val --output-dir eval_resultsOn Windows PowerShell, the evaluate command (step 7) becomes:
python -m src.training.evaluate `
--config configs/pose_transformer.yaml `
--checkpoint checkpoints/best_model.pt `
--split val --output-dir eval_resultsThe Kaggle archive contains all ~12K videos covering every WLASL variant. After downloading, you choose which variant to train on by setting wlasl_variant in your YAML config (and matching the --subset flag during preprocessing). The variant controls the number of sign classes — num_classes is auto-derived, so you never set it manually.
| Variant | Classes | Approx. Training Samples | Difficulty | Recommended For |
|---|---|---|---|---|
wlasl_variant: 100 |
100 | 800–1,200 | Easiest | First-time setup, prototyping, CPU training |
wlasl_variant: 300 |
300 | 2,000–3,500 | Moderate | Better vocabulary coverage with a GPU |
wlasl_variant: 1000 |
1,000 | 5,000–8,000 | Hard | Research, large-GPU setups |
wlasl_variant: 2000 |
2,000 | 8,000–12,000 | Hardest | Full dataset, consider fusion (Approach C) |
Start with wlasl_variant: 100 — it has the most samples per class, trains fastest, and gives the highest per-class accuracy. Scale up once your pipeline is working.
Larger variants need more model capacity. See the Recommended Configurations section for variant-specific hyperparameters (deeper layers, wider d_model, adjusted LR/dropout).
After downloading the Kaggle dataset, adjust configs/pose_transformer.yaml for your hardware before training (step 6). Set wlasl_variant to match the subset you preprocessed in step 5.
CPU-only (no GPU):
approach: pose_transformer
wlasl_variant: 100 # match your preprocessed subset (100, 300, 1000, or 2000)
fp16: false # FP16 only works on CUDA
batch_size: 8 # smaller batches to avoid memory pressure
num_workers: 2
T: 64
d_model: 256
dropout: 0.3
lr: 1.0e-4
scheduler: onecycle
weighted_sampling: true
epochs: 100Use --device cpu for inference and live demo. Stick to Approach A (pose_transformer) — video models are too slow on CPU for training.
GPU / CUDA:
approach: pose_transformer
wlasl_variant: 100 # match your preprocessed subset (100, 300, 1000, or 2000)
fp16: true # faster training, lower memory
batch_size: 32 # increase to 64 for large GPUs
num_workers: 4
T: 64
d_model: 256
dropout: 0.3
lr: 1.0e-4
scheduler: onecycle
weighted_sampling: true
epochs: 100Monitor GPU memory with nvidia-smi. If you run out of memory, reduce batch_size first, then T.
Apple Silicon (M1/M2/M3/M4):
approach: pose_transformer
wlasl_variant: 100 # match your preprocessed subset (100, 300, 1000, or 2000)
fp16: false # MPS does not support FP16 reliably
batch_size: 16
num_workers: 2
T: 64
d_model: 256
dropout: 0.3
lr: 1.0e-4
scheduler: onecycle
weighted_sampling: true
epochs: 100Use --device cpu for the live demo to avoid MPS overhead. Install MediaPipe with pip install mediapipe-silicon if the standard package fails.
The preprocessing pipeline is source-agnostic — it reads mp4 files from data/raw/ regardless of whether they were downloaded via Kaggle (Option A) or URL-based scripts (Option B). No extra flags or options are needed.
Extract MediaPipe Holistic keypoints (543 landmarks per frame) from all valid videos:
python -m src.data.preprocess --data-dir data --subset WLASL100 --mode keypointsThis creates:
data/processed/<video_id>.npy # keypoints, shape (T, 543, 3) — shared across variants
data/splits/WLASL100/train.csv # variant-specific split files
data/splits/WLASL100/val.csv
data/splits/WLASL100/test.csv
To extract raw frames instead (needed for Approach B/C training):
python -m src.data.preprocess --data-dir data --subset WLASL100 --mode framesUse --max-workers N to control parallel extraction (default: 4).
WLASL100 ⊂ WLASL300 ⊂ WLASL1000 ⊂ WLASL2000 — each larger variant is a superset of the smaller ones. You can preprocess multiple variants without conflicts:
data/raw/anddata/processed/are shared — keypoints are stored byvideo_idand reused across variants. Already-extracted.npyfiles are skipped automatically.data/splits/WLASL{N}/is variant-specific — each variant gets its owntrain/val/test.csvfiles that never overwrite each other.
With Kaggle (recommended): Since Kaggle downloads all ~12K videos at once, you already have all the data. Just preprocess each variant:
# Download once (all variants included)
python scripts/download_kaggle.py --subset WLASL100
# Preprocess WLASL100
python -m src.data.preprocess --data-dir data --subset WLASL100
# Scale up — only new videos are extracted; WLASL100 splits are untouched
python -m src.data.preprocess --data-dir data --subset WLASL300With URL-based download: Download annotations per variant, then add videos:
# First variant
python scripts/download_wlasl.py --subset WLASL100
# ...download videos to data/raw/...
python -m src.data.preprocess --data-dir data --subset WLASL100
# Scale up — only new videos are extracted; WLASL100 splits are untouched
python scripts/download_wlasl.py --subset WLASL300
# ...download the additional WLASL300 videos to data/raw/...
python -m src.data.preprocess --data-dir data --subset WLASL300After two variants, your data/ tree looks like:
data/
├── raw/ # All videos (shared)
├── processed/ # .npy keypoints by video_id (shared)
├── annotations/
│ └── WLASL_v0.3.json
└── splits/
├── WLASL100/ # 100-class splits
│ ├── train.csv
│ ├── val.csv
│ └── test.csv
└── WLASL300/ # 300-class splits — coexist safely
├── train.csv
├── val.csv
└── test.csv
Set wlasl_variant in your config to match the subset you preprocessed. All scripts automatically resolve split files from data/splits/WLASL{N}/.
# Approach A: Pose Transformer (recommended starting point)
python -m src.training.train --config configs/pose_transformer.yaml
# Approach B: Video Classifier
python -m src.training.train --config configs/video_classifier.yaml
# Approach C: Hybrid Fusion
python -m src.training.train --config configs/fusion.yamlTraining checkpoints are saved to checkpoints/ and logs to logs/.
# Monitor training in real time
tensorboard --logdir logs/To train on a different variant, either edit wlasl_variant in an existing config or use a separate config file:
# Copy and modify — only change wlasl_variant (num_classes is auto-derived)
cp configs/pose_transformer.yaml configs/pose_wlasl300.yaml # Linux/macOS
# copy configs\pose_transformer.yaml configs\pose_wlasl300.yaml # Windows
# Edit wlasl_variant: 300 in the new file
python -m src.training.train --config configs/pose_wlasl300.yamlLinux/macOS:
python -m src.training.evaluate \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt \
--split val \
--output-dir eval_resultsWindows (PowerShell):
python -m src.training.evaluate `
--config configs/pose_transformer.yaml `
--checkpoint checkpoints/best_model.pt `
--split val `
--output-dir eval_resultsThis prints top-1/top-5 accuracy, per-class breakdown, and saves a confusion matrix heatmap to eval_results/.
Linux/macOS:
python -m src.inference.live_demo \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt \
--camera 0 \
--device cpuWindows (PowerShell):
python -m src.inference.live_demo `
--config configs/pose_transformer.yaml `
--checkpoint checkpoints/best_model.pt `
--camera 0 `
--device cpuControls:
q— quits— save the current prediction to a log file
The demo runs three threads: a capture thread reads webcam frames continuously, an inference thread runs the model every 0.5 s on a rolling buffer of T frames, and the main thread renders the overlay. Predictions are smoothed over the last 5 inference windows and only displayed when confidence exceeds the configured threshold (default: 0.6).
Linux/macOS:
# From a video file
python -m src.inference.predict \
--video path/to/video.mp4 \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt
# From a pre-extracted keypoint .npy file
python -m src.inference.predict \
--keypoints data/processed/12345.npy \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt
# Specify device (auto, cpu, cuda, mps)
python -m src.inference.predict \
--video path/to/video.mp4 \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt \
--device cpuWindows (PowerShell):
# From a video file
python -m src.inference.predict `
--video path\to\video.mp4 `
--config configs\pose_transformer.yaml `
--checkpoint checkpoints\best_model.pt
# From a pre-extracted keypoint .npy file
python -m src.inference.predict `
--keypoints data\processed\12345.npy `
--config configs\pose_transformer.yaml `
--checkpoint checkpoints\best_model.pt
# Specify device (auto, cpu, cuda, mps)
python -m src.inference.predict `
--video path\to\video.mp4 `
--config configs\pose_transformer.yaml `
--checkpoint checkpoints\best_model.pt `
--device cpuReturns the predicted gloss, confidence score, and top-5 alternatives.
Linux/macOS:
python -m src.inference.export_onnx \
--config configs/pose_transformer.yaml \
--checkpoint checkpoints/best_model.pt \
--output model.onnx \
--verify \
--benchmarkWindows (PowerShell):
python -m src.inference.export_onnx `
--config configs/pose_transformer.yaml `
--checkpoint checkpoints/best_model.pt `
--output model.onnx `
--verify `
--benchmark--verify runs a forward pass through ONNX Runtime to confirm output shapes match. --benchmark measures average inference latency over 100 runs. Use --opset N to set the ONNX opset version (default: 17).
Linux/macOS:
source .venv/bin/activate
python -m pytest # full test suite (277 tests)
python -m pytest tests/test_augment.py # specific test file
python -m pytest tests/test_dependencies.py # dependency compatibility tests
python -m pytest -q # quiet outputOr without activating the venv:
.venv/bin/python -m pytestWindows (PowerShell):
.venv\Scripts\Activate.ps1
python -m pytest # full test suite (277 tests)
python -m pytest tests\test_augment.py # specific test file
python -m pytest -q # quiet outputOr without activating the venv:
.venv\Scripts\python -m pytestWindows (Command Prompt):
.venv\Scripts\activate.bat
python -m pytestTests are fully isolated — they use pytest's tmp_path fixture for all file I/O and never touch project data, configs, or checkpoints. The pyproject.toml configures test discovery, verbose output, and warning suppression.
Note: pytest must be installed in the venv (pip install pytest). It is not listed in requirements.txt because it is a dev-only dependency.
The test_dependencies.py file (110 tests) verifies that every third-party library used by the src/ code is importable, meets the minimum version from requirements.txt, and that the specific features relied upon (e.g. batch_first Transformers, label_smoothing in CrossEntropyLoss, seaborn heatmaps, ONNX Runtime sessions) work correctly with the installed versions.
.
├── configs/
│ ├── pose_transformer.yaml # Approach A defaults
│ ├── video_classifier.yaml # Approach B defaults
│ └── fusion.yaml # Approach C defaults
├── data/
│ ├── raw/ # Downloaded video files — Kaggle or URL-based (shared)
│ ├── processed/ # Extracted keypoints as .npy (shared across variants)
│ ├── annotations/ # WLASL JSON annotation file
│ └── splits/
│ ├── WLASL100/ # train/val/test CSVs for 100-class variant
│ ├── WLASL300/ # train/val/test CSVs for 300-class variant
│ └── ... # one subdirectory per variant
├── src/
│ ├── data/
│ │ ├── preprocess.py # Download, parse, extract & normalize keypoints
│ │ ├── augment.py # Temporal & spatial augmentations
│ │ └── dataset.py # PyTorch Dataset + motion feature computation
│ ├── models/
│ │ ├── pose_transformer.py # Transformer & BiLSTM (Approach A)
│ │ ├── video_i3d.py # 3D CNN backbones (Approach B)
│ │ └── fusion.py # Multi-modal fusion (Approach C)
│ ├── training/
│ │ ├── config.py # Config dataclass + YAML serialization
│ │ ├── train.py # Training loop with mixup regularization
│ │ └── evaluate.py # Metrics, TTA, confusion matrix
│ └── inference/
│ ├── predict.py # Single-video prediction
│ ├── export_onnx.py # ONNX export & latency benchmark
│ └── live_demo.py # Real-time webcam demo
├── tests/
│ ├── conftest.py # Shared fixtures (tmp datasets, keypoint helpers)
│ ├── test_augment.py # Augmentation classes & pipeline presets
│ ├── test_config.py # Config defaults, load/save, YAML roundtrip
│ ├── test_dataset.py # Dataset, DataLoader, pad/crop, motion features
│ ├── test_evaluate.py # Metrics, TTA flip, hard negatives, latency
│ ├── test_export_onnx.py # ONNX export & verification
│ ├── test_live_demo.py # FrameBuffer, prediction smoothing
│ ├── test_models.py # PoseTransformer, PoseBiLSTM, FusionModel
│ ├── test_predict.py # SignPredictor inference paths
│ ├── test_preprocess.py # Normalization, annotation parsing, splits
│ ├── test_train.py # Accuracy, mixup helpers
│ └── test_dependencies.py # Library version & feature compatibility (110 tests)
├── notebooks/
│ ├── 01_data_exploration.ipynb
│ ├── 02_keypoint_visualization.ipynb
│ └── 03_error_analysis.ipynb
├── scripts/
│ ├── download_wlasl.py # Download annotations, print video instructions
│ ├── download_kaggle.py # Download videos from Kaggle (fast alternative)
│ ├── validate_videos.py # Detect and remove HTML-disguised video files
│ ├── reset_configs.py # Reset all configs/ to README.md recommended defaults
│ ├── check_mediapipe.py # Verify MediaPipe installation and diagnose issues
│ └── auto_config.py # Auto-detect hardware and generate optimized configs
├── checkpoints/ # Saved model weights
├── logs/ # TensorBoard training logs
├── pyproject.toml # Pytest configuration
├── requirements.txt
└── STRUCTURE.md # Full workflow & dependency graph (which file calls which)
For a detailed breakdown of the entire pipeline — data flow diagrams, file dependency graphs, model architecture flow, and all CLI entry points — see STRUCTURE.md.
All hyperparameters live in YAML files under configs/. The table below shows all settings and their defaults from the Config dataclass (src/training/config.py). You only need to override values that differ from the defaults.
To reset all config files back to the recommended defaults:
python scripts/reset_configs.py # reset all three configs
python scripts/reset_configs.py --only pose # reset only pose_transformer.yaml
python scripts/reset_configs.py --only video # reset only video_classifier.yaml
python scripts/reset_configs.py --only fusion # reset only fusion.yaml
python scripts/reset_configs.py --dry-run # preview without writingDetect your GPU/CPU and generate an optimized config automatically. The script probes CUDA VRAM, Apple Silicon MPS, or CPU, classifies your hardware into a performance tier, and writes a ready-to-train config:
# Pose approach (recommended starting point)
python scripts/auto_config.py --approach pose
# Video approach
python scripts/auto_config.py --approach video --variant 100
# Fusion approach
python scripts/auto_config.py --approach fusion --variant 300
# Preview without writing
python scripts/auto_config.py --approach pose --dry-run
# Force CPU mode (e.g. no GPU or Apple Silicon)
python scripts/auto_config.py --approach pose --device cpu
# Back up existing config before overwriting
python scripts/auto_config.py --approach pose --backupHardware tiers (auto-detected from CUDA VRAM):
| Tier | VRAM | Pose batch_size |
Video batch_size |
fp16 |
|---|---|---|---|---|
| high | >= 16 GB | 64 | 16 | true |
| mid | >= 8 GB | 32 | 8 | true |
| low | >= 4 GB | 16 | 4 | true |
| cpu | MPS / CPU | 8 | 4 | false |
Windows (PowerShell / Command Prompt): the commands are identical — just run them in your terminal.
Paths:
| Parameter | Description | Default |
|---|---|---|
data_dir |
Root data directory | data |
output_dir |
Output directory | outputs |
checkpoint_dir |
Checkpoint save directory | checkpoints |
log_dir |
Training log directory | logs |
Dataset:
| Parameter | Description | Default |
|---|---|---|
wlasl_variant |
Dataset size: 100, 300, 1000, 2000 |
100 |
num_classes |
Auto-derived from wlasl_variant — do not set manually |
100 |
T |
Temporal sequence length in frames | 64 |
image_size |
Spatial resolution for video models (Approach B/C) | 224 |
num_workers |
DataLoader worker processes | 4 |
Model:
| Parameter | Description | Default |
|---|---|---|
approach |
pose_transformer, pose_bilstm, video, fusion |
pose_transformer |
backbone |
Video backbone: r2plus1d_18, r3d_18, mc3_18, slow_r50, slowfast_r50, x3d_m |
r2plus1d_18 |
pretrained |
Use pretrained backbone weights (Approach B/C) | true |
num_keypoints |
Number of MediaPipe landmarks per frame (33 pose + 21 left hand + 21 right hand + 468 face) | 543 |
d_model |
Transformer/LSTM embedding dimension | 256 |
nhead |
Number of attention heads | 8 |
num_layers |
Number of encoder layers | 4 |
dropout |
Dropout rate | 0.3 |
use_motion |
Concatenate velocity (frame differences) with position features | true |
fusion |
Fusion strategy: concat or attention (Approach C only) |
concat |
fusion_dim |
Fusion layer dimension (Approach C only) | 256 |
Training:
| Parameter | Description | Default |
|---|---|---|
epochs |
Maximum training epochs | 100 |
batch_size |
Training batch size | 32 |
lr |
Learning rate | 1e-4 |
weight_decay |
AdamW weight decay | 1e-4 |
warmup_epochs |
Linear warmup epochs before scheduler takes over | 10 |
label_smoothing |
Label smoothing factor (0 = disabled) | 0.1 |
grad_clip |
Max gradient norm for clipping | 1.0 |
fp16 |
Mixed-precision (FP16) training | true |
weighted_sampling |
Weighted sampler to counter class imbalance | false |
early_stopping_patience |
Epochs without val improvement before stopping | 20 |
mixup_alpha |
Mixup interpolation parameter (0 = disabled) | 0.2 |
scheduler |
LR scheduler: onecycle or cosine (warmup + cosine annealing) |
onecycle |
Evaluation:
| Parameter | Description | Default |
|---|---|---|
use_tta |
Test-time augmentation via horizontal flip averaging | false |
Inference / Live Demo:
| Parameter | Description | Default |
|---|---|---|
confidence_threshold |
Minimum confidence for live display | 0.6 |
smoothing_window |
Number of inference windows to smooth predictions over | 5 |
buffer_size |
Rolling frame buffer size for live demo | 64 |
fps_display |
Show FPS counter on live demo overlay | true |
Logging:
| Parameter | Description | Default |
|---|---|---|
use_wandb |
Enable Weights & Biases logging | false |
use_tensorboard |
Enable TensorBoard logging | true |
wandb_project |
W&B project name | wlasl-recognition |
wandb_run_name |
W&B run name (auto-generated if not set) | null |
log_interval |
Steps between log entries | 10 |
Resume:
| Parameter | Description | Default |
|---|---|---|
resume_checkpoint |
Path to checkpoint for resuming training | null |
num_classes is always auto-derived from wlasl_variant (100 → 100, 300 → 300, etc.). Any explicit num_classes in the YAML is silently overridden — do not set it manually.
The recommended starting approach. MediaPipe Holistic extracts 543 landmarks per frame (33 pose + 21 left hand + 21 right hand + 468 face), centered on the shoulder midpoint and scaled by shoulder width. When use_motion: true (default), frame-to-frame velocity is concatenated with position, producing 6 features per keypoint (x, y, z, dx, dy, dz).
Advantages: Lightweight, fast inference, background-invariant.
Pipeline: Video → MediaPipe → Shoulder-Centered Normalization → Motion Features → Augment → Transformer → Softmax
Data augmentation pipeline (training only):
- Temporal speed perturbation (0.8x–1.2x)
- Random temporal crop to T frames
- Keypoint rotation (up to 15 degrees)
- Keypoint translation (up to 0.1 shift)
- Keypoint horizontal flip with landmark swapping
- Keypoint dropout (frame-level and landmark-level)
- Keypoint noise (sigma=0.02)
- Random scaling (0.9x–1.1x)
Regularization: Mixup interpolation (mixup_alpha: 0.2), label smoothing, dropout, and weighted sampling for class imbalance.
Uses pretrained 3D CNN backbones to classify raw video frames.
Supported backbones: r2plus1d_18, r3d_18, mc3_18 (torchvision), slow_r50, slowfast_r50, x3d_m (pytorchvideo)
Advantages: Captures full visual context including texture and fine finger details.
Combines Approaches A and B. Two fusion modes:
concat— Concatenate pose and video feature vectors before the classification head.attention— Cross-attention between pose tokens and video spatial features.
Expired WLASL URLs often return an HTML lander page (saved as .mp4) rather than a 404. Run scripts/validate_videos.py --delete before preprocessing to clean these up (see Step 2).
- Compatible with Python 3.9–3.12.
- On macOS with Apple Silicon:
pip install mediapipe-silicon - Run the diagnostic script to verify your installation:
This prints your mediapipe version, available modules, and whether the
python scripts/check_mediapipe.py
Holisticmodel is accessible. - If you see
AttributeError: module 'mediapipe' has no attribute 'solutions', your MediaPipe version is incompatible. This commonly happens on Windows with Python 3.12 and newer mediapipe builds. Fix with:pip install --force-reinstall mediapipe==0.10.11(ormediapipe-siliconon Apple Silicon). The code has a fallback import path (mediapipe.python.solutions) that resolves this for some versions automatically. - Zero-padded keypoints for frames where detection fails are handled automatically.
- Preprocessing uses
spawnmultiprocessing context (notfork) to avoid MediaPipe crashes on macOS.
- Reduce
batch_size(try 8 or 4). - Enable
fp16: true. - Reduce
T(e.g., 32 instead of 64). - For video models, set
image_size: 112.
- Try
--camera 1or--camera 2. - Linux: check
ls -la /dev/video*. - macOS: grant camera access in System Settings → Privacy & Security → Camera.
- Windows: check Device Manager → Cameras. Grant camera access in Settings → Privacy & security → Camera.
- Check split CSV row counts to ensure enough training videos were downloaded.
- Enable
weighted_sampling: truefor class-imbalanced subsets. - Run the error analysis notebook (
notebooks/03_error_analysis.ipynb) to find confused class pairs.
Many WLASL URLs have expired, so you will likely end up with far fewer usable videos than the annotation file lists. This is the single biggest factor in accuracy. Check your effective dataset size:
Linux/macOS:
# Row counts in split CSVs (includes videos you may not have)
wc -l data/splits/WLASL100/*.csv
# How many .npy keypoint files were actually produced
ls data/processed/*.npy | wc -lWindows (PowerShell):
# Row counts in split CSVs
Get-ChildItem data\splits\WLASL100\*.csv | ForEach-Object { Write-Host "$($_.Name): $((Get-Content $_).Count) lines" }
# How many .npy keypoint files were actually produced
(Get-ChildItem data\processed\*.npy).CountCross-platform (Python):
# Effective training samples (rows in CSV that have matching .npy files)
python -c "
import pandas as pd; from pathlib import Path
train = pd.read_csv('data/splits/WLASL100/train.csv')
npy = Path('data/processed')
eff = train[train['video_id'].apply(lambda v: (npy/f'{v}.npy').exists())]
counts = eff['label_idx'].value_counts()
print(f'Effective train: {len(eff)} samples, {eff[\"label_idx\"].nunique()} classes')
print(f'Samples/class: min={counts.min()}, mean={counts.mean():.1f}, max={counts.max()}')
print(f'Classes with <=2 samples: {(counts<=2).sum()}')
"If effective samples < 500: Training will be very challenging. The default configs/pose_transformer.yaml is already tuned for this scenario (high dropout, weighted sampling, low LR). Expect 30–50% top-1 accuracy.
To get more data:
- Use the Kaggle download script for the full ~12K video archive:
python scripts/download_kaggle.py - Re-run preprocessing after adding new videos — already-processed
.npyfiles are skipped automatically. - Try
--subset WLASL300to include more glosses (you may have videos for classes outside WLASL100).
num_classes is always auto-derived from wlasl_variant. If your YAML says wlasl_variant: 300 but you only preprocessed WLASL100, training will fail because data/splits/WLASL300/train.csv does not exist. Make sure wlasl_variant in your config matches the subset you preprocessed.
These are tuned starting points for each dataset variant. Copy the base config and modify:
~2,000 annotations, ~100 glosses. Expect 400–1,200 usable training samples depending on download availability.
approach: pose_transformer
wlasl_variant: 100
T: 64
use_motion: true # velocity features (position + frame differences)
d_model: 256
nhead: 8
num_layers: 4
dropout: 0.3
batch_size: 32
lr: 1.0e-3
scheduler: onecycle
warmup_epochs: 10
label_smoothing: 0.1
mixup_alpha: 0.2 # mixup regularization
weighted_sampling: true # important — classes are imbalanced
early_stopping_patience: 20
epochs: 100With very few training videos (<500 usable), increase regularization:
dropout: 0.4
label_smoothing: 0.15
mixup_alpha: 0.3
batch_size: 16~5,000 annotations, 300 glosses. More data per class on average.
approach: pose_transformer
wlasl_variant: 300
T: 64
d_model: 256
nhead: 8
num_layers: 6 # deeper than WLASL100
dropout: 0.25
batch_size: 32
lr: 5.0e-4
scheduler: cosine
warmup_epochs: 10
label_smoothing: 0.1
weighted_sampling: true
early_stopping_patience: 25
epochs: 150Much larger class count. Needs more model capacity and training time.
approach: pose_transformer
wlasl_variant: 1000 # or 2000
T: 64
d_model: 384 # wider
nhead: 8
num_layers: 6
dropout: 0.2
batch_size: 64
lr: 5.0e-4
scheduler: cosine
warmup_epochs: 15
label_smoothing: 0.1
weighted_sampling: true
early_stopping_patience: 30
epochs: 200For WLASL2000, consider the video approach (Approach B) or fusion (Approach C) — the added visual detail helps disambiguate the larger vocabulary.
Use when you have sufficient GPU memory and want to leverage pretrained RGB features.
approach: video
backbone: r2plus1d_18
pretrained: true
T: 32 # video models are memory-heavy, keep T lower
image_size: 224 # reduce to 112 if GPU memory is tight
batch_size: 8 # 3D CNNs need small batches
lr: 1.0e-4 # lower LR for finetuning pretrained backbone
dropout: 0.4
fp16: true # essential for video modelsCombines Approaches A and B for highest accuracy. Requires both keypoints and raw videos.
approach: fusion
fusion: concat # start with concat, try attention if concat plateaus
fusion_dim: 256
backbone: r2plus1d_18
pretrained: true
T: 64
batch_size: 8
lr: 1.0e-4
dropout: 0.3
fp16: trueCPU-only (no GPU)
fp16: false # FP16 only works on CUDA
batch_size: 8 # smaller batches to avoid memory pressure
num_workers: 2Stick to Approach A (pose_transformer). Video models are too slow on CPU for training (inference is manageable).
GPU / CUDA
fp16: true
batch_size: 32 # increase to fill GPU memory (or 64 for large GPUs)
num_workers: 4Monitor GPU memory with nvidia-smi. If you run out of memory, reduce batch_size first, then T, then image_size (for video models).
Apple Silicon (M1/M2/M3)
- Install MediaPipe:
pip install mediapipe-silicon - MPS backend is supported by PyTorch but gains over CPU are inconsistent for these model sizes. Use
--device cpufor the live demo to avoid MPS overhead. - Preprocessing already uses
spawnmultiprocessing context to avoid macOS fork crashes.
WLASL's expired URLs mean you may only get 30–60% of the annotated videos. When your training set is small (<500 samples for 100 classes):
- Enable weighted sampling (
weighted_sampling: true) — ensures every class is seen equally despite imbalance. - Increase dropout to 0.4–0.5 to reduce overfitting.
- Increase label smoothing to 0.15–0.2 for better calibration.
- Use smaller batch sizes (8–16) so the model sees more update steps per epoch.
- Lower the learning rate to 5e-4 or 3e-4 with cosine scheduler.
- Try BiLSTM (
approach: pose_bilstm) — fewer parameters, less prone to overfitting on tiny datasets. - Download from Kaggle (
python scripts/download_kaggle.py) — the full ~12K video archive is available as a single download.
- Start with Approach A (pose_transformer). It trains fastest and is easiest to debug.
- Enable motion features (
use_motion: true) — velocity information captures signing dynamics and typically adds 5–8% accuracy. - Use mixup (
mixup_alpha: 0.2) — regularizes training by interpolating between random sample pairs. - Enable TTA for evaluation (
use_tta: true) — averages predictions over original + horizontally flipped input for 2–4% evaluation boost. - Use the error analysis notebook (
notebooks/03_error_analysis.ipynb) to find which classes are confused, then inspect those videos manually. - Try the cosine scheduler (
scheduler: cosine) if onecycle doesn't converge well — cosine with warm-up is often more stable. - Increase sequence length (
T: 96orT: 128) if signs in your dataset are long — some signs take 3+ seconds at 25 fps. - Scale up to fusion (Approach C) once you've maxed out Approach A's accuracy — it typically adds 3–8% top-1 over pose-only.
Realistic accuracy ranges depend heavily on how many videos you have:
| Dataset | Training Samples | Approach A (expected) | Approach C (expected) |
|---|---|---|---|
| WLASL100 | 400–800 | 45–60% top-1 | 55–68% top-1 |
| WLASL100 | 800–1,500 | 60–70% top-1 | 68–78% top-1 |
| WLASL300 | 2,000–4,000 | 45–55% top-1 | 55–65% top-1 |
If your val loss is around ln(num_classes) (e.g., 4.6 for 100 classes), the model is near random — check that enough training samples are being loaded.
- HTML videos: Run
scripts/validate_videos.py --deletebefore preprocessing. Expired WLASL URLs return HTML pages saved as.mp4files. - Corrupt videos: Some downloads are truncated (OpenCV reports "moov atom not found"). The preprocessing pipeline skips these automatically, but they inflate your file count.
- Wrong video count: The official WLASL download scripts fetch ALL ~21,000 videos (all 2,000 glosses), not just your target variant. The preprocessing pipeline filters to the correct subset.
- MediaPipe warnings:
inference_feedback_manager.ccwarnings are harmless TFLite logs. Suppress withGLOG_minloglevel=2 python ...(Linux/macOS) or$env:GLOG_minloglevel=2; python ...(Windows PowerShell) orset GLOG_minloglevel=2 && python ...(Windows cmd). - Empty val set: If the val split CSV has very few rows, some classes may have zero val samples. This makes early stopping and accuracy metrics unreliable — check row counts after preprocessing:
wc -l data/splits/WLASL100/*.csv(Linux/macOS) orGet-ChildItem data\splits\WLASL100\*.csv | ForEach-Object { Write-Host "$($_.Name): $((Get-Content $_).Count)" }(Windows PowerShell). - OneCycleLR NaN loss: If you resume training from a checkpoint with a different total step count, the scheduler can go out of range. Start fresh or use
scheduler: cosinefor resumed runs.
This project requires torch>=2.1.0,<2.5.0. The table below shows which CUDA toolkit versions are compatible with each PyTorch release, and the matching torchvision / torchaudio versions.
| PyTorch | torchvision | torchaudio | CUDA 11.8 | CUDA 12.1 | CUDA 12.4 | Install command |
|---|---|---|---|---|---|---|
| 2.4.1 | 0.19.1 | 2.4.1 | Yes | Yes | Yes | pip install torch==2.4.1 torchvision==0.19.1 torchaudio==2.4.1 --index-url https://download.pytorch.org/whl/cu124 |
| 2.3.1 | 0.18.1 | 2.3.1 | Yes | Yes | No | pip install torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 --index-url https://download.pytorch.org/whl/cu121 |
| 2.2.2 | 0.17.2 | 2.2.2 | Yes | Yes | No | pip install torch==2.2.2 torchvision==0.17.2 torchaudio==2.2.2 --index-url https://download.pytorch.org/whl/cu121 |
| 2.1.2 | 0.16.2 | 2.1.2 | Yes | Yes | No | pip install torch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 --index-url https://download.pytorch.org/whl/cu121 |
How to choose:
- CUDA 12.4 — Use PyTorch 2.4.x. Best for RTX 30xx/40xx/50xx with recent drivers.
- CUDA 12.1 — Supported by all versions above. Safe default for most setups.
- CUDA 11.8 — Supported by all versions above. Use if your driver is older or you're on a shared cluster.
- CPU only — Append
--index-url https://download.pytorch.org/whl/cputo any install command.
Check your CUDA version with nvidia-smi (top-right corner shows the maximum CUDA version your driver supports).
Note: The
--index-urlflag must match your CUDA version, not your PyTorch version. If you install the wrong CUDA variant,torch.cuda.is_available()will returnFalse.
| Platform | Package | Version Range | Install command |
|---|---|---|---|
| Linux / Windows | mediapipe |
>=0.10.7,<=0.10.14 |
pip install mediapipe (included in requirements.txt) |
| macOS (Apple Silicon) | mediapipe-silicon |
>=0.10.7 |
pip install mediapipe-silicon |
| macOS (Intel) | mediapipe |
>=0.10.7,<=0.10.14 |
pip install mediapipe |
- MediaPipe is compatible with Python 3.9–3.12.
- On Apple Silicon, the standard
mediapipepackage may fail to install. Usemediapipe-siliconinstead — it provides the same API. - On Windows with Python 3.12, some mediapipe versions expose
solutionsundermediapipe.python.solutionsinstead ofmediapipe.solutions. If you hit this issue, pin tomediapipe==0.10.11. - Both packages provide
mediapipe.solutions.holisticused by the preprocessing pipeline.
| Library | Required Version | Notes |
|---|---|---|
opencv-python |
>=4.8.0,<4.11.0 |
Video I/O and frame capture. Webcam access requires system camera permissions. |
numpy |
>=1.24.0,<2.1.0 |
NumPy 2.x introduced breaking changes — stay below 2.1 for compatibility with all dependencies. |
onnxruntime |
>=1.16.0,<1.20.0 |
For ONNX export verification. Use onnxruntime-gpu instead if you want GPU-accelerated ONNX inference. |
pytorchvideo |
>=0.1.5,<0.2.0 |
SlowFast and X3D backbones (Approach B). Only needed if using video models. |
albumentations |
>=1.3.1,<1.5.0 |
Image augmentations for video frame preprocessing (Approach B/C). |
kaggle |
>=1.6.0,<1.8.0 |
Kaggle API for dataset download. Only needed if using scripts/download_kaggle.py. |
For the full list of dependencies with version ranges, see requirements.txt.
Source: PyTorch CUDA Compatibility Matrix | PyTorch Previous Versions
@inproceedings{li2020word,
title={Word-level Deep Sign Language Recognition from Video: A New Large-scale Dataset and Methods Comparison},
author={Li, Dongxu and Rodriguez, Cristian and Yu, Xin and Li, Hongdong},
booktitle={Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)},
pages={1459--1469},
year={2020}
}This project is for educational and research purposes. The WLASL dataset has its own licensing terms — check the official repository before commercial use.