Files
iddaai-be/scripts/audit_backtest.py
fahricansecer 2f0b85a0c7
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s
first (part 2: other directories)
2026-04-16 15:11:25 +03:00

110 lines
3.9 KiB
Python
Executable File

import os
import sys
import torch
import torch.nn.functional as F
import pandas as pd
import numpy as np
# Path alignment
sys.path.append(os.getcwd())
sys.path.append(os.path.join(os.getcwd(), 'ai-engine'))
from pipeline.tiered_loader import TieredDataLoader
from pipeline.sequence_builder import SequenceBuilder
from models.hybrid_v11 import HybridDeepModel
from features.odds_history import OddsHistoryEngine
from features.synthetic_xg import SyntheticXGModel
DEVICE = 'cpu'
MODEL_PATH = 'ai-engine/models/v11_hybrid_model.pth'
TARGET_ID = 'en78ih6ec7exnpxcku3xc3das'
def audit():
print(f"🕵️ Auditing Match: {TARGET_ID}")
# 1. Pipeline Data
builder = SequenceBuilder()
X, y, meta = builder.build_sequences()
# Check if target is in dataset
idx_list = meta.index[meta['match_id'] == TARGET_ID].tolist()
if not idx_list:
print("❌ Match not found in generated sequences. Is it too old or too new?")
return
idx = idx_list[0]
row_meta = meta.iloc[idx]
# 2. Features
loader = TieredDataLoader()
odds_df = loader.load_gold_data([TARGET_ID])
eng = OddsHistoryEngine()
xg_model = SyntheticXGModel()
# Team Mapping
unique_teams = meta['team_id'].unique()
team_map = {tid: i for i, tid in enumerate(unique_teams)}
# 3. Predict exactly like Backtest
state = torch.load(MODEL_PATH, map_location=DEVICE)
emb_key = 'entity_emb.weight' if 'entity_emb.weight' in state else 'team_embedding.weight'
saved_vocab_size = state[emb_key].shape[0]
model = HybridDeepModel(num_teams=saved_vocab_size)
new_state = {k.replace('team_embedding', 'entity_emb'): v for k, v in state.items()}
model.load_state_dict(new_state, strict=False)
model.eval()
# Data components
team_idx = team_map.get(row_meta['team_id'], 0)
entities = torch.LongTensor([team_idx, 0]).unsqueeze(0)
seq = torch.FloatTensor(X[idx]).unsqueeze(0)
# Context (Odds + xG)
odds_lookup = {}
for _, r in odds_df.iterrows():
mid = r['match_id']
if mid not in odds_lookup: odds_lookup[mid] = {}
if r['category'] == 'Maç Sonucu': odds_lookup[mid][r['selection']] = r['odd_value']
elif r['category'] == '2,5 Alt/Üst':
if 'Üst' in r['selection']: odds_lookup[mid]['Over'] = r['odd_value']
else: odds_lookup[mid]['Under'] = r['odd_value']
odds = odds_lookup.get(TARGET_ID, {'1': 1.0, 'X': 1.0, '2': 1.0, 'Over': 1.0, 'Under': 1.0})
syn_xg = 1.35 # Placeholder in trainer for xG component if used
hist_win_rate = eng.get_feature(row_meta['team_id'], float(odds.get('1', 1.0)))
ctx = torch.FloatTensor([
float(odds.get('1', 1.0)), float(odds.get('X', 1.0)), float(odds.get('2', 1.0)),
float(odds.get('Over', 1.0)), float(odds.get('Under', 1.0)),
syn_xg, syn_xg,
hist_win_rate
]).unsqueeze(0)
with torch.no_grad():
logits_res, pred_goals, logits_btts, logits_ht_ft = model(entities, seq, ctx)
probs = F.softmax(logits_res, dim=1).numpy()[0]
prob_btts = torch.sigmoid(logits_btts).item()
probs_ht = F.softmax(logits_ht_ft, dim=1).numpy()[0]
print("\n📊 INTERNAL PIPELINE PREDICTION:")
print(f"Target Team: {row_meta['team_id']}")
print(f"1X2 Probs: Home:{probs[0]:.4f} Draw:{probs[1]:.4f} Away:{probs[2]:.4f}")
print(f"BTTS Prob: {prob_btts:.4f}")
ht_map = ["1/1", "1/X", "1/2", "X/1", "X/X", "X/2", "2/1", "2/X", "2/2"]
top3_ht = np.argsort(probs_ht)[-3:][::-1]
print("Top 3 HT/FT:")
for idx_ht in top3_ht:
print(f" {ht_map[idx_ht]}: {probs_ht[idx_ht]:.4f}")
actual_res = y[idx][0]
actual_ht_idx = int(y[idx][3])
print(f"\n✅ ACTUAL REALITY:")
print(f"Result (Y): {actual_res} (0.0=Away)")
print(f"HT/FT Class: {actual_ht_idx} ({ht_map[actual_ht_idx]})")
if __name__ == "__main__":
audit()