File size: 4,981 Bytes
3f9bf12 | 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 | """md-agent candidate models (rebuilt post-outage). Registered by run.py."""
import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, RobustScaler
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import BaggingClassifier, HistGradientBoostingClassifier
import batteryswap_example.models_cv as mc
# risk-direction of p42 in each feature (0 = unconstrained)
MONO = {
'v_last': -1, 'v_drop': +1, 'slope_30': -1, 'slope_90': -1,
'v_min_30': -1, 'staleness': +1, 'age_days': +1, 'slope_lastk': -1,
'drop_recent': +1, 'below_frac': +1, 'accel': -1, 'n_30': 0,
'tau_projected': -1,
'vrel': -1, 'vmin7_rank': -1, 'r_vlast': -1, 'r_tau': -1,
}
class _DaysMixin:
def _fit_days(self, train):
dying = train[train['dies42'] == 1]
self.days = mc.DayQuantiles().fit(dying[mc.FEATURES], dying['days_to_eol'])
def _out(self, rows, p42):
out = self.days.predict(rows[mc.FEATURES])
out['p42'] = p42
return out
class HGBMono(_DaysMixin):
"""HistGradientBoosting with monotonic constraints (risk-direction by name)."""
def __init__(self, **kw):
self.kw = dict(max_iter=200, max_depth=3, learning_rate=0.1,
min_samples_leaf=20, random_state=0)
self.kw.update(kw)
def fit(self, train):
feats = list(mc.FEATURES)
cst = [MONO.get(f, 0) for f in feats]
self.feats = feats
self.clf = HistGradientBoostingClassifier(monotonic_cst=cst, **self.kw)
self.clf.fit(train[feats], train['dies42'])
self._fit_days(train)
return self
def predict(self, rows):
return self._out(rows, self.clf.predict_proba(rows[self.feats])[:, 1])
class MeanBlend(_DaysMixin):
"""Mean of logbag p42 and a partner model's p42."""
def __init__(self, partner, w=0.5):
self.partner_factory = partner
self.w = w
def fit(self, train):
self.a = mc.SklearnRUL('logbag').fit(train)
self.b = self.partner_factory().fit(train)
return self
def predict(self, rows):
out = self.a.predict(rows)
out['p42'] = ((1 - self.w) * out['p42']
+ self.w * self.b.predict(rows)['p42'])
return out
class MeanBlendCM(MeanBlend):
"""Mean blend, count-matched: p42 rescaled so sum(p42) equals logbag's."""
def predict(self, rows):
pa = self.a.predict(rows)
pb = self.b.predict(rows)['p42']
blend = (1 - self.w) * pa['p42'] + self.w * pb
s = blend.sum()
if s > 0:
blend = (blend * (pa['p42'].sum() / s)).clip(upper=1.0)
out = pa
out['p42'] = blend
return out
class OrderBlend3(_DaysMixin):
"""Value-preserving 3-way ORDER blend: logbag p42 values kept (count
machinery untouched), ordering = weighted mean of pct-ranks of
logbag / partner2 / partner3."""
def __init__(self, p2, w2, p3=None, w3=0.0):
self.p2f, self.w2 = p2, w2
self.p3f, self.w3 = p3, w3
def fit(self, train):
self.a = mc.SklearnRUL('logbag').fit(train)
self.b = self.p2f().fit(train)
self.c = self.p3f().fit(train) if self.p3f is not None else None
return self
def predict(self, rows):
out = self.a.predict(rows)
w1 = 1.0 - self.w2 - self.w3
r = (w1 * out['p42'].rank(pct=True)
+ self.w2 * self.b.predict(rows)['p42'].rank(pct=True))
if self.c is not None:
r = r + self.w3 * self.c.predict(rows)['p42'].rank(pct=True)
vals = np.sort(out['p42'].values)[::-1]
out['p42'] = pd.Series(
vals, index=r.sort_values(ascending=False).index).reindex(out.index)
return out
def register():
M = mc.MODELS
M['md_hgb_d2'] = lambda: HGBMono(max_depth=2, l2_regularization=1.0)
M['md_hgb_d2l2'] = lambda: HGBMono(max_depth=2, l2_regularization=2.0)
M['md_hgb_d2l10'] = lambda: HGBMono(max_depth=2, l2_regularization=10.0)
M['md_hgb_d1'] = lambda: HGBMono(max_depth=1, l2_regularization=1.0,
learning_rate=0.05, max_iter=400)
M['md_hgb_d1l25'] = lambda: HGBMono(max_depth=1, l2_regularization=5.0,
learning_rate=0.05, max_iter=400)
M['md_bl_hgbd2'] = lambda: MeanBlend(M['md_hgb_d2'], 0.5)
M['md_blcm_hgbd2'] = lambda: MeanBlendCM(M['md_hgb_d2'], 0.5)
M['md_blcm_hgbd1'] = lambda: MeanBlendCM(M['md_hgb_d1'], 0.5)
# 3-way order blend on the frontier recipe: logbag .55 / hgb .25 / wiener .20
M['md_ord3_d2'] = lambda: OrderBlend3(M['md_hgb_d2'], 0.25, mc.WienerFHT, 0.20)
M['md_ord3_d1l25'] = lambda: OrderBlend3(M['md_hgb_d1l25'], 0.25,
mc.WienerFHT, 0.20)
M['md_ord3_d2w15'] = lambda: OrderBlend3(M['md_hgb_d2'], 0.15, mc.WienerFHT, 0.20)
|