Computer Science and AI student at Zewail City.
I work on making machine learning models explainable, monitored, and honestly evaluated — the parts that decide whether a model can be trusted in production rather than the parts that win leaderboards. Most of my projects end with a result I did not expect, and I report those rather than the ones that would have looked better.
Currently focused on time-series forecasting, model risk, and open-source Python tooling.
📧 abdelgalilabdelrahman0@gmail.com
Found and reported a bug where Explanation.cohorts() raised a TypeError
on integer and boolean cohort labels, then submitted the fix: safe label
coercion plus regression tests covering integer, boolean and string labels.
- Issue: shap/shap#4801
- Pull request: shap/shap#4816 — open, under review
Reporting the bug and fixing it were separate pieces of work: the first required reading the library's internals closely enough to notice the failure, the second required a fix that did not change behaviour for existing users.
Contributed to sktime-mcp, the agent tooling for sktime, the standard time-series ML library in Python.
- Pull request: sktime/sktime-mcp#299 — merged
Standardised .venv setup instructions, added a module-invocation fallback
for MCP clients, and corrected the client configuration path. Verified the
full install flow end to end before submitting.
An honest comparison of gradient boosting against classical forecasting baselines on 5,160 hours of transaction volume, using rolling-origin evaluation across 52 origins.
The headline result is a null result. Gradient boosting scored MASE 0.629 against 0.715 for an hour-of-week seasonal mean — but a Diebold-Mariano test put that gap at p = 0.124. It beat four of five alternatives significantly and could not be separated from taking an average.
Two findings the aggregate ranking hides:
- Error inflated 4.1× on documented anomaly days (holidays, a blizzard). Seasonal models degraded 6.3×, while naive persistence actually improved — when the weekly pattern breaks, the last observation beats the seasonal one.
- Recast as a threshold-breach classification problem, training directly on the decision beat thresholding the point forecast by 6 points of F1.
Python scikit-learn rolling-origin backtesting MASE Diebold-Mariano
A consumer lending scorecard built the way a lender would need it: 0.570 Gini, 0.455 KS on holdout.
- The cut-off comes from the cost matrix, not from 0.5. Approving a defaulter costs five times what declining a sound applicant does; optimising for that cut expected credit loss by 40%. The unconstrained optimum approves only 36% of applicants, so the shipped policy re-optimises subject to a 60% origination target — and reports what that commercial constraint costs in credit loss.
- Every decline produces adverse action reason codes in plain language, mapped from SHAP attributions. A waterfall plot does not satisfy the duty to tell an applicant why they were declined.
- The explainable model is the shipped model. Gradient boosting won by 0.015 Gini — inside the fold-to-fold noise — so logistic regression ships. Running the benchmark is what makes that a decision rather than an assumption.
- Sex is dropped at ingestion; age is audited. The fairness report surfaced a 34-point gap in false decline rate across age bands, documented rather than quietly shipped.
Python SQL (CTEs, window functions) scikit-learn SHAP calibration PSI
Interactive SHAP dashboard turning a trained Random Forest into an auditable model — global ranking, per-sample waterfalls, and signed contribution breakdowns. 0.97 F1 / 0.99 ROC-AUC on Breast Cancer Wisconsin.
Streamlit scikit-learn SHAP
Tracks a 0.956-accuracy baseline against simulated production traffic. With drift injected into 7 of 30 features, the drift score flagged 4 and caught a 36% → 41% shift in prediction mix — detecting distribution change without any ground-truth labels, which is the situation monitoring actually runs in.
Python scikit-learn Streamlit
Leakage-free ColumnTransformer pipeline on 48.8k rows. Five-fold CV showed
Random Forest and Logistic Regression statistically tied (0.8527 ± 0.0027 vs
0.8513 ± 0.0020), and diagnosed 0.63 recall on the minority class as class
imbalance rather than model capacity.
scikit-learn ColumnTransformer cross-validation
Controlled benchmark of L1, L2, Elastic Net, Dropout and Batch Normalization on a fixed architecture, holding data, initialisation and training budget constant. Batch Normalization led at 98.27% — but the spread across all five techniques was only 0.38 points, so the choice mattered far less than the literature framing suggests.
TensorFlow/Keras MNIST
Forward and backward passes implemented in NumPy, no deep learning framework.
Built to understand what .fit() is actually doing.
NumPy
Languages — Python, SQL (CTEs, window functions), C++, Java, Bash
Forecasting — Rolling-origin backtesting, MASE/sMAPE, Diebold-Mariano testing, seasonal baselines, lag engineering, anomaly-aware evaluation
ML & Modelling — scikit-learn, TensorFlow/Keras, classification, regression, feature engineering, cross-validation, model evaluation
Risk & Explainability — SHAP, adverse action reason codes, Gini/KS, PSI drift monitoring, probability calibration, cost-based thresholds, disparate impact auditing
Data — pandas, NumPy, matplotlib, seaborn, EDA, missing-value handling, encoding, scaling, preprocessing pipelines
Engineering — Git, GitHub, pytest, Linux, virtual environments, reproducible project structure
- Time-series forecasting: benchmarking newer methods against established baselines and establishing where they actually win
- Model risk: calibration, drift monitoring, and fairness auditing as standard steps rather than afterthoughts
- Contributing to the open-source Python libraries I use daily