SuperModelingFactory 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ExcelMaster/ExcelFormatTool.py +487 -0
- ExcelMaster/ExcelMaster.py +917 -0
- ExcelMaster/Template.py +525 -0
- ExcelMaster/Utility.py +233 -0
- ExcelMaster/__init__.py +3 -0
- Modeling_Tool/Core/Binning_Tool.py +1608 -0
- Modeling_Tool/Core/Binning_Tool.pyi +48 -0
- Modeling_Tool/Core/Check_DuckDB_Compatibility.py +835 -0
- Modeling_Tool/Core/Json_Data_Converter.py +621 -0
- Modeling_Tool/Core/Model_Registry_Tool.py +199 -0
- Modeling_Tool/Core/ODPS_Tool.py +284 -0
- Modeling_Tool/Core/Slope_Tool.py +356 -0
- Modeling_Tool/Core/Slope_Tool.pyi +31 -0
- Modeling_Tool/Core/XOR_Encryptor.py +207 -0
- Modeling_Tool/Core/XOR_Encryptor.pyi +26 -0
- Modeling_Tool/Core/__init__.py +99 -0
- Modeling_Tool/Core/kDataFrame.py +228 -0
- Modeling_Tool/Core/kDataFrame.pyi +43 -0
- Modeling_Tool/Core/sample_weight_utils.py +77 -0
- Modeling_Tool/Core/utils.py +2672 -0
- Modeling_Tool/Eval/Evaluation_Tool.py +1452 -0
- Modeling_Tool/Eval/Evaluation_Tool.pyi +47 -0
- Modeling_Tool/Eval/Model_Eval_Tool.py +2548 -0
- Modeling_Tool/Eval/Model_Eval_Tool.pyi +37 -0
- Modeling_Tool/Eval/__init__.py +54 -0
- Modeling_Tool/Eval/evaluate_model.py +2008 -0
- Modeling_Tool/Eval/evaluate_model.pyi +50 -0
- Modeling_Tool/Eval/weighted_eval_utils.py +326 -0
- Modeling_Tool/Explainability/Coalition_Structure.py +305 -0
- Modeling_Tool/Explainability/Model_Explainer.py +743 -0
- Modeling_Tool/Explainability/__init__.py +24 -0
- Modeling_Tool/Feature/Distribution_Tool.py +509 -0
- Modeling_Tool/Feature/Distribution_Tool.pyi +42 -0
- Modeling_Tool/Feature/Feature_Insights.py +762 -0
- Modeling_Tool/Feature/Feature_Insights.pyi +33 -0
- Modeling_Tool/Feature/PSI_Tool.py +1195 -0
- Modeling_Tool/Feature/PSI_Tool.pyi +29 -0
- Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +355 -0
- Modeling_Tool/Feature/__init__.py +40 -0
- Modeling_Tool/Model/Backward_Tool.py +778 -0
- Modeling_Tool/Model/Backward_Tool.pyi +45 -0
- Modeling_Tool/Model/GBM_Search_Tool.py +251 -0
- Modeling_Tool/Model/GBM_Tool.py +1610 -0
- Modeling_Tool/Model/GBM_Tool.pyi +90 -0
- Modeling_Tool/Model/LRM_Tool.py +1198 -0
- Modeling_Tool/Model/LRM_Tool.pyi +47 -0
- Modeling_Tool/Model/__init__.py +61 -0
- Modeling_Tool/Sample/Distribution_Adaptation.py +131 -0
- Modeling_Tool/Sample/Distribution_Adaptation.pyi +30 -0
- Modeling_Tool/Sample/Reject_Infer.py +413 -0
- Modeling_Tool/Sample/Reject_Infer.pyi +43 -0
- Modeling_Tool/Sample/Sample_Split.py +520 -0
- Modeling_Tool/Sample/Sample_Split.pyi +43 -0
- Modeling_Tool/Sample/__init__.py +31 -0
- Modeling_Tool/UAT/UAT_Consistency_Checker.py +1180 -0
- Modeling_Tool/UAT/__init__.py +19 -0
- Modeling_Tool/WOE/WOE_Adapter.py +204 -0
- Modeling_Tool/WOE/WOE_Adapter.pyi +21 -0
- Modeling_Tool/WOE/WOE_Master.py +491 -0
- Modeling_Tool/WOE/WOE_Master.pyi +40 -0
- Modeling_Tool/WOE/WOE_Monotone_Binner.py +3324 -0
- Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +71 -0
- Modeling_Tool/WOE/WOE_Plot_Tool.py +919 -0
- Modeling_Tool/WOE/WOE_Plot_Tool.pyi +46 -0
- Modeling_Tool/WOE/WOE_Report_Builder.py +214 -0
- Modeling_Tool/WOE/WOE_Report_Builder.pyi +24 -0
- Modeling_Tool/WOE/WOE_Tool.py +1094 -0
- Modeling_Tool/WOE/WOE_Tool.pyi +41 -0
- Modeling_Tool/WOE/__init__.py +86 -0
- Modeling_Tool/WOE/plot_woe_tool.py +290 -0
- Modeling_Tool/WOE/plot_woe_tool.pyi +25 -0
- Modeling_Tool/__init__.py +176 -0
- Report/Report_Tool.py +380 -0
- Report/__init__.py +3 -0
- supermodelingfactory-0.2.0.dist-info/METADATA +268 -0
- supermodelingfactory-0.2.0.dist-info/RECORD +79 -0
- supermodelingfactory-0.2.0.dist-info/WHEEL +5 -0
- supermodelingfactory-0.2.0.dist-info/licenses/LICENSE +102 -0
- supermodelingfactory-0.2.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Modeling_Tool.Model.Backward_Tool
|
|
3
|
+
# -----------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) 2026 Kyle Sun <github.com/Kyle-J-Sun>. All rights reserved.
|
|
5
|
+
# SuperModelingFactory — Licensed under the Business Source License 1.1.
|
|
6
|
+
#
|
|
7
|
+
# This stub describes the public API of a closed-source module compiled to a
|
|
8
|
+
# native extension (.so / .pyd). The original source is not distributed.
|
|
9
|
+
# Production / commercial use requires a separate commercial license.
|
|
10
|
+
#
|
|
11
|
+
# FINGERPRINT: SMF-BACKWARDTOOL-11b388c0
|
|
12
|
+
# (Unique trace marker. Do not remove or alter — used for plagiarism
|
|
13
|
+
# detection across the public internet.)
|
|
14
|
+
# =============================================================================
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import time
|
|
18
|
+
import logging
|
|
19
|
+
import numpy as np
|
|
20
|
+
import pandas as pd
|
|
21
|
+
from collections import OrderedDict
|
|
22
|
+
from .GBM_Tool import lgbm_quick_train, lgb_varimp, xgbm_quick_train, xgb_varimp
|
|
23
|
+
from Modeling_Tool.Core.utils import save_model, load_model
|
|
24
|
+
from Modeling_Tool.Eval.Model_Eval_Tool import get_perf_summary
|
|
25
|
+
def backward_lgbm(train_data, validation_data, x, y, varreduct_params, results_output_dir, varreduct_perf_filename, modelsave_dir, cat_x = None, wgt_col = None, test_data_dict = None, stopping_metric = 'AUC', seed = 12345, varreduct_modelid_prefix = 'varreduct_test0', varreduct_max_nmodels = 50, varreduct_minvar = 30, cum_varimp = 0.98, backward_continue_from_nmodel = 0, scrutiny_range = None, scrutiny_step = None): ...
|
|
26
|
+
def backward_xgbm(train_data, validation_data, x, y, varreduct_params, results_output_dir, varreduct_perf_filename, modelsave_dir, mc_dict = None, cat_x = None, wgt_col = None, test_data_dict = None, stopping_metric = 'AUC', seed = 12345, varreduct_modelid_prefix = 'varreduct_test0', varreduct_max_nmodels = 50, varreduct_minvar = 30, cum_varimp = 0.98, backward_continue_from_nmodel = 0, scrutiny_range = None, scrutiny_step = None): ...
|
|
27
|
+
|
|
28
|
+
class BackwardVariableEliminator:
|
|
29
|
+
def __init__(self, model_type = None, train_data = None, validation_data = None, params = None, y = None, oot_data = None, wgt_col = None, cat_x = None, mc_dict = None, stopping_metric = 'AUC', seed = 12345, modelsave_dir = None, algorithm = 'lgb', results_output_dir = None, varreduct_perf_filename = 'perf.csv', varreduct_modelid_prefix = 'varreduct'): ...
|
|
30
|
+
def _validate_train_params(self): ...
|
|
31
|
+
def _validate_analyze_params(self): ...
|
|
32
|
+
def _get_hyperparams_preset(self): ...
|
|
33
|
+
def _merge_params(self): ...
|
|
34
|
+
def fit(self, x, varreduct_max_nmodels = 50, varreduct_minvar = 30, cum_varimp = 0.98, backward_continue_from_nmodel = 0, scrutiny_range = None, scrutiny_step = None, test_data_dict = None): ...
|
|
35
|
+
def get_backward_perf(self, overfitting_cut = [np.inf, np.inf]): ...
|
|
36
|
+
def get_backward_summary(self, grp_name = None, subgroup_value = None, min_data_size = 10): ...
|
|
37
|
+
def add_group(self, grp_name, subgroup_value = None): ...
|
|
38
|
+
def set_overfitting_cut(self, auc_shift = np.inf, ks_shift = np.inf): ...
|
|
39
|
+
def analyze(self, result_dir = None): ...
|
|
40
|
+
def fit_and_analyze(self, x, results_output_dir = None, modelsave_dir = None, varreduct_perf_filename = 'perf.csv', varreduct_modelid_prefix = 'varreduct', varreduct_max_nmodels = 50, varreduct_minvar = 30, cum_varimp = 0.98, scrutiny_range = None, scrutiny_step = None): ...
|
|
41
|
+
def switch_model(self, new_model_type): ...
|
|
42
|
+
def get_result(self): ...
|
|
43
|
+
|
|
44
|
+
class BackwardEliminationAnalyzer(BackwardVariableEliminator):
|
|
45
|
+
def __init__(self, ins_df = None, oos_df = None, oot_df = None, dep = None, dir_path = None, algorithm = 'xgb', min_data_size = 10): ...
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Holdout hyperparameter search extension for GradientBoostingModel."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import itertools
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from sklearn.metrics import roc_auc_score
|
|
10
|
+
|
|
11
|
+
from Modeling_Tool.Core.sample_weight_utils import resolve_sample_weight
|
|
12
|
+
from .GBM_Tool import GradientBoostingModel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _native(value):
|
|
16
|
+
return value.item() if hasattr(value, "item") else value
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _validate_columns(data, varlist, tgt_name, eval_sets, weight_col=None, eval_weight_col=None):
|
|
20
|
+
required = list(varlist) + [tgt_name]
|
|
21
|
+
missing = [c for c in required if c not in data.columns]
|
|
22
|
+
if missing:
|
|
23
|
+
raise KeyError("training data missing columns: {0}".format(missing))
|
|
24
|
+
if weight_col is not None and weight_col not in data.columns:
|
|
25
|
+
raise KeyError("training data missing weight column: {0}".format(weight_col))
|
|
26
|
+
for name, df_eval in eval_sets.items():
|
|
27
|
+
miss = [c for c in required if c not in df_eval.columns]
|
|
28
|
+
if miss:
|
|
29
|
+
raise KeyError("eval set '{0}' missing columns: {1}".format(name, miss))
|
|
30
|
+
if eval_weight_col is not None and eval_weight_col not in df_eval.columns:
|
|
31
|
+
raise KeyError("eval set '{0}' missing weight column: {1}".format(name, eval_weight_col))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _resolve_primary_and_refs(eval_sets, primary_set, gap_ref_sets):
|
|
35
|
+
if not eval_sets:
|
|
36
|
+
raise ValueError("eval_sets must be a non-empty {name: DataFrame} mapping.")
|
|
37
|
+
set_names = list(eval_sets.keys())
|
|
38
|
+
if primary_set is None:
|
|
39
|
+
primary_set = set_names[-1]
|
|
40
|
+
if primary_set not in eval_sets:
|
|
41
|
+
raise ValueError("primary_set '{0}' not in eval_sets {1}".format(primary_set, set_names))
|
|
42
|
+
if gap_ref_sets is None:
|
|
43
|
+
gap_ref_sets = [name for name in set_names if name != primary_set]
|
|
44
|
+
for name in gap_ref_sets:
|
|
45
|
+
if name not in eval_sets:
|
|
46
|
+
raise ValueError("gap_ref_sets contains unknown eval set: {0}".format(name))
|
|
47
|
+
return set_names, primary_set, gap_ref_sets
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _resolve_validation_set(eval_sets, primary_set, validation_set):
|
|
51
|
+
if validation_set is not None:
|
|
52
|
+
if validation_set not in eval_sets:
|
|
53
|
+
raise ValueError("validation_set '{0}' not in eval_sets".format(validation_set))
|
|
54
|
+
return validation_set
|
|
55
|
+
for name in ("oos", "validation", "valid"):
|
|
56
|
+
if name in eval_sets:
|
|
57
|
+
return name
|
|
58
|
+
return primary_set
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _score_from_metrics(metric_dict, objective, primary_set, gap_ref_sets):
|
|
62
|
+
if callable(objective):
|
|
63
|
+
return float(objective(metric_dict))
|
|
64
|
+
if objective == "max_primary":
|
|
65
|
+
return float(metric_dict[primary_set])
|
|
66
|
+
if objective == "oot_gap_penalized":
|
|
67
|
+
primary = float(metric_dict[primary_set])
|
|
68
|
+
if gap_ref_sets:
|
|
69
|
+
ref = float(np.mean([metric_dict[name] for name in gap_ref_sets]))
|
|
70
|
+
return primary - abs(ref - primary)
|
|
71
|
+
return primary
|
|
72
|
+
raise ValueError("Unknown objective: {0}".format(objective))
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _positive_class_score(pred):
|
|
76
|
+
arr = np.asarray(pred)
|
|
77
|
+
return arr[:, 1] if arr.ndim == 2 else arr
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _evaluate_candidate(candidate, eval_sets, varlist, tgt_name, metric, eval_weight_col=None):
|
|
81
|
+
if metric != "auc":
|
|
82
|
+
raise ValueError("Only metric='auc' is currently supported.")
|
|
83
|
+
metric_dict = {}
|
|
84
|
+
for name, df_eval in eval_sets.items():
|
|
85
|
+
proba = _positive_class_score(candidate.predict(df_eval[varlist]))
|
|
86
|
+
sw = resolve_sample_weight(data=df_eval, weight_col=eval_weight_col, expected_len=len(df_eval))
|
|
87
|
+
metric_dict[name] = roc_auc_score(df_eval[tgt_name], proba, sample_weight=sw)
|
|
88
|
+
return metric_dict
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _fit_candidate(model_type, base_params, candidate_params, data, varlist, tgt_name,
|
|
92
|
+
validation_df, fit_kwargs, weight_col=None, eval_weight_col=None):
|
|
93
|
+
params = {**base_params, **candidate_params}
|
|
94
|
+
candidate = GradientBoostingModel(model_type, params)
|
|
95
|
+
fk = dict(fit_kwargs)
|
|
96
|
+
train_sw = resolve_sample_weight(data=data, weight_col=weight_col, expected_len=len(data))
|
|
97
|
+
eval_sw = resolve_sample_weight(data=validation_df, weight_col=eval_weight_col, expected_len=len(validation_df))
|
|
98
|
+
if train_sw is not None:
|
|
99
|
+
fk["sample_weight"] = train_sw
|
|
100
|
+
if eval_sw is not None:
|
|
101
|
+
fk["eval_sample_weight"] = eval_sw
|
|
102
|
+
candidate.fit(data[varlist], data[tgt_name], validation_df[varlist], validation_df[tgt_name], **fk)
|
|
103
|
+
return candidate
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _grid_combinations(search_space):
|
|
107
|
+
param_names = list(search_space.keys())
|
|
108
|
+
if not param_names:
|
|
109
|
+
return param_names, [()]
|
|
110
|
+
values = [list(search_space[name]) for name in param_names]
|
|
111
|
+
if any(len(v) == 0 for v in values):
|
|
112
|
+
raise ValueError("search_space values must be non-empty iterables.")
|
|
113
|
+
return param_names, list(itertools.product(*values))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _suggest_from_spec(trial, name, spec):
|
|
117
|
+
if isinstance(spec, Mapping):
|
|
118
|
+
kind = spec.get("type")
|
|
119
|
+
if kind == "int":
|
|
120
|
+
return trial.suggest_int(name, spec["low"], spec["high"], step=spec.get("step", 1), log=spec.get("log", False))
|
|
121
|
+
if kind == "float":
|
|
122
|
+
return trial.suggest_float(name, spec["low"], spec["high"], step=spec.get("step"), log=spec.get("log", False))
|
|
123
|
+
if kind == "categorical":
|
|
124
|
+
return trial.suggest_categorical(name, spec["choices"])
|
|
125
|
+
raise ValueError("Unknown optuna search_space spec type for {0}: {1}".format(name, kind))
|
|
126
|
+
if not isinstance(spec, (tuple, list)) or not spec:
|
|
127
|
+
raise ValueError("Optuna spec for {0} must be a tuple/list or dict.".format(name))
|
|
128
|
+
kind = spec[0]
|
|
129
|
+
if kind == "int":
|
|
130
|
+
log = len(spec) >= 5 and spec[4] == "log"
|
|
131
|
+
step = spec[3] if len(spec) >= 4 and spec[3] != "log" else 1
|
|
132
|
+
return trial.suggest_int(name, spec[1], spec[2], step=step, log=log)
|
|
133
|
+
if kind == "float":
|
|
134
|
+
log = len(spec) >= 4 and spec[3] == "log"
|
|
135
|
+
return trial.suggest_float(name, spec[1], spec[2], log=log)
|
|
136
|
+
if kind == "categorical":
|
|
137
|
+
return trial.suggest_categorical(name, list(spec[1]))
|
|
138
|
+
raise ValueError("Unknown optuna search_space spec type for {0}: {1}".format(name, kind))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _format_search_row(params, set_names, metric_dict, score, use_gap, primary_set, gap_ref_sets):
|
|
142
|
+
row = dict(params)
|
|
143
|
+
for name in set_names:
|
|
144
|
+
row["AUC_{0}".format(name)] = round(float(metric_dict[name]), 5)
|
|
145
|
+
if use_gap:
|
|
146
|
+
ref = float(np.mean([metric_dict[name] for name in gap_ref_sets]))
|
|
147
|
+
row["gap"] = round(ref - metric_dict[primary_set], 5)
|
|
148
|
+
row["score"] = round(float(score), 5)
|
|
149
|
+
return row
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _gbm_param_search(self, data, varlist, tgt_name, eval_sets, search_space,
|
|
153
|
+
engine="grid", objective="oot_gap_penalized",
|
|
154
|
+
primary_set=None, gap_ref_sets=None, metric="auc",
|
|
155
|
+
validation_set=None, n_trials=50, refit=True,
|
|
156
|
+
verbose=True, fit_kwargs=None, random_state=None,
|
|
157
|
+
weight_col=None, eval_weight_col=None):
|
|
158
|
+
if metric != "auc":
|
|
159
|
+
raise ValueError("Only metric='auc' is currently supported.")
|
|
160
|
+
fit_kwargs = {} if fit_kwargs is None else dict(fit_kwargs)
|
|
161
|
+
set_names, primary_set, gap_ref_sets = _resolve_primary_and_refs(eval_sets, primary_set, gap_ref_sets)
|
|
162
|
+
_validate_columns(data, varlist, tgt_name, eval_sets, weight_col, eval_weight_col)
|
|
163
|
+
validation_name = _resolve_validation_set(eval_sets, primary_set, validation_set)
|
|
164
|
+
validation_df = eval_sets[validation_name]
|
|
165
|
+
use_gap = (not callable(objective)) and objective == "oot_gap_penalized" and len(gap_ref_sets) > 0
|
|
166
|
+
rows = []
|
|
167
|
+
|
|
168
|
+
if engine == "grid":
|
|
169
|
+
param_names, combos = _grid_combinations(search_space)
|
|
170
|
+
if verbose:
|
|
171
|
+
print("param_search(grid): {0} combinations".format(len(combos)))
|
|
172
|
+
for combo in combos:
|
|
173
|
+
params = dict(zip(param_names, combo))
|
|
174
|
+
candidate = _fit_candidate(self.model_type, self.params, params, data, varlist, tgt_name,
|
|
175
|
+
validation_df, fit_kwargs, weight_col, eval_weight_col)
|
|
176
|
+
metric_dict = _evaluate_candidate(candidate, eval_sets, varlist, tgt_name, metric, eval_weight_col)
|
|
177
|
+
score = _score_from_metrics(metric_dict, objective, primary_set, gap_ref_sets)
|
|
178
|
+
rows.append(_format_search_row(params, set_names, metric_dict, score, use_gap, primary_set, gap_ref_sets))
|
|
179
|
+
elif engine == "optuna":
|
|
180
|
+
try:
|
|
181
|
+
import optuna
|
|
182
|
+
except ImportError as exc:
|
|
183
|
+
raise ImportError("engine='optuna' requires the optional optuna dependency. Install with: pip install supermodelingfactory[optuna]") from exc
|
|
184
|
+
sampler = optuna.samplers.TPESampler(seed=random_state) if random_state is not None else None
|
|
185
|
+
study = optuna.create_study(direction="maximize", sampler=sampler)
|
|
186
|
+
|
|
187
|
+
def _objective(trial):
|
|
188
|
+
params = {name: _suggest_from_spec(trial, name, spec) for name, spec in search_space.items()}
|
|
189
|
+
candidate = _fit_candidate(self.model_type, self.params, params, data, varlist, tgt_name,
|
|
190
|
+
validation_df, fit_kwargs, weight_col, eval_weight_col)
|
|
191
|
+
metric_dict = _evaluate_candidate(candidate, eval_sets, varlist, tgt_name, metric, eval_weight_col)
|
|
192
|
+
score = _score_from_metrics(metric_dict, objective, primary_set, gap_ref_sets)
|
|
193
|
+
for set_name, value in metric_dict.items():
|
|
194
|
+
trial.set_user_attr("AUC_{0}".format(set_name), float(value))
|
|
195
|
+
if use_gap:
|
|
196
|
+
ref = float(np.mean([metric_dict[name] for name in gap_ref_sets]))
|
|
197
|
+
trial.set_user_attr("gap", float(ref - metric_dict[primary_set]))
|
|
198
|
+
return score
|
|
199
|
+
|
|
200
|
+
study.optimize(_objective, n_trials=n_trials)
|
|
201
|
+
for trial in study.trials:
|
|
202
|
+
if trial.value is None:
|
|
203
|
+
continue
|
|
204
|
+
row = {"trial_number": trial.number, **trial.params}
|
|
205
|
+
for name in set_names:
|
|
206
|
+
key = "AUC_{0}".format(name)
|
|
207
|
+
if key in trial.user_attrs:
|
|
208
|
+
row[key] = round(float(trial.user_attrs[key]), 5)
|
|
209
|
+
if use_gap and "gap" in trial.user_attrs:
|
|
210
|
+
row["gap"] = round(float(trial.user_attrs["gap"]), 5)
|
|
211
|
+
row["score"] = round(float(trial.value), 5)
|
|
212
|
+
rows.append(row)
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError("engine must be 'grid' or 'optuna'")
|
|
215
|
+
|
|
216
|
+
if not rows:
|
|
217
|
+
raise RuntimeError("param_search produced no successful candidates.")
|
|
218
|
+
search_df = pd.DataFrame(rows).sort_values("score", ascending=False).reset_index(drop=True)
|
|
219
|
+
param_cols = [c for c in search_space.keys() if c in search_df.columns]
|
|
220
|
+
best_row = search_df.iloc[0]
|
|
221
|
+
self.best_params_ = {k: _native(best_row[k]) for k in param_cols}
|
|
222
|
+
self.search_results_ = search_df
|
|
223
|
+
self.params = {**self.params, **self.best_params_}
|
|
224
|
+
if refit:
|
|
225
|
+
refit_kwargs = dict(fit_kwargs)
|
|
226
|
+
train_sw = resolve_sample_weight(data=data, weight_col=weight_col, expected_len=len(data))
|
|
227
|
+
eval_sw = resolve_sample_weight(data=validation_df, weight_col=eval_weight_col, expected_len=len(validation_df))
|
|
228
|
+
if train_sw is not None:
|
|
229
|
+
refit_kwargs["sample_weight"] = train_sw
|
|
230
|
+
if eval_sw is not None:
|
|
231
|
+
refit_kwargs["eval_sample_weight"] = eval_sw
|
|
232
|
+
self.fit(data[varlist], data[tgt_name], validation_df[varlist], validation_df[tgt_name], **refit_kwargs)
|
|
233
|
+
return search_df
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def attach_gbm_param_search(cls=GradientBoostingModel):
|
|
237
|
+
"""Attach param_search when the class permits runtime mutation."""
|
|
238
|
+
try:
|
|
239
|
+
cls.param_search = _gbm_param_search
|
|
240
|
+
if not hasattr(cls, "best_params_"):
|
|
241
|
+
cls.best_params_ = None
|
|
242
|
+
if not hasattr(cls, "search_results_"):
|
|
243
|
+
cls.search_results_ = None
|
|
244
|
+
except (TypeError, AttributeError):
|
|
245
|
+
# Cython extension types can be immutable; Model.__init__ exports a
|
|
246
|
+
# subclass wrapper in that case.
|
|
247
|
+
pass
|
|
248
|
+
return cls
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
attach_gbm_param_search()
|