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.
Files changed (79) hide show
  1. ExcelMaster/ExcelFormatTool.py +487 -0
  2. ExcelMaster/ExcelMaster.py +917 -0
  3. ExcelMaster/Template.py +525 -0
  4. ExcelMaster/Utility.py +233 -0
  5. ExcelMaster/__init__.py +3 -0
  6. Modeling_Tool/Core/Binning_Tool.py +1608 -0
  7. Modeling_Tool/Core/Binning_Tool.pyi +48 -0
  8. Modeling_Tool/Core/Check_DuckDB_Compatibility.py +835 -0
  9. Modeling_Tool/Core/Json_Data_Converter.py +621 -0
  10. Modeling_Tool/Core/Model_Registry_Tool.py +199 -0
  11. Modeling_Tool/Core/ODPS_Tool.py +284 -0
  12. Modeling_Tool/Core/Slope_Tool.py +356 -0
  13. Modeling_Tool/Core/Slope_Tool.pyi +31 -0
  14. Modeling_Tool/Core/XOR_Encryptor.py +207 -0
  15. Modeling_Tool/Core/XOR_Encryptor.pyi +26 -0
  16. Modeling_Tool/Core/__init__.py +99 -0
  17. Modeling_Tool/Core/kDataFrame.py +228 -0
  18. Modeling_Tool/Core/kDataFrame.pyi +43 -0
  19. Modeling_Tool/Core/sample_weight_utils.py +77 -0
  20. Modeling_Tool/Core/utils.py +2672 -0
  21. Modeling_Tool/Eval/Evaluation_Tool.py +1452 -0
  22. Modeling_Tool/Eval/Evaluation_Tool.pyi +47 -0
  23. Modeling_Tool/Eval/Model_Eval_Tool.py +2548 -0
  24. Modeling_Tool/Eval/Model_Eval_Tool.pyi +37 -0
  25. Modeling_Tool/Eval/__init__.py +54 -0
  26. Modeling_Tool/Eval/evaluate_model.py +2008 -0
  27. Modeling_Tool/Eval/evaluate_model.pyi +50 -0
  28. Modeling_Tool/Eval/weighted_eval_utils.py +326 -0
  29. Modeling_Tool/Explainability/Coalition_Structure.py +305 -0
  30. Modeling_Tool/Explainability/Model_Explainer.py +743 -0
  31. Modeling_Tool/Explainability/__init__.py +24 -0
  32. Modeling_Tool/Feature/Distribution_Tool.py +509 -0
  33. Modeling_Tool/Feature/Distribution_Tool.pyi +42 -0
  34. Modeling_Tool/Feature/Feature_Insights.py +762 -0
  35. Modeling_Tool/Feature/Feature_Insights.pyi +33 -0
  36. Modeling_Tool/Feature/PSI_Tool.py +1195 -0
  37. Modeling_Tool/Feature/PSI_Tool.pyi +29 -0
  38. Modeling_Tool/Feature/WOE_Engine_Feature_Patch.py +355 -0
  39. Modeling_Tool/Feature/__init__.py +40 -0
  40. Modeling_Tool/Model/Backward_Tool.py +778 -0
  41. Modeling_Tool/Model/Backward_Tool.pyi +45 -0
  42. Modeling_Tool/Model/GBM_Search_Tool.py +251 -0
  43. Modeling_Tool/Model/GBM_Tool.py +1610 -0
  44. Modeling_Tool/Model/GBM_Tool.pyi +90 -0
  45. Modeling_Tool/Model/LRM_Tool.py +1198 -0
  46. Modeling_Tool/Model/LRM_Tool.pyi +47 -0
  47. Modeling_Tool/Model/__init__.py +61 -0
  48. Modeling_Tool/Sample/Distribution_Adaptation.py +131 -0
  49. Modeling_Tool/Sample/Distribution_Adaptation.pyi +30 -0
  50. Modeling_Tool/Sample/Reject_Infer.py +413 -0
  51. Modeling_Tool/Sample/Reject_Infer.pyi +43 -0
  52. Modeling_Tool/Sample/Sample_Split.py +520 -0
  53. Modeling_Tool/Sample/Sample_Split.pyi +43 -0
  54. Modeling_Tool/Sample/__init__.py +31 -0
  55. Modeling_Tool/UAT/UAT_Consistency_Checker.py +1180 -0
  56. Modeling_Tool/UAT/__init__.py +19 -0
  57. Modeling_Tool/WOE/WOE_Adapter.py +204 -0
  58. Modeling_Tool/WOE/WOE_Adapter.pyi +21 -0
  59. Modeling_Tool/WOE/WOE_Master.py +491 -0
  60. Modeling_Tool/WOE/WOE_Master.pyi +40 -0
  61. Modeling_Tool/WOE/WOE_Monotone_Binner.py +3324 -0
  62. Modeling_Tool/WOE/WOE_Monotone_Binner.pyi +71 -0
  63. Modeling_Tool/WOE/WOE_Plot_Tool.py +919 -0
  64. Modeling_Tool/WOE/WOE_Plot_Tool.pyi +46 -0
  65. Modeling_Tool/WOE/WOE_Report_Builder.py +214 -0
  66. Modeling_Tool/WOE/WOE_Report_Builder.pyi +24 -0
  67. Modeling_Tool/WOE/WOE_Tool.py +1094 -0
  68. Modeling_Tool/WOE/WOE_Tool.pyi +41 -0
  69. Modeling_Tool/WOE/__init__.py +86 -0
  70. Modeling_Tool/WOE/plot_woe_tool.py +290 -0
  71. Modeling_Tool/WOE/plot_woe_tool.pyi +25 -0
  72. Modeling_Tool/__init__.py +176 -0
  73. Report/Report_Tool.py +380 -0
  74. Report/__init__.py +3 -0
  75. supermodelingfactory-0.2.0.dist-info/METADATA +268 -0
  76. supermodelingfactory-0.2.0.dist-info/RECORD +79 -0
  77. supermodelingfactory-0.2.0.dist-info/WHEEL +5 -0
  78. supermodelingfactory-0.2.0.dist-info/licenses/LICENSE +102 -0
  79. supermodelingfactory-0.2.0.dist-info/top_level.txt +3 -0
@@ -0,0 +1,1610 @@
1
+ """
2
+ 梯度提升模型训练工具包
3
+ =============================
4
+
5
+ 本模块提供LightGBM、XGBoost和CatBoost模型的快速训练和评估功能,
6
+ 包括模型训练、特征重要性提取等常用操作。
7
+
8
+ Functions
9
+ ---------
10
+ set_num_leaves
11
+ 根据最大深度计算叶子节点数,避免过拟合。
12
+ lgb_model
13
+ 快速训练LightGBM模型。
14
+ lgb_varimp
15
+ 获取LightGBM特征重要性。
16
+ lgbm_quick_train
17
+ 快速训练LightGBM模型(使用DataFrame接口)。
18
+ xgb_model
19
+ 训练XGBoost模型。
20
+ xgb_varimp
21
+ 获取XGBoost特征重要性。
22
+ xgbm_quick_train
23
+ 快速训练XGBoost模型(使用DataFrame接口)。
24
+ catboost_model
25
+ 训练CatBoost模型。
26
+ catboost_varimp
27
+ 获取CatBoost特征重要性。
28
+ catboost_quick_train
29
+ 快速训练CatBoost模型(使用DataFrame接口)。
30
+
31
+ Classes
32
+ -------
33
+ LightGBMModel
34
+ LightGBM模型封装类,提供统一的训练和评估接口。
35
+ XGBoostModel
36
+ XGBoost模型封装类,提供统一的训练和评估接口。
37
+ CatBoostModel
38
+ CatBoost模型封装类,提供统一的训练和评估接口。
39
+ GradientBoostingModel
40
+ 统一封装类,支持LightGBM、XGBoost和CatBoost切换。
41
+
42
+ Examples
43
+ --------
44
+ # 函数式调用
45
+ >>> model = lgb_model(x_train, y_train, x_val, y_val, params)
46
+ >>> varimp = lgb_varimp(model)
47
+
48
+ # 类封装调用
49
+ >>> lgb_model = LightGBMModel(params)
50
+ >>> lgb_model.fit(x_train, y_train, x_val, y_val)
51
+ >>> varimp = lgb_model.get_feature_importance()
52
+
53
+ # 统一接口调用
54
+ >>> model = GradientBoostingModel('lgb', params)
55
+ >>> model.fit(x_train, y_train, x_val, y_val)
56
+ """
57
+
58
+ import pandas as pd
59
+ import numpy as np
60
+ from Modeling_Tool.Core.utils import load_model, save_model
61
+ from sklearn.calibration import CalibratedClassifierCV, calibration_curve
62
+ from sklearn.metrics import brier_score_loss, roc_auc_score
63
+
64
+ # lightgbm, xgboost, and catboost are imported lazily inside each function/method to
65
+ # avoid triggering the lightgbm → dask → numpy_compat → np.float chain at
66
+ # module import time. Old dask versions (<2022) use np.float which was
67
+ # removed in NumPy 1.20+, causing AttributeError on import.
68
+ def _get_lgb():
69
+ try:
70
+ import lightgbm as lgb
71
+ return lgb
72
+ except ImportError:
73
+ raise ImportError("lightgbm is required. Install with: pip install lightgbm>=3.3.0")
74
+
75
+ def _get_xgb():
76
+ try:
77
+ import xgboost as xgb
78
+ return xgb
79
+ except ImportError:
80
+ raise ImportError("xgboost is required. Install with: pip install xgboost>=1.7.0")
81
+
82
+ def _get_catboost():
83
+ try:
84
+ from catboost import CatBoostClassifier
85
+ return CatBoostClassifier
86
+ except ImportError:
87
+ raise ImportError("catboost is required. Install with: pip install catboost>=1.2.0")
88
+
89
+
90
+ def _coerce_feature_names(feature_names):
91
+ """Normalize estimator feature-name metadata to a non-empty list."""
92
+ if feature_names is None:
93
+ return None
94
+ try:
95
+ names = list(feature_names)
96
+ except TypeError:
97
+ return None
98
+ return names or None
99
+
100
+
101
+ def _extract_estimator_feature_names(estimator):
102
+ """Extract feature names across sklearn / LightGBM / XGBoost / CatBoost versions."""
103
+ for attr in ("feature_names_in_", "feature_name_", "feature_names_"):
104
+ names = _coerce_feature_names(getattr(estimator, attr, None))
105
+ if names is not None:
106
+ return names
107
+
108
+ booster = getattr(estimator, "booster_", None)
109
+ feature_name = getattr(booster, "feature_name", None)
110
+ if callable(feature_name):
111
+ names = _coerce_feature_names(feature_name())
112
+ if names is not None:
113
+ return names
114
+
115
+ get_booster = getattr(estimator, "get_booster", None)
116
+ if callable(get_booster):
117
+ try:
118
+ booster = get_booster()
119
+ except Exception:
120
+ booster = None
121
+ names = _coerce_feature_names(getattr(booster, "feature_names", None))
122
+ if names is not None:
123
+ return names
124
+
125
+ return None
126
+
127
+
128
+ def _ensure_feature_names_in(estimator, feature_names):
129
+ """Backfill sklearn-style feature_names_in_ on legacy estimators if possible."""
130
+ if feature_names is None or hasattr(estimator, "feature_names_in_"):
131
+ return
132
+ try:
133
+ estimator.feature_names_in_ = np.asarray(feature_names, dtype=object)
134
+ except Exception:
135
+ # Some third-party estimators expose read-only / slot-backed metadata.
136
+ # __getattr__ on the wrapper still provides a compatible fallback.
137
+ pass
138
+
139
+
140
+ # CatBoost metric names are case-sensitive; map common lower-case aliases to the
141
+ # spellings CatBoost expects. Unlisted strings are passed through unchanged.
142
+ _CATBOOST_METRIC_ALIASES = {'auc': 'AUC', 'logloss': 'Logloss'}
143
+
144
+
145
+ def _normalize_catboost_params(params_dict):
146
+ """Normalize unified GBM params to CatBoost classifier kwargs.
147
+
148
+ Maps sklearn-style names (n_estimators, max_depth, random_state) to CatBoost
149
+ equivalents and extracts fit-only arguments. ``eval_metric`` is a CatBoost
150
+ constructor argument (it is not accepted by ``fit()``), so it is folded into
151
+ the returned classifier params.
152
+
153
+ ``eval_metric`` defaults to ``'AUC'`` when neither ``eval_metric`` nor
154
+ ``metric`` is supplied. Common lower-case aliases (e.g. ``'auc'``,
155
+ ``'logloss'``) are normalized to the case-sensitive names CatBoost expects;
156
+ any other string is left untouched.
157
+
158
+ Returns
159
+ -------
160
+ tuple
161
+ (classifier_params, early_stopping_rounds, eval_metric, cat_features)
162
+ """
163
+ params = dict(params_dict)
164
+ early_stopping_rounds = params.pop('early_stopping_rounds', None)
165
+ eval_metric = params.pop('eval_metric', params.pop('metric', None))
166
+ cat_features = params.pop('cat_features', None)
167
+
168
+ if 'n_estimators' in params:
169
+ params['iterations'] = params.pop('n_estimators')
170
+ if 'max_depth' in params:
171
+ params['depth'] = params.pop('max_depth')
172
+ if 'random_state' in params:
173
+ params['random_seed'] = params.pop('random_state')
174
+
175
+ params.setdefault('loss_function', 'Logloss')
176
+ params.setdefault('verbose', False)
177
+
178
+ # eval_metric must be passed to the CatBoostClassifier constructor, not fit().
179
+ if eval_metric is None:
180
+ eval_metric = 'AUC'
181
+ elif isinstance(eval_metric, str):
182
+ eval_metric = _CATBOOST_METRIC_ALIASES.get(eval_metric.lower(), eval_metric)
183
+ params['eval_metric'] = eval_metric
184
+
185
+ return params, early_stopping_rounds, eval_metric, cat_features
186
+
187
+ # ============================================================================
188
+ # 工具函数(保持独立)
189
+ # ============================================================================
190
+
191
+ def set_num_leaves(max_depth=5, wgt=1):
192
+ """根据最大深度设置叶子节点数,避免过拟合。
193
+
194
+ 根据给定的最大深度和权重系数,计算合适的叶子节点数量。
195
+ 计算公式:2^max_depth - 2^max_depth * wgt
196
+
197
+ Parameters
198
+ ----------
199
+ max_depth : int, default 5
200
+ 树的最大深度
201
+ wgt : float, default 1
202
+ 权重系数,取値范围[0, 1]
203
+
204
+ Returns
205
+ -------
206
+ int
207
+ 建议的叶子节点数
208
+
209
+ Examples
210
+ --------
211
+ >>> set_num_leaves(max_depth=5)
212
+ 32
213
+ >>> set_num_leaves(max_depth=5, wgt=0.5)
214
+ 16
215
+ """
216
+ return int(2 ** max_depth - 2 ** max_depth * wgt)
217
+
218
+
219
+ def lgb_model(x, y, valx, valy, params_dict, wgt=None, init_score=None, eval_sample_weight=None):
220
+ """快速训练LightGBM模型。
221
+
222
+ 使用训练集和验证集训练LightGBM模型,支持早停机制。
223
+
224
+ Parameters
225
+ ----------
226
+ x : array-like or pd.DataFrame
227
+ 训练集特征
228
+ y : array-like
229
+ 训练集标签
230
+ valx : array-like or pd.DataFrame
231
+ 验证集特征
232
+ valy : array-like
233
+ 验证集标签
234
+ params_dict : dict
235
+ LightGBM参数字典
236
+ wgt : array-like, optional
237
+ 样本权重
238
+ init_score : array-like, optional
239
+ 初始化分数
240
+
241
+ Returns
242
+ -------
243
+ lgb.LGBMClassifier
244
+ 训练好的LightGBM模型
245
+
246
+ Examples
247
+ --------
248
+ >>> params = {
249
+ ... 'n_estimators': 100,
250
+ ... 'max_depth': 5,
251
+ ... 'learning_rate': 0.1,
252
+ ... 'early_stopping_rounds': 20,
253
+ ... 'eval_metric': 'auc'
254
+ ... }
255
+ >>> model = lgb_model(x_train, y_train, x_val, y_val, params)
256
+ """
257
+ lgb = _get_lgb()
258
+
259
+ lgb_params = {k: v for k, v in params_dict.items() if k != 'eval_metric'}
260
+ model = lgb.LGBMClassifier(**lgb_params)
261
+ # NOTE: `verbose=False` was removed from .fit() in lightgbm>=4. Log control
262
+ # is now done via callbacks only (log_evaluation).
263
+ model.fit(
264
+ x, y,
265
+ eval_set=[(valx, valy)],
266
+ eval_metric=params_dict['eval_metric'] if 'eval_metric' in params_dict else params_dict.get('metric', 'auc'),
267
+ callbacks=[
268
+ lgb.early_stopping(stopping_rounds=params_dict['early_stopping_rounds'], verbose=False),
269
+ lgb.log_evaluation(period=0),
270
+ ],
271
+ sample_weight=wgt,
272
+ eval_sample_weight=[eval_sample_weight] if eval_sample_weight is not None else None,
273
+ init_score=init_score
274
+ )
275
+ return model
276
+
277
+
278
+ def lgb_varimp(model):
279
+ """获取LightGBM模型特征重要性。
280
+
281
+ 返回按特征重要性排序的DataFrame。
282
+
283
+ Parameters
284
+ ----------
285
+ model : lgb.LGBMClassifier
286
+ 训练好的LightGBM模型
287
+
288
+ Returns
289
+ -------
290
+ pd.DataFrame
291
+ 包含 feature 和 importance 列的DataFrame,按重要性降序排列
292
+
293
+ Examples
294
+ --------
295
+ >>> varimp = lgb_varimp(model)
296
+ >>> varimp.head(10)
297
+ """
298
+ feature_names = model.booster_.feature_name()
299
+ importance = model.booster_.feature_importance(importance_type='gain')
300
+ varimp_df = pd.DataFrame({'feature': feature_names, 'importance': importance})
301
+ varimp_df = varimp_df.sort_values('importance', ascending=False).reset_index(drop=True)
302
+ return varimp_df
303
+
304
+
305
+ def lgbm_quick_train(train_data, validation_data, x, y, params, wgt_col = None, val_wgt_col = None, cat_x_train=None):
306
+ """快速训练LightGBM模型(使用DataFrame接口)。
307
+
308
+ 接受DataFrame格式的训练集和验证集,自动提取特征和标签。
309
+
310
+ Parameters
311
+ ----------
312
+ train_data : pd.DataFrame
313
+ 训练数据集
314
+ validation_data : pd.DataFrame
315
+ 验证数据集
316
+ x : list of str
317
+ 特征列名列表
318
+ y : str
319
+ 目标变量列名
320
+ params : dict
321
+ LightGBM参数字典
322
+ wgt_col : str, optional
323
+ 样本权重列名
324
+ cat_x_train : list of str, optional
325
+ 类别型特征列名列表
326
+
327
+ Returns
328
+ -------
329
+ lgb.LGBMClassifier
330
+ 训练好的LightGBM模型
331
+
332
+ Examples
333
+ --------
334
+ >>> model = lgbm_quick_train(
335
+ ... train_data=train_df,
336
+ ... validation_data=val_df,
337
+ ... x=['feat1', 'feat2'],
338
+ ... y='target',
339
+ ... params=params_dict
340
+ ... )
341
+ """
342
+ lgb = _get_lgb()
343
+
344
+ wgt = train_data[wgt_col] if wgt_col is not None else None
345
+ eval_wgt = validation_data[val_wgt_col] if val_wgt_col is not None else None
346
+ model = lgb_model(
347
+ x=train_data[x],
348
+ y=train_data[y],
349
+ valx=validation_data[x],
350
+ valy=validation_data[y],
351
+ params_dict=params,
352
+ wgt=wgt,
353
+ eval_sample_weight=eval_wgt
354
+ )
355
+ return model
356
+
357
+
358
+ def xgb_model(x, y, valx, valy, params_dict, sample_weight=None, sample_weight_eval_set=None, base_margin=None):
359
+ """训练XGBoost模型。
360
+
361
+ 使用训练集和验证集训练XGBoost模型,支持早停机制。
362
+
363
+ Parameters
364
+ ----------
365
+ x : array-like or pd.DataFrame
366
+ 训练集特征
367
+ y : array-like
368
+ 训练集标签
369
+ valx : array-like or pd.DataFrame
370
+ 验证集特征
371
+ valy : array-like
372
+ 验证集标签
373
+ params_dict : dict
374
+ XGBoost参数字典
375
+ sample_weight : array-like, optional
376
+ 训练集样本权重
377
+ sample_weight_eval_set : list, optional
378
+ 验证集样本权重列表
379
+ base_margin : array-like, optional
380
+ 基础边际度
381
+
382
+ Returns
383
+ -------
384
+ xgb.XGBClassifier
385
+ 训练好的XGBoost模型
386
+
387
+ Examples
388
+ --------
389
+ >>> params = {
390
+ ... 'n_estimators': 100,
391
+ ... 'max_depth': 5,
392
+ ... 'learning_rate': 0.1,
393
+ ... 'early_stopping_rounds': 20,
394
+ ... 'eval_metric': 'auc'
395
+ ... }
396
+ >>> model = xgb_model(x_train, y_train, x_val, y_val, params)
397
+ """
398
+ xgb = _get_xgb()
399
+
400
+ xgb_params = {k: v for k, v in params_dict.items() if k not in ('eval_metric',)}
401
+ model = xgb.XGBClassifier(**xgb_params)
402
+ model.fit(
403
+ x, y,
404
+ eval_set=[(valx, valy)],
405
+ verbose=False,
406
+ sample_weight=sample_weight,
407
+ sample_weight_eval_set=sample_weight_eval_set,
408
+ base_margin=base_margin
409
+ )
410
+ return model
411
+
412
+
413
+ def xgb_varimp(model):
414
+ """获取XGBoost模型特征重要性。
415
+
416
+ 返回按特征重要性排序的DataFrame。
417
+
418
+ Parameters
419
+ ----------
420
+ model : xgb.XGBClassifier
421
+ 训练好的XGBoost模型
422
+
423
+ Returns
424
+ -------
425
+ pd.DataFrame
426
+ 包含 feature 和 importance 列的DataFrame,按重要性降序排列
427
+
428
+ Examples
429
+ --------
430
+ >>> varimp = xgb_varimp(model)
431
+ >>> varimp.head(10)
432
+ """
433
+ importance = model.get_booster().get_fscore()
434
+ varimp_df = pd.DataFrame(
435
+ list(importance.items()),
436
+ columns=['feature', 'importance']
437
+ ).sort_values('importance', ascending=False).reset_index(drop=True)
438
+ return varimp_df
439
+
440
+
441
+ def xgbm_quick_train(train_data, validation_data, x, y, wgt_col=None, params=None,
442
+ sample_weight_eval_set=None, val_wgt_col=None):
443
+ """快速训练XGBoost模型(使用DataFrame接口)。
444
+
445
+ 接受DataFrame格式的训练集和验证集,自动提取特征和标签。
446
+
447
+ Parameters
448
+ ----------
449
+ train_data : pd.DataFrame
450
+ 训练数据集
451
+ validation_data : pd.DataFrame
452
+ 验证数据集
453
+ x : list of str
454
+ 特征列名列表
455
+ y : str
456
+ 目标变量列名
457
+ wgt_col : str
458
+ 样本权重列名
459
+ params : dict
460
+ XGBoost参数字典
461
+ sample_weight_eval_set : list, optional
462
+ 验证集样本权重列表
463
+
464
+ Returns
465
+ -------
466
+ xgb.XGBClassifier
467
+ 训练好的XGBoost模型
468
+
469
+ Examples
470
+ --------
471
+ >>> model = xgbm_quick_train(
472
+ ... train_data=train_df,
473
+ ... validation_data=val_df,
474
+ ... x=['feat1', 'feat2'],
475
+ ... y='target',
476
+ ... wgt_col='weight',
477
+ ... params=params_dict
478
+ ... )
479
+ """
480
+ xgb = _get_xgb()
481
+
482
+ wgt = train_data[wgt_col] if wgt_col is not None else None
483
+ if sample_weight_eval_set is None and val_wgt_col is not None:
484
+ sample_weight_eval_set = [validation_data[val_wgt_col]]
485
+ model = xgb_model(
486
+ x=train_data[x],
487
+ y=train_data[y],
488
+ valx=validation_data[x],
489
+ valy=validation_data[y],
490
+ params_dict=params,
491
+ sample_weight=wgt,
492
+ sample_weight_eval_set=sample_weight_eval_set
493
+ )
494
+ return model
495
+
496
+
497
+ def catboost_model(x, y, valx, valy, params_dict, sample_weight=None):
498
+ """训练CatBoost模型。
499
+
500
+ 使用训练集和验证集训练CatBoost模型,支持早停机制。
501
+
502
+ Parameters
503
+ ----------
504
+ x : array-like or pd.DataFrame
505
+ 训练集特征
506
+ y : array-like
507
+ 训练集标签
508
+ valx : array-like or pd.DataFrame
509
+ 验证集特征
510
+ valy : array-like
511
+ 验证集标签
512
+ params_dict : dict
513
+ CatBoost参数字典(支持 n_estimators / max_depth / random_state 别名)
514
+ sample_weight : array-like, optional
515
+ 训练集样本权重
516
+
517
+ Returns
518
+ -------
519
+ CatBoostClassifier
520
+ 训练好的CatBoost模型
521
+
522
+ Examples
523
+ --------
524
+ >>> params = {
525
+ ... 'n_estimators': 100,
526
+ ... 'max_depth': 5,
527
+ ... 'learning_rate': 0.1,
528
+ ... 'early_stopping_rounds': 20,
529
+ ... 'eval_metric': 'AUC'
530
+ ... }
531
+ >>> model = catboost_model(x_train, y_train, x_val, y_val, params)
532
+ """
533
+ CatBoostClassifier = _get_catboost()
534
+ cb_params, early_stopping_rounds, eval_metric, cat_features = (
535
+ _normalize_catboost_params(params_dict)
536
+ )
537
+ # eval_metric is folded into cb_params by _normalize_catboost_params because
538
+ # CatBoost only accepts it as a constructor argument, not in fit().
539
+ model = CatBoostClassifier(**cb_params)
540
+ fit_kwargs = {
541
+ 'eval_set': (valx, valy),
542
+ 'verbose': cb_params.get('verbose', False),
543
+ }
544
+ if early_stopping_rounds is not None:
545
+ fit_kwargs['early_stopping_rounds'] = early_stopping_rounds
546
+ if cat_features is not None:
547
+ fit_kwargs['cat_features'] = cat_features
548
+ if sample_weight is not None:
549
+ fit_kwargs['sample_weight'] = sample_weight
550
+ model.fit(x, y, **fit_kwargs)
551
+ return model
552
+
553
+
554
+ def catboost_varimp(model):
555
+ """获取CatBoost模型特征重要性。
556
+
557
+ 返回按特征重要性排序的DataFrame。
558
+
559
+ Parameters
560
+ ----------
561
+ model : CatBoostClassifier
562
+ 训练好的CatBoost模型
563
+
564
+ Returns
565
+ -------
566
+ pd.DataFrame
567
+ 包含 feature 和 importance 列的DataFrame,按重要性降序排列
568
+
569
+ Examples
570
+ --------
571
+ >>> varimp = catboost_varimp(model)
572
+ >>> varimp.head(10)
573
+ """
574
+ feature_names = _extract_estimator_feature_names(model)
575
+ if feature_names is None:
576
+ feature_names = list(range(model.feature_count_))
577
+ importance = model.get_feature_importance()
578
+ varimp_df = pd.DataFrame({'feature': feature_names, 'importance': importance})
579
+ varimp_df = varimp_df.sort_values('importance', ascending=False).reset_index(drop=True)
580
+ return varimp_df
581
+
582
+
583
+ def catboost_quick_train(train_data, validation_data, x, y, params, wgt_col=None,
584
+ val_wgt_col=None, cat_features=None):
585
+ """快速训练CatBoost模型(使用DataFrame接口)。
586
+
587
+ 接受DataFrame格式的训练集和验证集,自动提取特征和标签。
588
+
589
+ Parameters
590
+ ----------
591
+ train_data : pd.DataFrame
592
+ 训练数据集
593
+ validation_data : pd.DataFrame
594
+ 验证数据集
595
+ x : list of str
596
+ 特征列名列表
597
+ y : str
598
+ 目标变量列名
599
+ params : dict
600
+ CatBoost参数字典
601
+ wgt_col : str, optional
602
+ 样本权重列名
603
+ cat_features : list, optional
604
+ 类别型特征列名或索引列表
605
+
606
+ Returns
607
+ -------
608
+ CatBoostClassifier
609
+ 训练好的CatBoost模型
610
+
611
+ Examples
612
+ --------
613
+ >>> model = catboost_quick_train(
614
+ ... train_data=train_df,
615
+ ... validation_data=val_df,
616
+ ... x=['feat1', 'feat2'],
617
+ ... y='target',
618
+ ... params=params_dict
619
+ ... )
620
+ """
621
+ _get_catboost()
622
+
623
+ params_dict = dict(params)
624
+ if cat_features is not None:
625
+ params_dict['cat_features'] = cat_features
626
+ wgt = train_data[wgt_col] if wgt_col is not None else None
627
+ model = catboost_model(
628
+ x=train_data[x],
629
+ y=train_data[y],
630
+ valx=validation_data[x],
631
+ valy=validation_data[y],
632
+ params_dict=params_dict,
633
+ sample_weight=wgt,
634
+ )
635
+ return model
636
+
637
+
638
+ class LightGBMModel:
639
+ """
640
+ LightGBM模型封装类。
641
+
642
+ 提供统一的LightGBM模型训练、预测、保存和加载接口。
643
+ 支持模型校准、特征重要性获取等功能。
644
+
645
+ Parameters
646
+ ----------
647
+ params : dict
648
+ LightGBM模型参数字典
649
+ model : lgb.LGBMClassifier, optional
650
+ 预加载的模型实例
651
+
652
+ Examples
653
+ --------
654
+ >>> lgb_clf = LightGBMModel(params)
655
+ >>> lgb_clf.fit(x_train, y_train, x_val, y_val)
656
+ >>> preds = lgb_clf.predict(x_test)
657
+ """
658
+
659
+ def __init__(self, params, model=None):
660
+ """
661
+ 初始化LightGBM模型封装类。
662
+
663
+ Parameters
664
+ ----------
665
+ params : dict
666
+ LightGBM模型参数字典
667
+ model : lgb.LGBMClassifier, optional
668
+ 预加载的模型实例
669
+ """
670
+ lgb = _get_lgb()
671
+ self.params = params
672
+ self.model = model
673
+ self.feature_names_ = None
674
+
675
+ def fit(self, x, y, valx, valy, wgt=None, init_score=None, sample_weight=None, eval_sample_weight=None):
676
+ """训练LightGBM模型。
677
+
678
+ 使用训练集和验证集训练模型,支持早停机制。
679
+
680
+ Parameters
681
+ ----------
682
+ x : array-like or pd.DataFrame
683
+ 训练集特征
684
+ y : array-like
685
+ 训练集标签
686
+ valx : array-like or pd.DataFrame
687
+ 验证集特征
688
+ valy : array-like
689
+ 验证集标签
690
+ wgt : array-like, optional
691
+ 样本权重
692
+ init_score : array-like, optional
693
+ 初始化分数
694
+
695
+ Returns
696
+ -------
697
+ self
698
+ """
699
+ if wgt is None:
700
+ wgt = sample_weight
701
+ self.model = lgb_model(
702
+ x=x, y=y, valx=valx, valy=valy,
703
+ params_dict=self.params, wgt=wgt, init_score=init_score,
704
+ eval_sample_weight=eval_sample_weight
705
+ )
706
+ if hasattr(x, 'columns'):
707
+ self.feature_names_ = list(x.columns)
708
+ return self
709
+
710
+ def predict(self, x):
711
+ """预测样本的概率。
712
+
713
+ Parameters
714
+ ----------
715
+ x : array-like or pd.DataFrame
716
+ 预测特征
717
+
718
+ Returns
719
+ -------
720
+ np.ndarray
721
+ 预测概率
722
+ """
723
+ return self.model.predict_proba(x)[:, 1]
724
+
725
+ def get_feature_importance(self, importance_type='gain'):
726
+ """获取特征重要性。
727
+
728
+ Parameters
729
+ ----------
730
+ importance_type : str, default 'gain'
731
+ 特征重要性类型,可选 'gain' 或 'split'
732
+
733
+ Returns
734
+ -------
735
+ pd.DataFrame
736
+ 包含 feature 和 importance 列的DataFrame
737
+ """
738
+ return lgb_varimp(self.model)
739
+
740
+ def save(self, path):
741
+ """保存模型。
742
+
743
+ Parameters
744
+ ----------
745
+ path : str
746
+ 模型保存路径
747
+ """
748
+ save_model(self.model, path)
749
+
750
+ def load(self, path):
751
+ """加载模型。
752
+
753
+ Parameters
754
+ ----------
755
+ path : str
756
+ 模型文件路径
757
+
758
+ Returns
759
+ -------
760
+ self
761
+ """
762
+ self.model = load_model(path)
763
+ return self
764
+
765
+ def calibrate(self, x, y, method='sigmoid', cv='prefit'):
766
+ """模型概率校准。
767
+
768
+ Parameters
769
+ ----------
770
+ x : array-like
771
+ 校准特征
772
+ y : array-like
773
+ 校准标签
774
+ method : str, default 'sigmoid'
775
+ 校准方法,'sigmoid' 或 'isotonic'
776
+ cv : str or int, default 'prefit'
777
+ 交叉验证方式
778
+
779
+ Returns
780
+ -------
781
+ self
782
+ """
783
+ self.model = CalibratedClassifierCV(self.model, method=method, cv=cv)
784
+ self.model.fit(x, y)
785
+ return self
786
+
787
+ def calibration_curve(self, x, y, n_bins=10):
788
+ """获取校准曲线数据。
789
+
790
+ Parameters
791
+ ----------
792
+ x : array-like
793
+ 特征
794
+ y : array-like
795
+ 标签
796
+ n_bins : int, default 10
797
+ 分箱数
798
+
799
+ Returns
800
+ -------
801
+ tuple
802
+ (fraction_of_positives, mean_predicted_value)
803
+ """
804
+ y_prob = self.predict(x)
805
+ return calibration_curve(y, y_prob, n_bins=n_bins)
806
+
807
+ def brier_score(self, x, y):
808
+ """计算Brier分数。
809
+
810
+ Parameters
811
+ ----------
812
+ x : array-like
813
+ 特征
814
+ y : array-like
815
+ 标签
816
+
817
+ Returns
818
+ -------
819
+ float
820
+ Brier分数
821
+ """
822
+ y_prob = self.predict(x)
823
+ return brier_score_loss(y, y_prob)
824
+
825
+ def roc_auc(self, x, y):
826
+ """计算ROC AUC。
827
+
828
+ Parameters
829
+ ----------
830
+ x : array-like
831
+ 特征
832
+ y : array-like
833
+ 标签
834
+
835
+ Returns
836
+ -------
837
+ float
838
+ ROC AUC分数
839
+ """
840
+ y_prob = self.predict(x)
841
+ return roc_auc_score(y, y_prob)
842
+
843
+
844
+ class XGBoostModel:
845
+ """
846
+ XGBoost模型封装类。
847
+
848
+ 提供统一的XGBoost模型训练、预测、保存和加载接口。
849
+ 支持模型校准、特征重要性获取等功能。
850
+
851
+ Parameters
852
+ ----------
853
+ params : dict
854
+ XGBoost模型参数字典
855
+ model : xgb.XGBClassifier, optional
856
+ 预加载的模型实例
857
+
858
+ Examples
859
+ --------
860
+ >>> xgb_clf = XGBoostModel(params)
861
+ >>> xgb_clf.fit(x_train, y_train, x_val, y_val)
862
+ >>> preds = xgb_clf.predict(x_test)
863
+ """
864
+
865
+ def __init__(self, params, model=None):
866
+ """
867
+ 初始化XGBoost模型封装类。
868
+
869
+ Parameters
870
+ ----------
871
+ params : dict
872
+ XGBoost模型参数字典
873
+ model : xgb.XGBClassifier, optional
874
+ 预加载的模型实例
875
+ """
876
+ xgb = _get_xgb()
877
+ self.params = params
878
+ self.model = model
879
+ self.feature_names_ = None
880
+
881
+ def fit(self, x, y, valx, valy, sample_weight=None, sample_weight_eval_set=None, base_margin=None):
882
+ """训练XGBoost模型。
883
+
884
+ Parameters
885
+ ----------
886
+ x : array-like or pd.DataFrame
887
+ 训练集特征
888
+ y : array-like
889
+ 训练集标签
890
+ valx : array-like or pd.DataFrame
891
+ 验证集特征
892
+ valy : array-like
893
+ 验证集标签
894
+ sample_weight : array-like, optional
895
+ 样本权重
896
+ sample_weight_eval_set : list, optional
897
+ 验证集样本权重列表
898
+ base_margin : array-like, optional
899
+ 基础边际(init_score / log-odds 偏移),用于增量训练(warm-start)。
900
+
901
+ Returns
902
+ -------
903
+ self
904
+ """
905
+ self.model = xgb_model(
906
+ x=x, y=y, valx=valx, valy=valy,
907
+ params_dict=self.params,
908
+ sample_weight=sample_weight,
909
+ sample_weight_eval_set=sample_weight_eval_set,
910
+ base_margin=base_margin
911
+ )
912
+ if hasattr(x, 'columns'):
913
+ self.feature_names_ = list(x.columns)
914
+ return self
915
+
916
+ def predict(self, x):
917
+ """预测样本的概率。
918
+
919
+ Parameters
920
+ ----------
921
+ x : array-like or pd.DataFrame
922
+ 预测特征
923
+
924
+ Returns
925
+ -------
926
+ np.ndarray
927
+ 预测概率
928
+ """
929
+ return self.model.predict_proba(x)[:, 1]
930
+
931
+ def get_feature_importance(self, importance_type='gain'):
932
+ """获取特征重要性。
933
+
934
+ Parameters
935
+ ----------
936
+ importance_type : str, default 'gain'
937
+ 特征重要性类型
938
+
939
+ Returns
940
+ -------
941
+ pd.DataFrame
942
+ 包含 feature 和 importance 列的DataFrame
943
+ """
944
+ return xgb_varimp(self.model)
945
+
946
+ def save(self, path):
947
+ """保存模型。
948
+
949
+ Parameters
950
+ ----------
951
+ path : str
952
+ 模型保存路径
953
+ """
954
+ save_model(self.model, path)
955
+
956
+ def load(self, path):
957
+ """加载模型。
958
+
959
+ Parameters
960
+ ----------
961
+ path : str
962
+ 模型文件路径
963
+
964
+ Returns
965
+ -------
966
+ self
967
+ """
968
+ self.model = load_model(path)
969
+ return self
970
+
971
+ def calibrate(self, x, y, method='sigmoid', cv='prefit'):
972
+ """模型概率校准。
973
+
974
+ Parameters
975
+ ----------
976
+ x : array-like
977
+ 校准特征
978
+ y : array-like
979
+ 校准标签
980
+ method : str, default 'sigmoid'
981
+ 校准方法
982
+ cv : str or int, default 'prefit'
983
+ 交叉验证方式
984
+
985
+ Returns
986
+ -------
987
+ self
988
+ """
989
+ self.model = CalibratedClassifierCV(self.model, method=method, cv=cv)
990
+ self.model.fit(x, y)
991
+ return self
992
+
993
+ def calibration_curve(self, x, y, n_bins=10):
994
+ """获取校准曲线数据。
995
+
996
+ Parameters
997
+ ----------
998
+ x : array-like
999
+ 特征
1000
+ y : array-like
1001
+ 标签
1002
+
1003
+ Returns
1004
+ -------
1005
+ tuple
1006
+ """
1007
+ y_prob = self.predict(x)
1008
+ return calibration_curve(y, y_prob, n_bins=n_bins)
1009
+
1010
+ def brier_score(self, x, y):
1011
+ """计算Brier分数。
1012
+
1013
+ Parameters
1014
+ ----------
1015
+ x : array-like
1016
+ y : array-like
1017
+
1018
+ Returns
1019
+ -------
1020
+ float
1021
+ """
1022
+ y_prob = self.predict(x)
1023
+ return brier_score_loss(y, y_prob)
1024
+
1025
+ def roc_auc(self, x, y):
1026
+ """计算ROC AUC。
1027
+
1028
+ Parameters
1029
+ ----------
1030
+ x : array-like
1031
+ y : array-like
1032
+
1033
+ Returns
1034
+ -------
1035
+ float
1036
+ """
1037
+ y_prob = self.predict(x)
1038
+ return roc_auc_score(y, y_prob)
1039
+
1040
+
1041
+ class CatBoostModel:
1042
+ """
1043
+ CatBoost模型封装类。
1044
+
1045
+ 提供统一的CatBoost模型训练、预测、保存和加载接口。
1046
+ 支持模型校准、特征重要性获取等功能。
1047
+
1048
+ Parameters
1049
+ ----------
1050
+ params : dict
1051
+ CatBoost模型参数字典
1052
+ model : CatBoostClassifier, optional
1053
+ 预加载的模型实例
1054
+
1055
+ Examples
1056
+ --------
1057
+ >>> cat_clf = CatBoostModel(params)
1058
+ >>> cat_clf.fit(x_train, y_train, x_val, y_val)
1059
+ >>> preds = cat_clf.predict(x_test)
1060
+ """
1061
+
1062
+ def __init__(self, params, model=None):
1063
+ """
1064
+ 初始化CatBoost模型封装类。
1065
+
1066
+ Parameters
1067
+ ----------
1068
+ params : dict
1069
+ CatBoost模型参数字典
1070
+ model : CatBoostClassifier, optional
1071
+ 预加载的模型实例
1072
+ """
1073
+ _get_catboost()
1074
+ self.params = params
1075
+ self.model = model
1076
+ self.feature_names_ = None
1077
+
1078
+ def fit(self, x, y, valx, valy, sample_weight=None):
1079
+ """训练CatBoost模型。
1080
+
1081
+ Parameters
1082
+ ----------
1083
+ x : array-like or pd.DataFrame
1084
+ 训练集特征
1085
+ y : array-like
1086
+ 训练集标签
1087
+ valx : array-like or pd.DataFrame
1088
+ 验证集特征
1089
+ valy : array-like
1090
+ 验证集标签
1091
+ sample_weight : array-like, optional
1092
+ 样本权重
1093
+
1094
+ Returns
1095
+ -------
1096
+ self
1097
+ """
1098
+ self.model = catboost_model(
1099
+ x=x, y=y, valx=valx, valy=valy,
1100
+ params_dict=self.params,
1101
+ sample_weight=sample_weight,
1102
+ )
1103
+ if hasattr(x, 'columns'):
1104
+ self.feature_names_ = list(x.columns)
1105
+ return self
1106
+
1107
+ def predict(self, x):
1108
+ """预测样本的概率。
1109
+
1110
+ Parameters
1111
+ ----------
1112
+ x : array-like or pd.DataFrame
1113
+ 预测特征
1114
+
1115
+ Returns
1116
+ -------
1117
+ np.ndarray
1118
+ 预测概率
1119
+ """
1120
+ return self.model.predict_proba(x)[:, 1]
1121
+
1122
+ def get_feature_importance(self, importance_type='gain'):
1123
+ """获取特征重要性。
1124
+
1125
+ Parameters
1126
+ ----------
1127
+ importance_type : str, default 'gain'
1128
+ 特征重要性类型(CatBoost 使用 PredictionValuesChange)
1129
+
1130
+ Returns
1131
+ -------
1132
+ pd.DataFrame
1133
+ 包含 feature 和 importance 列的DataFrame
1134
+ """
1135
+ return catboost_varimp(self.model)
1136
+
1137
+ def save(self, path):
1138
+ """保存模型。
1139
+
1140
+ Parameters
1141
+ ----------
1142
+ path : str
1143
+ 模型保存路径
1144
+ """
1145
+ save_model(self.model, path)
1146
+
1147
+ def load(self, path):
1148
+ """加载模型。
1149
+
1150
+ Parameters
1151
+ ----------
1152
+ path : str
1153
+ 模型文件路径
1154
+
1155
+ Returns
1156
+ -------
1157
+ self
1158
+ """
1159
+ self.model = load_model(path)
1160
+ return self
1161
+
1162
+ def calibrate(self, x, y, method='sigmoid', cv='prefit'):
1163
+ """模型概率校准。
1164
+
1165
+ Parameters
1166
+ ----------
1167
+ x : array-like
1168
+ 校准特征
1169
+ y : array-like
1170
+ 校准标签
1171
+ method : str, default 'sigmoid'
1172
+ 校准方法
1173
+ cv : str or int, default 'prefit'
1174
+ 交叉验证方式
1175
+
1176
+ Returns
1177
+ -------
1178
+ self
1179
+ """
1180
+ self.model = CalibratedClassifierCV(self.model, method=method, cv=cv)
1181
+ self.model.fit(x, y)
1182
+ return self
1183
+
1184
+ def calibration_curve(self, x, y, n_bins=10):
1185
+ """获取校准曲线数据。
1186
+
1187
+ Parameters
1188
+ ----------
1189
+ x : array-like
1190
+ 特征
1191
+ y : array-like
1192
+ 标签
1193
+ n_bins : int, default 10
1194
+ 分箱数
1195
+
1196
+ Returns
1197
+ -------
1198
+ tuple
1199
+ """
1200
+ y_prob = self.predict(x)
1201
+ return calibration_curve(y, y_prob, n_bins=n_bins)
1202
+
1203
+ def brier_score(self, x, y):
1204
+ """计算Brier分数。
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ x : array-like
1209
+ y : array-like
1210
+
1211
+ Returns
1212
+ -------
1213
+ float
1214
+ """
1215
+ y_prob = self.predict(x)
1216
+ return brier_score_loss(y, y_prob)
1217
+
1218
+ def roc_auc(self, x, y):
1219
+ """计算ROC AUC。
1220
+
1221
+ Parameters
1222
+ ----------
1223
+ x : array-like
1224
+ y : array-like
1225
+
1226
+ Returns
1227
+ -------
1228
+ float
1229
+ """
1230
+ y_prob = self.predict(x)
1231
+ return roc_auc_score(y, y_prob)
1232
+
1233
+
1234
+ class GradientBoostingModel:
1235
+ """
1236
+ 统一梯度提升模型封装类。
1237
+
1238
+ 支持LightGBM、XGBoost和CatBoost三种框架的统一接口。
1239
+ 通过model_type参数切换框架,其他接口保持一致。
1240
+
1241
+ Parameters
1242
+ ----------
1243
+ model_type : str
1244
+ 模型类型,'lgb'、'xgb' 或 'cat'('catboost' 别名)
1245
+ params : dict
1246
+ 模型参数字典
1247
+
1248
+ Examples
1249
+ --------
1250
+ >>> model = GradientBoostingModel('lgb', params)
1251
+ >>> model.fit(x_train, y_train, x_val, y_val)
1252
+ >>> preds = model.predict(x_test)
1253
+
1254
+ # 增量学习(warm-start)
1255
+ >>> base_margin = init_model.get_base_margin(x_train)
1256
+ >>> new_model = GradientBoostingModel('xgb', params)
1257
+ >>> new_model.fit(x_train, y_train, x_val, y_val, init_score=base_margin)
1258
+ >>> proba = new_model.predict_with_base_margin(
1259
+ ... x_score, init_model.get_base_margin(x_score))
1260
+
1261
+ # 适配已训练好的裸估计器(如历史直接 pickle 的 XGBClassifier)
1262
+ >>> init_model = GradientBoostingModel.from_fitted(load_model(path))
1263
+ >>> init_model.get_base_margin(x_train)
1264
+ """
1265
+
1266
+ def __init__(self, model_type, params):
1267
+ """
1268
+ 初始化统一模型封装类。
1269
+
1270
+ Parameters
1271
+ ----------
1272
+ model_type : str
1273
+ 模型类型,'lgb'、'xgb' 或 'cat'('catboost' 别名)
1274
+ params : dict
1275
+ 模型参数字典
1276
+
1277
+ Raises
1278
+ ------
1279
+ ValueError
1280
+ 当model_type不为支持类型时
1281
+ """
1282
+ if model_type == 'catboost':
1283
+ model_type = 'cat'
1284
+ if model_type not in ('lgb', 'xgb', 'cat'):
1285
+ raise ValueError(
1286
+ f"model_type must be 'lgb', 'xgb', or 'cat', got: {model_type!r}"
1287
+ )
1288
+ self.model_type = model_type
1289
+ self.params = params
1290
+ if model_type == 'lgb':
1291
+ self._model = LightGBMModel(params)
1292
+ elif model_type == 'xgb':
1293
+ self._model = XGBoostModel(params)
1294
+ else:
1295
+ self._model = CatBoostModel(params)
1296
+
1297
+ @staticmethod
1298
+ def _detect_model_type(estimator):
1299
+ """从已训练的估计器类名 / 模块推断 'lgb'、'xgb' 或 'cat'。
1300
+
1301
+ Parameters
1302
+ ----------
1303
+ estimator : object
1304
+ 已 fit 的 XGBClassifier / LGBMClassifier / CatBoostClassifier 等。
1305
+
1306
+ Returns
1307
+ -------
1308
+ str
1309
+ 'lgb'、'xgb' 或 'cat'。
1310
+
1311
+ Raises
1312
+ ------
1313
+ ValueError
1314
+ 无法从类型推断时。
1315
+ """
1316
+ cls = type(estimator)
1317
+ tag = f"{cls.__module__}.{cls.__name__}".lower()
1318
+ if 'catboost' in tag:
1319
+ return 'cat'
1320
+ if 'xgboost' in tag or 'xgb' in tag:
1321
+ return 'xgb'
1322
+ if 'lightgbm' in tag or 'lgb' in tag:
1323
+ return 'lgb'
1324
+ raise ValueError(
1325
+ f"cannot infer model_type from {cls.__name__!r}; "
1326
+ "pass model_type='lgb', 'xgb', or 'cat' explicitly"
1327
+ )
1328
+
1329
+ @classmethod
1330
+ def from_fitted(cls, model, model_type=None, params=None):
1331
+ """用一个【已训练好】的估计器(或封装)构造 GradientBoostingModel。
1332
+
1333
+ 用于适配历史上直接以 sklearn 估计器(``XGBClassifier`` /
1334
+ ``LGBMClassifier`` / ``CatBoostClassifier``)形式保存的模型,使其无需重训即可使用
1335
+ :meth:`get_base_margin` / :meth:`predict_with_base_margin` 等统一接口。
1336
+
1337
+ Parameters
1338
+ ----------
1339
+ model : object
1340
+ 已 fit 的 ``XGBClassifier`` / ``LGBMClassifier`` / ``CatBoostClassifier``,或
1341
+ ``LightGBMModel`` / ``XGBoostModel`` / ``CatBoostModel`` / ``GradientBoostingModel`` 封装。
1342
+ model_type : {'lgb', 'xgb', 'cat'}, optional
1343
+ 不传则按估计器类型自动推断。
1344
+ params : dict, optional
1345
+ 参数字典;不传则尽量从估计器的 ``get_params()`` 读取。
1346
+
1347
+ Returns
1348
+ -------
1349
+ GradientBoostingModel
1350
+
1351
+ Raises
1352
+ ------
1353
+ ValueError
1354
+ 传入未 fit / 空模型时。
1355
+ """
1356
+ if isinstance(model, cls):
1357
+ try:
1358
+ object.__getattribute__(model, '_model')
1359
+ return model
1360
+ except AttributeError:
1361
+ raise ValueError(
1362
+ "Loaded GradientBoostingModel is missing _model (Cython pickle bug "
1363
+ "in a previous package version). Please retrain and re-save the model."
1364
+ )
1365
+ # 解包 LightGBMModel / XGBoostModel / CatBoostModel(它们把裸估计器放在 .model)
1366
+ estimator = getattr(model, 'model', model)
1367
+ if estimator is None:
1368
+ raise ValueError("from_fitted received an unfitted / empty model")
1369
+ mt = model_type or cls._detect_model_type(estimator)
1370
+ if mt == 'catboost':
1371
+ mt = 'cat'
1372
+ if params is None:
1373
+ params = estimator.get_params() if hasattr(estimator, 'get_params') else {}
1374
+ obj = cls(mt, params)
1375
+ obj._model.model = estimator
1376
+ feat = _extract_estimator_feature_names(estimator)
1377
+ if feat is not None:
1378
+ obj._model.feature_names_ = feat
1379
+ _ensure_feature_names_in(estimator, feat)
1380
+ return obj
1381
+
1382
+ def __getattr__(self, name):
1383
+ """把未知属性委托给底层已训练估计器。
1384
+
1385
+ 使封装实例成为原始 LGBM/XGB/CatBoost 估计器的超集(如 ``get_params`` /
1386
+ ``feature_names_in_`` / ``predict_proba`` 等可直接透传)。仅在常规
1387
+ 属性查找失败时调用;对 dunder 名与 ``_model`` 缺失(如反序列化
1388
+ 中途)安全抛出 AttributeError,以免干扰 pickle / copy。
1389
+ """
1390
+ if name.startswith('__') and name.endswith('__'):
1391
+ raise AttributeError(name)
1392
+ if name == '_model':
1393
+ raise AttributeError(name)
1394
+ try:
1395
+ model = object.__getattribute__(self, '_model')
1396
+ except AttributeError:
1397
+ raise AttributeError(name)
1398
+ inner = getattr(model, 'model', None)
1399
+ if inner is not None and hasattr(inner, name):
1400
+ return getattr(inner, name)
1401
+ if name == 'feature_names_in_':
1402
+ feat = getattr(model, 'feature_names_', None)
1403
+ if feat is None and inner is not None:
1404
+ feat = _extract_estimator_feature_names(inner)
1405
+ if feat is not None:
1406
+ return np.asarray(feat, dtype=object)
1407
+ raise AttributeError(name)
1408
+
1409
+ def __getstate__(self):
1410
+ return {
1411
+ 'model_type': self.model_type,
1412
+ 'params': self.params,
1413
+ '_model_params': self._model.params,
1414
+ '_model_model': self._model.model,
1415
+ '_model_feature_names_': getattr(self._model, 'feature_names_', None),
1416
+ }
1417
+
1418
+ def __setstate__(self, state):
1419
+ self.model_type = state['model_type']
1420
+ self.params = state['params']
1421
+ mt = self.model_type
1422
+ mp = state.get('_model_params', self.params)
1423
+ if mt == 'lgb':
1424
+ self._model = LightGBMModel(mp)
1425
+ elif mt == 'xgb':
1426
+ self._model = XGBoostModel(mp)
1427
+ else:
1428
+ self._model = CatBoostModel(mp)
1429
+ self._model.model = state.get('_model_model')
1430
+ self._model.feature_names_ = state.get('_model_feature_names_')
1431
+
1432
+ @staticmethod
1433
+ def _sigmoid(z):
1434
+ """数値稳定的 Sigmoid(log-odds → 概率)。"""
1435
+ z = np.clip(np.asarray(z, dtype=float), -709, 709)
1436
+ return 1.0 / (1.0 + np.exp(-z))
1437
+
1438
+ def fit(self, x, y, valx, valy, init_score=None, sample_weight=None, eval_sample_weight=None, sample_weight_eval_set=None, **kwargs):
1439
+ """训练模型(支持增量学习 warm-start)。
1440
+
1441
+ 当传入 ``init_score`` 时,以其作为 log-odds 偏移在新数据上继续训练:
1442
+ LightGBM 走 ``init_score``,XGBoost 走 ``base_margin``(两者语义一致,
1443
+ 本方法统一对外暴露为 ``init_score``)。CatBoost 不支持 ``init_score``。
1444
+
1445
+ Parameters
1446
+ ----------
1447
+ x : array-like or pd.DataFrame
1448
+ 训练集特征
1449
+ y : array-like
1450
+ 训练集标签
1451
+ valx : array-like or pd.DataFrame
1452
+ 验证集特征
1453
+ valy : array-like
1454
+ 验证集标签
1455
+ init_score : array-like, optional
1456
+ 初始 log-odds 偏移(增量学习起点)。一般由基准模型的
1457
+ :meth:`get_base_margin` 产生。``None`` 时即普通从头训练。
1458
+ **kwargs
1459
+ 其余参数透传给底层模型(如 lgb 的 ``wgt``、xgb 的
1460
+ ``sample_weight`` / ``sample_weight_eval_set``、cat 的
1461
+ ``sample_weight``)。
1462
+
1463
+ Returns
1464
+ -------
1465
+ self
1466
+
1467
+ Raises
1468
+ ------
1469
+ NotImplementedError
1470
+ CatBoost 不支持 ``init_score`` 增量学习。
1471
+
1472
+ Notes
1473
+ -----
1474
+ 与既有生产流程一致,偏移仅作用于训练集;验证集未注入偏移,因此早停
1475
+ 的 eval 指标是在"未加偏移"的空间上评估的。如需严格一致,可后续透传
1476
+ lgb 的 ``eval_init_score`` / xgb 的 ``base_margin_eval_set``。
1477
+ """
1478
+ if self.model_type == 'cat':
1479
+ if init_score is not None:
1480
+ raise NotImplementedError(
1481
+ "CatBoost does not support init_score in GradientBoostingModel.fit"
1482
+ )
1483
+ self._model.fit(x, y, valx, valy, sample_weight=sample_weight, **kwargs)
1484
+ elif self.model_type == 'lgb':
1485
+ self._model.fit(
1486
+ x, y, valx, valy,
1487
+ init_score=init_score,
1488
+ sample_weight=sample_weight,
1489
+ eval_sample_weight=eval_sample_weight,
1490
+ **kwargs,
1491
+ )
1492
+ else:
1493
+ if sample_weight_eval_set is None and eval_sample_weight is not None:
1494
+ sample_weight_eval_set = [eval_sample_weight]
1495
+ self._model.fit(
1496
+ x, y, valx, valy,
1497
+ base_margin=init_score,
1498
+ sample_weight=sample_weight,
1499
+ sample_weight_eval_set=sample_weight_eval_set,
1500
+ **kwargs,
1501
+ )
1502
+ return self
1503
+
1504
+ def get_base_margin(self, x):
1505
+ """返回本模型对 ``x`` 的原始 log-odds(base margin / init score)。
1506
+
1507
+ 统一兼容三种框架取"未经 sigmoid 的原始分数":
1508
+
1509
+ - XGBoost: ``predict(x, output_margin=True)``
1510
+ - LightGBM: ``predict(x, raw_score=True)``
1511
+ - CatBoost: ``predict(x, prediction_type='RawFormulaVal')``
1512
+
1513
+ 该结果可作为下一个增量模型 :meth:`fit` 的 ``init_score``,或喂给
1514
+ :meth:`predict_with_base_margin` 做融合预测。
1515
+
1516
+ Parameters
1517
+ ----------
1518
+ x : array-like or pd.DataFrame
1519
+ 待计算的特征
1520
+
1521
+ Returns
1522
+ -------
1523
+ np.ndarray
1524
+ 一维 log-odds 数组,形状 ``(n_samples,)``
1525
+
1526
+ Raises
1527
+ ------
1528
+ RuntimeError
1529
+ 当模型尚未训练(``fit`` 之前)时
1530
+ """
1531
+ est = self._model.model
1532
+ if est is None:
1533
+ raise RuntimeError("model is not fitted yet; call fit() first")
1534
+ if self.model_type == 'lgb':
1535
+ margin = est.predict(x, raw_score=True)
1536
+ elif self.model_type == 'cat':
1537
+ margin = est.predict(x, prediction_type='RawFormulaVal')
1538
+ else:
1539
+ margin = est.predict(x, output_margin=True)
1540
+ return np.asarray(margin).ravel()
1541
+
1542
+ def predict_with_base_margin(self, x, base_margin, return_prob=True):
1543
+ """融合预测:``sigmoid(base_margin + 本模型 raw score)``。
1544
+
1545
+ 把一个基准模型的 log-odds(``base_margin``,通常来自
1546
+ ``init_model.get_base_margin(x)``)与本(增量)模型自身的 raw score
1547
+ 在 log-odds 空间相加,再做 sigmoid。这种手动融合是唯一对 lgb 与 xgb
1548
+ 行为一致的方式——LightGBM 在预测期并不支持注入 init_score。
1549
+
1550
+ Parameters
1551
+ ----------
1552
+ x : array-like or pd.DataFrame
1553
+ 待预测的特征
1554
+ base_margin : array-like
1555
+ 基准模型的 log-odds 偏移,形状须与 ``x`` 的样本数一致
1556
+ return_prob : bool, default True
1557
+ ``True`` 返回概率(sigmoid 后);``False`` 返回融合后的原始
1558
+ log-odds
1559
+
1560
+ Returns
1561
+ -------
1562
+ np.ndarray
1563
+ 一维数组,``return_prob=True`` 时取值于 ``[0, 1]``
1564
+ """
1565
+ combined = np.asarray(base_margin).ravel() + self.get_base_margin(x)
1566
+ return self._sigmoid(combined) if return_prob else combined
1567
+
1568
+ def predict(self, x):
1569
+ """预测样本的概率。
1570
+
1571
+ Parameters
1572
+ ----------
1573
+ x : array-like or pd.DataFrame
1574
+
1575
+ Returns
1576
+ -------
1577
+ np.ndarray
1578
+ """
1579
+ return self._model.predict(x)
1580
+
1581
+ def get_feature_importance(self, importance_type='gain'):
1582
+ """获取特征重要性。
1583
+
1584
+ Returns
1585
+ -------
1586
+ pd.DataFrame
1587
+ """
1588
+ return self._model.get_feature_importance(importance_type=importance_type)
1589
+
1590
+ def save(self, path):
1591
+ """保存模型。"""
1592
+ self._model.save(path)
1593
+
1594
+ def load(self, path):
1595
+ """加载模型。"""
1596
+ self._model.load(path)
1597
+ return self
1598
+
1599
+ def calibrate(self, x, y, method='sigmoid', cv='prefit'):
1600
+ """模型概率校准。"""
1601
+ self._model.calibrate(x, y, method=method, cv=cv)
1602
+ return self
1603
+
1604
+ def brier_score(self, x, y):
1605
+ """计算Brier分数。"""
1606
+ return self._model.brier_score(x, y)
1607
+
1608
+ def roc_auc(self, x, y):
1609
+ """计算ROC AUC。"""
1610
+ return self._model.roc_auc(x, y)