各位观众,各位朋友,走过路过不要错过!今天咱们不卖大力丸,只聊机器学习里的一件神秘武器:自定义评估指标!
先问大家一个问题:你真的了解你的模型有多厉害吗?
是不是每次训练完,就盯着那几个默认的评分,比如Accuracy、Precision、Recall、F1-score? 它们就像商店里批量生产的衣服,虽然款式经典,但未必合你身。
想象一下,你是一位医生,要诊断病人是否得了罕见病。如果使用Accuracy,模型可能会告诉你:“没问题,99%的病人都很健康!” 因为罕见病患者只占总人口的1%。 但这显然毫无意义,你真正关心的是,模型能不能准确地揪出那些真正患病的人。
这时候,就需要我们的主角登场了:自定义评估指标! 它可以让你像裁缝一样,为你的机器学习任务量身定制一套评估标准,让模型真正理解你的需求。
一、为什么需要自定义评估指标?
简单来说,默认的评估指标不够用!
-
数据不平衡: 就像刚才的罕见病例子,Accuracy 会被数量多的类别“绑架”,无法反映模型在少数类别上的表现。
-
业务目标特殊: 不同的业务场景,对“好”的定义不同。例如,在金融风控中,宁可错杀一千,不可放过一个坏人;而在推荐系统中,更希望推荐准确率高,而不是召回率高。
-
模型输出形式特殊: 有些模型输出的不是简单的类别标签,而是概率、排序或者其他形式,需要特定的评估方法。
二、SciKit-Learn 中的评估指标接口
SciKit-Learn 提供了强大的评估指标工具,我们可以利用它们来构建自己的评估函数。 核心接口是 sklearn.metrics.make_scorer
。 它可以将一个普通的 Python 函数转换成一个可以被 SciKit-Learn 识别的 scorer 对象。
make_scorer
的基本用法如下:
from sklearn.metrics import make_scorer
def my_custom_metric(y_true, y_pred, **kwargs):
"""
你的自定义评估指标函数
Args:
y_true: 真实标签
y_pred: 模型预测结果
**kwargs: 其他参数,例如 sample_weight
Returns:
评估指标的值
"""
# 在这里编写你的评估逻辑
return score
my_scorer = make_scorer(my_custom_metric, greater_is_better=True) # 如果指标值越大越好,则设置为 True
重要参数:
score_func
: 你定义的评估函数。greater_is_better
: 指示指标值越大是否越好。 如果是,则设置为True
;否则,设置为False
。 默认为True
。needs_proba
: 指示你的评估函数是否需要概率预测值(而不是硬标签)。 如果是,则设置为True
。needs_threshold
: 指示你的评估函数是否需要决策阈值。 如果是,则设置为True
。
三、案例实战:自定义评估指标
接下来,咱们通过几个具体的例子,来学习如何自定义评估指标。
案例一:加权 F1-score(Weighted F1-score)
假设你正在处理一个类别不平衡的数据集,你希望更关注少数类别。 我们可以使用加权 F1-score。
import numpy as np
from sklearn.metrics import f1_score, make_scorer
def weighted_f1(y_true, y_pred, weights):
"""
计算加权 F1-score
Args:
y_true: 真实标签
y_pred: 模型预测结果
weights: 每个类别的权重(字典)
Returns:
加权 F1-score
"""
f1 = 0
for label in np.unique(y_true):
f1 += weights[label] * f1_score(y_true == label, y_pred == label)
return f1
# 示例:
y_true = np.array([0, 0, 0, 1, 1])
y_pred = np.array([0, 0, 1, 1, 0])
weights = {0: 0.2, 1: 0.8} # 类别 1 更重要
weighted_f1_scorer = make_scorer(weighted_f1, greater_is_better=True, weights=weights)
# 如何在 cross_val_score 中使用:
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
scores = cross_val_score(model, np.random.rand(5, 2), y_true, cv=2, scoring=weighted_f1_scorer) #scoring 参数指定评估函数
print(f"Cross-validation scores: {scores}")
代码解释:
weighted_f1
函数计算加权 F1-score。 它遍历每个类别,计算该类别的 F1-score,并乘以该类别的权重。make_scorer
将weighted_f1
函数转换成一个 scorer 对象。cross_val_score
使用weighted_f1_scorer
作为评估指标。 注意scoring
参数的设置。
案例二:针对排序任务的 NDCG(Normalized Discounted Cumulative Gain)
假设你正在构建一个推荐系统,你需要评估模型对物品排序的能力。 NDCG 是一个常用的排序指标。
import numpy as np
from sklearn.metrics import make_scorer
def dcg_at_k(r, k):
"""
计算 DCG(Discounted Cumulative Gain)
Args:
r: 相关性得分列表(例如,[3, 2, 3, 0, 1, 2])
k: 截断位置
Returns:
DCG 值
"""
r = np.asfarray(r)[:k]
if r.size:
return np.sum((np.power(2, r) - 1) / np.log2(np.arange(2, r.size + 2)))
return 0.
def ndcg_at_k(r, k):
"""
计算 NDCG(Normalized Discounted Cumulative Gain)
Args:
r: 相关性得分列表
k: 截断位置
Returns:
NDCG 值
"""
idcg = dcg_at_k(sorted(r, reverse=True), k)
if not idcg:
return 0.
return dcg_at_k(r, k) / idcg
def ndcg_scorer(y_true, y_pred, k=10):
"""
计算 NDCG,适用于 make_scorer
Args:
y_true: 真实相关性得分,与 y_pred 一一对应
y_pred: 预测相关性得分,用于排序
Returns:
NDCG 值
"""
# 将真实标签与预测值按照预测值排序,取前 k 个
ranked_indices = np.argsort(y_pred)[::-1][:k]
relevant_labels = y_true[ranked_indices]
return ndcg_at_k(relevant_labels, k)
# 示例:
y_true = np.array([3, 2, 3, 0, 1, 2]) # 真实相关性得分
y_pred = np.array([0.8, 0.5, 0.7, 0.1, 0.3, 0.6]) # 预测相关性得分
ndcg_at_10_scorer = make_scorer(ndcg_scorer, greater_is_better=True)
# 如何使用:
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingRegressor
X = np.random.rand(6, 5) # 假设有 6 个样本,每个样本有 5 个特征
X_train, X_test, y_train, y_test = train_test_split(X, y_true, test_size=0.2, random_state=42)
model = GradientBoostingRegressor()
model.fit(X_train, y_pred[np.array([0,2,4,5])]) # 注意:这里训练的时候使用的是预测值,因为 NDCG 是排序指标
y_pred_test = model.predict(X_test)
score = ndcg_scorer(y_test, y_pred_test)
print(f"NDCG@10: {score}")
代码解释:
dcg_at_k
函数计算 DCG。ndcg_at_k
函数计算 NDCG。ndcg_scorer
函数用于make_scorer
, 接受真实标签和预测标签, 然后按照预测标签排序, 并计算排序后的真实标签的 NDCG 值make_scorer
将ndcg_scorer
函数转换成一个 scorer 对象。- 在训练模型时,需要注意,NDCG 是一个排序指标,所以通常需要用模型预测的相关性得分进行训练。在预测的时候,用模型预测的相关性得分进行排序,然后计算 NDCG。
案例三:考虑误判代价的评估指标(Cost-sensitive Metric)
假设你正在构建一个疾病诊断模型,将健康的人误判为病人(假阳性)的代价远低于将病人误判为健康的人(假阴性)。 你可以自定义一个考虑误判代价的评估指标。
import numpy as np
from sklearn.metrics import confusion_matrix, make_scorer
def cost_sensitive_metric(y_true, y_pred, cost_fp=1, cost_fn=10):
"""
考虑误判代价的评估指标
Args:
y_true: 真实标签
y_pred: 模型预测结果
cost_fp: 假阳性的代价
cost_fn: 假阴性的代价
Returns:
代价
"""
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
cost = cost_fp * fp + cost_fn * fn
return -cost # 注意:这里返回的是负代价,因为 make_scorer 默认 greater_is_better=True
# 示例:
y_true = np.array([0, 0, 0, 1, 1])
y_pred = np.array([0, 0, 1, 0, 0])
cost_fp = 1
cost_fn = 10
cost_sensitive_scorer = make_scorer(cost_sensitive_metric, greater_is_better=True, cost_fp=cost_fp, cost_fn=cost_fn)
# 如何使用:
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
param_grid = {'C': [0.1, 1, 10], 'gamma': [0.1, 1]}
grid_search = GridSearchCV(SVC(), param_grid, scoring=cost_sensitive_scorer, cv=2) #scoring 参数指定评估函数
grid_search.fit(np.random.rand(5, 2), y_true)
print(f"Best parameters: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_}")
代码解释:
cost_sensitive_metric
函数计算总代价。 它使用混淆矩阵计算假阳性和假阴性的数量,然后根据它们的代价计算总代价。make_scorer
将cost_sensitive_metric
函数转换成一个 scorer 对象。 注意,由于我们希望代价越小越好,所以我们返回的是负代价,并设置greater_is_better=True
。GridSearchCV
使用cost_sensitive_scorer
作为评估指标来选择最佳模型参数。
四、高级技巧:利用 needs_proba
和 needs_threshold
有些评估指标需要模型的概率预测值或者决策阈值。 make_scorer
提供了 needs_proba
和 needs_threshold
参数来满足这些需求。
needs_proba=True
: 你的评估函数需要predict_proba
的输出(概率)。needs_threshold=True
: 你的评估函数需要一个决策阈值。 这通常用于评估模型在不同阈值下的表现,例如绘制 ROC 曲线。
案例四:使用概率预测值的评估指标(AUC-PR)
AUC-PR(Area Under the Precision-Recall Curve)是一个常用的评估指标,特别是在数据不平衡的情况下。 它需要模型的概率预测值。
from sklearn.metrics import precision_recall_curve, auc, make_scorer
import numpy as np
def auc_pr(y_true, y_proba):
"""
计算 AUC-PR
Args:
y_true: 真实标签
y_proba: 模型预测的概率值 (predict_proba 的输出)
Returns:
AUC-PR 值
"""
precision, recall, _ = precision_recall_curve(y_true, y_proba)
return auc(recall, precision)
auc_pr_scorer = make_scorer(auc_pr, greater_is_better=True, needs_proba=True)
# 示例:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
X = np.random.rand(100, 5)
y = np.random.randint(0, 2, 100)
model = LogisticRegression()
scores = cross_val_score(model, X, y, cv=5, scoring=auc_pr_scorer)
print(f"Cross-validation AUC-PR scores: {scores}")
代码解释:
auc_pr
函数计算 AUC-PR。 它使用precision_recall_curve
函数计算精确率和召回率,然后使用auc
函数计算 AUC。make_scorer
将auc_pr
函数转换成一个 scorer 对象,并设置needs_proba=True
。 这告诉 SciKit-Learn 在调用评估函数时,传递模型的predict_proba
输出。
案例五:使用决策阈值的评估指标
假设你想评估模型在不同决策阈值下的性能,例如绘制 ROC 曲线。
from sklearn.metrics import roc_curve, make_scorer
import numpy as np
def custom_roc_auc_score(y_true, y_score):
"""
自定义 ROC AUC 评分函数
Args:
y_true: 真实标签
y_score: 模型输出的得分(可以是概率或其他值)
Returns:
ROC AUC 值
"""
fpr, tpr, thresholds = roc_curve(y_true, y_score)
auc_value = np.trapz(tpr, fpr)
return auc_value
# 创建自定义评分器
roc_auc_scorer = make_scorer(custom_roc_auc_score, greater_is_better=True)
# 示例使用
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 生成一些示例数据
X = np.random.rand(100, 2)
y = np.random.randint(0, 2, 100)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练 Logistic 回归模型
model = LogisticRegression()
model.fit(X_train, y_train)
# 预测测试集概率值
y_scores = model.predict_proba(X_test)[:, 1] # 预测概率,取正类概率
# 使用自定义评分器评估模型
score = roc_auc_scorer(model, X_test, y_test) #注意: 这里传递的是 model, X_test, y_test
print(f"Custom ROC AUC Score: {score}")
五、注意事项
- 一致性: 确保你的自定义评估指标与你的业务目标一致。
- 可解释性: 尽量选择易于理解和解释的指标。
- 避免过拟合: 不要过度优化评估指标,否则可能会导致模型在真实场景中表现不佳。
make_scorer
的参数传递: 使用make_scorer
时,可以通过**kwargs
传递额外的参数给你的评估函数。- 评分函数的形式: 当使用
make_scorer
时,评分函数的参数顺序必须是y_true, y_pred, **kwargs
。 如果你需要访问模型本身,或者特征矩阵X
,你需要使用_score
函数的形式,例如scorer(estimator, X, y)
。 在这种情况下,你需要将模型作为 scorer 的第一个参数传递进去。
六、总结
自定义评估指标是机器学习工具箱中的一件利器。 它可以让你更好地理解你的模型,并使其更符合你的业务需求。 不要再满足于默认的评估指标了,勇敢地去定制你的专属评估标准吧!
记住,机器学习不仅仅是算法和代码,更重要的是理解你的数据和你的目标!
表格总结
特性 | 默认评估指标 | 自定义评估指标 |
---|---|---|
适用性 | 通用场景,适用于大多数情况 | 特定场景,针对特定业务目标和数据特征 |
灵活性 | 较低,无法根据具体需求进行调整 | 较高,可以根据需求灵活定义评估逻辑和参数 |
可解释性 | 较高,容易理解和解释 | 视具体定义而定,需要仔细设计才能保证可解释性 |
示例 | Accuracy, Precision, Recall, F1-score, AUC | 加权 F1-score, NDCG, 考虑误判代价的评估指标, AUC-PR |
SciKit-Learn 工具 | 内置的 metrics 模块 | sklearn.metrics.make_scorer |
最后,希望今天的分享对大家有所帮助! 祝大家都能训练出令人满意的模型! 谢谢大家!