ML_评价

如何评价模型的好坏。
学习目标有三个:数据拆分:训练数据集&测试数据集(西瓜书第二章)

  

(一)数据拆分的原因:防止过拟合,即只有训练数据在模型中表现好,而之外的数据都会出现拟合较差的情况——泛化能力差

  learning目的:寻找泛化误差小的模型,但是又依赖于经验误差——将样本集分为training &test(互斥),用test error近似泛化误差

  常见split方法:

   1. 留出hold_out:直接将数据集D划分为两个互斥的集合

      tips!training samples 和test samples的划分要尽可能保持数据分布的一致性,避免由于数据划分引起的偏差对结果产生影响。如——分层抽样

      单次使用留出法得到的结果不可靠,一般采用若干次随机划分,每次都评估,留出法返回的是多个结果的平均

      划分比例:若训练集S中包含绝大多数样本,则训练出来的模型会更加接近于D训练出来的模型,但是T比较小,评估结果可能会不稳定。若T中包含较多的样本,则S与D的差别较大,降低了评估的保真性fidelity。为了避免这些问题,常将2/3—4/5的样本用于training,剩下的testing

  2.cross validation较差验证法

  将D划分为k个大小相似,数据分布尽可能一致的互斥子集,(如分层),将K-个子集的并集作为training,剩余testing。则可以做k次检验,最终返回K次结果的均值

   留一法—K=n

  3.自助法bootstrapping——减少训练样本规模变小的影响

  bootstrap sample采样:每次都从D(m)中取出一个样本放到D‘中,最终D'中有m个样本

 调参:对每个参数选择范围和步长

最终模型:先用训练集进行评估测试,最后选定算法和参数,再用D重新训练模型,这就是提交给用户的最终模型

   在训练集上训练模型(参数),在验证集上评估模型,一旦找到的最佳的参数(超参数),就在测试集上最后测试一次,测试集上的误差作为泛化误差的近似。

  吴恩达老师的视频中,如果当数据量不是很大的时候(万级别以下)的时候将训练集、验证集以及测试集划分为6:2:2;若是数据很大,可以将训练集、验证集、测试集比例调整为98:1:1;但是当可用的数据很少的情况下也可以使用一些高级的方法,比如留出方,K折交叉验证等。

#%%数据拆分开
#%%#0x01 判断模型好坏
import numpy as np
from sklearn import datasets
import matplotlib.pyplot as  plt

iris=datasets.load_iris()
X=iris.data
y=iris.target
X.shape
y.shape
#拆分工作
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3) #方法一
#将X和y合并为同一个矩阵,然后对矩阵进行shuffle,之后再分解
#对y的索引进行乱序,根据索引确定与X的对应关系,最后再通过乱序的索引进行赋值
#shuffle() 方法将序列的所有元素随机排序。
tempConcat = np.concatenate((X, y.reshape(-1,1)), axis=1) #方法一
    #reshape(-1,1)表示行数自动计算,axis=1按列拼接
np.random.shuffle(tempConcat)
shuffle_X,shuffle_y = np.split(tempConcat, [4], axis=1) 
test_ratio = 0.2
test_size = int(len(X) * test_ratio) # 设置划分的比例
X_train = shuffle_X[test_size:]
y_train = shuffle_y[test_size:]
X_test = shuffle_X[:test_size]
y_test = shuffle_y[:test_size]

shuffle_index = np.random.permutation(len(X))  # 方法2
    # 将x长度这么多的数,返回一个新的打乱顺序的数组,注意,数组中的元素不是原来的数据,而是混乱的索引
test_ratio = 0.2
test_size = int(len(X) * test_ratio)# 指定测试数据的比例
test_index = shuffle_index[:test_size]
train_index = shuffle_index[test_size:]
X_train = X[train_index]
X_test = X[test_index]
y_train = y[train_index]
y_test = y[test_index]
y_test.shape
#
from sklearn.neighbors import KNeighborsClassifier
knn_0307=KNeighborsClassifier(n_neighbors=3)
knn_0307.fit(X_train,y_train)
y_predict=knn_0307.predict(X_test)

#对比
np.row_stack([y_predict,y_test])  #合并
#准确率
sum(y_predict == y_test) / len(y_test)
knn_0307.score(X_test,y_test)
#%%0x02 分类准确度accuracy
#数据探索
digits = datasets.load_digits() # 手写数字数据集,封装好的对象,可以理解为一个字段
X=digits.data
X.shape
y=digits.target
y.shape
digits.target_names#分类标签
# 去除某一个具体的数据,查看其特征以及标签信息
some_digit = X[666]
y[666]
some_digmit_image = some_digit.reshape(8, 8)
plt.imshow(some_digmit_image, cmap = plt.cm.binary)
plt.show()

#%%超参数:所谓超参数,就是在机器学习算法模型执行之前需要指定的参数。(调参调的就是超参数) 如kNN算法中的k
# 指定最佳值的分数,初始化为0.0;设置最佳值k,初始值为-1
best_score = 0.0
best_k = -1
for k in range(1, 11):  # 暂且设定到1~11的范围内
    knn_clf = KNeighborsClassifier(n_neighbors=k)
    knn_clf.fit(X_train, y_train)
    score = knn_clf.score(X_test, y_test)   
    if score > best_score:
        best_k = k
        best_score = score
print("best_k = ", best_k)
print("best_score = ", best_score)
#权重
#在 sklearn.neighbors 的构造函数 KNeighborsClassifier 中有一个参数:weights,默认是uniform即不考虑距离,也可以写distance来考虑距离权重
best_method = ""
best_score = 0.0
best_k = -1
for method in ["uniform","distance"]:    
    for k in range(1, 11):
        knn_clf = KNeighborsClassifier(n_neighbors=k, weights=method, p=2)
        knn_clf.fit(X_train, y_train)
        score = knn_clf.score(X_test, y_test)        
        if score > best_score:
            best_k = k
            best_score = score
            best_method = method
print("best_method = ", method)
print("best_k = ", best_k)
print("best_score = ", best_score)

#网格搜索——一次性地把我们想要得到最好的超参数组合列出来
param_search = [
    { "weights":["uniform"], "n_neighbors":[i for i in range(1,11)] },
    { "weights":["distance"], "n_neighbors":[i for i in range(1,11)],"p":[i for i in range(1,6)]}
        ]
knn_clf = KNeighborsClassifier()# 调用网格搜索方法
from sklearn.model_selection import GridSearchCV
    # 定义网格搜索的对象grid_search,其构造函数的第一个参数表示对哪一个分类器进行算法搜索,第二个参数表示网格搜索相应的参数
grid_search = GridSearchCV(knn_clf, param_search)
grid_search.fit(X_train, y_train)
grid_search.best_estimator_
grid_search.best_score_

#随机划分
data = np.hstack([X,y[:,np.newaxis]])
#先将数据集进行拼接,要不然我们只针对样本进行采样的话,会找不到对应的标签的
train_set,test_set = train_test_split(data,test_size = 0.2,random_state = 42)
print(len(train_set),len(test_set))
#分层划分样本集
from sklearn.model_selection import StratifiedShuffleSplit


split = StratifiedShuffleSplit(n_splits = 1,test_size = 0.2,random_state = 42)


for train_index,test_index in split.split(data,data[:,-1]):
train_set = data[train_index,:]
test_set = data[test_index,:]


print(len(train_set),len(test_set))
#如果想要知道抽取的各个样本的比例——转换为dataframe
import pandas as pd
train_data = pd.DataFrame(train_set)
train_data.info()
#查看各个类别的比例-count函数
print(train_data[4].value_counts() / len(train_data))

 

(二)评价分类结果:精准度、混淆矩阵、精准率、召回率、F1 Score、ROC曲线等

1、分类算法的评价

  对于一个回归问题,可以使用MSE、RMSE、MAE、R方。对于一个分类问题,可以使用分类精准度。但是实际上,分类精准度是存在陷阱的,有时候我们会需要更加全面的信息。

  对于极度偏斜(Skewed Data)的数据,只使用分类准确度是不能衡量。这是就需要使用混淆矩阵(Confusion Matrix)做进一步分析。

  混淆矩阵:第二个字母:What's your judgement about the sample?;第一个字母:Is your judgement right(true) or not(false)?

2、精准率和召回率

    precesion = TP/(TP+FP) 即,查准率,检索结果中,都是你认为应该为正的样本(第二个字母都是P),但是其中有你判断正确的和判断错误的(第一个字母有T ,F)。通常更关注值为1的特征,比如“患病”,比如“有风险”。精准率为我们关注的那个事件,预测的有多准。

    recall = TP/(TP+FN)即,查全率,检索结果中,你判断为正的样本也确实为正的,以及那些没在检索结果中被你判断为负但是事实上是正的(FN)。召回率也就是我们关注的那个事件真实的发生情况下,我们成功预测的比例是多少。

思考

①是否只关注accuracy?

虽然准确率可以判断总的正确率,但是如果存在样本不均衡的情况下,就不能使用accuracy来进行衡量了。比如,一个总样本中,正样本占90%,负样本占10%,那么只需要将所有样本预测为正,就可以拿到90%的准确率,而这显然是无意义的。正因为如此,我们就需要精准率和召回率了。

②那么精准率和召回率这两个指标如何解读?如果对于一个模型来说,两个指标一个低一个高该如何取舍呢?

精准率的应用场景:预测癌症。医生预测为癌症,患者确实患癌症的比例。

召回率的应用场景:网贷违约率,相对好用户,我们更关心坏用户。召回率越高,代表实际坏用户中被预测出来的概率越高。

例如对一般搜索的情况是在保证召回率的情况下提升准确率,而如果是疾病监测、反垃圾邮件等,则是在保证准确率的条件下,提升召回率。但有时候,需要兼顾两者,那么就可以用F-score指标

3、F1score_是调和平均数,只有两者都高的时候调和平均数才会高

4、ROC曲线受试者工作特征曲线 receiver operating characteristic curve ) 的简写,又称为感受性曲线(sensitivity curve)

  阈值:为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 0.5,但阈值取决于具体问题,因此您必须对其进行调整 

  横轴假正率(FP率),纵轴为真正率(TP率)-TPR和FPR之间是成正比的,TPR高,FPR也高。ROC曲线就是刻画这两个指标之间的关系。

TPR就是所有正例中,有多少被正确地判定为正;FPR是所有负例中,有多少被错误地判定为正。 分类阈值取不同值,TPR和FPR的计算结果也不同,最理想情况下,我们希望所有正例 & 负例 都被成功预测  TPR=1,FPR=0,即 所有的正例预测值 > 所有的负例预测值,此时阈值取最小正例预测值与最大负例预测值之间的值即可。

TPR越大越好,FPR越小越好,但这两个指标通常是矛盾的。为了增大TPR,可以预测更多的样本为正例,与此同时也增加了更多负例被误判为正例的情况。

ROC曲线是通过遍历所有阈值来描述整条曲线的。如果我们不断遍历所有阈值,预测的正样本与负样本数就在不断变化,相应的就会在ROC曲线移动。注意,此时ROC曲线本身的形状不会变化。

思考:

改变阈值并不会影响ROC曲线本身,那么如何判断ROC曲线的好坏?

重新回归到曲线本身,横轴是假正率,纵轴是真正率,我们希望在FP率低的情况下,TP率高。也就是说,ROC曲越陡越好

 5、AUC

一般在ROC曲线中,我们关注是曲线下面的面积, 称为AUC(Area Under Curve)。这个AUC是横轴范围(0,1 ),纵轴是(0,1)所以总面积是小于1的。

ROC和AUC的主要应用:比较两个模型哪个好?主要通过AUC能够直观看出来。

ROC曲线下方由梯形组成,矩形可以看成特征的梯形。因此,AUC的面积可以这样算:(上底+下底)* 高 / 2,曲线下面的面积可以由多个梯形面积叠加得到。AUC越大,分类器分类效果越好。

  • AUC = 1,是完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。
  • 0.5 < AUC < 1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,能有预测价值。
  • AUC = 0.5,跟随机猜测一样,模型没有预测价值。
  • AUC < 0.5,比随机猜测还差;但只要总是反预测而行,就优于随机猜测。
  • #%%分类评价
    import numpy as np
    from sklearn import datasets
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LogisticRegression
    
    digits=datasets.load_digits()
    X=digits.data
    y=digits.target.copy()
    y.shape
    # 要构造偏斜数据,将数字9的对应索引的元素设置为1,0~8设置为0
    y[digits.target==9]=1
    y[digits.target!=9]=0
    
    X_train,X_test,y_train,y_test=train_test_split(X, y, random_state=666)
    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)
    #定义混淆矩阵的四个指标:TN
    def TN(y_true, y_predict):
        assert len(y_true) == len(y_predict)
        # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
        # 向量与向量按位与,结果还是布尔向量
        # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
        return np.sum((y_true == 0) & (y_predict == 0))  # 向量与向量按位与,结果还是向量
    TN(y_test, y_log_predict)
    
    #定义混淆矩阵的四个指标:FP
    def FP(y_true, y_predict):
        assert len(y_true) == len(y_predict)
        # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
        # 向量与向量按位与,结果还是布尔向量
        # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
        return np.sum((y_true == 0) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
    FP(y_test, y_log_predict)
    
    #定义混淆矩阵的四个指标:FN
    def FN(y_true, y_predict):
        assert len(y_true) == len(y_predict)
        # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
        # 向量与向量按位与,结果还是布尔向量
        # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
        return np.sum((y_true == 1) & (y_predict == 0))  # 向量与向量按位与,结果还是向量
    FN(y_test, y_log_predict)
    #定义混淆矩阵的四个指标:TP
    def TP(y_true, y_predict):
        assert len(y_true) == len(y_predict)
        # (y_true == 0):向量与数值按位比较,得到的是一个布尔向量
        # 向量与向量按位与,结果还是布尔向量
        # np.sum 计算布尔向量中True的个数(True记为1,False记为0)
        return np.sum((y_true == 1) & (y_predict == 1))  # 向量与向量按位与,结果还是向量
    TP(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)
    #精准率
    def precision_score(y_true, y_predict):
        tp = TP(y_true, y_predict)
        fp = FP(y_true, y_predict)
        try:
            return tp / (tp + fp)
        except:
            return 0.0
    precision_score(y_test, y_log_predict)
    #召回率
    def recall_score(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
    recall_score(y_test, y_log_predict)
    # scikit-learn中的混淆矩阵,精准率和召回率
    from sklearn.metrics import confusion_matrix,precision_score,recall_score
    confusion_matrix(y_test,y_log_predict)
    precision_score(y_test,y_log_predict)
    recall_score(y_test,y_log_predict)
    #F1_SCORE
    import numpy as np
    def f1_score(precision, recall):
        try:
            return 2 * precision * recall / (precision + recall)
        except:
            return 0.0
    precision = 0.5
    recall = 0.5
    f1_score(precision, recall)
    precision = 0.9
    recall = 0.1
    f1_score(precision, recall)
    #decision_function,即返回分类阈值
    decision_scores = log_reg.decision_function(X_test)
    y_predict = np.array(decision_scores >= 5, dtype='int')  #大于等于5则为1
    
    # =============================================================================
    # #ROC
    # =============================================================================
    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 LogisticRegression
    
    digits = datasets.load_digits()
    X = digits.data
    y = digits.target.copy()
    
    # 要构造偏斜数据,将数字9的对应索引的元素设置为1,0~8设置为0
    y[digits.target==9]=1
    y[digits.target!=9]=0
    
    # 使用逻辑回归做一个分类
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
    
    log_reg = LogisticRegression()
    log_reg.fit(X_train,y_train)
    # 计算逻辑回归给予X_test样本的决策数据值
    # 通过decision_function可以调整精准率和召回率
    decision_scores = log_reg.decision_function(X_test)
    
    # TPR
    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
    
    # FPR
    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
    
    fprs = []    
    tprs = []
    
    # 以0.1为步长,遍历decision_scores中的最小值到最大值的所有数据点,将其作为阈值集合
    thresholds = np.arange(np.min(decision_scores), np.max(decision_scores), 0.1)
    for threshold in thresholds:
        # decision_scores >= threshold 是布尔型向量,用dtype设置为int
        # 大于等于阈值threshold分类为1,小于为0,用这种方法得到预测值
        y_predict = np.array(decision_scores >= threshold, dtype=int)
        #print(y_predict)
        # print(y_test)
        #print(FPR(y_test, y_predict))
        # 对于每个阈值,所得到的FPR和TPR都添加到相应的队列中
        fprs.append(FPR(y_test, y_predict))
        tprs.append(TPR(y_test, y_predict))
        
    # 绘制ROC曲线,x轴是fpr的值,y轴是tpr的值
    plt.plot(fprs, tprs)
    plt.show()
    #SKLEARN中的roc
    from sklearn.metrics import roc_curve
    fprs, tprs, thresholds = roc_curve(y_test, decision_scores)
    plt.plot(fprs, tprs)
    plt.show()
    #sklearn中的auc
    from sklearn.metrics import roc_auc_score
    roc_auc_score(y_test,decision_scores)

(三)评价回归结果:MSE、RMSE、MAE、R Squared

1、均方误差MSE 

为了抵消掉数据量的形象,可以除去数据量,抵消误差。通过这种处理方式得到的结果叫做 均方误差MSE(Mean Squared Error)

2、 均方根误差RMSE

3、平均绝对误差MAE

 

在之前确定损失函数时,我们提过,绝对值函数不是处处可导的,因此没有使用绝对值。但是在评价模型时不影响。因此模型的评价方法可以和损失函数不同。

4、R_square

  • 对于分子来说,预测值和真实值之差的平方和,即使用我们的模型预测产生的错误。
  • 对于分母来说,是均值和真实值之差的平方和,即认为“预测值=样本均值”这个模型(Baseline Model)所产生的错误。
  • 我们使用Baseline模型产生的错误较多,我们使用自己的模型错误较少。因此用1减去较少的错误除以较多的错误,实际上是衡量了我们的模型拟合住数据的地方,即没有产生错误的相应指标

 

#%%波士顿放假——回归评价
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

# 查看数据集描述
boston = datasets.load_boston()
print(boston.DESCR)
# 查看数据集的特征列表
boston.feature_names
#RM average number of rooms per dwelling 每个住宅的平均房间数 选一个特征建模
x = boston.data[:,5]
x.shape
# 取出样本标签
y = boston.target
y.shape
plt.scatter(x,y)
#数据清洗——去掉50

np.max(y)
# 这里有一个骚操作,用比较运算符返回一个布尔值的向量,将其作为索引,直接在矩阵里对每个元素进行过滤。
x = x[y < 50.0]
y = y[y < 50.0]
plt.scatter(x,y)
plt.show()
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.3)
y.shape
x.shape
from sklearn import linear_model
reg=linear_model.LinearRegression()
reg.fit(x_train.reshape(-1, 1),y_train)
reg.intercept_
reg.coef_
y_predict = reg.predict(x_test.reshape(-1, 1))
print(y_predict)
# =============================================================================
#MSE
mse_test = np.sum((y_predict - y_test) ** 2) / len(y_test)
mse_test

from math import sqrt
rmse_test = sqrt(mse_test)
rmse_test
#MAE
mae_test = np.sum(np.absolute(y_predict - y_test)) / len(y_test)
mae_test
#SKLEARN
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
mean_squared_error(y_test, y_predict)
mean_absolute_error(y_test,y_predict)
# =============================================================================
#r^2
1 - mean_squared_error(y_test, y_predict) / np.var(y_test)
from sklearn.metrics import r2_score
r2_score(y_test, y_predict)
原文地址:https://www.cnblogs.com/wjAllison/p/12437339.html