fix(ai-engine): sync FEATURE_COLS with trained models (82→102 features)
Deploy Iddaai Backend / build-and-deploy (push) Successful in 6s
Deploy Iddaai Backend / build-and-deploy (push) Successful in 6s
- Load feature columns dynamically from feature_cols.json - Add 20 missing odds_*_present boolean flags to fallback list - Fixes LightGBM 'features in data (82) != training data (102)' crash
This commit is contained in:
@@ -141,8 +141,10 @@ class V25Predictor:
|
|||||||
Each market (MS, OU25, BTTS) has its own trained models.
|
Each market (MS, OU25, BTTS) has its own trained models.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Feature columns (82 features, NO target leakage)
|
# Feature columns — loaded dynamically from feature_cols.json to stay
|
||||||
FEATURE_COLS = [
|
# in sync with the trained models. The hardcoded list below is only a
|
||||||
|
# fallback in case the JSON file is missing.
|
||||||
|
_FALLBACK_FEATURE_COLS = [
|
||||||
# ELO Features (8)
|
# ELO Features (8)
|
||||||
'home_overall_elo', 'away_overall_elo', 'elo_diff',
|
'home_overall_elo', 'away_overall_elo', 'elo_diff',
|
||||||
'home_home_elo', 'away_away_elo',
|
'home_home_elo', 'away_away_elo',
|
||||||
@@ -178,6 +180,17 @@ class V25Predictor:
|
|||||||
'odds_ht_ou15_o', 'odds_ht_ou15_u',
|
'odds_ht_ou15_o', 'odds_ht_ou15_u',
|
||||||
'odds_btts_y', 'odds_btts_n',
|
'odds_btts_y', 'odds_btts_n',
|
||||||
|
|
||||||
|
# Odds Presence Flags (20)
|
||||||
|
'odds_ms_h_present', 'odds_ms_d_present', 'odds_ms_a_present',
|
||||||
|
'odds_ht_ms_h_present', 'odds_ht_ms_d_present', 'odds_ht_ms_a_present',
|
||||||
|
'odds_ou05_o_present', 'odds_ou05_u_present',
|
||||||
|
'odds_ou15_o_present', 'odds_ou15_u_present',
|
||||||
|
'odds_ou25_o_present', 'odds_ou25_u_present',
|
||||||
|
'odds_ou35_o_present', 'odds_ou35_u_present',
|
||||||
|
'odds_ht_ou05_o_present', 'odds_ht_ou05_u_present',
|
||||||
|
'odds_ht_ou15_o_present', 'odds_ht_ou15_u_present',
|
||||||
|
'odds_btts_y_present', 'odds_btts_n_present',
|
||||||
|
|
||||||
# League Features (4)
|
# League Features (4)
|
||||||
'home_xga', 'away_xga',
|
'home_xga', 'away_xga',
|
||||||
'league_avg_goals', 'league_zero_goal_rate',
|
'league_avg_goals', 'league_zero_goal_rate',
|
||||||
@@ -198,6 +211,24 @@ class V25Predictor:
|
|||||||
'home_missing_impact', 'away_missing_impact',
|
'home_missing_impact', 'away_missing_impact',
|
||||||
'home_goals_form', 'away_goals_form',
|
'home_goals_form', 'away_goals_form',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _load_feature_cols() -> list:
|
||||||
|
"""Load feature columns from feature_cols.json, falling back to hardcoded list."""
|
||||||
|
feature_json = os.path.join(MODELS_DIR, 'feature_cols.json')
|
||||||
|
try:
|
||||||
|
if os.path.exists(feature_json):
|
||||||
|
with open(feature_json, 'r', encoding='utf-8') as f:
|
||||||
|
cols = json.load(f)
|
||||||
|
if isinstance(cols, list) and len(cols) > 0:
|
||||||
|
print(f"[V25] Loaded {len(cols)} feature columns from feature_cols.json")
|
||||||
|
return cols
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[V25] Warning: could not load feature_cols.json: {e}")
|
||||||
|
print(f"[V25] Using fallback feature columns ({len(V25Predictor._FALLBACK_FEATURE_COLS)} features)")
|
||||||
|
return V25Predictor._FALLBACK_FEATURE_COLS
|
||||||
|
|
||||||
|
FEATURE_COLS = _load_feature_cols.__func__()
|
||||||
|
|
||||||
# Model weights for ensemble
|
# Model weights for ensemble
|
||||||
DEFAULT_WEIGHTS = {
|
DEFAULT_WEIGHTS = {
|
||||||
|
|||||||
Reference in New Issue
Block a user