A deepfake audio detection system that uses deep learning to classify audio as real (human-recorded) or fake (AI-generated). The project includes a model training pipeline, a Flask REST API backend, and a React frontend.
├── model/ # ML training pipeline
│ ├── main.py # Entry point for training
│ ├── feature_extraction.py
│ ├── model_training.py
│ ├── inference.py
│ ├── run_project.py
│ ├── DATASET-balanced.csv
│ ├── saved_models/ # Trained model files (shared with backend)
│ │ ├── dynamic_model_detector.h5
│ │ ├── feature_dim.joblib
│ │ └── max_seq_length.txt
│ └── results/ # Training evaluation outputs
├── backend/ # Flask REST API
│ ├── Dockerfile
│ ├── main.py
│ └── model_integration.py # Loads model from ../model/saved_models/
├── frontend/ # React web app
│ ├── Dockerfile.dev
│ ├── src/
│ └── public/
├── docker-compose.yml # Local full-stack testing (see below)
├── config.py # Shared configuration
├── requirements.txt # Python dependencies
├── .env.example # Template for local environment variables
└── .gitignore
The backend reads the trained model directly from
model/saved_models/— there is no separate copy insidebackend/.
- Python 3.12+
- Node.js 18+
- Docker Desktop (only if using the Docker Compose workflow below)
git clone https://github.com/your-username/voxguard.git
cd voxguardpython -m venv venv
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
pip install -r requirements.txtcd frontend
npm installThis model is trained on two public Kaggle datasets:
- The Fake-or-Real (FoR) Dataset — use the
for-2secondsvariant, which is pre-trimmed to 2-second clips - Deep Voice: Deepfake Voice Recognition
Download via the Kaggle API:
pip install kaggle
# place your API token at ~/.kaggle/kaggle.json (from kaggle.com/settings)
kaggle datasets download -d mohammedabdeldayem/the-fake-or-real-dataset
kaggle datasets download -d birdy654/deep-voice-deepfake-voice-recognition
unzip the-fake-or-real-dataset.zip -d for_dataset
unzip deep-voice-deepfake-voice-recognition.zip -d deep_voice_datasetMerge both into the folder layout the training pipeline expects — any folder name containing real/fake is auto-detected, so consolidate everything into one real_audio/ and one fake_audio/ folder:
mkdir -p training_data/real_audio training_data/fake_audio
# FoR dataset (for-2seconds) — merge its train/test/validation splits
cp for_dataset/for-2seconds/training/real/*.wav training_data/real_audio/
cp for_dataset/for-2seconds/testing/real/*.wav training_data/real_audio/
cp for_dataset/for-2seconds/validation/real/*.wav training_data/real_audio/
cp for_dataset/for-2seconds/training/fake/*.wav training_data/fake_audio/
cp for_dataset/for-2seconds/testing/fake/*.wav training_data/fake_audio/
cp for_dataset/for-2seconds/validation/fake/*.wav training_data/fake_audio/
# Deep Voice dataset — prefix filenames to avoid collisions with the FoR set
for f in deep_voice_dataset/REAL/*.wav; do cp "$f" "training_data/real_audio/dv_$(basename "$f")"; done
for f in deep_voice_dataset/FAKE/*.wav; do cp "$f" "training_data/fake_audio/dv_$(basename "$f")"; donetraining_data/
├── real_audio/ (or any_name_with_real_in_it)
│ ├── sample1.wav
│ └── ...
└── fake_audio/ (or any_name_with_fake_in_it)
├── sample1.wav
└── ...
Class balance: the two datasets aren't the same size, so after merging, check the "Number of real/fake audio samples" printout at the start of training.
MAX_INITIAL_SAMPLES(20,000) and stratified splitting handle moderate imbalance, but if one class heavily outweighs the other, consider capping the larger folder to roughly match the smaller one before training.
Retraining an existing setup? Before running the trainer again, move the existing
model/results/,model/saved_models/, andmodel/DATASET-balanced.csvintobackupdata/so the new run doesn't overwrite your previous results and trained model.
Copy .env.example to .env and adjust values for your machine:
cp .env.example .env# Only needed if you're retraining the model
DVD_BASE_PATH=path/to/your/training/data
# Origins allowed to call the backend API (defaults to the React dev server)
ALLOWED_ORIGINS=http://localhost:3000
# Keep false unless you're actively debugging the Flask server locally
FLASK_DEBUG=falseconfig.py and backend/main.py both load .env automatically — no code changes needed for local setup.
cd model
python main.pyTrained model files are saved to model/saved_models/.
cd backend
python main.pyAPI runs at http://localhost:5000. Verify it's up:
curl http://localhost:5000/health| Method | Route | Description |
|---|---|---|
| POST | /analyze |
Upload an audio file for detection |
| GET | /health |
Health check |
Example:
curl -X POST -F "file=@your_audio.wav" http://localhost:5000/analyzecd frontend
npm startApp runs at http://localhost:3000. Verify it's up:
curl http://localhost:3000The model uses MFCC (Mel-frequency cepstral coefficients) features extracted from audio, fed into a Bidirectional LSTM + GRU classifier trained on balanced real/fake audio samples.
- Input:
.wav,.mp3,.ogg,.flac(max 16MB) - Output:
REALorFAKEwith a confidence percentage
| Metric | Score |
|---|---|
| Accuracy | See model/results/evaluation_report.txt |
| ROC AUC | See model/results/model_metrics.json |
Training plots are saved in model/results/.
There are two separate workflows depending on what you're trying to do: run the full production pipeline (train on Kaggle, deploy backend to Render, deploy frontend to Vercel) or run the whole stack locally in containers for testing (Docker Compose).
1. Train the model on Kaggle
- Kaggle → New Notebook → Add Data → attach both datasets directly (no manual download needed)
- Enable GPU: Settings → Accelerator → GPU T4 x2
- Upload the
model/scripts, pointBASE_PATHat the mounted/kaggle/input/...folders - Run
main.py, then download the resultingsaved_models/andresults/from the notebook's output panel - Replace your local
model/saved_models/andmodel/results/with these files, then commit and push — this is the exact model that gets deployed
2. Deploy the backend to Render
- render.com → New → Web Service → connect your GitHub repo
- Runtime: Docker, Dockerfile path:
backend/Dockerfile, build context: repo root - Set environment variables:
ALLOWED_ORIGINS,FLASK_DEBUG=false - Deploy → gives you a URL like
https://voxguard-backend.onrender.com - Verify:
curl https://voxguard-backend.onrender.com/health
3. Deploy the frontend to Vercel
- vercel.com → Add New → Project → import the same repo
- Root directory:
frontend/ - Environment variable:
REACT_APP_API_URL=https://voxguard-backend.onrender.com(the Render URL from step 2) - Deploy → gives you a URL like
https://voxguard.vercel.app - Back on Render, add the Vercel URL to
ALLOWED_ORIGINSand redeploy the backend so CORS allows it
4. CI/CD via GitHub Actions
.github/workflows/ci.ymlruns a build/sanity check on every push (installs backend deps + importsmain, runsnpm run buildfor the frontend)- Render and Vercel already auto-deploy on push to
main; this adds a pass/fail gate alongside those deploys
Note: Render's free tier sleeps after 15 minutes of inactivity and takes ~30–50s to wake on the next request. The frontend should show a "waking up the server" message if a request is taking a while, rather than looking broken.
Use this to test the whole app (backend + frontend) in isolated containers on your own machine, without installing Python/Node globally.
docker compose up --build- Frontend:
http://localhost:3000 - Backend:
http://localhost:5000(curl http://localhost:5000/healthto check) - Stop with
Ctrl+C, then clean up with:
docker compose downThis uses backend/Dockerfile (production-style, gunicorn + gevent) and frontend/Dockerfile.dev (dev server with hot reload), orchestrated by docker-compose.yml at the project root. This workflow is independent of the Render/Vercel deployment above — it's purely for local verification before pushing.
| Area | Contributor |
|---|---|
| DevOps and Frontend | Nikhilesh Sakhare |
| ML Model and Backend | Sushanth Bangera |