前言
对于回归问题,我们有mse,rmse,mae,最好的评价指标是r²-squared。
但是对于分类问题,我们一直使用的都是分类准确度这个评价指标,但是这个评价指标却不一定准确。
比如一个癌症测试系统,我们的算法准确度是99.9%,我们能说这个算法好吗?不一定。因为如果患癌症的概率只有0.1%,那我们只要假设每个人都是健康的,也可以达到99.9%的准确度。如果患癌症概率只有0.01%,那我们的算法相比而言是不好的。
这就需要其他的评价指标
一、混淆矩阵(confusion matrix)?
举一个例子:
解析:比如9978,就是9978个人没有患癌症,使用算法得到的是正确的
12个人没有患癌症,但是使用算法得到的却是患癌症的。
以此类推
二、精准率和召回率
1.精准率
在实际中,我们更看重的是1这个指标。
精准率:即我们预测的那个对象真正发生的概率
举一个例子:比如上述是患癌症的概率图。
则精准率即在我们预测患癌症的100个人中实际患癌症的概率只有40人。
2.召回率
召回率:即我们关注的那个对象发生了,并且我们真正预测出来的概率
举个例子 如上述混淆矩阵仍然是患癌症的矩阵
那么召回率就是实际上有100个人患病,患病的100个人中我们预测出来患病的概率为百分之80%
这张图也可以很好的说明精准率与召回率
3、自己实现精准率和召回率以及混淆矩阵
import numpy as np
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target.copy()
y[digits.target==9]=1 #改为二分类问题,这里变成了极度偏斜的数据
y[digits.target!=9]=0
分类准确度:
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg=LogisticRegression()
log_reg.fit(x_train,y_train)
y_log_predict=log_reg.predict(x_test)
log_reg.score(x_test,y_test)
def FN(y_true,y_predict):
assert len(y_true)==len(y_predict) #个数必须相同
return np.sum((y_true==1)&(y_predict==0))
FN(y_test,y_log_predict)
def FP(y_true,y_predict):
assert len(y_true)==len(y_predict) #个数必须相同
return np.sum((y_true==0)&(y_predict==1))
FP(y_test,y_log_predict)
def TP(y_true,y_predict):
assert len(y_true)==len(y_predict) #个数必须相同
return np.sum((y_true==1)&(y_predict==1))
TP(y_test,y_log_predict)
def TN(y_true,y_predict):
assert len(y_true)==len(y_predict) #个数必须相同
return np.sum((y_true==0)&(y_predict==0))
TN(y_test,y_log_predict)
def precision_score(y_true,y_predict):
fp=FP(y_true,y_predict)
tp=TP(y_true,y_predict)
try:
return tp/(fp+tp)
except:
return 0
def recall_score(y_true,y_predict):
fn=FN(y_true,y_predict)
tp=TP(y_true,y_predict)
try:
return tp/(fn+tp)
except:
return 0
precision_score(y_test,y_log_predict)
recall_score(y_test,y_log_predict)
def confusion_matrix(y_true,y_predict):
return np.array([
[TN(y_true,y_predict),FP(y_true,y_predict)],
[FN(y_true,y_predict),TP(y_true,y_predict)]
])
confusion_matrix(y_test,y_log_predict)
4、sklearn中的精准率召回率以及混淆矩阵
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
precision_score(y_test,y_log_predict)
recall_score(y_test,y_log_predict)
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test,y_log_predict)
三、f1_score
1、引入
我们已经提出了分类问题中的两个指标,精准率和召回率
但是对于同一个问题不同的算法得到的指标数值是不一样的。
我们如何根据指标来选择算法呢?
不同的问题对指标的侧重程度不同
比如股票预测
若假设股票上升为1,则我们更应该关注精准率
因为我们希望我们预测的股票上升的都是正确的,此时才能降低我们亏损的风险。
而对于召回率,即使那些原本上升的股票我们没有预测出来,也不影响什么,因为我们并不会往那些股票里投资金。
又比如病人患病系统
若假设生病病人为1,则我们更应该关注召回率
因为我们希望那些真正的病的病人被我们更好的预测出来。
而对于精准率,那些原本没有生病的病人即使被我们误诊为生病了,也无妨,只要后续在进行一些诊断就可说明其没有生病。
但对于某些问题,我们不能说侧重于某个指标,此时就需要将两者合在一起
2、f1_score
f1_score就是精准率和召回率的调和平均值。这里不是两者的算数平均值 调和平均值更准确
对于算数平均值,即使一个指标很小,但如果另一个指标很大,那么也会得到一个相当不错的指标。
但是调和平均不同,如果一个很小,一个很大,那么得到的指标也会比较小。
1、自己实现的f1_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
precision=precision_score(y_test,y_log_predict)
recall=recall_score(y_test,y_log_predict)
def f1_score(precision,recall):
try:
return 2*precision*recall/(precision+recall)
except:
return 0.0
f1_score(precision,recall)
2、sklearn的f1_score
注意参数不同
from sklearn.metrics import f1_score
f1_score(y_test,y_log_predict)
3、f1_score取值范围
若精准率和召回率全为1,则f1取最大值1,若其中有一个0,则f1就为0
四、precision和recall的平衡
1、引入阈值
回顾逻辑回归
在逻辑回归中我们取阈值为0,但是我们也可以通过修改阈值来获取不同的精准率和召回率
精准率和召回率是相互平衡相互制约的
图片中五角星为我们关注的对象即为1,竖线即不同的阈值。
相互制约和平衡其实也很好理解
比如我们想要提高精准率(预测出来为1的实际上也为1)那么就需要我们只有很大的把握时才把其分类为1,
而我们想要提高召回率(实际上为1的都被我们预测了出来),即我们就可能在即使有百分之10的把握也会把其分类为1。
如图,阈值增大了,则精准率提高了但是召回率下降了
阈值减小,则召回率提高但是精准率下降了。
2、代码举例
import numpy as np
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target.copy()
y[digits.target==9]=1 #改为二分类问题,这里变成了极度偏斜的数据
y[digits.target!=9]=0
log_reg=LogisticRegression()
log_reg.fit(x_train,y_train)
y_predict=log_reg.predict(x_test)
log_reg.score(x_test,y_test)
阈值为0时
阈值为5时
阈值为-5时
3、可视化方式来看精准率和召回率关系。
1、自己实现
import matplotlib.pyplot as plt
desicion_scores=log_reg.decision_function(x_test)
thresholds=np.arange(np.min(desicion_scores),np.max(desicion_scores),0.1)
precisions=[]
recalls=[]
for threshold in thresholds:
y_predict=np.array(desicion_scores>=threshold,dtype='int')
precisions.append(precision_score(y_test,y_predict))
recalls.append(recall_score(y_test,y_predict))
plt.plot(thresholds,precisions)
plt.plot(thresholds,recalls)
plt.show()
plt.plot(precisions,recalls)
plt.show()
precision-recall 曲线
2、sklearn中的precision-recall 曲线
注意:
sklearn中的pr曲线得到的precisions和recalls会比thresholds多一个值
在官方文档中是这样说的,最后对应的是precisions为1,recals为0,没有对应的阈值
所以在编写代码绘制曲线是要注意
from sklearn.metrics import precision_recall_curve
precisions,recalls,thresholds=precision_recall_curve(y_test,desicion_scores)
plt.plot(thresholds,precisions[:-1])
plt.plot(thresholds,recalls[:-1]) #去掉最后一个
plt.show()
sklearn会根据我们传入的值来自己取合适的步长。
pr曲线可以用来衡量曲线优劣,一般而言外部的那根曲线对应的模型比里边这跟模型对应曲线的模型好
五、ROC曲线
1、引入
评价一个问题不同超参数模型的好坏,我们一般不采用和pr模型,pr模型用来强调精准度和召回率比较适合。
强调模型的好坏,我们更多使用的是ROC曲线
2、TRP与FPR
TPR是我们预测为1在实际中恰好为1的概率
FPR是我们预测为1但是实际为0的概率
3、tpr.fpr与阈值
五角星代表1,⚪代表0
我们发现fpr与fpr的变化是一致的,这是由于如果我们想要提高tpr
就要降低阈值,而降低阈值之后又会导致fpr升高。
4、自己实现tpr,fpr并绘制roc曲线
def TPR(y_true,y_predict):
tp=TP(y_true,y_predict)
fn=FN(y_true,y_predict)
try:
return tp/(tp+fn)
except:
return 0.0
def FPR(y_true,y_predict):
fp=FP(y_true,y_predict)
tn=TN(y_true,y_predict)
try:
return fp/(fp+tn)
except:
return 0.0
#绘制不同阈值对应的tpr与fpr
tprs=[]
fprs=[]
thresholds=np.arange(np.min(desicion_scores),np.max(desicion_scores),0.1)
for threshold in thresholds:
y_predict=np.array(desicion_scores>=threshold,dtype='int')
tprs.append(TPR(y_test,y_predict))
fprs.append(FPR(y_test,y_predict))
plt.plot(fprs,tprs)
plt.show()
5、sklearn中的roc
from sklearn.metrics import roc_curve
fprs,tprs,thresholds=roc_curve(y_test,desicion_scores)
plt.plot(fprs,tprs)
plt.show()
6、通过roc评判模型好坏
roc曲线可以用围成面积来评判模型好坏,面积取值为0-1
但是我们可以看出来roc受极偏斜数据的影响很小,所以我们要结合精确率和召回率
来判断模型好坏。
六、 多分类问题
上述我们都是通过使用二分类问题来看分类算法
下边我们简单来看一下多分类问题
import numpy as np
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target.copy()
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=666)
from sklearn.linear_model import LogisticRegression
log_reg=LogisticRegression()
log_reg.fit(x_train,y_train)
y_log_predict=log_reg.predict(x_test)
from sklearn.metrics import precision_score
precision_score(y_test,y_log_predict,average="micro")
precision average 默认是Binary 所以如果是多分类问题需要修改average参数
1、 多分类的混淆矩阵
在这里我们重点讲解多分类的混淆矩阵 混淆矩阵天然可以解决多分类
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test,y_log_predict)
我们把这个矩阵对应的数字大小通过颜色绘制出来
颜色越白代表数值越大
但是在这里我们并不关注哪些值是对的,我们希望知道那些判断错误的数值
row_sum=np.sum(cfm,axis=1)
err_cfm=cfm/row_sum
np.fill_diagonal(err_cfm,0) #我们只想看哪写数据是错误的概率 把对角线值设为0
plt.matshow(err_cfm,cmap=plt.cm.gray)
plt.show()
我们可以看出我们把1当成8,3当成8,以及8看作1的错误率更大。
总结:
对于机器学习,如果我们得到的指标不够好,不能说明这个算法是不对的,也有可能是数据的问题。比如前图的手写数据集,为什么1被误判成8以及3被误判成8的概率大呢?
不一定就是算法层面的问题,也有可能是数据本身的问题,不妨把1,8,3对应的图像拿出来看一下。