模型的评估指标
在机器学习中,模型的评估指标是衡量模型性能的重要工具。不同的任务和数据类型需要不同的评估指标。以下是一些常见的模型评估指标:
分类问题评估指标
准确率(Accuracy)
准确率是指预测正确的结果占总样本的百分比。公式如下:
准确率 = (TP + TN) / (TP + TN + FP + FN)
然而,在样本不平衡的情况下,准确率可能会失效
1
。
精确率(Precision)
精确率是指所有被预测为正的样本中实际为正的样本的概率。公式如下:
精确率 = TP / (TP + FP)
精确率关注的是预测为正样本的准确性
召回率(Recall)
召回率是指实际为正的样本中被预测为正样本的概率。公式如下:
召回率 = TP / (TP + FN)
召回率关注的是正样本的覆盖率
F1分数
F1分数是精确率和召回率的调和平均数,用于综合考虑两者的表现。公式如下:
F1 = 2 (Precision Recall) / (Precision + Recall)
F1分数在精确率和召回率之间找到一个平衡点
ROC曲线和AUC
ROC曲线以假阳性率(FPR)为横坐标,真阳性率(TPR)为纵坐标,反映了模型的分类性能。AUC是ROC曲线下的面积,AUC越接近1,模型性能越好
回归问题评估指标
均方误差(MSE)
均方误差是预测值和真实值之间差异的平方和的平均值。公式如下:
MSE = (1/n) * Σ(y_i - y_pred_i)^2
MSE对异常值较为敏感
均方根误差(RMSE)
均方根误差是MSE的平方根,提供了与原始数据相同量级的误差度量。公式如下:
RMSE = sqrt(MSE)
RMSE更直观地反映了预测误差
平均绝对误差(MAE)
平均绝对误差是预测值和真实值之间差异的绝对值的平均值。公式如下:
MAE = (1/n) * Σ|y_i - y_pred_i|
MAE对异常值不如MSE敏感
选择评估指标的建议
分类问题:在样本不平衡的情况下,建议使用精确率、召回率和F1分数,而不是仅仅依赖准确率
回归问题:根据具体需求选择MSE、RMSE或MAE。若异常值对业务重要,选择MSE;若异常值为噪声,选择MAE
通过合理选择和使用评估指标,可以更准确地评估模型的性能,从而优化模型的效果。
1. 计算评估指标的Python程序
- 首先明确一些概念:
- 真正例(True Positive, $TP$):模型预测为正例且实际为正例的样本数。
- 真反例(True Negative, $TN$):模型预测为反例且实际为反例的样本数。
- 假正例(False Positive, $FP$):模型预测为正例但实际为反例的样本数。
- 假反例(False Negative, $FN$):模型预测为反例但实际为正例的样本数。
- 基于这些,可以定义计算各个指标的函数如下:
```python
def get_tp_fp_tn_fn(y_true, y_prob, threshold = 0.5):
tp, fp, tn, fn = 0, 0, 0, 0
for i in range(len(y_true)):
if y_true[i]==1 and y_prob[i]>=threshold:
tp += 1
elif y_true[i]==1 and y_prob[i]<threshold:
fn += 1
elif y_true[i]==0 and y_prob[i]<threshold:
tn += 1
else:
fp += 1
return tp, fp, tn, fn
def accuracy(tp, fp, tn, fn):
return (tp + tn)/(tp+tn+fp+fn)
def precision(tp, fp):
if tp + fp == 0:
return 0
return tp / (tp + fp)
def recall(tp, fn):
if tp+fn==0:
return 0
return tp / (tp + fn)
def f1_score(pre, rec):
if pre + rec == 0:
return 0
return 2 (pre rec)/(pre + rec)
def cohen_kappa(tp, fp, tn, fn):
p0=(tp + tn)/(tp+tn+fp+fn)
pe=((tp + fp)*(tp + fn)+(fn + tn)*(fp + tn))/((tp+tn+fp+fn)**2)
if pe==1:
return 1
return (p0 - pe)/(1 - pe)
def matthews_corrcoef(tp, fp, tn, fn):
numerator=(tp*tn - fp*fn)
denominator=((tp + fp)*(tp + fn)*(tn + fp)*(tn + fn))**0.5
if denominator==0:
return 0
return numerator/denominator
def auc_roc(y_true, y_prob):
# 这里简单实现一个近似的AUC - ROC计算
# 首先对概率进行排序,记录排名
pairs=list(zip(y_prob, y_true))
pairs.sort(key = lambda pair: pair[0], reverse = True)
pos_rank_sum=0
pos_count=sum(y_true)
total_count=len(y_true)
for i, (prob, label) in enumerate(pairs):
if label==1:
pos_rank_sum += i + 1
auc=(pos_rank_sum-(pos_count*(pos_count + 1)/2))/(pos_count*(total_count - pos_count))
return auc
def auc_pr(y_true, y_prob):
# 简单近似计算
pairs=list(zip(y_prob, y_true))
pairs.sort(key = lambda pair: pair[0], reverse = True)
precision_sum = 0
recall_step = 1/sum(y_true)
recall_start = 0
for prob, label in pairs:
tp = sum([l for p, l in pairs[:i + 1] if l == 1])
fp = sum([l for p, l in pairs[:i + 1] if l == 0])
prec = tp/(tp + fp) if tp+fp>0 else 0
rec = tp/sum(y_true)
if rec>recall_start:
precision_sum+=(rec - recall_start)*prec
recall_start=rec
return precision_sum
# 使用示例
y_true = [1, 0, 1, 0, 1, 1, 0, 0, 1, 0]
y_prob = [0.8, 0.3, 0.6, 0.2, 0.9, 0.7, 0.4, 0.3, 0.75, 0.1]
tp, fp, tn, fn = get_tp_fp_tn_fn(y_true, y_prob)
acc = accuracy(tp, fp, tn, fn)
prec = precision(tp, fp)
rec = recall(tp, fn)
f1 = f1_score(prec, rec)
kappa = cohen_kappa(tp, fp, tn, fn)
mcc = matthews_corrcoef(tp, fp, tn, fn)
auc_roc_val = auc_roc(y_true, y_prob)
auc_pr_val = auc_pr(y_true, y_prob)
print(f"准确率(Accuracy): {acc}")
print(f"精确率(Precision): {prec}")
print(f"召回率(Recall): {rec}")
print(f"F1分数(F1): {f1}")
print(f"科恩kappa系数(Kappa): {kappa}")
print(f"马修斯相关系数(MCC): {mcc}")
print(f"ROC曲线下面积(AUC - ROC): {auc_roc_val}")
print(f"PR曲线下面积(AUC - PR): {auc_pr_val}")
def analyze_imbalance_indicators():
# 在不平衡数据集的情况下
# 假设正例非常少
y_true_imbalance = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
y_prob_imbalance = [0.8, 0.1, 0.2, 0.15, 0.1, 0.05, 0.12, 0.1, 0.08, 0.09]
tp_im, fp_im, tn_im, fn_im = get_tp_fp_tn_fn(y_true_imbalance, y_prob_imbalance)
acc_im = accuracy(tp_im, fp_im, tn_im, fn_im)
prec_im = precision(tp_im, fp_im)
rec_im = recall(tp_im, fn_im)
f1_im = f1_score(prec_im, rec_im)
kappa_im = cohen_kappa(tp_im, fp_im, tn_im, fn_im)
mcc_im = matthews_corrcoef(tp_im, fp_im, tn_im, fn_im)
auc_roc_im = auc_roc(y_true_imbalance, y_prob_imbalance)
auc_pr_im = auc_pr(y_true_imbalance, y_prob_imbalance)
print("\n不平衡数据集下的指标:")
print(f"准确率(Accuracy): {acc_im}")
print(f"精确率(Precision): {prec_im}")
print(f"召回率(Recall): {rec_im}")
print(f"F1分数(F1): {f1_im}")
print(f"科恩kappa系数(Kappa): {kappa_im}")
print(f"马修斯相关系数(MCC): {mcc_im}")
print(f"ROC曲线下面积(AUC - ROC): {auc_roc_im}")
print(f"PR曲线下面积(AUC - PR): {auc_pr_im}")
analyze_imbalance_indicators()
def explain_misleading():
# 解释某些指标在不平衡数据时可能产生误导的原因
print("\n某些指标在不平衡数据时产生误导的原因:")
print("精确率(Precision):如果正例很少,即使模型把所有样本都预测为反例,精确率可能依然很高,因为FP为0。")
print("准确率(Accuracy):在极不平衡数据集下,模型只要把多数类全部预测正确,准确率就会很高,掩盖了对少数类预测的失败。")
explain_misleading()
```
2. 分析在不平衡数据集上的表现及误导性
- 准确率(Accuracy)
- 在不平衡数据集上,如果多数类占比过大,例如99%的样本都是负例。一个简单的模型只要总是预测为负例,就能取得很高的准确率(接近99%),但这样的模型其实没有任何能力来区分正例。所以准确率在不平衡数据集上可能会隐藏模型实际上不能正确识别正例的事实。
- 精确率(Precision)
- 如果正例非常少,当模型把所有样本都预测为反例时,假正例($FP$)为0。按照公式$Precision=\frac{TP}{TP + FP}$,此时精确率为1,但这个模型显然毫无价值,因为它没有识别出任何正例。
- 召回率(Recall)
- 召回率是针对正例的指标。在不平衡数据集上,如果模型更加关注多数类的预测准确性,对于少数类的召回率会较低,因为少数类的样本很容易被错误分类为多数类,从而导致$\frac{TP}{TP + FN}$变小。
- F1分数(F1)
- F1分数是精确率和召回率的调和平均数,由于它依赖于精确率和召回率,在不平衡数据集上,当精确率或召回率被扭曲时,F1分数也不能准确反映模型的性能。
- 科恩kappa系数(Kappa)
- 科恩kappa系数衡量的是观测一致性和期望一致性之间的比例。在不平衡数据集上,如果模型倾向于预测多数类,会影响实际一致性和期望一致性的计算,进而可能导致kappa系数偏高,不能真实反映模型的分类性能。
- 马修斯相关系数(MCC)
- 马修斯相关系数综合考虑了真正例($TP$)、假正例($FP$)、真反例($TN$)和假反例($FN$)。在不平衡数据集上,如果模型过度偏向多数类的预测,会使其中一些值(如$TN$可能很大,而$TP$很小)变化异常,导致MCC不能有效评估模型性能。
- ROC曲线下面积(AUC - ROC)
- 在不平衡数据集上相对比较稳定。因为它在计算时考虑了不同阈值下的$TPR$(真阳率,等同于召回率)和$FPR$(假阳率),能够在一定程度上反映模型对两类样本的区分能力。但如果数据集极度不平衡,AUC - ROC的值可能也不能完全体现模型对少数类的性能。
- PR曲线下面积(AUC - PR)
- 更关注正例的情况,在不平衡数据集下,它能在一定程度上反映模型对于正例的预测能力。与AUC - ROC相比,它能更直接地体现出对正例的分类效果。但同样在极端不平衡的情况下,也可能存在一定的误导性,不过相对要好一些。
import pandas as np
import pandas as pd
fath=pd.read_csv(open('datas_0220.csv'))
y_true = fath["y_true"]
y_prob = fath["y_prob"]
import numpy as np
# 计算混淆矩阵
def confusion_matrix(y_true, y_pred):
TP = np.sum((y_true == 1) & (y_pred == 1))
TN = np.sum((y_true == 0) & (y_pred == 0))
FP = np.sum((y_true == 0) & (y_pred == 1))
FN = np.sum((y_true == 1) & (y_pred == 0))
return TP, TN, FP, FN
# 计算准确率
def accuracy(TP, TN, FP, FN):
return (TP + TN) / (TP + TN + FP + FN)
# 计算精确率
def precision(TP, FP):
return TP / (TP + FP)
# 计算召回率
def recall(TP, FN):
return TP / (TP + FN)
# 计算F1分数
def f1_score(precision, recall):
return 2 * (precision * recall) / (precision + recall)
# 计算AUC-ROC
def auc_roc(y_true, y_prob):
thresholds = np.sort(np.unique(y_prob))[::-1]
tpr = []
fpr = []
for threshold in thresholds:
y_pred = (y_prob >= threshold).astype(int)
TP, TN, FP, FN = confusion_matrix(y_true, y_pred)
tpr.append(TP / (TP + FN))
fpr.append(FP / (FP + TN))
auc = np.trapz(tpr, fpr)
return auc
# 计算AUC-PR
def auc_pr(y_true, y_prob):
thresholds = np.sort(np.unique(y_prob))[::-1]
precision_list = []
recall_list = []
for threshold in thresholds:
y_pred = (y_prob >= threshold).astype(int)
TP, TN, FP, FN = confusion_matrix(y_true, y_pred)
precision_list.append(precision(TP, FP))
recall_list.append(recall(TP, FN))
auc = np.trapz(precision_list, recall_list)
return auc
# 计算科恩kappa系数
def kappa(accuracy, expected_accuracy):
return (accuracy - expected_accuracy) / (1 - expected_accuracy)
# 计算马修斯相关系数
def mcc(TP, TN, FP, FN):
numerator = TP * TN - FP * FN
denominator = np.sqrt((TP + FP) * (TP + FN) * (TN + FP) * (TN + FN))
return numerator / denominator
# 计算期望准确率
def expected_accuracy(y_true):
p0 = np.mean(y_true == 1)
p1 = np.mean(y_true == 0)
return p0 * p0 + p1 * p1
# 主函数
def evaluate_metrics(y_true, y_prob, threshold=0.5):
y_pred = (y_prob >= threshold).astype(int)
TP, TN, FP, FN = confusion_matrix(y_true, y_pred)
acc = accuracy(TP, TN, FP, FN)
prec = precision(TP, FP)
rec = recall(TP, FN)
f1 = f1_score(prec, rec)
auc_roc_value = auc_roc(y_true, y_prob)
auc_pr_value = auc_pr(y_true, y_prob)
expected_acc = expected_accuracy(y_true)
kappa_value = kappa(acc, expected_acc)
mcc_value = mcc(TP, TN, FP, FN)
return {
'Accuracy': acc,
'Precision': prec,
'Recall': rec,
'F1 Score': f1,
'AUC-ROC': auc_roc_value,
'AUC-PR': auc_pr_value,
'Kappa': kappa_value,
'MCC': mcc_value
}
print(evaluate_metrics(y_true, y_prob, threshold=0.5))
评论区