Tabular Classification
Transformers
PyTorch
English
ufc
mma
fight-prediction
machine-learning
xgboost
lightgbm
gpu
sports-analytics
ensemble
Instructions to use benjamintia/ufc-fight-predictor with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use benjamintia/ufc-fight-predictor with Transformers:
# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("benjamintia/ufc-fight-predictor", dtype="auto") - Notebooks
- Google Colab
- Kaggle
| """ | |
| UFC Fight Predictor CLI | |
| Predict the outcome of a specific UFC matchup using the trained ensemble model. | |
| Usage: | |
| python scripts/predict_fight.py --fighter_a "Islam Makhachev" --fighter_b "Charles Oliveira" | |
| python scripts/predict_fight.py -a "Jon Jones" -b "Tom Aspinall" --verbose | |
| python scripts/predict_fight.py --list-fighters | |
| """ | |
| import os | |
| import sys | |
| import argparse | |
| import json | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| import numpy as np | |
| DATA_DIR = Path(__file__).parent.parent / "data" | |
| KNOWN_FIGHTERS = { | |
| "Jon Jones": {"slpm": 4.29, "sapm": 2.22, "td_avg": 1.93, "td_def": 95, "strike_acc": 57, "strike_def": 65, | |
| "sub_avg": 0.5, "win_rate": 0.95, "height_inches": 76, "reach_inches": 84.5, "num_fights": 28, | |
| "momentum": 0.8, "sentiment": 0.6}, | |
| "Islam Makhachev": {"slpm": 3.51, "sapm": 1.45, "td_avg": 3.17, "td_def": 89, "strike_acc": 62, "strike_def": 67, | |
| "sub_avg": 0.7, "win_rate": 0.94, "height_inches": 70, "reach_inches": 72.0, "num_fights": 26, | |
| "momentum": 0.9, "sentiment": 0.7}, | |
| "Alexander Volkanovski": {"slpm": 6.23, "sapm": 3.35, "td_avg": 1.72, "td_def": 72, "strike_acc": 58, "strike_def": 63, | |
| "sub_avg": 0.4, "win_rate": 0.89, "height_inches": 66, "reach_inches": 71.5, "num_fights": 29, | |
| "momentum": 0.3, "sentiment": 0.4}, | |
| "Israel Adesanya": {"slpm": 4.93, "sapm": 3.77, "td_avg": 0.08, "td_def": 77, "strike_acc": 51, "strike_def": 61, | |
| "sub_avg": 0.1, "win_rate": 0.83, "height_inches": 76, "reach_inches": 80.0, "num_fights": 27, | |
| "momentum": -0.2, "sentiment": -0.2}, | |
| "Alex Pereira": {"slpm": 5.26, "sapm": 4.33, "td_avg": 0.00, "td_def": 70, "strike_acc": 62, "strike_def": 54, | |
| "sub_avg": 0.0, "win_rate": 0.81, "height_inches": 76, "reach_inches": 79.0, "num_fights": 11, | |
| "momentum": 0.7, "sentiment": 0.6}, | |
| "Charles Oliveira": {"slpm": 3.45, "sapm": 3.41, "td_avg": 2.88, "td_def": 63, "strike_acc": 54, "strike_def": 51, | |
| "sub_avg": 1.4, "win_rate": 0.79, "height_inches": 70, "reach_inches": 74.0, "num_fights": 40, | |
| "momentum": 0.4, "sentiment": 0.3}, | |
| "Leon Edwards": {"slpm": 2.74, "sapm": 2.89, "td_avg": 1.60, "td_def": 68, "strike_acc": 48, "strike_def": 55, | |
| "sub_avg": 0.3, "win_rate": 0.85, "height_inches": 74, "reach_inches": 74.0, "num_fights": 22, | |
| "momentum": 0.5, "sentiment": 0.5}, | |
| "Max Holloway": {"slpm": 7.24, "sapm": 4.75, "td_avg": 0.20, "td_def": 84, "strike_acc": 51, "strike_def": 60, | |
| "sub_avg": 0.1, "win_rate": 0.79, "height_inches": 71, "reach_inches": 69.0, "num_fights": 30, | |
| "momentum": 0.6, "sentiment": 0.5}, | |
| "Dustin Poirier": {"slpm": 5.57, "sapm": 4.38, "td_avg": 1.70, "td_def": 62, "strike_acc": 50, "strike_def": 52, | |
| "sub_avg": 0.7, "win_rate": 0.77, "height_inches": 69, "reach_inches": 72.0, "num_fights": 35, | |
| "momentum": 0.2, "sentiment": 0.2}, | |
| "Justin Gaethje": {"slpm": 7.70, "sapm": 7.99, "td_avg": 0.11, "td_def": 75, "strike_acc": 55, "strike_def": 53, | |
| "sub_avg": 0.1, "win_rate": 0.75, "height_inches": 71, "reach_inches": 70.0, "num_fights": 28, | |
| "momentum": 0.3, "sentiment": 0.1}, | |
| "Sean O'Malley": {"slpm": 6.79, "sapm": 3.75, "td_avg": 0.00, "td_def": 70, "strike_acc": 62, "strike_def": 62, | |
| "sub_avg": 0.0, "win_rate": 0.88, "height_inches": 71, "reach_inches": 72.0, "num_fights": 18, | |
| "momentum": 0.7, "sentiment": 0.7}, | |
| "Ilia Topuria": {"slpm": 4.91, "sapm": 3.47, "td_avg": 1.36, "td_def": 74, "strike_acc": 52, "strike_def": 56, | |
| "sub_avg": 0.6, "win_rate": 1.0, "height_inches": 67, "reach_inches": 69.0, "num_fights": 15, | |
| "momentum": 0.95, "sentiment": 0.9}, | |
| "Sean Strickland": {"slpm": 5.94, "sapm": 4.03, "td_avg": 0.82, "td_def": 74, "strike_acc": 41, "strike_def": 68, | |
| "sub_avg": 0.0, "win_rate": 0.75, "height_inches": 73, "reach_inches": 76.0, "num_fights": 30, | |
| "momentum": -0.1, "sentiment": 0.0}, | |
| "Jiri Prochazka": {"slpm": 5.55, "sapm": 5.08, "td_avg": 0.33, "td_def": 60, "strike_acc": 54, "strike_def": 44, | |
| "sub_avg": 0.3, "win_rate": 0.88, "height_inches": 76, "reach_inches": 80.0, "num_fights": 31, | |
| "momentum": 0.5, "sentiment": 0.4}, | |
| "Khamzat Chimaev": {"slpm": 4.82, "sapm": 1.92, "td_avg": 4.29, "td_def": 60, "strike_acc": 62, "strike_def": 50, | |
| "sub_avg": 0.4, "win_rate": 1.0, "height_inches": 74, "reach_inches": 75.0, "num_fights": 13, | |
| "momentum": 0.7, "sentiment": 0.6}, | |
| "Tom Aspinall": {"slpm": 7.63, "sapm": 3.67, "td_avg": 2.42, "td_def": 100, "strike_acc": 53, "strike_def": 63, | |
| "sub_avg": 0.6, "win_rate": 0.92, "height_inches": 77, "reach_inches": 78.0, "num_fights": 18, | |
| "momentum": 0.85, "sentiment": 0.8}, | |
| "Ciryl Gane": {"slpm": 5.08, "sapm": 2.51, "td_avg": 0.76, "td_def": 63, "strike_acc": 60, "strike_def": 62, | |
| "sub_avg": 0.3, "win_rate": 0.84, "height_inches": 76, "reach_inches": 83.0, "num_fights": 15, | |
| "momentum": 0.1, "sentiment": 0.2}, | |
| "Colby Covington": {"slpm": 4.16, "sapm": 3.35, "td_avg": 4.12, "td_def": 72, "strike_acc": 36, "strike_def": 56, | |
| "sub_avg": 0.4, "win_rate": 0.79, "height_inches": 71, "reach_inches": 72.0, "num_fights": 20, | |
| "momentum": -0.3, "sentiment": -0.3}, | |
| "Robert Whittaker": {"slpm": 4.78, "sapm": 3.62, "td_avg": 1.62, "td_def": 85, "strike_acc": 42, "strike_def": 61, | |
| "sub_avg": 0.1, "win_rate": 0.81, "height_inches": 72, "reach_inches": 73.5, "num_fights": 30, | |
| "momentum": 0.2, "sentiment": 0.1}, | |
| "Arman Tsarukyan": {"slpm": 4.42, "sapm": 2.86, "td_avg": 3.62, "td_def": 72, "strike_acc": 46, "strike_def": 58, | |
| "sub_avg": 0.5, "win_rate": 0.83, "height_inches": 67, "reach_inches": 72.0, "num_fights": 23, | |
| "momentum": 0.6, "sentiment": 0.5}, | |
| "Brandon Moreno": {"slpm": 4.41, "sapm": 3.59, "td_avg": 1.71, "td_def": 64, "strike_acc": 43, "strike_def": 55, | |
| "sub_avg": 0.7, "win_rate": 0.72, "height_inches": 67, "reach_inches": 70.0, "num_fights": 27, | |
| "momentum": -0.1, "sentiment": 0.0}, | |
| "Shavkat Rakhmonov": {"slpm": 4.20, "sapm": 2.38, "td_avg": 1.63, "td_def": 100, "strike_acc": 51, "strike_def": 58, | |
| "sub_avg": 1.0, "win_rate": 1.0, "height_inches": 73, "reach_inches": 77.0, "num_fights": 18, | |
| "momentum": 0.9, "sentiment": 0.8}, | |
| "Petr Yan": {"slpm": 5.62, "sapm": 3.99, "td_avg": 1.77, "td_def": 88, "strike_acc": 47, "strike_def": 60, | |
| "sub_avg": 0.1, "win_rate": 0.70, "height_inches": 67, "reach_inches": 66.5, "num_fights": 20, | |
| "momentum": -0.2, "sentiment": 0.0}, | |
| "Cory Sandhagen": {"slpm": 5.49, "sapm": 3.85, "td_avg": 1.40, "td_def": 64, "strike_acc": 41, "strike_def": 66, | |
| "sub_avg": 0.3, "win_rate": 0.76, "height_inches": 71, "reach_inches": 70.0, "num_fights": 19, | |
| "momentum": 0.3, "sentiment": 0.2}, | |
| "Gilbert Burns": {"slpm": 2.72, "sapm": 3.32, "td_avg": 2.55, "td_def": 42, "strike_acc": 45, "strike_def": 52, | |
| "sub_avg": 0.7, "win_rate": 0.72, "height_inches": 69, "reach_inches": 71.0, "num_fights": 27, | |
| "momentum": -0.2, "sentiment": 0.0}, | |
| "Belal Muhammad": {"slpm": 4.66, "sapm": 3.41, "td_avg": 1.94, "td_def": 100, "strike_acc": 51, "strike_def": 56, | |
| "sub_avg": 0.1, "win_rate": 0.80, "height_inches": 70, "reach_inches": 72.0, "num_fights": 24, | |
| "momentum": 0.8, "sentiment": 0.6}, | |
| } | |
| FIGHTER_WEIGHT_CLASS = { | |
| "Jon Jones": 9, "Islam Makhachev": 5, "Alexander Volkanovski": 4, "Israel Adesanya": 7, | |
| "Alex Pereira": 8, "Charles Oliveira": 5, "Leon Edwards": 6, "Max Holloway": 4, | |
| "Dustin Poirier": 5, "Justin Gaethje": 5, "Sean O'Malley": 3, "Ilia Topuria": 4, | |
| "Sean Strickland": 7, "Jiri Prochazka": 8, "Khamzat Chimaev": 7, "Tom Aspinall": 9, | |
| "Ciryl Gane": 9, "Colby Covington": 6, "Robert Whittaker": 7, "Arman Tsarukyan": 5, | |
| "Brandon Moreno": 2, "Shavkat Rakhmonov": 6, "Petr Yan": 3, "Cory Sandhagen": 3, | |
| "Gilbert Burns": 6, "Belal Muhammad": 6, "Conor McGregor": 5, "Kamaru Usman": 6, | |
| "Francis Ngannou": 9, "Stipe Miocic": 9, "Deiveson Figueiredo": 3, "Henry Cejudo": 3, | |
| "Aljamain Sterling": 4, "Merab Dvalishvili": 3, "Tony Ferguson": 5, "Michael Chandler": 5, | |
| "Kevin Holland": 6, "Stephen Thompson": 6, "Derrick Lewis": 9, "Sergei Pavlovich": 9, | |
| "Jailton Almeida": 9, "Magomed Ankalaev": 8, "Jan Blachowicz": 8, "Marlon Vera": 3, | |
| "Yair Rodriguez": 4, "Arnold Allen": 4, "Calvin Kattar": 4, "Brian Ortega": 4, | |
| "Josh Emmett": 4, "Jack Della Maddalena": 6, "Geoff Neal": 6, "Alexandre Pantoja": 2, | |
| "Manon Fiorot": 2, "Erin Blanchfield": 2, "Rose Namajunas": 2, "Zhang Weili": 1, | |
| "Valentina Shevchenko": 2, "Alexa Grasso": 2, "Jared Cannonier": 7, "Paulo Costa": 7, | |
| "Marvin Vettori": 7, "Dominick Reyes": 8, "Curtis Blaydes": 9, "Tai Tuivasa": 9, | |
| "Beneil Dariush": 5, "Mateusz Gamrot": 5, "Rafael Fiziev": 5, "Dan Hooker": 5, | |
| "Neil Magny": 6, "Derek Brunson": 7, "Anthony Smith": 8, "Aleksandar Rakic": 8, | |
| } | |
| def list_fighters(): | |
| """Print all known fighters.""" | |
| print(f"\n Available Fighters ({len(KNOWN_FIGHTERS)}):") | |
| print(" " + "-" * 60) | |
| for name, stats in sorted(KNOWN_FIGHTERS.items()): | |
| momentum = stats.get("momentum", 0) | |
| momentum_indicator = "+" if momentum > 0.3 else "-" if momentum < -0.3 else "~" | |
| record = f"{stats.get('win_rate', 0)*100:.0f}% win rate" if stats.get("win_rate") else "" | |
| print(f" {momentum_indicator} {name:<25} {record}") | |
| print() | |
| def format_prediction(result, fighter_a, fighter_b): | |
| """Pretty-print prediction results.""" | |
| print("\n" + "=" * 60) | |
| print(" UFC FIGHT PREDICTION") | |
| print("=" * 60) | |
| print(f"\n {fighter_a} vs {fighter_b}") | |
| print(f" {'-' * 40}") | |
| prob_a = result["fighter_a_win_probability"] | |
| prob_b = result["fighter_b_win_probability"] | |
| bar_width = 36 | |
| a_bars = int(bar_width * prob_a) | |
| b_bars = bar_width - a_bars | |
| bar_a = "#" * a_bars | |
| bar_b = "#" * b_bars | |
| print(f"\n {fighter_a:<22} {bar_a} {prob_a:.1%}") | |
| print(f" {fighter_b:<22} {bar_b} {prob_b:.1%}") | |
| print(f"\n {'Predicted Winner:':<22} {result['predicted_winner']}") | |
| print(f" {'Confidence:':<22} {result['confidence']:.1%}") | |
| individual = result.get("individual_predictions", {}) | |
| if individual: | |
| print(f"\n Individual Model Predictions (Fighter A win probability):") | |
| print(f" {'XGBoost:':<22} {individual['xgb_fighter_a_prob']:.1%}") | |
| print(f" {'LightGBM:':<22} {individual['lgb_fighter_a_prob']:.1%}") | |
| print(f" {'Neural Net:':<22} {individual['nn_fighter_a_prob']:.1%}") | |
| agreement = result.get("model_agreement", 0) | |
| print(f"\n {'Model Agreement:':<22} {agreement:.1%} ({'High' if agreement > 0.85 else 'Moderate' if agreement > 0.7 else 'Low - models disagree'})") | |
| print("\n" + "=" * 60) | |
| def get_fighter_features(fighter_name): | |
| """Get feature dict for a fighter, with graceful fuzzy matching.""" | |
| if fighter_name in KNOWN_FIGHTERS: | |
| features = KNOWN_FIGHTERS[fighter_name].copy() | |
| features["weight_class"] = FIGHTER_WEIGHT_CLASS.get(fighter_name, 5) | |
| return features | |
| name_lower = fighter_name.lower() | |
| for known_name, stats in KNOWN_FIGHTERS.items(): | |
| if name_lower in known_name.lower() or known_name.lower() in name_lower: | |
| print(f" (Matched '{fighter_name}' to '{known_name}')") | |
| return stats | |
| print(f" WARNING: '{fighter_name}' not found in database. Using generic fighter profile.") | |
| return { | |
| "slpm": 3.5, "sapm": 3.5, "td_avg": 1.5, "td_def": 65, | |
| "strike_acc": 48, "strike_def": 55, "sub_avg": 0.3, | |
| "win_rate": 0.70, "height_inches": 71, "reach_inches": 72.0, | |
| "num_fights": 20, "momentum": 0.0, "sentiment": 0.0, | |
| "weight_class": 5, | |
| } | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="UFC Fight Predictor - Predict fight outcomes using ML ensemble", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| Examples: | |
| python predict_fight.py -a "Islam Makhachev" -b "Charles Oliveira" | |
| python predict_fight.py -a "Jon Jones" -b "Tom Aspinall" --verbose --json | |
| python predict_fight.py --list-fighters | |
| """, | |
| ) | |
| parser.add_argument("-a", "--fighter_a", type=str, help="Name of Fighter A") | |
| parser.add_argument("-b", "--fighter_b", type=str, help="Name of Fighter B") | |
| parser.add_argument("--list-fighters", action="store_true", help="List all available fighters") | |
| parser.add_argument("--verbose", action="store_true", help="Show detailed output") | |
| parser.add_argument("--json", action="store_true", help="Output result as JSON") | |
| parser.add_argument("--model-dir", type=str, default=None, help="Custom model directory path") | |
| args = parser.parse_args() | |
| if args.list_fighters: | |
| list_fighters() | |
| return | |
| if not args.fighter_a or not args.fighter_b: | |
| parser.print_help() | |
| print("\n Please specify both fighters. Examples:") | |
| print(' python scripts/predict_fight.py -a "Islam Makhachev" -b "Charles Oliveira"') | |
| print(' python scripts/predict_fight.py --list-fighters') | |
| return | |
| features_a = get_fighter_features(args.fighter_a) | |
| features_b = get_fighter_features(args.fighter_b) | |
| try: | |
| from inference import UFCPredictor | |
| predictor = UFCPredictor(model_dir=args.model_dir) | |
| except (FileNotFoundError, ImportError, ModuleNotFoundError): | |
| print("\n Models not found or dependencies missing. Using fallback heuristic.") | |
| print(" For full ML predictions, run: conda activate ufc_predictor && pip install -r requirements.txt") | |
| features_a["name"] = args.fighter_a | |
| features_b["name"] = args.fighter_b | |
| a_score = ( | |
| features_a.get("slpm", 3.5) * 0.15 | |
| - features_a.get("sapm", 3.5) * 0.10 | |
| + features_a.get("td_avg", 1.5) * 0.15 | |
| + features_a.get("strike_acc", 48) * 0.10 | |
| + features_a.get("strike_def", 55) * 0.10 | |
| + features_a.get("td_def", 65) * 0.10 | |
| + features_a.get("sub_avg", 0.3) * 0.10 | |
| + features_a.get("win_rate", 0.7) * 0.10 | |
| + features_a.get("momentum", 0) * 0.10 | |
| ) | |
| b_score = ( | |
| features_b.get("slpm", 3.5) * 0.15 | |
| - features_b.get("sapm", 3.5) * 0.10 | |
| + features_b.get("td_avg", 1.5) * 0.15 | |
| + features_b.get("strike_acc", 48) * 0.10 | |
| + features_b.get("strike_def", 55) * 0.10 | |
| + features_b.get("td_def", 65) * 0.10 | |
| + features_b.get("sub_avg", 0.3) * 0.10 | |
| + features_b.get("win_rate", 0.7) * 0.10 | |
| + features_b.get("momentum", 0) * 0.10 | |
| ) | |
| total = a_score + b_score + 1e-9 | |
| prob_a = a_score / total | |
| result = { | |
| "fighter_a_win_probability": prob_a, | |
| "fighter_b_win_probability": 1 - prob_a, | |
| "predicted_winner": args.fighter_a if prob_a > 0.5 else args.fighter_b, | |
| "confidence": abs(prob_a - 0.5) * 2, | |
| "individual_predictions": {}, | |
| "model_agreement": 0.0, | |
| } | |
| else: | |
| result = predictor.predict_proba(args.fighter_a, args.fighter_b, features_a, features_b) | |
| if args.json: | |
| print(json.dumps(result, indent=2)) | |
| else: | |
| format_prediction(result, args.fighter_a, args.fighter_b) | |
| if __name__ == "__main__": | |
| main() | |