第二周:如何评价模型的好坏

目录

1. 学习目标

2. 数据拆分

2.1 数据拆分介绍

2.2 手动实现数据拆分函数

2.3 调用sklearn中的数据拆分函数train_test_split()

3. 评价分类结果的指标

3.1 准确率(accuracy)

3.1.1定义

3.1.2 编程实现准确率的计算(以KNN对鸢尾花数据的分类为例)

3.2 混淆矩阵及其衍生指标

3.2.1 混淆矩阵

3.2.2 精确率(precision)

3.2.3 召回率(recall)

3.2.4 F1值

3.2.5 准确率(accuracy)

3.2.6 编程实现混淆矩阵及其衍生指标的计算(以KNN对手写数字的二分类为例)

3.3 PR曲线与ROC曲线

3.3.1 PR曲线

3.3.2 ROC曲线

3.3.3 PR曲线与ROC曲线的比较

4. 评价回归结果的指标

4.1 一般指标

4.1.1 均方误差(MSE)

4.1.2 均方根误差(RMSE)

4.1.3 平均绝对误差(MAE)

4.2 R^2

4.3 编程实现一般指标和R^2的计算(以线性回归对波士顿房价数据的回归预测为例)

5. 参考文献


1. 学习目标

1. 数据拆分:训练数据集和测试数据集

2. 评价分类结果:精准度、混淆矩阵、精准率、召回率、F1_score、ROC曲线等

3. 评价回归结果:MSE、RMSE、MAE和R_squared

2. 数据拆分

2.1 数据拆分介绍

原始数据需要拆分成两部分:训练数据和测试数据。前者用于训练模型,后者用于检验模型效果。

如果数据是随机排列的,那么可以按照一定的比例将原数据从某处切分成两部分(一般是80%用于训练,20%用于测试)。如果数据是按照一定次序排列的,那么最好要先将数据进行乱序操作然后再切分。此时,如果数据的特征和标签是分开存放的,那么可以考虑两种方法:第一种是将二者合并然后乱序;第二种是生成乱序的索引,通过索引去存取对应的数据[1]。

2.2 手动实现数据拆分函数

import numpy as np
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
y = iris.target

def train_test_split(X, y, test_size = 0.2, random_state = None):
    if random_state != None:
        index = np.random.permutation(len(y))
    else:
        index = np.arange(len(y))
    test_len = int(test_size * len(y))
    test_index = index[:test_len]
    train_index = index[test_len:]
    X_train, y_train = X[train_index], y[train_index]
    X_test, y_test = X[test_index], y[test_index]
    return X_train, X_test, y_train, y_test
    
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 2020)
print(X_train, X_test, y_train, y_test)

2.3 调用sklearn中的数据拆分函数train_test_split()

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

iris = datasets.load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_tain, y_test = train_test_split(X, y, test_size = 0.2)
print(X_train, X_test, y_train, y_test)

3. 评价分类结果的指标

3.1 准确率(accuracy)

3.1.1定义

定义:被正确分类的样本所占的比例。

准确率易于理解且适用性广泛,但是有的时候未必是评价一个分类模型的最佳指标。

3.1.2 编程实现准确率的计算(以KNN对鸢尾花数据的分类为例)

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 导入评价指标模块
from sklearn.metrics import accuracy_score

iris = datasets.load_iris()
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

knn_clf = KNeighborsClassifier(n_neighbors = 5)
knn_clf.fit(X_train, y_train)
predictions = knn_clf.predict(X_test)

# 手动计算
accuracy_by_hands = np.sum(predictions == y_test) / len(y_test)
print(accuracy_by_hands)
# 调包计算
accuracy_by_sklearn = accuracy_score(y_test, predictions)
print(accuracy_by_sklearn)

3.2 混淆矩阵及其衍生指标

3.2.1 混淆矩阵

在用准确度评价分类算法时,有可能会产生很大的问题。在遇到样本不均衡的情况时尤甚。

例如,对于一个癌症预测系统,输入检查指标,判断是否患有癌症,预测准确度99.9%。这个系统是好是坏呢?

如果癌症产生的概率是0.1%,那其实根本不需要任何机器学习算法,只要系统输出所有人都是健康的,即可达到99.9%的准确率。也就是说对于极度偏斜(Skewed Data)的数据,只使用分类准确度是不能正确衡量其效果的。

此时,就需要使用混淆矩阵(Confusion Matrix)做进一步分析[2]。

对于一个二分类问题,假设所有的问题被分为0和1两类,混淆矩阵是2*2的矩阵:

  预测值:0 预测值:1
真实值:0 TN(真阴) FP(假阳)
真实值:1 FN(假阴) TP(真阳)

3.2.2 精确率(precision)

定义:\text {precision }=\frac{T P}{T P+F P},即预测为1的样本中,预测正确的样本所占的比例。

精确率可以体现我们关注的那个事件的预测有多准确。因此,精确率也叫做查准率。

3.2.3 召回率(recall)

定义:\text {recall}=\frac{T P}{T P+F N},即真实为1的样本中,预测正确的样本所占的比例。

召回率体现的是:在我们关注的那个事件发生的情况下,可以成功预测的可能性有多大。召回率越大,表示成功预测可以覆盖的样本越全面。因此,召回率也叫做查全率。

例如,上面的癌症预测系统问题,查全率就要比精确率和准确率更能体现系统预测效果的优劣。

3.2.4 F1值

精确率和召回率是存在“此消彼长”的变化关系的。因此,在这种情况下我们很难通过不同模型精确率和召回率的同时比较来判断哪个分类模型效果更好。于是,便出现了F1值这个指标。F1值将精确率和召回率统一成了一个指标,通过比较这个统一的参数,我们就可以判断哪个分类模型效果更好。

定义:\text {F1}=\frac{2*precision*recall}{precision+recall},即F1值实际上是精确率和召回率的调和平均数。

3.2.5 准确率(accuracy)

这里的准确率的定义同3.1中的定义。之所以还要在这里提出是因为,利用混淆矩阵也可以进行准确率的计算。

根据混淆矩阵和准确率的定义,可得:\text {accuracy}=\frac{T P + TN}{T P+T N+F P+F N}

3.2.6 编程实现混淆矩阵及其衍生指标的计算(以KNN对手写数字的二分类为例)

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 导入评价指标模块
from sklearn.metrics import confusion_matrix
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn.metrics import accuracy_score

digits = datasets.load_digits()
X = digits.data
# 由于一会儿要按照原数据修改标签,因此这里最好传入数组的拷贝
y = digits.target.copy()
# 构造偏斜数据
y[digits.target == 9] = 1
y[digits.target != 9] = 0

X_train, X_test, y_train, y_test = train_test_split(X, y, \
                                                    test_size = 0.2, \
                                                    random_state = 2019)

knn_clf = KNeighborsClassifier(n_neighbors = 5)
knn_clf.fit(X_train, y_train)
predictions = knn_clf.predict(X_test)

# 手动计算混淆矩阵
TP = np.sum((predictions == 1) & (y_test == 1))
FP = np.sum((predictions == 1) & (y_test == 0))
TN = np.sum((predictions == 0) & (y_test == 0))
FN = np.sum((predictions == 0) & (y_test == 1))
confusion_matrix_by_hands = np.array([TN, FP, TP, FN]).reshape(2, 2)
print(confusion_matrix_by_hands)
# 调包计算混淆矩阵
print(confusion_matrix(y_test, predictions))

# 手动计算精确率
precision_by_hands = TP / (TP + FP)
print(precision_by_hands)
# 调包计算精确率
print(precision_score(y_test, predictions))

# 手动计算召回率
recall_by_hands = TP / (TP + FN)
print(recall_by_hands)
# 调包计算召回率
print(recall_score(y_test, predictions))

# 手动计算F1值
F1_by_hands = 2 / (1 / precision_by_hands + 1 / recall_by_hands)
print(F1_by_hands)
# 调包计算F1值
print(f1_score(y_test, predictions))

# 手动计算准确率
accuracy_by_hands = (TP + TN) / (TP + TN + FP + FN)
print(accuracy_by_hands)
# 调包计算准确率
print(accuracy_score(y_test, predictions))

3.3 PR曲线与ROC曲线

3.3.1 PR曲线

假如我们可以将模型对样本的预测结果对样本进行排序,越排在前面的样本越是会被模型认为最可能为“正例”。按照此顺序逐一将样本视为正例对样本进行预测,分别得到对应的精确率和召回率。以精确率为纵轴,召回率为横轴就可以得到一条曲线,即PR曲线[4]。

第二周:如何评价模型的好坏_第1张图片 图3.1 PR曲线[4]

如果一个模型的PR曲线可以被另一个的PR曲线完全包住,则可以认为后者的效果要优于前者。如果出现了交叉,则无法判断到底谁的效果更好,只能在具体的精确率和召回率上判断谁更优。但是,人们依然提出了一些度量方式来衡量出现PR曲线交叉的模型的优劣。首先是计算PR曲线所包围区域的面积,面积越大效果越好。但是面积并不好计算。于是,人们提出了另一种指标,那就是平衡点(BEP)。平衡点是指精确率与召回率相等时的取值,该值越大说明模型效果越好。不过,平衡点又太过简略。因此,人们更愿意使用F1值来取代平衡点。

3.3.2 ROC曲线

仍然沿用PR曲线中的条件,即将模型对样本的预测结果对样本进行排序,越排在前面的样本越是会被模型认为最可能为“正例”。分类模型实际上相当于在这个排序上寻找一个合适的截断点。不同的应用需求不一样,如果更重视查准率则会将截断点提前设置;如果更重视查全率则会将截断点滞后设置。排序自身质量的好坏可以直接影响模型的泛化能力。ROC曲线的提出就是为了在此角度上研究模型的泛化能力[4][5]。

ROC曲线全称叫做“受试者工作特征”曲线。它的纵轴为真正例率(\mathrm{TPR}=\frac{T P}{T P+F N}),横轴为假正例率(\mathrm{FPR}=\frac{F P}{T N+F P})。

第二周:如何评价模型的好坏_第2张图片 图3.2 ROC曲线和AUC[4]

与PR曲线类似,如果一个模型的ROC曲线可以被另一个的ROC曲线完全包住,则可以认为后者的效果要优于前者。如果出现了交叉,则无法判断到底谁的效果更好。此时最好的方法就是比较ROC曲线所包围的区域的面积即AUC。AUC越大,模型的效果越好。

3.3.3 PR曲线与ROC曲线的比较

二者的区别及优劣可以参考[6]。

4. 评价回归结果的指标

4.1 一般指标

4.1.1 均方误差(MSE)

MSE=\frac{1}{m} \sum_{i=1}^{m}\left(y_{t e s t}^{(i)}-\hat{y}_{t e s t}^{(i)}\right)^{2}

4.1.2 均方根误差(RMSE)

RMSE=\sqrt{\frac{1}{m} \sum_{i=1}^{m}\left(y_{t e s t}^{(i)}-\hat{y}_{t e s t}^{(i)}\right)^{2}}=\sqrt{M S E_{t e s t}}

之所以引入RMSE是因为MSE会将单位取平方,而RMSE会消除单位的平方带来的影响。

4.1.3 平均绝对误差(MAE)

MAE=\frac{1}{m} \sum_{i=1}^{m}\left|y_{t e s t}^{(i)}-\hat{y}_{t e s t}^{(i)}\right|

通常,对于线性回归模型,其损失函数往往是采用MSE的形式。因为含有绝对值的函数不一定处处可导从而会对后续最优化工作带来阻碍。但是评价回归模型的函数可以使用不同于损失函数。

4.2 R^2

以上的几个指标都存在一个共性那就是它们都是含有单位的。单位的存在会导致模型的比较只可以局限在同一个单位的数据上。而有时候我们希望可以用一个值来衡量不同模型的预测效果,这个值最好是一个无量纲的标量。这样,即使预测的数据单位不同,我们还是可以通过这个无量纲的标量来比较模型的优劣[3]。而R^2可以满足这种需要。

R^{2}=1-\frac{S S_{\text {residual}}}{S S_{\text {total}}}=1-\frac{\sum\left(\hat{y}^{(i)}-y^{(i)}\right)^{2}}{\sum\left(\bar{y}-y^{(i)}\right)^{2}}=1-\frac{M S E\left(\hat{y}^{(i)}, y\right)}{\operatorname{Var}(y)}

其中分数部分,分母部分指的是样本自身的误差,分子部分指的是预测结果与真实值之间的误差。

R^2具有以下性质:

1. R^2不大于1;

2. R^2越接近1说明拟合效果越好;

3. R^2等于0说明当前模型效果等同于基准模型;

4. R^2小于0说明当前模型效果还不如基准模型。

4.3 编程实现一般指标和R^2的计算(以线性回归对波士顿房价数据的回归预测为例)

import numpy as np
import matplotlib.pyplot as plt 
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 导入评价指标模块
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score

boston = datasets.load_boston()
X = boston.data[:, 5]
y = boston.target
# 数据存在上限,故过滤掉上限数据
X = X[y < 50.0].reshape(-1, 1)
y = y[y < 50.0].reshape(-1, 1)

X_train, X_test, y_train, y_test = train_test_split(X, y, \
                                                    test_size = 0.2, \
                                                    random_state = 2020)

lr_reg = LinearRegression()
lr_reg.fit(X_train, y_train)
predictions = lr_reg.predict(X_test)

plt.scatter(X_test, y_test, c = 'b')
plt.scatter(X_test, predictions, c = 'r')
plt.show()

# 手动计算MSE
mse_by_hands = (np.linalg.norm(y_test - predictions, ord = 2)) **2 / len(y_test)
print(mse_by_hands)
# 调包计算MSE
print(mean_squared_error(y_test, predictions))

# 手动计算RMSE
rmse_by_hands = (np.linalg.norm(y_test - predictions, ord = 2)) / np.sqrt(len(y_test))
print(rmse_by_hands)
# 调包计算RMSE
print(np.sqrt(mean_squared_error(y_test, predictions)))

# 手动计算MAE
mae_by_hands = (np.linalg.norm(y_test - predictions, ord = 1)) / len(y_test)
print(mae_by_hands)
# 调包计算MAE
print(mean_absolute_error(y_test, predictions))

# 手动计算R^2
r_2 = 1 - mse_by_hands / np.var(y_test)
print(r_2)
# 调包计算R^2
print(r2_score(y_test, predictions))

5. 参考文献

1. https://mp.weixin.qq.com/s/vvCM0vWH5kmRfrRWxqXT8Q

2. https://mp.weixin.qq.com/s/Fi13jaEkM5EGjmS7Mm_Bjw

3. https://mp.weixin.qq.com/s/BEmMdQd2y1hMu9wT8QYCPg

4. 《机器学习》(周志华)第二章

5. https://www.jianshu.com/p/c61ae11cc5f6

6. https://blog.csdn.net/weixin_31866177/article/details/88776718

你可能感兴趣的:(MLgroup)