分类模型的性能评估——以SAS Logistic回归为例(1): 混淆矩阵

跑完分类模型(Logistic回归、决策树、神经网络等),我们经常面对一大堆模型评估的报表和指标,如Confusion Matrix、ROC、Lift、Gini、K-S之类(这个单子可以列很长),往往让很多在业务中需要解释它们的朋友头大:“这个模型的Lift是4,表明模型运作良好。——啊,怎么还要解释ROC,ROC如何如何,表明模型表现良好……”如果不明白这些评估指标的背后的直觉,就很可能陷入这样的机械解释中,不敢多说一句,就怕哪里说错。本文就试图用一个统一的例子(SAS Logistic回归),从实际应用而不是理论研究的角度,对以上提到的各个评估指标逐一点评,并力图表明:

  1. 这些评估指标,都是可以用白话(plain English, 普通话)解释清楚的;
  2. 它们是可以手算出来的,看到各种软件包输出结果,并不是一个无法探究的“黑箱”;
  3. 它们是相关的。你了解一个,就很容易了解另外一个。

本文从混淆矩阵(Confusion Matrix,或分类矩阵,Classification Matrix)开始,它最简单,而且是大多数指标的基础。

数据

本文使用一个在信用评分领域非常有名的免费数据集,German Credit Dataset,你可以在UCI Machine Learning Repository找到(下载数据描述)。另外,你还可以在SAS系统的Enterprise Miner的演示数据集中找到该数据的一个版本(dmagecr.sas7bdat)。以下把这个数据分为两部分,训练数据train和验证数据valid,所有的评估指标都是在valid数据中计算(纯粹为了演示评估指标,在train数据里计算也未尝不可),我们感兴趣的二分变量是good_bad,取值为{good, bad}:

Train data
good_bad    Frequency     Percent
-------------------------------------------
bad               154              25.67
good             446              74.33

Valid data
good_bad    Frequency     Percent
--------------------------------------------
bad               146              36.50
good             254              63.50

信用评分指帮助贷款机构发放消费信贷的一整套决策模型及其支持技术。一般地,信用评分技术将客户分为好客户与坏客户两类,比如说,好客户(good)能够按期还本付息(履约),违约的就是坏客户(bad)。具体做法是根据历史上每个类别(履约、违约)的若干样本,从已知的数据中考察借款人的哪些特征对其拖欠或违约行为有影响,从而测量借款人的违约风险,为信贷决策提供依据。Logistic回归是信用评分领域运用最成熟最广泛的统计技术。

约定

在我们的示例数据中,要考察的二分变量是good_bad,我们把感兴趣的那个取值bad(我们想计算违约的概率),称作正例(Positive, 1),另外那个取值(good)称作负例(Negative, 0)。在SAS的Logistic回归中,默认按二分类取值的升序排列取第一个为positive,所以默认的就是求bad的概率。(若需要求good的概率,需要特别指定)。

模型

如果没有特别说明,以下所有的SAS代码都在SAS 9.1.3 SP4系统中调试并运行成功(在生成ROC曲线时,我还会提到SAS9.2的新功能)。

proc logistic data=train;

model good_bad=checking history duration savings property;

run;

这个数据很整齐,能做出很漂亮的模型,以下就直接贴出参数估计的结果:

Analysis of Maximum Likelihood Estimates

Standard     Wald
Parameter    DF    Estimate       Error     Chi-Square    Pr > ChiSq

Intercept     1      0.6032      0.4466        1.8242        0.1768
checking      1     -0.6536      0.0931       49.3333        <.0001
history         1     -0.4083      0.0980       17.3597        <.0001
duration      1      0.0248     0.00907        7.4820        0.0062
savings        1     -0.2017      0.0745        7.3308        0.0068
property      1      0.3157      0.1052        9.0163        0.0027

回归方程就是:

logit[p(bad)]=log(p/1-p)
             =0.6032-0.6536*checking-0.4083*history+0.0248*duration
              -0.2017*savings+0.3157*property

用下面的公式就可以求出正例的概率(bad的概率):

p=exp(logit)/(exp(logit)+1)

上式求出的是概率值,如何根据概率值把各个客户归类,还需要一个阈值,比如,这里我们简单地规定,违约概率超过0.5的就归为bad,其余为good。把上述公式代入valid数据中,

data valid_p;

set valid;

logit=0.6032-0.6536*checking-0.4083*history+0.0248*duration-0.2017*savings+0.3157*property;

p=exp(logit)/(exp(logit)+1);

if p<0.5 then good_bad_predicted='good';

else good_bad_predicted='bad';

keep good_bad p good_bad_predicted;

run;

从下面的局部的数据valid_p可以看到,一些实际上是good的客户,根据我们的模型(阈值p取0.5),却预测他为bad(套用我们假设检验的黑话,这就犯了“弃真”的错误),对一些原本是bad的客户,却预测他为good(“取伪”错误),当然,对更多的客户,good还预测成good,bad还预测成bad:

good_bad       p       good_bad_predicted
bad       0.61624       bad
bad       0.03607       good
good      0.12437      good
good      0.21680      good
good      0.34833      good
good      0.69602      bad
bad       0.68873       bad
good      0.48351      good
good      0.03288      good
good      0.06789      good
good      0.61195      bad
good      0.15306      good

Confusion Matrix, 混淆矩阵

一个完美的分类模型就是,如果一个客户实际上(Actual)属于类别good,也预测成(Predicted)good,处于类别bad,也就预测成bad。但从上面我们看到,一些实际上是good的客户,根据我们的模型,却预测他为bad,对一些原本是bad的客户,却预测他为good。我们需要知道,这个模型到底预测对了多少,预测错了多少,混淆矩阵就把所有这些信息,都归到一个表里:

		预测
		1	0
实	1	d, True Positive	c, False Negative	c+d, Actual Positive
际	0	b, False Positive	a, True Negative	a+b, Actual Negative
		b+d, Predicted Positive	a+c, Predicted Negative

其中,

  1. a是正确预测到的负例的数量, True Negative(TN,0->0)
  2. b是把负例预测成正例的数量, False Positive(FP, 0->1)
  3. c是把正例预测成负例的数量, False Negative(FN, 1->0)
  4. d是正确预测到的正例的数量, True Positive(TP, 1->1)
  5. a+b是实际上负例的数量,Actual Negative
  6. c+d是实际上正例的个数,Actual Positive
  7. a+c是预测的负例个数,Predicted Negative
  8. b+d是预测的正例个数,Predicted Positive

以上似乎一下子引入了许多概念,其实不必像咋一看那么复杂,有必要过一下这里的概念。实际的数据中,客户有两种可能{good, bad},模型预测同样这两种可能,可能匹配可能不匹配。匹配的好说,0->0(读作,实际是Negative,预测成Negative),或者 1->1(读作,实际是Positive,预测成Positive),这就是True Negative(其中Negative是指预测成Negative)和True Positive(其中Positive是指预测成Positive)的情况。

同样,犯错也有两种情况。实际是Positive,预测成Negative (1->0) ,这就是False Negative;实际是Negative,预测成Positive (0->1) ,这就是False Positive;

我们可以通过SAS的proc freq得到以上数字:

proc freq data=valid_p;
tables good_bad*good_bad_predicted/nopercent nocol norow;
run;

对照上表,结果如下:

	预测
		1	0
实	1,bad	d, True Positive,48	c, False Negative,98	c+d, Actual Positive,146
际	0,good	b, False Positive,25	a, True Negative,229	a+b, Actual Negative,254
		b+d, Predicted Positive,73	a+c, Predicted Negative,327	400

根据上表,以下就有几组常用的评估指标(每个指标分中英文两行):

1. 准确(分类)率VS.误分类率

准确(分类)率=正确预测的正反例数/总数

Accuracy=true positive and true negative/total cases= a+d/a+b+c+d=(48+229)/(48+98+25+229)=69.25%

误分类率=错误预测的正反例数/总数

Error rate=false positive and false negative/total cases=b+c/a+b+c+d=1-Accuracy=30.75%

2. (正例的)覆盖率VS. (正例的)命中率

覆盖率=正确预测到的正例数/实际正例总数,

Recall(True Positive Rate,or Sensitivity)=true positive/total actual positive=d/c+d=48/(48+98)=32.88%

/*注:覆盖率(Recall)这个词比较直观,在数据挖掘领域常用。因为感兴趣的是正例(positive),比如在信用卡欺诈建模中,我们感兴趣的是有高欺诈倾向的客户,那么我们最高兴看到的就是,用模型正确预测出来的欺诈客户(True Positive)cover到了大多数的实际上的欺诈客户,覆盖率,自然就是一个非常重要的指标。这个覆盖率又称Sensitivity, 这是生物统计学里的标准词汇,SAS系统也接受了(谁有直观解释?)。 以后提到这个概念,就表示为, Sensitivity(覆盖率,True Positive Rate)。 */

命中率=正确预测到的正例数/预测正例总数

Precision(Positive Predicted Value,PV+)=true positive/ total predicted positive=d/b+d=48/(48+25)=65.75%

/*注:这是一个跟覆盖率相对应的指标。对所有的客户,你的模型预测,有b+d个正例,其实只有其中的d个才击中了目标(命中率)。在数据库营销里,你预测到b+d个客户是正例,就给他们邮寄传单发邮件,但只有其中d个会给你反馈(这d个客户才是真正会响应的正例),这样,命中率就是一个非常有价值的指标。 以后提到这个概念,就表示为PV+(命中率,Positive Predicted Value)*。/

3.Specificity VS. PV-

负例的覆盖率=正确预测到的负例个数/实际负例总数

Specificity(True Negative Rate)=true negative/total actual negative=a/a+b=229/(25+229)=90.16%

/*注:Specificity跟Sensitivity(覆盖率,True Positive Rate)类似,或者可以称为“负例的覆盖率”,也是生物统计用语。以后提到这个概念,就表示为Specificity(负例的覆盖率,True Negative Rate) 。*/

负例的命中率=正确预测到的负例个数/预测负例总数

Negative predicted value(PV-)=true negative/total predicted negative=a/a+c=229/(98+229)=70.03%

/*注:PV-跟PV+(命中率,Positive Predicted value)类似,或者可以称为“负例的命中率”。 以后提到这个概念,就表示为PV-(负例的命中率,Negative Predicted Value)。*/

以上6个指标,可以方便地由上面的提到的proc freq得到:

proc freq data=valid_p;

tables good_bad*good_bad_predicted ;

run;

PV

其中,准确率=12.00%+57.25%=69.25% ,覆盖率=32.88% ,命中率=65.75% ,Specificity=90.16%,PV-=70.03% 。

或者,我们可以通过SAS logistic回归的打分程序(score)得到一系列的Sensitivity和Specificity,

proc logistic data=train;

model good_bad=checking history duration savings property;

score data=valid outroc=valid_roc;

run;

数据valid_roc中有几个我们感兴趣的变量:

  • _PROB_:阈值,比如以上我们选定的0.5
  • _SENSIT_:sensitivity(覆盖率,true positive rate)
  • _1MSPEC_ :1-Specificity,为什么提供1-Specificity而不是Specificity,下文有讲究。
_PROB_ _SENSIT_ _1MSPEC_

0.54866 0.26712 0.07087

0.54390 0.27397 0.07874

0.53939 0.28767 0.08661

0.52937 0.30137 0.09055

0.51633 0.31507 0.09449

0.50583 0.32877 0.09843

0.48368 0.36301 0.10236

0.47445 0.36986 0.10630

如果阈值选定为0.50583,sensitivity(覆盖率,true positive rate)就为0.32877,Specificity就是1-0.098425=0.901575,与以上我们通过列联表计算出来的差不多(阈值0.5)。

下期预告:ROC

以上我们用列联表求覆盖率等指标,需要指定一个阈值(threshold)。同样,我们在valid_roc数据中,看到针对不同的阈值,而产生的相应的覆盖率。我们还可以看到,随着阈值的减小(更多的客户就会被归为正例),sensitivity和1-Specificity也相应增加(也即Specificity相应减少)。把基于不同的阈值而产生的一系列sensitivity和Specificity描绘到直角坐标上,就能更清楚地看到它们的对应关系。由于sensitivity和Specificity的方向刚好相反,我们把sensitivity和1-Specificity描绘到同一个图中,它们的对应关系,就是传说中的ROC曲线,全称是receiver operating characteristic curve,中文叫“接受者操作特性曲线”。欲知后事如何,且听下回分解。

参考资料:

  1. Mithat Gonen. 2007. Analyzing Receiver Operating Characteristic Curves with SAS. Cary, NC: SAS Institute Inc.
  2. Dan Kelly, etc. 2007. Predictive Modeling Using Logistic Regression Course Notes. Cary, NC: SAS Institute Inc.
  3. Confusion Matrix, see http://www2.cs.uregina.ca/~dbd/cs831/notes/confusion_matrix/confusion_matrix.html

分类模型的性能评估——以SAS Logistic回归为例(1): 混淆矩阵》有22个想法

  1. 很好的文章,读了很受启发。谢谢。
    对于类似于SARS病例的医学诊断中,如果除了test positive(阳性) 和test negative(阴性) 外还有test suspected(疑似)的情形,有什么好的方法没有。也就是说,Truth只有两种(感染和未感染两种),但是诊断(Test)却有三种(感染,未感染,疑似)的情况,应该怎么分析呢,这时的ROC曲线又如何设计呢?
    如果Truth也有三种,比如(SARS一型、SARS二型、SARS三型),又有什么方法可以处理呢?谢谢。

    1. 这个就变成了3X3的confusion matrix了,理解方式和2X2的一样,只是更多了而已。

  2. 有一个问题撒
    如果两个class 的training data是不平衡的,或者test data是不平衡的,那怎么去描述你所说的这些指标呢。。很明显,好用户应该是比坏用户多一些的,而且,最关键的是,辨别出一个坏用户的作用比辨别出一个好用户的经济价值要大很多,那confusion matrix还有用没?

  3. 对于齐同学的第一个问题,确实是个很有意思的问题哈。一般来说,在疑似的病例来说,直接丢掉不管,有点可惜,因为它可能藏有感染者的一般信息。但是直接把感染的和疑似的病例并到一块分析,又违反了统计学上的基本假设iid。 所以这一块的信息怎么利用一直是一个很有意思,而且能拿到funding的课题。根据我的经验,在这一块来说,主要有三个思路。一是来自meta analysis,属于生物统计学,这一派比较古老,用的方法也比较经典。另一派来自cs中的multi-kernel learning, 这一派主张用多个kernel来实现比较high level的信息整合,而且目前来看已经获得了一定的成功。还有一派就是来自贝叶斯学派,bayesian hierarchical model, multilevel model 也是一个经常拿来说的东西。你可以google这些关键词,一找一大砣

  4. to齐韬:

    我没做过医药方面的项目,讲个我熟悉的例子吧,信用评分,本文的例子也是这块。在建立个人信用评分模型时,一般要求数据的包含了贷款者的还款历史,拥有还款历史的贷款者才能被清除地归为“好”或“坏”这两个类别。如果还款期尚在模型建立的时间窗口内,对各种类别的划分就不是那么直接了,这时一些账户就不能够确定地归为“好”或“坏”这两个类别。比如,在还款期内,一个有三笔或以上欠账的账户是“坏”的账户,而“好”账户则没有欠账,那么一个有两笔欠款的账户,只要它还没有达到三笔欠款,就不能把它归为“坏”的一类。这种“中间”账户(indeterminate,或者poor)的存在会给信用评分模型的建立带来很多麻烦,一种解决方案是直接剔除掉这些“中间”账户,但无疑这会失去很多有意义的信息。

    看过一些研究,一般会有这么几种处理办法。把个人消费信贷的申请人分为三种:好的(good)、坏的(bad)和中间者(indeterminate,或者poor),按照这三个输出属性的不同组合,就产生了以下5 个模型:

    模型1:多项模型,输出属性分为以上3 类
    模型2:二项模型,输出属性分为good、bad(直接删除poor)
    模型3:二项模型,输出属性分为good(包含poor)、bad
    模型4:二项模型,输出属性分为good、bad(包含poor)
    模型5:二项模型,输出属性为为good、bad,这两个类别都包括了根据模型2 估计出来的poor 类别,这些poor 或者被估计为good,或者被估计为bad。

    跟上文提到的类似,可以定义一个总命中率(total hit rate),它测量的是在验证样本中,模型正确分类的个数占总数的百分比,还有平均命中率(average hit rate)指good、bad、poor 三个类别的命中率的平均。这两个指标应该也挺好用。

    一些实证研究表明(见下),在命中率方面,径向基网络至少给出了不弱于Logistic 回归的结果,但处理“坏”账户的能力更强一些。在以上5 个模型中,多项模型(把poor 作为单独的一类)的命中率最低,表明有必要在建立模型时把“中间”账户归为“好”账户或者“坏”账户。另外,神经网络模型倾向于把 “中间”归为“坏”账户,而Logistic 回归模型则倾向于把它归于“好”账户,所以模型2(直接删除掉“中间”账户)和模型5(poor 类别的账户根据模型2 而指派到good或者bad 类别)似乎更稳定一些。

    参见:

    N.Sarlija, M.Bensic, and M.Zekic-Susac. A neural network classification of credit applicants in consumer credit scoring.Proceedings of the 24th IASTED International Multi-Conference,2006

  5. Not oversampling。。oversampling只是一种虚假的增加样本的手段。你现在所说的忽略了银行为什么拿钱给你干这活的真实目的。我本身为美国银行做过这样一个项目,实际上,他们最关心的就是outlier的诊断。这就是说,模型应该放更多的cost在风险大的客户上面,多诊断出一个坏账比多诊断出十个好客户都要好。我们当时面临的一个问题就是,既然事实是这样的事实,怎么样来选择模型?这个问题我反正是没有做出满意的结果。。

    另外,其实银行不care到底是用ann还是logistic回归,也不可能说哪个模型就一定要好--数据决定一切,具体事情具体分析。事实上为什么会有银行信用数据的处理,最关键的就是我们无法得到最想要得到的信息---income. 客户的收入直接决定了他的信用能力,但是这个信息只能从其他方面去推算。所以,选什么样的模型,关键在于你手头的数据类型和多少。我当时就用的比较简单的pca,当然在具体应用过程中有一些trick,效果比那些复杂的ann和非线性回归要好得多,而且容易理解。。

  6. to 6楼:

    可能我们关注的点不一样吧。我提到的信用评分例子,比如申请人评分,需要找出风险高的申请人,从而拒绝其贷款申请。但同时不能“错杀三千”,因为拒绝了优质客户也是损失。分类模型可以把这些决策成本考虑在内,是综合考虑了这些tradeoff的。你说的极值诊断,应该更有讲究,但显然不在分类模型的考究之内。

    上面提到的几个指标,像覆盖率,应该是欺诈诊断喜欢的东西。

  7. 可能我没说清楚。。。
    其实一个分类器交给客户以后,人家只是拿来当辅助工具,既然要当辅助工具,言下之意就是在坏的客户上面具有比常人灵敏度要高的性质,这样才能给客户提个醒。当然,诊断到了风险大的客户,银行看到以后他们自己还要具体事情具体分析的嘛。。但是如果这个分类器对于坏的客户诊断能力太差,而主要的精度都在好的客户那一边的话,这个分类器就没有那么大的辅助作用了。。
    所以在这里有一个tradeoff,我觉得confusion matrix在这样的情况下作用就不大了。因为它weight the two sides equally。

  8. 呵呵, 楼主辛苦辛苦. 学习了.

    我来做个脚注:
    Sensitivity就是灵敏度, 是疾病发生后出现症状的概率.specificity就是特异度, 是疾病不发生时不出现症状的概率.

    假设肺癌中90\%抽烟, 没有肺癌的30\%抽烟. 此处疾病为肺癌, 症状为抽烟. 灵敏度为肺癌中抽烟的概率为0.9, 特异度为没有肺癌的不抽烟的概率0.7.

    (参考文献: Bernard Rosner. 孙尚拱 译. 生物统计学基础(Fundamentals of
    Biostatistics), 第五版. 科学出版社, 2004.)

  9. Pingback: | 统计之都
  10. to xjx: 多谢补充。看来我手头也得备一部生统的书了。

    to 8楼:“confusion matrix ……weight the two sides equally”,却是不必。前面我提到“分类模型可以把这些决策成本考虑在内,是综合考虑了这些tradeoff的”,下面展开些来讲吧。

    上面提到,“随着阈值的减小(更多的客户就会被归为正例),Sensitivity和1-Specificity也相应增加(也即Specificity相应减少)”,Sensitivity和Specificity之间有个tradeoff的关系。如果目标是增加Sensitivity(如果银行想要,“在坏的客户上面具有比常人灵敏度要高”),最佳的分类器就倾向于把客户归为类别positive(这里是bad),这样阈值就要选小些;同样,如果目标是要增加Specificity(文中提到的数据库营销例子),阈值就要大些。

    要选择一个最优的阈值,可以根据业务规则,利用Misclassification Costs Matrix,最优的决策将是达到(预期)成本最小 。或者,如果信息足够,最优决策还可以基于Profit Matrix。无论如何,这样提供的confusion matrix都是有意义的。

  11. 学习了。
    有个小地方有疑问:

    这里bad是作为关注事件,所以,good被预测为bad是相当于第二类错误,应该是取伪错误,bad预测为good应该是第一类错误,也就是去真错误.
    ==共同探讨下,呵呵.^_^
    Saint13

  12. 你好,胡江堂。你的文章写得很好,浅显直白。实在是很缺这方面的知识,谢谢!
    这篇文章最后写“为什么提供1-Specificity而不是Specificity,下文有讲究。 ”

    可是我看了半天还是没看懂为什么选1-Specificity而不是Specificity?

    在下愚钝,还望包涵。

  13. 您好!我也用您提到的数据做过信用评分模型的研究,有两个问题想请教您一下:
    (1)您是怎样从20个特征变量中挑选出checking history duration savings property这5个变量的?
    (2)在预测的结果中为什么对于坏客户的预测结果相对较差,有将近2/3的坏客户被预测成了好客户?这是由于样本数据中的坏客户数量太少的原因造成的吗?
    请您多多指教!

  14. 对楼上的第一个问题也很有兴趣。当然楼主的目的在解说验证评估指标,模型建立过程略说也可理解,不过要能细讲讲就最好了。
    对第二个问题,个人感觉跟违约阀值的设定有关系。阀值设为50%,说明对坏客户的认定较为保守,许多违约概率在50%下的坏客户被认定为是好客户,从而导致对坏客户的预测能力较差,同时对好客户的预测能力极强。如果降低阀值(比如30%),那么第I类错误(坏认为好)会下降许多,但同时第II类错误(好认为坏)也会上升。
    我自己算的例子(阀值从50%降为30%)中(跟楼主的公式有些差异——数据集划分的误差),第I类错误会从63%降至35%,第II类错误从8%上升至23%。这么看的话,似乎要均衡了许多,也许楼主设的50%可能更多是偏向业务扩张型的。

  15. 有个问题请教江堂兄,sas logistic回归中可以预测出两类的概率,如果信用评分好的概率大于信用评分差的概率,那么就应该视为good,没必要去设定阀值啊??

    1. 我觉得也不对,checking这个变量就是,是不是应该改成dummy 再来建模?

发表评论

邮箱地址不会被公开。 必填项已用*标注