分类模型评价指标说明

分类涉及到的指标特别容易搞混,不是这个率就是那个率,最后都分不清谁是谁,这份文档就是为此给大家梳理一下。

文章目录

  • 分类模型评价指标说明
    • 混淆矩阵
      • 例子
      • 混淆矩阵定义
      • 混淆矩阵代码
    • 正确率
    • 真阳率和假阳率
      • 真阳率
      • 假阳率
      • 真阳率和假阳率的公式比较
    • ROC/AUC
      • 例子
      • 阈值对TPR和FPR的影响
      • ROC曲线
      • ROC曲线的用处
      • AUC
    • 精准率和召回率
      • 精准率
      • 召回率
      • 两者公式比较
      • 精准率和召回率的关系
      • 阈值对精准率和召回率的影响
      • P-R曲线
      • P-R曲线的用处
      • AP
        • 原始计算方式
        • 其他计算方式
    • F1分数
    • Matthews相关系数

混淆矩阵

混淆矩阵很重要,很多指标都是源于混淆矩阵,这个务必要弄懂。

例子

为了解释混淆矩阵,先来看看下面这个二分类的例子。

例:有20个病人来医院检查,是否患病的预测值和真实值如下表所示。

病号 预测值 真实值 病号 预测值 真实值
1 1 1 11 0 0
2 0 0 12 0 0
3 1 1 13 0 0
4 0 0 14 1 1
5 0 0 15 0 0
6 1 1 16 1 0
7 0 0 17 1 1
8 0 0 18 0 0
9 0 1 19 0 0
10 0 0 20 0 1

其中,1表示患病,0表示不患病。

本文档默认用0和1来作为二分类符号。

你也可以用其他符号来表示,如1表示患病,-1表示不患病。只要能区分就行。

这样就出现4种结果:

  • 预测为1,实际也为1,包括病号1,3,6,14,17,一共5个样本;
  • 预测为1,实际为0,包括病号16,只有1个样本;
  • 预测为0,实际为1,包括病号9,20,只有2个样本;
  • 预测为0,实际也为0,包括病号2,4,5,7,8,10,11,12,13,15,18,19,一共12个样本。

我们把各个结果的数量填到下面这个表格中

这就是病患例子的混淆矩阵。

混淆矩阵定义

二分类混淆矩阵的一般定义只是将1和0叫做正例和负例,把4种结果的样本数量用符号来表示,用什么符号呢?

如果我们用P(Positive)代表1,用N(Negative)代表0,那这四种结果分别是PP,PN,NP,NN,但这样表示有点问题,譬如,PN的意思是预测为1实际为0还是预测为0实际为1?需要规定好了,还得记住,好麻烦。

干脆再引入符号T(True)代表预测正确,F(False)表示预测错误,那么之前的P和N代表预测是1还是0,T和F表示预测是否正确。

四种情况可以分别表示为

  • TP:预测为1,预测正确,即实际也为1;
  • FP:预测为1,预测错误,即实际为0;
  • FN:预测为0,预测错误,即实际为1;
  • TN:预测为0,预测正确,即实际也为0。

混淆矩阵的定义如下:

混淆矩阵代码

采用sklearn.metrics中的confusion_matrix函数计算混淆矩阵,数据用的还是之前那个病患检查的样本。

from sklearn.metrics import confusion_matrix# 真实值
y_true = [1,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1]
# 预测值
y_pred = [1,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,1,0,0,0]c_matrix = confusion_matrix(y_true, y_pred)
print(c_matrix)

代码输出

[[12  1][ 2  5]]

有了混淆矩阵,就可以定义一些指标了。

正确率

准确率(Accuracy)的定义很简单,就是猜对的样本占总样本的比例,公式如下:
Accuracy=猜对的样本量样本总量=TP+TNTP+FP+FN+TN\text{Accuracy} = \frac{猜对的样本量}{样本总量} = \frac{TP+TN}{TP+FP+FN+TN} Accuracy=样本总量猜对的样本量​=TP+FP+FN+TNTP+TN​

正样本是实际为正例的样本,负样本是实际为负例的样本。

计算正确率可以调用sklearn.metrics的accuracy_score函数,代码如下:

from sklearn.metrics import accuracy_score# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 预测值
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0]mc = accuracy_score(y_true, y_pred)
print('Accuracy: %.2f'%mc)

结果为

Accuracy: 0.85

正确率作为评价指标有一个很致命的缺点,就是样本不平衡时正确率无法反映模型结果的好坏。

举个例子,预估某个网站上某一天广告的点击率,假如一天有1000个人浏览,实际有50个人点击广告,假如分类器预测没有人会点击,那么这个模型结果的正确率是多少呢?

我们算一下:分类器预测正确的有950个样本,一共有1000个样本,根据定义Accuracy=9501000=0.95\text{Accuracy} = \frac{950}{1000} = 0.95Accuracy=1000950​=0.95,正确率为95%!!!

一个点击的人都没有预测对,正确率都能有95%,那这个指标对模型的评价不合理。

那样本不平衡的时候怎么办呢?

细心想想,样本不平衡的问题是正负样本在数量上有很大差距,数量少的那方被重视程度低,比较吃亏,要解决这个问题,把正负样本分开评价不就好啦,大家河水不犯井水。

按照这个思路,引入下面两个概念:真阳率和假阳率。

真阳率和假阳率

真阳率

真阳率(True Positive Rate, TPR)的定义是:正样本中猜对的比例。公式如下
TPR=TPTP+FNTPR = \frac{TP}{TP+FN} TPR=TP+FNTP​

假阳率

假阳率(False Positive Rate, FPR)的定义是:负样本中猜错的比例。公式如下
FPR=FPTN+FPFPR = \frac{FP}{TN+FP} FPR=TN+FPFP​

真阳率和假阳率的公式比较

TPR=TPTP+FNFPR=FPTN+FP\begin{aligned} TPR &= \frac{TP}{TP+FN} \\ FPR &= \frac{FP}{TN+FP} \end{aligned} TPRFPR​=TP+FNTP​=TN+FPFP​​

TPR公式的分母是正样本数量,FPR公式的分母是负样本数量,这就遵循了正负样本分开评价的思路。

TPR公式的分子是TP,说明这个指标关注正确率;FPR公式的分子是FP,说明这个指标关注错误率。

通常,这两个指标不单独使用,那要怎么用呢?

那就不得不介绍ROC/AUC的概念了。

ROC/AUC

例子

还是那个病患事例,不同在于预测值不是0和1的离散值,而是一个0到1的连续值,叫做置信度(confidence score),可以理解为”概率“,越接近1,结果越可能为1;越接近0,结果越可能为0。

病号 置信度 真实值 病号 置信度 真实值
1 0.8 1 11 0.8 0
2 0.2 0 12 0.1 0
3 0.4 1 13 0.2 0
4 0.1 0 14 0.9 1
5 0.4 0 15 0.3 0
6 0.8 1 16 0.6 0
7 0.3 0 17 0.8 1
8 0.2 0 18 0.2 0
9 0.6 1 19 0.2 0
10 0.5 0 20 0.4 1

预测值是置信度的话,要怎么算TPR和FPR呢?

很简单,给个阈值就行,不小于这个阈值就设为1,小于设为0。

注意,在实际的做法中,一般不用卡阈值的方法,而是按照置信度排序,然后取前N条样本,其实效果等同取阈值。

但阈值设多大好呢?

这就很关键了,因为阈值的大小会影响TPR和FPR。

阈值对TPR和FPR的影响

假如病患例子的阈值设为0.9,阈值判决后的预测结果如下表。

病号 预测值 真实值 病号 预测值 真实值
1 0 1 11 0 0
2 0 0 12 0 0
3 0 1 13 0 0
4 0 0 14 1 1
5 0 0 15 0 0
6 0 1 16 0 0
7 0 0 17 0 1
8 0 0 18 0 0
9 0 1 19 0 0
10 0 0 20 0 1

可以算出TP=1,TN=13,FP=0,FN=6,那么
TPR=TPTP+FN=11+6≈0.14FPR=FPTN+FP=0\begin{aligned} TPR &= \frac{TP}{TP+FN} = \frac{1}{1+6} \approx 0.14 \\ FPR &= \frac{FP}{TN+FP} = 0 \end{aligned} TPRFPR​=TP+FNTP​=1+61​≈0.14=TN+FPFP​=0​
这结果TPR和FPR都很低,FPR低是好事,说明负样本的预测错误率低,但TPR也低就不好了,因为正样本的预测正确率不高。

那换个阈值再试试,阈值设为0.1,就是全部猜作正例,不列详细计算过程了,直接给出结果
TPR=1FPR=1\begin{aligned} TPR &= 1 \\ FPR &= 1 \end{aligned} TPRFPR​=1=1​
这结果刚好相反,TPR和FPR都很高,正样本的预测正确率上来了,负样本的预测错误率也变大了。

通过上面的比较,能看出来:阈值设得越高,TPR和FPR越低;阈值设得越低,TPR和FPR越高。

ROC曲线

上一节我们知道了TPR和FPR会随阈值变化而变化,你要是把所有阈值对应的TPR和FPR求出来,画个直角坐标系,以FPR为横轴,TPR为纵轴,把不同阈值下的(FPR,TPR)坐标点标上并连起来,你就能看到TPR和FPR的整个变化曲线,而这条曲线就称为ROC(Receiver Operating Characteristic)曲线。

Receiver Operating Characteristic这名字挺奇怪的,可能是因为最早出现在雷达信号检测领域,用于评价接收器(Receiver)侦测敌机的能力。

尝试画出病患事例的ROC曲线,先求不同阈值下的FPR和TPR,置信度从大到小(重复的不算)排列为[0.9, 0.8, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1],一共有8个阈值,如果手算TPR和FPR那太费劲了,幸好sklearn.metrics模块有现成的roc_curve函数来算,代码如下:

import pandas as pd
from sklearn.metrics import roc_curve# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]# 计算TPR和FPR
fpr, tpr, thresholds = roc_curve(y_true, y_score)# 把fpr,tpr,thresholds用DataFrame表格保存,方便显示
result = pd.DataFrame([thresholds,tpr,fpr], index=['threshold','TPR','FPR'])
print(result)

结果如下

             0         1         2         3         4         5         6  \
threshold  1.9  0.900000  0.800000  0.600000  0.500000  0.400000  0.300000
TPR        0.0  0.142857  0.571429  0.714286  0.714286  1.000000  1.000000
FPR        0.0  0.000000  0.076923  0.153846  0.230769  0.307692  0.461538   7    8
threshold  0.200000  0.1
TPR        1.000000  1.0
FPR        0.846154  1.0

上面结果有两点需要注意:

  • roc_curve函数结果的第一列没有什么实际意义,只是画ROC曲线图一般都会有原点(0,0),它直接帮用户给加上了。
  • 关于第一列的threshold为什么是1.9?根据官方API的解释,它是用max(y_score) + 1算的,为什么要这么算?官方API没有说明,所以我也不知道这脑洞是怎么来的。

接下来,就是根据FPR和TPR结果画ROC曲线,画出来如下图。


画图代码如下:

import matplotlib.pyplot as pltplt.figure()
# 画散点图,标出点的位置
plt.scatter(fpr, tpr)
# 画ROC曲线图
plt.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve')plt.xlim([-0.05, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()

如果样本多了之后,画出来的ROC曲线会平滑得多。

ROC曲线的用处

当你需要评价多个分类模型结果时,ROC曲线能帮你看出这些模型的优劣。

下面给出了A和B两个分类模型的ROC曲线图,哪一个模型的结果比较好呢?


很显然是模型A,为什么呢?

因为模型A的ROC曲线要比模型B的往左上凸,这样的话,如果固定FPR,模型A的TPR大于模型B;如果固定TPR,模型A的FPR要小于模型B。怎么样都是模型A比模型B强。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHPaDfif-1603466581745)(pict/2019-07-13 19-13-08屏幕截图.png)]

模型C是有特殊意义的,如果抛硬币来做二分类预测(取任一类的概率是0.5),最后画出来的ROC曲线图就跟C很接近。

可以做个实验:用概率为0.5取0或1来预测真实值,看看算出来的TPR和FPR的结果。

先构造一个1000样本的真实值列表。

from sklearn.metrics import confusion_matrix
import random# 构造真实值,正例有100个,负例有900个,用shuffle随机打乱顺序
y_true = [1]*100+[0]*900
random.shuffle(y_true)

用概率为0.5取0或1做预测,并计算TPR和FPR。

import numpy as np
# 随机生成1000个0和1的预测值
y_pred = np.random.randint(0,2,size=1000)# 计算TPR和FPR
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
print('FPR: %.2f'%(fp/(tn+fp)))
print('TPR: %.2f'%(tp/(tp+fn)))

结果如下

FPR: 0.50
TPR: 0.53

由于预测值是随机的,每次出来结果会有不同,但基本都围绕在点(FPR,TRP)=(0.5,0.5)附近,也就是说,按概率为0.5取0或1的方式做预测,势必经过(0.5,0.5),其ROC曲线就会表现为一条往右上的对角线。

某个模型全面碾压的情况不太多,大多数情况会如下图所示,两个模型的ROC曲线是相交的。


那哪个模型的结果比较好呢?

需要分情况。比如,如果限定FPR要小于相交点,无疑模型A好于模型B。

AUC

如果没有特定的限制,那怎么选模型呢?有一招,直接算ROC曲线下的面积,称为AUC(Area Under Curve)。


AUC越大,模型结果越好,下面算算医患事例的AUC,用sklearn.metrics的roc_auc_score函数。

from sklearn.metrics import roc_auc_score# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]# 计算AUC
auc = roc_auc_score(y_true, y_score)
print('AUC: %.2f'%auc)

结果是

AUC: 0.89

AUC能评价二分类模型结果,其实是有概率解释的,AUC的概率含义是:随机从样本集中取一对正负样本,正样本得分(置信度)大于负样本的概率。实际上可以理解为,模型把正样本(按照置信度)排在负样本前面的概率。

具体的解释参考下面链接:

https://tracholar.github.io/machine-learning/2018/01/26/auc.html#auc%E5%AF%B9%E6%AD%A3%E8%B4%9F%E6%A0%B7%E6%9C%AC%E6%AF%94%E4%BE%8B%E4%B8%8D%E6%95%8F%E6%84%9F

精准率和召回率

在信息检索、Web搜索领域,时常会关心“检索的信息有多少是用户感兴趣的”、“用户感兴趣的信息有多少被检索出来”,为满足这样的评价需求,有了精准率和召回率这两个指标。

精准率

精准率(Precision)的定义是:预测为正的样本中猜对的比例。公式如下
Precision=TPTP+FP\text{Precision} = \frac{TP}{TP+FP} Precision=TP+FPTP​


这个指标反映的是你预测正样本预测有多准,关键在,因此Precision也被称为查准率。

召回率

召回率(Recall)的定义是:实际为正的样本中被猜对的比例。公式如下
Recall=TPTP+FN\text{Recall} = \frac{TP}{TP+FN} Recall=TP+FNTP​


看定义,召回率是有点不好理解,举个例子吧。

假如患病的为正样本,不患病的为负样本,100个人里面有10个病患,医生检查出了病患中的8个,那这个结果的召回率是多少?

按照定义,先看实际为正的样本,也就是患病的人,共有10个,这里面医生猜对的有8个,那么Recall=810=0.8\text{Recall} = \frac{8}{10} = 0.8Recall=108​=0.8。由此可知,召回率关注的是病患(正样本)是不是都找全了,关键在,因此Recall也被称为查全率。

两者公式比较

Precision=TPTP+FPRecall=TPTP+FN\begin{aligned} \text{Precision} &= \frac{TP}{TP+FP} \\ \text{Recall} &= \frac{TP}{TP+FN} \end{aligned} PrecisionRecall​=TP+FPTP​=TP+FNTP​​

Precision和Recall公式的分子都是TP,这表示两者都关心有多少猜对的正样本。

差别在于分母:Precision的是TP+FP,即预测为1(Positive)的样本;Recall的是TP+FN,即实际为1的样本(FN表示预测为0但没猜对,实际是1)。

它们的关注点都是跟1(一般为少数类)有关的样本,根本没有考虑TN(预测为0,实际为0)。

所以,精准率和召回率这两个指标的本质是:从精确度和全面性的角度来考察少数类样本的预测结果

由于精准率和召回率更关注少数类样本的预测情况,所以用它们作为评价指标也可以解决样本不平衡的问题。

精准率和召回率的关系

还是用那个病患事例(预测值是置信度的情况)说明。

病号 置信度 真实值 病号 置信度 真实值
1 0.8 1 11 0.8 0
2 0.2 0 12 0.1 0
3 0.4 1 13 0.2 0
4 0.1 0 14 0.9 1
5 0.4 0 15 0.3 0
6 0.8 1 16 0.6 0
7 0.3 0 17 0.8 1
8 0.2 0 18 0.2 0
9 0.6 1 19 0.2 0
10 0.5 0 20 0.4 1

和TPR/FPR一样,需要对置信度卡阈值判定0和1后,才能计算Precision和Recall。

下面先看看阈值的大小对Precision和Recall的影响。

阈值对精准率和召回率的影响

阈值设为0.9,讲TPR/FPR的时候算过,为TP=1,TN=13,FP=0,FN=6,那么
Precision=TPTP+FP=11+0=1Recall=TPTP+FN=11+6≈0.14\begin{aligned} Precision &= \frac{TP}{TP+FP} = \frac{1}{1+0} = 1 \\ Recall &= \frac{TP}{TP+FN} = \frac{1}{1+6} \approx 0.14 \end{aligned} PrecisionRecall​=TP+FPTP​=1+01​=1=TP+FNTP​=1+61​≈0.14​
这结果Precision很高,Recall很低,说明猜正样本猜得很准,预测为正样本的都猜对了,只是猜得不全,还有好多正样本没猜到。

如果阈值为0.1,就是全部猜作正样本,不列详细计算过程了,直接给出结果
Precision=0.35Recall=1\begin{aligned} Precision &= 0.35 \\ Recall &= 1 \end{aligned} PrecisionRecall​=0.35=1​
这结果刚好相反,Precision很低,Recall很高,说明正样本都找全了,就是猜得不怎么准。

通过上面的比较,能看出来阈值对Precision和Recall的影响:

  • 把阈值设得高,预测正样本的把握确实要大,但会漏掉好多正样本;

  • 把阈值设得低,正样本都能找到,但是预测正样本的准度就不怎么样了。

举个现实的例子:

  • 没有99.99%的概率(阈值设得高)会赚钱就不投资,当然投了基本都赚,但会失去很多赚大钱的机会;
  • 热点项目不管能不能赚钱(阈值设得低)都投,当然很可能把大鱼(如初创的google,facebook)都逮到,但会有好多投资的项目是赔钱的。

P-R曲线

Precision和Recall是一对矛盾体,一方大了另一方就小,随着阈值的变动此起彼伏。

和ROC曲线一样,算出不同阈值下的Precision和Recall,以Recall为横轴,以Precision为纵轴,也可以画出一条曲线图,称为P-R(精确率-召回率)曲线。

尝试把病患事例的PR曲线图画出来,先算不同阈值下的Precision和Recall,调用sklearn.metrics中的precision_score和recall_score函数来算,代码如下:

# 阈值划分函数
def binary_by_thres(x,t):if x >= t:return 1else:return 0from sklearn.metrics import precision_score,recall_score# 阈值列表
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.8,0.9]
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]precision = []
recall = []for t in thresholds:# 根据阈值t把预测值划分为0和1y_thres = list( map(binary_by_thres, y_score, [t]*len(y_score)) )precision.append( precision_score(y_thres, y_true) )recall.append( recall_score(y_thres, y_true) )result = pd.DataFrame([thresholds,precision,recall], index=['threshold','precision','recall'])
print(result)

结果如下:

              0         1         2         3         4         5         6  \
threshold  0.10  0.200000  0.300000  0.400000  0.500000  0.600000  0.800000
precision  0.35  0.388889  0.538462  0.636364  0.625000  0.714286  0.800000
recall     1.00  1.000000  1.000000  1.000000  0.714286  0.714286  0.571429   7
threshold  0.900000
precision  1.000000
recall     0.142857

画个直角坐标系,以Recall为横轴,Precision为纵轴,看看不同阈值下的(Recall,Precision)坐标点的P-R曲线变化,画出的图如下:


上图有两点需要注意:

  • 最后一个(Recall,Precision)坐标点规定是(0,1),跟阈值无关。官方API解释说是为了画图从纵轴开始。

  • 画图代码计算Precision和Recall用的是sklearn.metrics中的precision_recall_curve函数,它的计算结果如下,和之前用precision_score和recall_score函数计算的结果不同,少了阈值为0.1,0.2,0.3的情况。

                      0         1         2         3         4    5
    threshold  0.400000  0.500000  0.600000  0.800000  0.900000  NaN
    precision  0.636364  0.625000  0.714286  0.800000  1.000000  1.0
    recall     1.000000  0.714286  0.714286  0.571429  0.142857  0.0
    

    对这个的解释:首先这三种情况的Recall都是1,都在Recall=1的直线上,画阶梯图时考不考虑这三个点对最终的图没有影响,所以precision_recall_curve函数就懒得输出了吧。

画图所用代码如下:

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]# 计算precision和recall
precision, recall, thresholds = precision_recall_curve(y_true, y_score)# 规定画布的大小
plt.figure(figsize=(12,8))# 画填充图
plt.fill_between(recall, precision, alpha=0.2, color='b', step='post')
# 画散点图,凸显坐标点位置
plt.scatter(recall, precision, alpha=0.8, color='r')plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.05])
plt.show()

如果样本多了之后,画出来的P-R曲线会平滑得多。

P-R曲线的用处

和ROC曲线一样,P-R曲线能评价多个模型结果的优劣。

下面给出了两个分类模型的P-R曲线图,哪一个模型的结果比较好呢?


显然是紫色的模型B,为什么呢?

因为模型B的PR曲线要比模型A的往右上凸。

如果固定Recall,模型B的Precision大于模型A;如果固定Precision,模型B的Recall还是大于模型A。怎么样都是模型B比模型A强。


当然,绝大多数情况如下图所示,两个模型的PR曲线是相交的。


那哪个模型的结果比较好呢?跟ROC曲线一样,需要分情况讨论。

如,Recall大于交点时,模型A比模型C好。

实际工作中,如果是做搜索,在保证召回率的情况下要尽量提升准确率,那就更愿意选模型A;如果做疾病监测、反垃圾邮件等,则是保精确率的条件下提升召回率,那更更倾向于选模型C。


那P-R曲线有没有像ROC曲线中的AUC那样的评价指标呢?

有的,P-R曲线下的面积其实是AP(Average Precision)。

AP

原始计算方式

AP(Average Precision)英文的意思就是平均精准度,为什么P-R曲线下的面积就是平均精准度呢?

先来看P-R曲线下面积的计算公式
∑tP(t)ΔR(t)\sum_{t} P(t) \Delta R(t) t∑​P(t)ΔR(t)
其中,ttt是阈值,P(t)P(t)P(t)是对应阈值ttt的Precision,R(t)R(t)R(t)是对应阈值ttt的Recall,ΔR(t)=R(t)−R(t−1)\Delta R(t)=R(t)-R(t-1)ΔR(t)=R(t)−R(t−1)。

把公式变一下形式
∑tΔR(t)P(t)\sum_{t} \Delta R(t) P(t) t∑​ΔR(t)P(t)
而且∑tΔR(t)=1\sum_t \Delta R(t) = 1∑t​ΔR(t)=1。

上面公式就是加权平均值的计算形式,每个P(t)P(t)P(t)对应的权值是Recall的变化量ΔR(t)\Delta R(t)ΔR(t)。

根据上面的说明,我们可以求病患事例的AP,先列出之前算出的threshold,precision,recall。

                  0         1         2         3         4    5
threshold  0.400000  0.500000  0.600000  0.800000  0.900000  NaN
precision  0.636364  0.625000  0.714286  0.800000  1.000000  1.0
recall     1.000000  0.714286  0.714286  0.571429  0.142857  0.0


AP=1.0×(0.142857−0.0)+0.8×(0.571429−0.142857)+0.714286×(0.714286−0.571429)+0.625×(0.714286−0.714286)+0.636364×(1−0.714286)=0.769573\begin{aligned} AP =& 1.0 \times (0.142857 - 0.0) + 0.8 \times (0.571429 - 0.142857) + 0.714286 \times (0.714286 - 0.571429) \\ &+ 0.625 \times (0.714286 - 0.714286) + 0.636364 \times (1-0.714286) \\ =& 0.769573 \end{aligned} AP==​1.0×(0.142857−0.0)+0.8×(0.571429−0.142857)+0.714286×(0.714286−0.571429)+0.625×(0.714286−0.714286)+0.636364×(1−0.714286)0.769573​
用代码来验证一下结果:

from sklearn.metrics import average_precision_score# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]# 计算average precision
ap = average_precision_score(y_true, y_score)
print('Average Precision:%.6f' % ap)

输出为

Average Precision:0.769573

其他计算方式

实际上,原始的AP计算方式用的不多,常用的是PASCAL VOC CHALLENGE的计算方法,它有两种计算方式:

  • 11-point Interpolated Average Precision

    给Recall设定一组阈值,[0, 0.1, 0.2, … , 1],对于Recall大于等于每一个阈值,都有一个对应的最大precision,这样我们就计算出了11个precision,11-point Interpolated Average Precision即为这11个precision的平均值。

    还是算算病患事例的11-point Interpolated Average Precision吧。

    之前通过precision_score和recall_score算得的病患事例Precision和Recall如下:

                  0         1         2         3         4         5         6  \
    threshold  0.10  0.200000  0.300000  0.400000  0.500000  0.600000  0.800000
    precision  0.35  0.388889  0.538462  0.636364  0.625000  0.714286  0.800000
    recall     1.00  1.000000  1.000000  1.000000  0.714286  0.714286  0.571429   7
    threshold  0.900000
    precision  1.000000
    recall     0.142857
    

    先算阈值0的情况。

    按照定义,Recall大于等于阈值0对应的Precision都算,有哪些呢?全部。然后从中挑选一个最大的,那肯定就是1了。

    这就算完了阈值为0的情况,其他的阈值也是依葫芦画瓢,不详细去算了,直接给吧。

    Recall大于等于的阈值 最大Precision
    0 1
    0.1 1
    0.2 0.8
    0.3 0.8
    0.4 0.8
    0.5 0.8
    0.6 0.714286
    0.7 0.714286
    0.8 0.636364
    0.9 0.636364
    1 0.636364

    把上面11个最大Precision求平均,就得到11-point Interpolated Average Precision,为0.776151。

  • PASCAL VOC CHALLENGE 2010版计算方法

    PASCAL VOC CHALLENGE自2010年后就换了另一种计算方法,跟11-point Interpolated Average Precision的区别不是特别大,差异在于给Recall设定阈值改为[1M,2M,⋯,M−1M,1][\frac{1}{M},\frac{2}{M},\cdots,\frac{M-1}{M},1][M1​,M2​,⋯,MM−1​,1],M为正样本的个数。

    老规矩,拿病患事例来说明,病患事例数据有7个正样本,M=7。

    那阈值就确定了,为[17,27,⋯,67,1][\frac{1}{7},\frac{2}{7},\cdots,\frac{6}{7},1][71​,72​,⋯,76​,1]。

    跟11-point Interpolated Average Precision一样算阈值的最大Precision,结果如下:

    Recall大于等于的阈值 最大Precision
    17\frac{1}{7}71​ 1
    27\frac{2}{7}72​ 1
    37\frac{3}{7}73​ 1
    47\frac{4}{7}74​ 0.8
    57\frac{5}{7}75​ 0.714286
    67\frac{6}{7}76​ 0.636364
    1 0.636364

    对最大Precision求平均,得0.826716。

AP的参考链接:

https://www.bbsmax.com/A/MAzAOw159p/

http://blog.sina.com.cn/s/blog_9db078090102whzw.html

F1分数

如果Precision和Recall两个指标都要求高,可以用F1分数来评价模型。

F1分数(F1 score)的计算公式
F1=2(Precision∗Recall)Precision+RecallF1 = \frac{2(Precision*Recall)}{Precision+Recall} F1=Precision+Recall2(Precision∗Recall)​
F1分数采用的是调和平均数(Harmonic Average)。

什么是调和平均数?其实就是倒数的平均数,看下面公式
1y=1N(1x1+1x2+⋯+1xN)\frac{1}{y} = \frac{1}{N}(\frac{1}{x_1} + \frac{1}{x_2} + \cdots +\frac{1}{x_N}) y1​=N1​(x1​1​+x2​1​+⋯+xN​1​)
F1分数公式变换一下形式就能看出来是调和平均数
F1=2(Precision∗Recall)Precision+Recall⇒1F1=12(1Precision+1Recall)\begin{aligned} & F1 = \frac{2(Precision*Recall)}{Precision+Recall} \\ \Rightarrow \quad& \frac{1}{F1} = \frac{1}{2} (\frac{1}{Precision}+\frac{1}{Recall}) \end{aligned} ⇒​F1=Precision+Recall2(Precision∗Recall)​F11​=21​(Precision1​+Recall1​)​
那为什么要用调和平均数?直接求算术平均数不行吗?

那我们举个极端的例子,Precision=0,Recall=1Precision=0,Recall=1Precision=0,Recall=1,现实中绝对不会出现这种情况,这只是为了凸显算术平均数和调和平均数之间的差异。

那么有
算术平均值:Mean=12(Precision+Recall)=0.5算术平均值:Mean = \frac{1}{2}(Precision+Recall)=0.5 算术平均值:Mean=21​(Precision+Recall)=0.5

调和平均值:1F1=12(1Precision+1Recall)=+∞⇒F1=0调和平均值:\frac{1}{F1} = \frac{1}{2} (\frac{1}{Precision}+\frac{1}{Recall}) = +\infin \Rightarrow F1=0 调和平均值:F11​=21​(Precision1​+Recall1​)=+∞⇒F1=0

这说明:调和平均比算术平均更关注值较小的数,就像马云的财富和你的财富算术平均一下,你也是亿万富翁,如果是用调和平均,那马云的财富水平也会和你的相当。

所以,F1分数如果比较大,那PrecisionPrecisionPrecision和RecallRecallRecall都不会小,这样就能平衡地看待两者。

计算病患例子的F1分数,代码如下:

# 阈值划分函数
def binary_by_thres(x,t):if x >= t:return 1else:return 0import pandas as pd
from sklearn.metrics import f1_score# 阈值列表
thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.8,0.9]
# 置信度
y_score = [0.8, 0.2, 0.4, 0.1, 0.4, 0.8, 0.3, 0.2, 0.6, 0.5,0.8, 0.1, 0.2, 0.9, 0.3, 0.6, 0.8, 0.2, 0.2, 0.4]
# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]fscore = []for t in thresholds:# 根据阈值t把预测值划分为0和1y_thres = list( map(binary_by_thres, y_score, [t]*len(y_score)) )# 计算F1分数fscore.append( f1_score(y_true, y_thres) )result = pd.DataFrame([thresholds,fscore], index=['threshold','f1_score'])
print(result)

输出为

                  0     1    2         3         4         5         6     7
threshold  0.100000  0.20  0.3  0.400000  0.500000  0.600000  0.800000  0.90
f1_score   0.518519  0.56  0.7  0.777778  0.666667  0.714286  0.666667  0.25

根据均值不等式(高中知识):21a+1b≤ab\frac{2}{\frac{1}{a}+\frac{1}{b}} \le \sqrt{ab}a1​+b1​2​≤ab​,当且仅当a=ba=ba=b时取等号。

那么,F1=21Precision+1Recall≤Precision⋅RecallF1 = \frac{2}{\frac{1}{Precision}+\frac{1}{Recall}} \le \sqrt{Precision \cdot Recall}F1=Precision1​+Recall1​2​≤Precision⋅Recall​,当且仅当Precision=RecallPrecision = RecallPrecision=Recall取等号。

所以,只有当Precision=RecallPrecision = RecallPrecision=Recall时F1分数取最大值。

在PR曲线图中,往右上的对角线与PR曲线的交点就是F1分数的最大点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSQgp1xz-1603466581791)(pict/2019-07-14 11-32-36屏幕截图.png)]

Matthews相关系数

Matthews相关性系数(Matthews Correlation Coefficient, MCC)跟之前的指标关系不大,所以放最后讲。

它的公式如下
MCC=TP⋅TN−FP⋅FN(TP+FP)(TP+FN)(TN+FP)(TN+FN)MCC= \frac{TP \cdot TN - FP \cdot FN}{\sqrt{(TP + FP)(TP + FN)(TN + FP)(TN + FN)}} MCC=(TP+FP)(TP+FN)(TN+FP)(TN+FN)​TP⋅TN−FP⋅FN​
乍看起来让人有点蒙,不知道怎么来的,下面我就来解释一下。

Matthews相关系数既然叫相关系数,我们第一联想到的会是什么呢?

就是概率论里面学的Pearson相关系数,先回归一下Pearson相关系数的知识。

Pearson相关系数计算公式如下:
ρ=E{[X−E(X)][Y−E(Y)]}Var(X)Var(Y)=E(XY)−E(X)E(Y)Var(X)Var(Y)\rho = \frac{E\{[X-E(X)][Y-E(Y)]\}}{\sqrt{Var(X)Var(Y)}} = \frac{E(XY)-E(X)E(Y)}{\sqrt{Var(X)Var(Y)}} ρ=Var(X)Var(Y)​E{[X−E(X)][Y−E(Y)]}​=Var(X)Var(Y)​E(XY)−E(X)E(Y)​
其中,XXX和YYY是两个变量,E(⋅)E(\cdot)E(⋅)和Var(⋅)Var(\cdot)Var(⋅)分别代表均值和方差。

Pearson相关系数的用处在于衡量XXX和YYY之间的线性关系,取值范围是[-1,1]。

  • ρ\rhoρ越接近1,越正相关,XXX和YYY越趋近于同向变化;
  • ρ\rhoρ越接近-1,越负相关,XXX和YYY越趋近于反向变化;
  • ρ\rhoρ越接近0,相关性越弱,XXX和YYY之间的线性关系越小。


那Pearson相关系数和Matthews相关系数之间有什么关系呢?

其实Matthews相关系数就是特殊的Pearson相关系数,Matthews相关系数针对的是XXX和YYY都是0-1分布的情况。

在实际中,只知道XXX和YYY的样本,只能通过样本来求均值和方差。

现在假定XXX是二分类的预测值,YYY是二分类的真实值,两者的取值都是0和1。

先复习一下样本均值和样本方差的知识。

设X1,X2,⋯,XNX_1,X_2,\cdots,X_NX1​,X2​,⋯,XN​是来自总体XXX的一个样本,x1,x2,⋯,xNx_1,x_2,\cdots,x_Nx1​,x2​,⋯,xN​是对应的观察值。

样本均值的观察值计算公式:
xˉ=1N∑i=1Nxi\bar{x} = \frac{1}{N} \sum_{i=1}^N x_i xˉ=N1​i=1∑N​xi​
样本方差的观察值计算公式:
s2=1N(∑i=1Nxi2−Nxˉ2)s^2 = \frac{1}{N} (\sum_{i=1}^N x_i^2 - N\bar{x}^2) s2=N1​(i=1∑N​xi2​−Nxˉ2)
概率论的书上计算样本方差时除以的是N−1N-1N−1,从无偏性来考虑书上确实是对的,但实际应用中NNN都比较大,所以常常直接除以NNN,其实对结果影响不大。

对于符合0-1分布的样本X,假设样本观察值取1的数量为N1N_1N1​,样本总量为NNN。

那么X的样本均值和样本方差的观察值分别是
xˉ=N1N\bar{x} = \frac{N_1}{N} xˉ=NN1​​

s2=1N(∑i=1Nxi2−Nxˉ2)=1N[N1−N(N1N)2]=N1N(1−N1N)s^2 = \frac{1}{N} (\sum_{i=1}^N x_i^2 - N\bar{x}^2) = \frac{1}{N} [N_1 - N(\frac{N_1}{N})^2] = \frac{N_1}{N} (1-\frac{N_1}{N}) s2=N1​(i=1∑N​xi2​−Nxˉ2)=N1​[N1​−N(NN1​​)2]=NN1​​(1−NN1​​)

XXX和YYY有关的样本均值和样本方差如下
E(X)=TP+FPN,(TP+FP)是X=1的样本数E(Y)=TP+FNN,(TP+FN)是Y=1的样本数E(XY)=TPN,TP是XY=1的样本数Var(X)=TP+FPN(1−TP+FPN)=TP+FPN⋅TN+FNNVar(Y)=TP+FNN(1−TP+FNN)=TP+FNN⋅TN+FPNE(X) = \frac{TP+FP}{N}, \quad (TP+FP)是X=1的样本数 \\ E(Y) = \frac{TP+FN}{N}, \quad (TP+FN)是Y=1的样本数 \\ E(XY) = \frac{TP}{N}, \quad TP是XY=1的样本数 \\ Var(X) = \frac{TP+FP}{N}(1-\frac{TP+FP}{N}) = \frac{TP+FP}{N} \cdot \frac{TN+FN}{N} \\ Var(Y) = \frac{TP+FN}{N}(1-\frac{TP+FN}{N}) = \frac{TP+FN}{N} \cdot \frac{TN+FP}{N} E(X)=NTP+FP​,(TP+FP)是X=1的样本数E(Y)=NTP+FN​,(TP+FN)是Y=1的样本数E(XY)=NTP​,TP是XY=1的样本数Var(X)=NTP+FP​(1−NTP+FP​)=NTP+FP​⋅NTN+FN​Var(Y)=NTP+FN​(1−NTP+FN​)=NTP+FN​⋅NTN+FP​
其中N=TP+FP+TN+FNN=TP+FP+TN+FNN=TP+FP+TN+FN。

把这些结果代入Pearson相关系数
MCC=E(XY)−E(X)E(Y)Var(X)Var(Y)=TPN−TP+FPN⋅TP+FNNTP+FPN⋅TN+FNN⋅TP+FNN⋅TN+FPN=TP⋅N−(TP+FP)(TP+FN)(TP+FP)(TN+FN)(TP+FN)(TN+FP)=TP(TP+FP+TN+FN)−(TP+FP)(TP+FN)(TP+FP)(TN+FN)(TP+FN)(TN+FP)=TP⋅TN−FP⋅FN(TP+FP)(TN+FN)(TP+FN)(TN+FP)MCC = \frac{E(XY) - E(X)E(Y)}{\sqrt{Var(X)Var(Y)}} = \frac{\frac{TP}{N} - \frac{TP+FP}{N} \cdot \frac{TP+FN}{N}}{\sqrt{\frac{TP+FP}{N} \cdot \frac{TN+FN}{N} \cdot\frac{TP+FN}{N} \cdot \frac{TN+FP}{N}}} \\ = \frac{TP \cdot N - (TP+FP)(TP+FN)}{\sqrt{(TP+FP)(TN+FN)(TP+FN)(TN+FP)}} \\ = \frac{TP(TP+FP+TN+FN) - (TP+FP)(TP+FN)}{\sqrt{(TP+FP)(TN+FN)(TP+FN)(TN+FP)}} \\ = \frac{TP \cdot TN - FP \cdot FN}{\sqrt{(TP+FP)(TN+FN)(TP+FN)(TN+FP)}} MCC=Var(X)Var(Y)​E(XY)−E(X)E(Y)​=NTP+FP​⋅NTN+FN​⋅NTP+FN​⋅NTN+FP​​NTP​−NTP+FP​⋅NTP+FN​​=(TP+FP)(TN+FN)(TP+FN)(TN+FP)​TP⋅N−(TP+FP)(TP+FN)​=(TP+FP)(TN+FN)(TP+FN)(TN+FP)​TP(TP+FP+TN+FN)−(TP+FP)(TP+FN)​=(TP+FP)(TN+FN)(TP+FN)(TN+FP)​TP⋅TN−FP⋅FN​
这就推出了Matthews相关系数。

从Pearson相关系数的含义可以很方便地去理解Matthews相关系数的意义:

Matthews相关系数的作用是衡量都服从0-1分布的XXX和YYY的线性关系。

  • MCC=1MCC=1MCC=1,表示XXX预测是1,真实值YYY确实是1,XXX预测是0,真实值YYY也确实是0,两者同向变化,说明预测全是对的;
  • MCC=−1MCC=-1MCC=−1,表示XXX预测是1,真实值YYY却是0,XXX预测是0,真实值YYY却是1,两者反向变化,说明预测全是错的;
  • MCC=0MCC=0MCC=0,表示XXX和YYY没有线性关系,预测有时候是对的,有时候是错的,而且对的和错的一样多,相当于你抛硬币瞎猜。

计算Matthews相关系数可以调用sklearn.metrics的matthews_corrcoef函数,代码如下:

from sklearn.metrics import matthews_corrcoef# 真实值
y_true = [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1]
# 预测值
y_pred = [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0]# 计算Matthews相关系数
mc = matthews_corrcoef(y_true, y_pred)
print('Matthews Correlation Coefficient: %.2f' % mc)

结果为

Matthews Correlation Coefficient: 0.66

对Matthews相关系数的理解启发于https://stats.stackexchange.com/questions/59343/relationship-between-the-phi-matthews-and-pearson-correlation-coefficients

分类模型评价指标说明相关推荐

  1. 机器学习100天(二十):020 分类模型评价指标-PR曲线

    机器学习100天!今天讲的是:分类模型评价指标-PR曲线! <机器学习100天>完整目录:目录 上一节我们已经了解了混淆矩阵的概念,并掌握了精确率.召回率的计算公式,在这里.现在我们来学习 ...

  2. 机器学习100天(二十二):022 分类模型评价指标-Python实现

    机器学习100天!今天讲的是:分类模型评价指标-Python实现! <机器学习100天>完整目录:目录 打开spyder,首先,导入标准库. import numpy as np impo ...

  3. 机器学习分类模型评价指标之ROC 曲线、 ROC 的 AUC 、 ROI 和 KS

    前文回顾: 机器学习模型评价指标之混淆矩阵 机器学习模型评价指标之Accuracy.Precision.Recall.F-Score.P-R Curve.AUC.AP 和 mAP 图裂的话请参考:ht ...

  4. 机器学习分类模型评价指标详述

    问题建模 机器学习解决问题的通用流程:问题建模--特征工程--模型选择--模型融合 其中问题建模主要包括:设定评估指标,选择样本,交叉验证 解决一个机器学习问题都是从问题建模开始,首先需要收集问题的资 ...

  5. 深入探讨分类模型评价指标

    每天给你送来NLP技术干货! 来自:AI算法小喵 前言 众所周知,机器学习分类模型常用评价指标有Accuracy, Precision, Recall和F1-score,而回归模型最常用指标有MAE和 ...

  6. 决策树及分类模型评价指标(混淆矩阵,LIFT曲线 重要)

    决策树评价指标:ROC lift(提升度):类似提纯:按照decile从高到低排列,之后计算每个decile里响应数与该decile里行数的比值得到一个response rate,另外,单独计算所有行 ...

  7. 详细讲解分类模型评价指标(混淆矩阵)python示例

    前言 1.回归模型(regression): 对于回归模型的评估方法,通常会采用平均绝对误差(MAE).均方误差(MSE).平均绝对百分比误差(MAPE)等方法. 2.聚类模型(clustering) ...

  8. 分类模型评价指标KS与IV的比较

    KS与IV均是用来衡量分类模型准确度的方法,在平常的实验中,用这两种方法得出来的结果往往表现一致.但是,这种一致并不是绝对的,在分析场景时,两个指标发生互相偏离的结果还是存在的.这种偏离的存在主要来自 ...

  9. 机器学习分类模型评价指标之Accuracy、Precision、Recall、F-Score、P-R Curve、AUC、AP 和 mAP

    前文:https://www.cnblogs.com/odesey/p/16902836.html 介绍了混淆矩阵.本文旨在说明其他机器学习模型的评价指标. 1. 准确率(Accuracy-Acc) ...

最新文章

  1. SVN 两种存储格式(BDB和FSFS)区别
  2. IDC与百度联合发报告:预测2019年人工智能十大趋势
  3. c语言链表拆分,C语言拆分链表程序
  4. Scala基础:定义变量和逻辑判断语句以及方法和函数
  5. Python调用茉莉机器人API接口
  6. 贪吃蛇游戏的C++代码
  7. 必须正确理解的---ng指令中的compile与link函数解析
  8. php获取页面a标签内容_AKCMS常用标签代码整理
  9. 机器人学习--pitch yaw roll
  10. maven 多模块项目:单独构建某个模块
  11. Intellij idea的Dependencies波浪线
  12. 容器学习 之 docker存储(十五)
  13. vue3.0js 非prop属性的值和setup函数的使用
  14. JavaScript内置对象Date----格式化时间
  15. layui 表单动态添加、删除input框
  16. 创业者需要知道的50句话
  17. python解析html的库_python解析html开发库pyquery使用方法
  18. 在Windows 下如何使用 AspNetCore Api 和 consul
  19. GB28181 PTZCmd控制指令笔记
  20. Java程序调用高德开放API——IP定位

热门文章

  1. 罗斯蒙特1056ph电极_Rosemount/罗斯蒙特1056-02-20-36-AN-UL 分析仪PH电极
  2. 完美世界16.65亿出售院线业务;共享床位涉黄被下线;宜家创始人去世丨价值早报
  3. 1108 String复读机(JAVA)
  4. 常见算法之Flood Fill算法
  5. /proc/cpuinfo 里的 CPU flags
  6. AD学习问题记录(四):AD21布线时如何更改线宽
  7. SCU - 4438 Censor
  8. 基于CentOS搭建个人Leanote云笔记本
  9. 有无可能在非IOS系统上实现苹果为网易/腾讯邮箱做的实时推送
  10. perl脚本实战总结