3.3 评估指标 (Metrics) LightGBM 评估指标 (Metrics) 详解与代码实践 3.3.1 评估指标的重要性 在深入 LightGBM 的具体评估指标之前,我们首先需要理解评估指标在机器学习流程中的重要性: 模型性能量化: 评估指标提供了一个数值化的标准,用于衡量模型在特定任务上的表现。例如,在分类任务中,我们可以使用准确率、精确率、召回率等指标来评估模型预测类别的能力。在回归任务中,我们可以使用均方误差、平均绝对误差等指标来评估模型预测数值的准确程度。 模型选择与比较: 当我们训练多个模型或者尝试不同的模型参数时,评估指标可以帮助我们进行客观的比较和选择。通过比较不同模型在相同评估指标上的表现,我们可以选择性能更优的模型。
在深入 LightGBM 的具体评估指标之前,我们首先需要理解评估指标在机器学习流程中的重要性:
模型性能量化: 评估指标提供了一个数值化的标准,用于衡量模型在特定任务上的表现。例如,在分类任务中,我们可以使用准确率、精确率、召回率等指标来评估模型预测类别的能力。在回归任务中,我们可以使用均方误差、平均绝对误差等指标来评估模型预测数值的准确程度。
模型选择与比较: 当我们训练多个模型或者尝试不同的模型参数时,评估指标可以帮助我们进行客观的比较和选择。通过比较不同模型在相同评估指标上的表现,我们可以选择性能更优的模型。
参数调优的指导: 模型训练的目标通常是优化某个或某些评估指标。在模型参数调优过程中,我们通过监控评估指标的变化来判断参数调整是否有效,并指导下一步的参数调整方向。
业务目标的对齐: 最终的模型部署是为了解决实际业务问题。评估指标的选择应该与业务目标紧密相关。例如,在医疗诊断领域,我们可能更关注模型的召回率,以尽可能减少漏诊的情况。
LightGBM 根据不同的学习任务 (Task) 提供了相应的评估指标。主要可以分为以下几类:
二分类 (Binary Classification) 指标: 用于评估二分类模型的性能,例如预测用户是否会点击广告、判断邮件是否为垃圾邮件等。
多分类 (Multiclass Classification) 指标: 用于评估多分类模型的性能,例如图像分类、文本分类等。
回归 (Regression) 指标: 用于评估回归模型的性能,例如预测房价、股票价格等。
排序 (Ranking) 指标: 用于评估排序模型的性能,例如搜索引擎结果排序、推荐系统物品排序等。
接下来,我们将详细介绍 LightGBM 中常用的评估指标,并结合代码示例进行演示。
二分类任务的目标是将数据样本分为两个类别,通常标记为 0 和 1。常用的二分类评估指标包括:
binary_logloss (对数损失): 衡量模型预测概率与真实标签之间的差异。值越小,模型性能越好。 它是 LightGBM 二分类任务的默认评估指标。
graph TD
A[真实标签 y_true] --> B[模型预测概率 y_pred];
B --> C[计算对数损失];
C --> D[binary_logloss];
style D fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` LogLoss = - (1/N) * Σ [y_i * log(p_i) + (1 - y_i) * log(1 - p_i)] ``` 其中,`N` 是样本数量,`y_i` 是第 `i` 个样本的真实标签 (0 或 1),`p_i` 是模型预测第 `i` 个样本为类别 1 的概率。 **代码示例:** ```python import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.datasets import make_classification from sklearn.metrics import log_loss # 生成二分类数据集 X, y = make_classification(n_samples=1000, n_features=10, n_classes=2, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # LightGBM 参数设置 params = { 'objective': 'binary', 'metric': 'binary_logloss', # 指定评估指标为 binary_logloss 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # 创建 LightGBM 数据集 train_data = lgb.Dataset(X_train, label=y_train) test_data = lgb.Dataset(X_test, label=y_test, reference=train_data) # 训练模型 model = lgb.train(params, train_data, num_boost_round=100, valid_sets=test_data, early_stopping_rounds=10, verbose_eval=False) # 预测测试集 y_pred_prob = model.predict(X_test) # 手动计算 log_loss 进行验证 logloss_sklearn = log_loss(y_test, y_pred_prob) print(f"Sklearn Log Loss: {logloss_sklearn:.4f}") # LightGBM 训练过程中会输出 binary_logloss,与 sklearn 的结果应该接近 ``` * **`auc` (Area Under the ROC Curve, ROC曲线下面积):** 衡量模型区分正负样本的能力。AUC 值越高,模型性能越好,通常 AUC > 0.8 表示模型性能较好。 AUC 是一种排序指标,对样本类别不均衡的情况相对鲁棒。 ```mermaid graph TD A[模型预测概率 y_pred_prob] --> B{计算 ROC 曲线} B --> C{计算 ROC 曲线下面积} C --> D[auc] style D fill:#f9f,stroke:#333,stroke-width:2px
**解释:** ROC 曲线以假正例率 (FPR) 为横轴,真正例率 (TPR) 为纵轴绘制。AUC 值就是 ROC 曲线下的面积。AUC 越大,表示模型在不同阈值下都能保持较好的区分能力。 **代码示例 (在上述代码基础上修改 `params`):** ```python params = { 'objective': 'binary', 'metric': 'auc', # 指定评估指标为 auc 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与上面示例相同,训练和预测部分) from sklearn.metrics import roc_auc_score auc_sklearn = roc_auc_score(y_test, y_pred_prob) print(f"Sklearn AUC: {auc_sklearn:.4f}") ```
binary_error (二分类错误率): 衡量模型分类错误的样本比例。值越小,模型性能越好。
graph TD
A[模型预测类别 y_pred] --> B{与真实标签 y_true 比较}
B --> C{计算错误样本数量}
C --> D{错误样本数量 / 总样本数量}
D --> E[binary_error]
style E fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` Error Rate = (FP + FN) / (TP + TN + FP + FN) ``` 其中,TP (True Positive) 是真阳性,TN (True Negative) 是真阴性,FP (False Positive) 是假阳性,FN (False Negative) 是假阴性。 **代码示例 (在上述代码基础上修改 `params`):** ```python params = { 'objective': 'binary', 'metric': 'binary_error', # 指定评估指标为 binary_error 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与上面示例相同,训练和预测部分) from sklearn.metrics import accuracy_score y_pred_class = [1 if p >= 0.5 else 0 for p in y_pred_prob] # 将概率转换为类别 (阈值 0.5) accuracy_sklearn = accuracy_score(y_test, y_pred_class) error_rate_sklearn = 1 - accuracy_sklearn print(f"Sklearn Binary Error Rate: {error_rate_sklearn:.4f}") ``` * **`f1_score` (F1 分数):** 精确率 (Precision) 和 召回率 (Recall) 的调和平均值,综合考虑了精确率和召回率。F1 分数越高,模型性能越好。 特别适用于类别不均衡的场景。 ```mermaid graph TD A[模型预测类别 y_pred] --> B{与真实标签 y_true 比较} B --> C{计算 Precision 和 Recall} C --> D{计算 F1 分数} D --> E[f1_score] style E fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` Precision = TP / (TP + FP) Recall = TP / (TP + FN) F1-score = 2 * (Precision * Recall) / (Precision + Recall) ``` **代码示例 (在上述代码基础上修改 `params` 并添加自定义评估函数):** ```python from sklearn.metrics import f1_score def lgb_f1_score(y_pred, dtrain): # 自定义 F1 评估函数 y_true = dtrain.get_label() y_pred_class = [1 if p >= 0.5 else 0 for p in y_pred] return 'f1_score', f1_score(y_true, y_pred_class), True # 返回指标名称,指标值,是否越大越好 (True 表示越大越好) params = { 'objective': 'binary', 'metric': 'binary_logloss', # 可以保留默认的 logloss,也可以设置为 None,只使用自定义的 f1_score 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # 训练模型时指定自定义评估函数 feval model = lgb.train(params, train_data, num_boost_round=100, valid_sets=test_data, early_stopping_rounds=10, feval=lgb_f1_score, verbose_eval=False) # 指定自定义评估函数 # 预测测试集 y_pred_prob = model.predict(X_test) y_pred_class = [1 if p >= 0.5 else 0 for p in y_pred_prob] f1_sklearn = f1_score(y_test, y_pred_class) print(f"Sklearn F1 Score: {f1_sklearn:.4f}") ```
precision (精确率): 预测为正例的样本中,真正例的比例。精确率越高,模型预测为正例的准确性越高。
recall (召回率): 真实为正例的样本中,被模型预测为正例的比例。召回率越高,模型对正例的识别能力越强。
average_precision (平均精确率): 对不同阈值下的精确率进行平均,常用于排序任务,也适用于二分类任务。
代码示例 (类似 F1-score,只需修改自定义评估函数中的指标计算部分):
from sklearn.metrics import precision_score, recall_score, average_precision_score def lgb_precision_score(y_pred, dtrain): y_true = dtrain.get_label() y_pred_class = [1 if p >= 0.5 else 0 for p in y_pred] return 'precision', precision_score(y_true, y_pred_class), True def lgb_recall_score(y_pred, dtrain): y_true = dtrain.get_label() y_pred_class = [1 if p >= 0.5 else 0 for p in y_pred] return 'recall', recall_score(y_true, y_pred_class), True def lgb_average_precision_score(y_pred, dtrain): y_true = dtrain.get_label() return 'average_precision', average_precision_score(y_true, y_pred), True # ... (params 和 train 代码与 F1-score 示例类似,只需修改 feval 参数) # 例如使用 precision model = lgb.train(params, train_data, num_boost_round=100, valid_sets=test_data, early_stopping_rounds=10, feval=lgb_precision_score, verbose_eval=False) # 预测和 sklearn 验证 (类似 F1-score 示例)
多分类任务的目标是将数据样本分为多个类别。常用的多分类评估指标包括:
multi_logloss (多分类对数损失): 与二分类对数损失类似,但适用于多分类任务。值越小,模型性能越好。 它是 LightGBM 多分类任务的默认评估指标。
graph TD
A[真实标签 y_true] --> B[模型预测概率 y_pred_prob];
B --> C{计算多分类对数损失};
C --> D[multi_logloss];
style D fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` MultiLogLoss = - (1/N) * Σ Σ [y_{ij} * log(p_{ij})] ``` 其中,`N` 是样本数量,`C` 是类别数量,`y_{ij}` 是一个指示变量,如果第 `i` 个样本属于第 `j` 个类别,则为 1,否则为 0, `p_{ij}` 是模型预测第 `i` 个样本属于第 `j` 个类别的概率。 **代码示例 (与二分类类似,只需修改 `objective` 和 `metric`):** ```python from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split import lightgbm as lgb from sklearn.metrics import log_loss # 生成多分类数据集 X, y = make_classification(n_samples=1000, n_features=10, n_classes=3, random_state=42) # n_classes=3 表示 3 个类别 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) params = { 'objective': 'multiclass', # 指定 objective 为 multiclass 'metric': 'multi_logloss', # 指定 metric 为 multi_logloss 'num_class': 3, # 指定类别数量 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与二分类示例类似,训练和预测部分) y_pred_prob = model.predict(X_test) # 预测概率,shape 为 (n_samples, n_classes) logloss_sklearn = log_loss(y_test, y_pred_prob) print(f"Sklearn Multi Log Loss: {logloss_sklearn:.4f}") ``` * **`multi_error` (多分类错误率):** 衡量模型分类错误的样本比例。值越小,模型性能越好。 ```mermaid graph TD A[模型预测类别 y_pred] --> B{与真实标签 y_true 比较} B --> C{计算错误样本数量} C --> D{错误样本数量 / 总样本数量} D --> E[multi_error] style E fill:#f9f,stroke:#333,stroke-width:2px
**代码示例 (修改 `metric`):** ```python params = { 'objective': 'multiclass', 'metric': 'multi_error', # 指定 metric 为 multi_error 'num_class': 3, 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与 multi_logloss 示例类似) y_pred_prob = model.predict(X_test) y_pred_class = y_pred_prob.argmax(axis=1) # 取概率最大的类别作为预测类别 accuracy_sklearn = accuracy_score(y_test, y_pred_class) error_rate_sklearn = 1 - accuracy_sklearn print(f"Sklearn Multi Error Rate: {error_rate_sklearn:.4f}") ```
auc_mu (Micro-averaged AUC): 计算 micro-averaged AUC,适用于多分类任务。它将所有类别的预测结果合并起来计算 AUC。
graph TD
A[模型预测概率 y_pred_prob] --> B{将多分类问题转化为二分类问题 one-vs-rest};
B --> C{计算 micro-averaged AUC};
C --> D[auc_mu];
style D fill:#f9f,stroke:#333,stroke-width:2px
**代码示例 (修改 `metric`):** ```python params = { 'objective': 'multiclass', 'metric': 'auc_mu', # 指定 metric 为 auc_mu 'num_class': 3, 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与 multi_logloss 示例类似) y_pred_prob = model.predict(X_test) auc_mu_sklearn = roc_auc_score(y_test, y_pred_prob, multi_class='ovo', average='macro') # 使用 sklearn 计算,需要指定 multi_class 和 average 参数 print(f"Sklearn AUC_mu: {auc_mu_sklearn:.4f}") # 注意 sklearn 的 auc_mu 和 LightGBM 的 auc_mu 计算方式可能略有差异 ``` #### 3.3.3.3 回归指标 (Regression Metrics) 回归任务的目标是预测一个连续的数值。常用的回归评估指标包括: * **`l2` (均方误差, Mean Squared Error, MSE):** 衡量预测值与真实值之间平方差的平均值。值越小,模型性能越好。 它是 LightGBM 回归任务的默认评估指标。 也称为 `mse` 或 `mean_squared_error`。 ```mermaid graph TD A[真实值] --> B[模型预测值]; B --> C{计算平方误差}; C --> D{计算平方误差的平均值}; D --> E[l2 损失(均方误差)]; style E fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` MSE = (1/N) * Σ (y_i - ŷ_i)^2 ``` 其中,`N` 是样本数量,`y_i` 是第 `i` 个样本的真实值,`ŷ_i` 是模型预测的第 `i` 个样本的值。 **代码示例:** ```python import lightgbm as lgb from sklearn.model_selection import train_test_split from sklearn.datasets import make_regression from sklearn.metrics import mean_squared_error # 生成回归数据集 X, y = make_regression(n_samples=1000, n_features=10, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) params = { 'objective': 'regression', # 指定 objective 为 regression 'metric': 'l2', # 指定 metric 为 l2 (MSE) 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } train_data = lgb.Dataset(X_train, label=y_train) test_data = lgb.Dataset(X_test, label=y_test, reference=train_data) model = lgb.train(params, train_data, num_boost_round=100, valid_sets=test_data, early_stopping_rounds=10, verbose_eval=False) y_pred = model.predict(X_test) mse_sklearn = mean_squared_error(y_test, y_pred) print(f"Sklearn MSE: {mse_sklearn:.4f}") ```
l1 (平均绝对误差, Mean Absolute Error, MAE): 衡量预测值与真实值之间绝对差的平均值。值越小,模型性能越好。 也称为 mae 或 mean_absolute_error。 MAE 对异常值不如 MSE 敏感。
graph TD
A[真实值 函数y_true] --> B[模型预测值 函数y_pred]
B --> C{计算绝对误差 绝对值y_true减y_pred}
C --> D{计算绝对误差的平均值}
D --> E[l1 平均绝对误差]
style E fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` MAE = (1/N) * Σ |y_i - ŷ_i| ``` **代码示例 (修改 `metric`):** ```python params = { 'objective': 'regression', 'metric': 'l1', # 指定 metric 为 l1 (MAE) 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与 l2 示例类似) from sklearn.metrics import mean_absolute_error mae_sklearn = mean_absolute_error(y_test, y_pred) print(f"Sklearn MAE: {mae_sklearn:.4f}") ``` * **`rmse` (均方根误差, Root Mean Squared Error, RMSE):** MSE 的平方根,与真实值的单位相同,更易于解释。值越小,模型性能越好。 ```mermaid graph TD A[l2 MSE] --> B{计算平方根} B --> C[rmse] style C fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` RMSE = √(MSE) = √[(1/N) * Σ (y_i - ŷ_i)^2] ``` **代码示例 (修改 `metric`):** ```python params = { 'objective': 'regression', 'metric': 'rmse', # 指定 metric 为 rmse 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # ... (其余代码与 l2 示例类似) rmse_sklearn = mean_squared_error(y_test, y_pred, squared=False) # sklearn 中 squared=False 返回 RMSE print(f"Sklearn RMSE: {rmse_sklearn:.4f}") ```
mape (平均绝对百分比误差, Mean Absolute Percentage Error, MAPE): 衡量预测值与真实值之间百分比误差的平均值。值越小,模型性能越好。 MAPE 对异常值敏感,且当真实值接近 0 时会变得不稳定。
graph TD
A[真实值] --> B[模型预测值];
B --> C{计算百分比误差
绝对值除以真实值};
C --> D{计算百分比误差的平均值};
D --> E[mape];
style E fill:#f9f,stroke:#333,stroke-width:2px
**公式:** ``` MAPE = (1/N) * Σ |(y_i - ŷ_i) / y_i| * 100% ``` **代码示例 (自定义评估函数):** ```python import numpy as np def lgb_mape(y_pred, dtrain): y_true = dtrain.get_label() mape_val = np.mean(np.abs((y_true - y_pred) / y_true)) * 100 return 'mape', mape_val, False # MAPE 越小越好,所以 is_higher_better=False params = { 'objective': 'regression', 'metric': 'l2', # 可以保留默认的 l2,也可以设置为 None,只使用自定义的 mape 'boosting_type': 'gbdt', 'num_leaves': 31, 'learning_rate': 0.05, 'feature_fraction': 0.9 } # 训练模型时指定自定义评估函数 feval model = lgb.train(params, train_data, num_boost_round=100, valid_sets=test_data, early_stopping_rounds=10, feval=lgb_mape, verbose_eval=False) y_pred = model.predict(X_test) def mape_sklearn(y_true, y_pred): # 手动计算 MAPE return np.mean(np.abs((y_true - y_pred) / y_true)) * 100 mape_val_sklearn = mape_sklearn(y_test, y_pred) print(f"Sklearn MAPE: {mape_val_sklearn:.4f}%") ``` * **其他回归指标:** LightGBM 还支持 `huber`, `fair`, `poisson`, `quantile`, `gamma`, `tweedie` 等回归指标,这些指标适用于特定的数据分布和业务场景。 例如: * `huber`: Huber loss,对异常值更鲁棒的损失函数。 * `fair`: Fair loss,也对异常值鲁棒。 * `poisson`: Poisson regression loss,适用于目标变量服从泊松分布的情况 (例如,计数数据)。 * `quantile`: Quantile regression loss,用于预测分位数。 #### 3.3.3.4 排序指标 (Ranking Metrics) 排序任务的目标是根据相关性对物品进行排序。常用的排序评估指标包括: * **`ndcg` (归一化折损累积增益, Normalized Discounted Cumulative Gain):** 衡量排序列表的质量。NDCG 值越高,排序质量越好。 常用的变体包括 `ndcg@n`,表示只考虑前 `n` 个排序结果的 NDCG。