CDSD Certification Project β RNCP35288
Data Science Designer & Developer
Recorded crime and delinquency data in France is publicly available but rarely surfaced in an accessible, analytical format. Law enforcement agencies, local authorities, and researchers require tools to identify trends, compare regions, and anticipate future developments.
OASIS Security addresses this gap by delivering a complete, production-grade data science pipeline β from raw government CSV to interactive forecasting dashboard and REST inference API β covering all 18 administrative regions of metropolitan and overseas France.
Key question:
Can we accurately model and forecast regional crime trends in France from 2016 to 2030 using recorded Police Nationale and Gendarmerie Nationale statistics?
Answer: Yes β our best model (LightGBM) achieves RΒ² = 0.979 on the held-out test set, with a cross-validated RΒ² of 0.978 Β± 0.002, confirming strong generalisation with no data leakage.
| Model | RΒ² Test | RMSE Test | MAE Test | CV RΒ² Mean | CV RΒ² Std |
|---|---|---|---|---|---|
| LightGBM β | 0.9793 | 48.84 | 29.95 | 0.9777 | 0.0022 |
| Gradient Boosting | 0.9793 | 48.84 | 29.95 | 0.9777 | 0.0022 |
| XGBoost | 0.9781 | 50.21 | 30.90 | 0.9766 | 0.0028 |
| Random Forest | 0.9724 | 56.33 | 39.72 | 0.9684 | 0.0026 |
| Ridge | 0.0218 | 335.48 | 249.28 | 0.0065 | 0.0458 |
LightGBM is selected as the production champion for its superior inference speed and native support for categorical features and missing values.
All experiments tracked with MLflow β seemlruns/for full run history, parameters, and artefacts.
Validation strategy:
TimeSeriesSplit(n=3) throughout β temporal ordering is respected in all folds, preventing any leakage from future to past.
| Property | Details |
|---|---|
| Source | data.gouv.fr |
| Publisher | Police Nationale & Gendarmerie Nationale |
| Scope | All 18 French administrative regions (INSEE 2025) |
| Period | 2016β2025 |
| Granularity | Region Γ Crime category Γ Year |
| Format | CSV (semicolon-delimited, UTF-8) |
| Update frequency | Annual |
| Direct URL | donnee-reg-data.gouv-2024.csv |
The dataset is loaded dynamically at runtime from its canonical URL on
static.data.gouv.fr, ensuring the application always reflects the latest
published figures without manual intervention.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DATA PIPELINE β
β β
β data.gouv.fr βββΊ load_data() βββΊ detect_columns() β
β β β
β ββββββββββΌβββββββββ β
β β Preprocessing β β
β β Β· Type casting β β
β β Β· Null handlingβ β
β β Β· Label mappingβ β
β ββββββββββ¬βββββββββ β
βββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β FEATURE ENGINEERING β
β β
β Β· Cyclic temporal features (year_sin, year_cos) β
β Β· Trend normalisation (year_trend) β
β Β· Lag features (lag1, lag2) β
β Β· Rolling mean (roll_mean_3) β
β Β· Regional aggregates (region_mean) β
β Β· Categorical encoding (ind_code, reg_code) β
βββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β MODELLING LAYER β
β β
β βββββββββββββββββββββ βββββββββββββββββββββββββββββ β
β β Train set β β Test set (held out) β β
β β 2016 β 2023 βββββββΊβ 2024β2025 β β
β βββββββββββ¬ββββββββββ βββββββββββββββββββββββββββββ β
β β β
β βββββββββββΌβββββββββββββββββββββββββββββββββββββββββββ β
β β LightGBM Β· XGBoost Β· GradientBoosting β β
β β RandomForest Β· Ridge Β· Prophet Β· Holt-Winters β β
β βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββ β
β β β
β TimeSeriesSplit cross-validation (n=3) β
β MLflow experiment tracking (20+ runs) β
β β Champion: LightGBM (RΒ²=0.979) β
βββββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββ
β SERVING LAYER β
β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
β β Streamlit Dashboard β β FastAPI REST API β β
β β (Hugging Face Spaces) β β (Docker container) β β
β β streamlit/app.py β β api.py β β
β ββββββββββββββββββββββββββ ββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Each (region, crime category) pair forms an independent supervised regression
problem. The target variable is the annual number of recorded offences per
100,000 inhabitants (taux_100k).
Production-grade features are constructed for each observation:
- Cyclic temporal encoding β
year_sinandyear_coscapture periodicity without imposing linearity on the year variable - Lag features β
lag1andlag2provide the model with recent history per (indicator, region) group - Rolling mean β
roll_mean_3smooths short-term volatility - Regional aggregates β
region_meancontextualises each series within its regional baseline - Categorical encoding β indicators and regions are ordinally encoded
LightGBM is selected over Gradient Boosting (sklearn) for three reasons beyond raw RΒ² score:
- Inference speed β GBDT histograms reduce prediction latency at serving time
- Native categorical support β no need for explicit one-hot encoding
- MLflow integration β
mlflow.lightgbmprovides richer artefact logging and model registry support compared tomlflow.sklearn
A TimeSeriesSplit with 3 folds is used throughout, respecting the temporal
ordering of observations and preventing data leakage from future to past.
shuffle=False is enforced on the train/test split for the same reason.
All model runs are logged with MLflow, including:
- Hyperparameters (
model,n_estimators,learning_rate, etc.) - Metrics (
r2_train,r2_test,rmse_test,mae_test,cv_r2_mean,cv_r2_std) - Model artefacts (serialised
.pklfiles) - Git commit hash for full reproducibility
| Layer | Technology | Version |
|---|---|---|
| Language | Python | 3.11 |
| Dashboard | Streamlit | 1.45 |
| Visualisation | Plotly Express & Graph Objects | β₯ 5.18 |
| Data processing | Pandas, NumPy | β₯ 2.0, β₯ 1.24 |
| ML β Champion | LightGBM | β₯ 4.3 |
| ML β Benchmark | XGBoost, GradientBoosting, RandomForest, Ridge | β₯ 1.7 |
| ML β Forecasting | Prophet, Statsmodels (Holt-Winters) | 1.1, β₯ 0.14 |
| ML β Utilities | Scikit-learn (TimeSeriesSplit, metrics) | β₯ 1.3 |
| Experiment tracking | MLflow | β₯ 2.12 |
| REST API | FastAPI + Uvicorn | β₯ 0.110 |
| Containerisation | Docker (multi-stage build) | β |
| Deployment | Hugging Face Spaces (Streamlit SDK) | β |
The inference pipeline is fully containerised using a multi-stage Docker build, cleanly separating the training environment from the production image.
Stage 1 β trainer
Β· Installs full ML stack (LightGBM, XGBoost, Prophet, statsmodelsβ¦)
Β· Receives DATA_URL as a build argument
Β· Runs train.py β serialises crime_predictor.pkl
Stage 2 β production
Β· Copies only the serialised artefact from Stage 1
Β· Installs minimal serving dependencies (fastapi, uvicorn, pandas, numpy, lightgbm)
Β· Exposes port 8000 with HEALTHCHECK
Β· Runs as non-root user (security best practice)
# Build
docker build \
--build-arg DATA_URL="https://static.data.gouv.fr/resources/crimes-et-delits-enregistres-par-les-services-de-gendarmerie-et-de-police-depuis-2012/20240130-135737/donnee-reg-data.gouv-2024.csv" \
-t oasis-security:latest \
./models/crime_predictor/
# Run
docker run -p 8000:8000 oasis-security:latest
# Health check
curl http://localhost:8000/health
# Inference
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{"year": 2030, "indicateur": "Coups et blessures volontaires", "region": "R11", "lag1": 280.5, "lag2": 275.0}'oasis-security/
β
βββ README.md # This file
βββ LICENSE
βββ .gitignore
βββ requirements.txt # Top-level dependencies
βββ api.py # FastAPI REST inference endpoint
βββ Dockerfile # Root-level compose target
βββ docker-compose.yml
β
βββ data/
β βββ raw/ # Source files (gitignored)
β βββ processed/ # Cleaned, model-ready CSVs
β βββ geo/ # Geospatial files (GeoJSON)
β βββ docs/ # Dataset documentation
β
βββ notebooks/
β βββ 01_exploration_crimes.ipynb # Data exploration & EDA
β βββ 02_benchmark_modeles.ipynb # Model comparison & selection
β βββ 03_analyse_departements.ipynb # Departmental deep-dive
β
βββ pipeline/ # Reusable data pipeline modules
β βββ preprocess.py
β βββ features.py
β βββ train.py
β βββ predict.py
β
βββ MlFlow/ # MLflow training module (standalone)
β βββ Dockerfile
β βββ train.py
β βββ requirements.txt
β βββ README.md
β
βββ models/
β βββ crime_predictor/
β βββ Dockerfile # Multi-stage build (train β serve)
β βββ artifacts/
β β βββ crime_predictor.pkl # Serialised champion model (LightGBM)
β β βββ metrics.json # Benchmark results (RΒ²=0.979)
β βββ src/
β β βββ config.yaml # Hyperparameters & data config
β β βββ model.py # CrimeRatePredictor class
β β βββ train.py # Training pipeline (5 models benchmarked)
β β βββ predict.py # Inference logic
β βββ tests/
β βββ test_model.py
β
βββ mlruns/ # MLflow tracking (20+ runs logged)
β
βββ images/ # Visuals for documentation
β
βββ streamlit/ # Hugging Face Space
βββ app.py
βββ requirements.txt
git clone https://github.com/Data-Science-Designer-and-Developer/oasis-security.git
cd oasis-security
pip install -r requirements.txtpython pipeline/preprocess.pypython models/crime_predictor/src/train.py
# Outputs: models/crime_predictor/artifacts/crime_predictor.pkl
# models/crime_predictor/artifacts/metrics.jsonmlflow ui --backend-store-uri ./mlruns
# Open http://localhost:5000streamlit run streamlit/app.pyuvicorn api:app --host 0.0.0.0 --port 8000
# Swagger UI: http://localhost:8000/docsdocker build \
--build-arg DATA_URL="https://static.data.gouv.fr/resources/crimes-et-delits-enregistres-par-les-services-de-gendarmerie-et-de-police-depuis-2012/20240130-135737/donnee-reg-data.gouv-2024.csv" \
-t oasis-security:latest \
./models/crime_predictor/
docker run -p 8000:8000 oasis-security:latestThe data used throughout this project is:
- Publicly available β published by French government authorities under Licence Ouverte v2.0
- Aggregated β figures are presented at regional level only; no individual-level records are processed or stored
- Non-identifiable β no re-identification of persons is possible from the published aggregates
This project is intended solely for informational, educational, and analytical purposes. Forecasts are indicative and subject to the inherent limitations of statistical modelling on short time series. The analysis carries no discriminatory intent with respect to geographical areas or populations.
Data processing complies with the principles of the GDPR (Regulation (EU) 2016/679), in particular data minimisation, purpose limitation, and storage limitation.
β οΈ Recorded crime figures reflect offences registered by police and gendarmerie services β not actual crime rates. Under-reporting, changes in classification practices, and variations in policing intensity may all influence the figures independently of true crime levels.
Data: Licence Ouverte v2.0 β Β© Police Nationale & Gendarmerie Nationale / data.gouv.fr
Code: MIT
FrΓ©dΓ©ric Tellier
LinkedIn: https://www.linkedin.com/in/frΓ©dΓ©ric-tellier-8a9170283/
Portfolio: https://github.com/Dreipfelt
CDSD Certification Project β Data Science Designer & Developer (RNCP35288)