LightGBM 建模与监控¶
本教程使用确定性合成数据完成时间切分、LightGBM 调参、打分和当前期监控。每个变量都在 Notebook 中定义,不依赖其他页面或缓存输出。
前置条件与数据角色¶
运行前安装 mars-risk[ml,tuning]==0.0.24。development_df 用于建模,baseline_df 提供监控基准,monitoring_df 表示需要评估的当前数据。固定日期只用于复现时间切分,不代表特定业务月份。
In [ ]:
Copied!
from datetime import date, timedelta
import numpy as np
import polars as pl
from mars.monitoring import MarsMonitor, generate_monitoring_alert
from mars.pipeline import MarsModelingPipeline, MarsModelingStep
SEED = 1206
ROW_COUNT = 240
rng = np.random.default_rng(SEED)
application_dates = [date(2025, 1, 1) + timedelta(days=index) for index in range(ROW_COUNT)]
income = rng.normal(5200, 1300, ROW_COUNT)
utilization = rng.uniform(0.05, 0.95, ROW_COUNT)
logit = -0.00045 * (income - 5200) + 3.2 * (utilization - 0.5)
probability = 1.0 / (1.0 + np.exp(-logit))
development_df = pl.DataFrame(
{
"apply_dt": application_dates,
"period": [value.strftime("%Y-%m") for value in application_dates],
"income": income,
"utilization": utilization,
"target": rng.binomial(1, probability),
}
)
development_df.head()
from datetime import date, timedelta
import numpy as np
import polars as pl
from mars.monitoring import MarsMonitor, generate_monitoring_alert
from mars.pipeline import MarsModelingPipeline, MarsModelingStep
SEED = 1206
ROW_COUNT = 240
rng = np.random.default_rng(SEED)
application_dates = [date(2025, 1, 1) + timedelta(days=index) for index in range(ROW_COUNT)]
income = rng.normal(5200, 1300, ROW_COUNT)
utilization = rng.uniform(0.05, 0.95, ROW_COUNT)
logit = -0.00045 * (income - 5200) + 3.2 * (utilization - 0.5)
probability = 1.0 / (1.0 + np.exp(-logit))
development_df = pl.DataFrame(
{
"apply_dt": application_dates,
"period": [value.strftime("%Y-%m") for value in application_dates],
"income": income,
"utilization": utilization,
"target": rng.binomial(1, probability),
}
)
development_df.head()
训练并打分¶
Pipeline 按日期严格切分 train/val/oot,只执行一个轻量 trial,并关闭 artifact 落盘。
In [ ]:
Copied!
pipeline = MarsModelingPipeline(
target="target",
features=["income", "utilization"],
steps=[
MarsModelingStep(
name="modeling",
model_type="lgb",
time_col="apply_dt",
split_ratios={"train": 0.6, "val": 0.2, "oot": 0.2},
tune_params={
"n_trials": 1,
"startup_trials": 1,
"num_boost_round": 20,
"early_stopping_rounds": 5,
"artifact_dir": None,
},
)
],
)
pipeline_result = pipeline.fit(development_df)
scored_df = pipeline.predict(development_df, pred_col="model_score")
pipeline_result.modeling_result.history_table.head()
pipeline = MarsModelingPipeline(
target="target",
features=["income", "utilization"],
steps=[
MarsModelingStep(
name="modeling",
model_type="lgb",
time_col="apply_dt",
split_ratios={"train": 0.6, "val": 0.2, "oot": 0.2},
tune_params={
"n_trials": 1,
"startup_trials": 1,
"num_boost_round": 20,
"early_stopping_rounds": 5,
"artifact_dir": None,
},
)
],
)
pipeline_result = pipeline.fit(development_df)
scored_df = pipeline.predict(development_df, pred_col="model_score")
pipeline_result.modeling_result.history_table.head()
构造基准期与当前期¶
较早样本作为 baseline_df;较新样本作为 monitoring_df,并将部分 target 置空以模拟尚未充分表现。
In [ ]:
Copied!
baseline_df = scored_df.filter(pl.col("period") < "2025-06")
monitoring_df = (
scored_df
.filter(pl.col("period") >= "2025-06")
.with_row_index("_row_index")
.with_columns(
pl.when(pl.col("_row_index") % 3 == 0)
.then(None)
.otherwise(pl.col("target"))
.cast(pl.Int64)
.alias("target")
)
.drop("_row_index")
)
monitoring_df.select("period", "target").head()
baseline_df = scored_df.filter(pl.col("period") < "2025-06")
monitoring_df = (
scored_df
.filter(pl.col("period") >= "2025-06")
.with_row_index("_row_index")
.with_columns(
pl.when(pl.col("_row_index") % 3 == 0)
.then(None)
.otherwise(pl.col("target"))
.cast(pl.Int64)
.alias("target")
)
.drop("_row_index")
)
monitoring_df.select("period", "target").head()
In [ ]:
Copied!
monitor_report = MarsMonitor(
binner_params={"method": "quantile", "n_bins": 5},
psi_include_missing=False,
).monitor(
monitoring_df,
features=["model_score", "income", "utilization"],
target="target",
benchmark_df=baseline_df,
group_col="period",
trend_column_order="asc",
)
monitor_report.summary_table.head()
monitor_report = MarsMonitor(
binner_params={"method": "quantile", "n_bins": 5},
psi_include_missing=False,
).monitor(
monitoring_df,
features=["model_score", "income", "utilization"],
target="target",
benchmark_df=baseline_df,
group_col="period",
trend_column_order="asc",
)
monitor_report.summary_table.head()
In [ ]:
Copied!
monitor_report.target_observation_table
monitor_report.target_observation_table
In [ ]:
Copied!
alert_text = generate_monitoring_alert(
monitor_report,
score_key="model_score",
model_features=["income", "utilization"],
)
print(alert_text)
alert_text = generate_monitoring_alert(
monitor_report,
score_key="model_score",
model_features=["income", "utilization"],
)
print(alert_text)
结果边界¶
pipeline_result 保存建模步骤和调参结果;monitor_report 保存当前期分布、已表现样本指标和覆盖率。报警摘要只是结构化 report 的默认文本视图,实际阈值、通知和处置由调用方管理。