Spaces:
Runtime error
Runtime error
File size: 8,739 Bytes
dc610d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | """
Feature Audit: Detect Data Leakage in Battery SOH Prediction Features
This script audits all 52 features for causality violations:
- Uses only data ≤ current cycle
- No future information leakage
- Document each feature's causality status
"""
import pandas as pd
import numpy as np
from typing import Dict, List, Tuple
import warnings
warnings.filterwarnings('ignore')
class FeatureAuditor:
"""Audit features for data leakage and causality violations."""
def __init__(self):
self.leakage_issues = []
self.safe_features = []
self.warnings = []
def audit_feature(self, feature_name: str, calculation_method: str,
uses_future_data: bool, description: str) -> Dict:
"""Audit a single feature."""
status = "❌ LEAKAGE" if uses_future_data else "✅ SAFE"
result = {
'feature': feature_name,
'status': status,
'leakage': uses_future_data,
'method': calculation_method,
'description': description
}
if uses_future_data:
self.leakage_issues.append(result)
else:
self.safe_features.append(result)
return result
def generate_audit_report(self) -> str:
"""Generate comprehensive audit report."""
report = []
report.append("="*80)
report.append("FEATURE CAUSALITY AUDIT REPORT")
report.append("="*80)
report.append(f"\nDate: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append(f"\nTotal Features Audited: {len(self.leakage_issues) + len(self.safe_features)}")
report.append(f"❌ Features with Leakage: {len(self.leakage_issues)}")
report.append(f"✅ Causal Features: {len(self.safe_features)}")
if self.leakage_issues:
report.append("\n" + "="*80)
report.append("🚨 DATA LEAKAGE ISSUES FOUND")
report.append("="*80)
for issue in self.leakage_issues:
report.append(f"\n❌ {issue['feature']}")
report.append(f" Method: {issue['method']}")
report.append(f" Issue: {issue['description']}")
report.append(f" Action Required: FIX OR REMOVE")
report.append("\n" + "="*80)
report.append("✅ SAFE CAUSAL FEATURES")
report.append("="*80)
# Group by category
categories = {}
for feature in self.safe_features:
category = feature['method'].split('_')[0] if '_' in feature['method'] else 'Other'
if category not in categories:
categories[category] = []
categories[category].append(feature)
for category, features in categories.items():
report.append(f"\n[{category.upper()}]")
for feature in features:
report.append(f" ✅ {feature['feature']}")
report.append("\n" + "="*80)
report.append("RECOMMENDATIONS")
report.append("="*80)
report.append("\n1. Remove or fix all features with ❌ LEAKAGE status")
report.append("2. Retrain model with only causal features")
report.append("3. Re-validate on independent test set")
report.append("4. Report honest R² score (even if lower)")
report.append("5. Document uncertainty quantification methodology")
return "\n".join(report)
def audit_all_features():
"""Audit all features in the engineering pipeline."""
auditor = FeatureAuditor()
# ========================================================================
# 1. RAW MEASUREMENTS - SAFE (uses only current cycle)
# ========================================================================
auditor.audit_feature(
'voltage_mean', 'current_cycle', False,
'Mean voltage within current cycle - CAUSAL'
)
auditor.audit_feature(
'current_mean', 'current_cycle', False,
'Mean current within current cycle - CAUSAL'
)
auditor.audit_feature(
'temperature_mean', 'current_cycle', False,
'Mean temperature within current cycle - CAUSAL'
)
auditor.audit_feature(
'capacity', 'current_cycle', False,
'Capacity at current cycle - CAUSAL'
)
# Normalization - CHECK: uses min/max across ALL cycles
auditor.audit_feature(
'voltage_norm', 'minmax_normalization', True,
'⚠️ NORMALIZATION LEAKAGE: Uses min/max across all cycles (future data) - FIX: Use only past cycles or fixed bounds'
)
auditor.audit_feature(
'current_norm', 'minmax_normalization', True,
'⚠️ NORMALIZATION LEAKAGE: Uses min/max across all cycles - FIX: Use only past cycles or fixed bounds'
)
auditor.audit_feature(
'temperature_norm', 'minmax_normalization', True,
'⚠️ NORMALIZATION LEAKAGE: Uses min/max across all cycles - FIX: Use only past cycles or fixed bounds'
)
# ========================================================================
# 2. STATISTICAL FEATURES - SAFE (within current cycle)
# ========================================================================
for stat in ['mean', 'std', 'min', 'max', 'range', 'median']:
for signal in ['voltage', 'current', 'temperature']:
auditor.audit_feature(
f'{signal}_{stat}', 'current_cycle', False,
f'{stat.capitalize()} {signal} within current cycle - CAUSAL'
)
# ========================================================================
# 3. TEMPORAL FEATURES - CHECK
# ========================================================================
auditor.audit_feature(
'cycles_from_start', 'temporal', False,
'Cycles since start - CAUSAL (only uses past)'
)
auditor.audit_feature(
'cycles_to_end', 'temporal', True,
'🚨 MAJOR LEAKAGE: Uses max(cycle_index) which requires future knowledge - REMOVE'
)
auditor.audit_feature(
'days_since_start', 'temporal', False,
'Days since start (estimated) - CAUSAL'
)
auditor.audit_feature(
'cumulative_time', 'temporal', False,
'Cumulative time up to current cycle - CAUSAL'
)
# ========================================================================
# 4. DEGRADATION FEATURES - CHECK ROLLING WINDOWS
# ========================================================================
auditor.audit_feature(
'soh_fade_rate', 'diff_backward', False,
'SOH difference from previous cycle - CAUSAL (uses only past)'
)
auditor.audit_feature(
'soh_rolling_mean_5', 'rolling_window', False,
'Rolling mean (window=5, center=False) - CAUSAL if backward-looking only'
)
auditor.audit_feature(
'soh_rolling_std_5', 'rolling_window', False,
'Rolling std (window=5, center=False) - CAUSAL if backward-looking only'
)
auditor.audit_feature(
'soh_ema', 'exponential_moving_average', False,
'Exponential moving average (backward) - CAUSAL'
)
auditor.audit_feature(
'capacity_fade_rate', 'diff_backward', False,
'Capacity change from previous cycle - CAUSAL'
)
# ========================================================================
# 5. PHYSICS-INFORMED FEATURES - SAFE (current cycle)
# ========================================================================
auditor.audit_feature(
'energy_throughput', 'current_cycle', False,
'Energy throughput within current cycle - CAUSAL'
)
auditor.audit_feature(
'power_mean', 'current_cycle', False,
'Mean power (V×I) in current cycle - CAUSAL'
)
auditor.audit_feature(
'temperature_stress', 'current_cycle', False,
'Temperature stress indicators in current cycle - CAUSAL'
)
auditor.audit_feature(
'charge_discharge_asymmetry', 'cumulative', True,
'⚠️ POTENTIAL LEAKAGE: Uses sum across all cycles - FIX: Use only past cycles or current cycle ratio'
)
return auditor
if __name__ == "__main__":
print("Starting Feature Causality Audit...")
auditor = audit_all_features()
report = auditor.generate_audit_report()
print("\n" + report)
# Save report
with open('./data_leakage_audit.txt', 'w') as f:
f.write(report)
print(f"\n✓ Audit report saved to: ./data_leakage_audit.txt")
print(f"✓ Found {len(auditor.leakage_issues)} features with leakage")
print(f"✓ Found {len(auditor.safe_features)} causal features")
|