机器学习第二周:如何评价模型的好坏

机器学习第二周:如何评价模型的好坏

一、目标

数据拆分:训练数据集&测试数据集

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

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

二、学习资料参考

  1. 《机器学习的敲门砖:kNN算法(中)》(https://mp.weixin.qq.com/s/vvCM0vWH5kmRfrRWxqXT8Q)全文,学习数据拆分,划分训练数据集&测试数据集的方法。分类准确度。

  2. 阅读《评价分类结果(上):混淆矩阵、精准率、召回率》(https://mp.weixin.qq.com/s/Fi13jaEkM5EGjmS7Mm_Bjw)

  3. 《评价分类结果(下):F1 Score、ROC、AUC》**,全面了解评价分类结果

  4. 阅读《模型之母:线性回归的评价指标》(https://mp.weixin.qq.com/s/BEmMdQd2y1hMu9wT8QYCPg),学习评价回归结果:MSE、RMSE、MAE、R Squared

    注:本文是上面这四篇文章的笔记,均来自与公众号 数据科学家联盟组织的学习小组,木东居士和饼干等分享的文章。

三、学习开始

  1. 数据拆分:

    KNN算法中:对数据集进行划分,包括训练集和测试集,在划分过程中尽可能随机选择,训练集数据要足够多,可以采用随机后采集0.8比例的数据作为训练集,剩余0.2作为测试数据集的数据进行。

    方法命名:https://mp.weixin.qq.com/s/vvCM0vWH5kmRfrRWxqXT8Q l来源: 数据科学家联盟 作者:Japson

    def  train_test_split(X, y, test_ratio = 0.2 , seed = None):
        if seed:
            np.random.seed(seed)
        shuffle_index = np.random.permutation(len(X))
        
        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]
        
        return X_train , X_test, y_train ,y_test
    

    参考下面链接:sklearn在代码中引入model_selection.train_test_split,可以随机分割训练集和测试集(随机数种子)。如果该方法不加random_state可能会造成确定模型和初始参数后,每运行一次都可能得到不同的准确率。

    from  sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
    

    https://blog.csdn.net/Tony_Stark_Wang/article/details/80407923

  2. 分类准确度accuracy

    在划分出测试数据集后,根据训练数据集训练出的模型,需要进行对模型验证,评价 其好坏。

    accuracy_score 计算分类准确率,返回被正确分类的样本比例(默认)或者数量

    在多标签分类问题中,该函数返回子集的准确率,对于一个给定的多标签样本,如果预测得到的标签集合与该样本真正的标签集严格吻合,则subset accuracy = 1.0 否则是0.0。但有些场合使用精度(查准率)和召回率(查全率)等更好些。

    def accuracy_score(y_true, y_predict):
        """计算y_true和y_predict之间的准确率"""
        assert y_true.shape[0] == y_predict.shape[0],"the size of y_true must be equal to the size of y_predict"
        return sum(y_true == y_predict) / len(y_true)
    

    有时只关心对应的测试结果,可以使用score,进一步进行分装

    def score(self, X_test, y_test):
        """根据X_test进行预测, 给出预测的真值y_test,计算预测算法的准确度"""
        y_predict = self.predict(X_test)    
    	return accuracy_score(y_test, y_predict)
    
  3. 超参数

    knn_clf = KNeighborsClassifier(n_neighbors=3)

    这里的数值,是传递一个默认的k值,但是这个参数该如何选择。

    超参数,在机器学习算法模型执行之前需要制定的参数。调参数理解调的就是超参数,如该处算法KNN中的k。

    在对于KNN算法中,最容易想到的就是利用训练数据,通过循环1…k,选择其中得到的score最佳的一个结果对应的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)
    
  4. 超参数:权重

    在KNN算法中,我们是利用对应的k个邻近点,每个点的权重都是1,如果根据距离远近增加判断结果所起的作用,影响大的增加权重大点。可以使用f(x)=1/x,对每种分类点距离倒数求和,判断数值那个最大,然后该点所属对应分类。

    感觉可以调整f(x)

    在sklearn.neighbors的构造函数 KNeighborsClassifier中有一个参数:weights ,默认uniform 是不考虑距离,也可以写distance来考虑距离权重(默认是欧拉距离,如果要是曼哈顿距离,则可以写参数P?)

    在找两个超参数,公众号介绍使用了双重循环,在每种距离运算下寻求k,最终寻求出一个最佳的两个参数

    # 两种方式进行比较
    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)
    
    输出:
    best_method =  distance
    best_k =  4
    best_score =  0.9916666666666667
    

    method循环uniform,distance ,每种循环里面嵌套子循环,子循环再遍历k,在寻找最佳的score过程中,通过遍历比较最终获得一个满意的k ,method

  5. 当有多个参数需要找寻最优时,是不是需要自己写多个循环去搜索最优参数

    饼干说,超参数过多,超参数之间互相依赖等都是在具体超参数搜索过程中遇到的问题。如何一次性把想要的“最好”的超参数组合出来,sklearn中专门封装了一个参数网格搜索方法GridSearch。使用时需要首先定义一个参数param_search。他是一个数据,数组中的每个元素是一个字典,字典中是对应的一组网格搜索,每一组网格搜索是这一组网格搜索每个参数的取值范围。键是参数名称,值是键所对应的的参数列表。

    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)
    
    %%time 在网格中寻找最佳参数组
    grid_search.fit(X_train, y_train)
    
    输出:CPU times: user 2min 21s, sys: 628 ms, total: 2min 21s
    Wall time: 2min 23s
    GridSearchCV(cv=None, error_score='raise',
           estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
               metric_params=None, n_jobs=1, n_neighbors=5, p=2,
               weights='uniform'),
           fit_params=None, iid=True, n_jobs=1,
           param_grid=[{'weights': ['uniform'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}, {'weights': ['distance'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'p': [1, 2, 3, 4, 5]}],
           pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
           scoring=None, verbose=0)
    
    # 返回的是网格搜索搜索到的最佳的分类器对应的参数 grid_search.best_estimator_
    
    输出:KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
               metric_params=None, n_jobs=1, n_neighbors=3, p=3,
               weights='distance')
    
    #best_score
    grid_search.best_score_
    
    输出:0.9853862212943633
    grid_search.beat_params_
    
    输出:{'n_neighbors':3, 'p':3, 'weights':'distance'}
    
    knn_clf = grid_search.beat_estimator_
    knn_clf.score(X_test, y_test)
    
    输出:0.9833333333328
    
  6. 评价分类结果:混淆矩阵、精准率、召回率

    在机器学习中如何评价一个算法的好坏,在回归问题中可以使用MSE/RMSE/MAE/R方。在使用分类精准度上,可能存在陷阱的,这是需要更加全面的信息。

    在对于极度偏斜skewed data的数据,在使用分类准确度是不能衡量的,还需要使用混淆矩阵confusion matrix 做分析。

    预测值0 预测值1
    真实值0 TN FP
    真实值1 FN TP

    TN:真实值是0,与测试值也是0,预测negative,预测正确

    FP:真实值是0,预测值是1,预测positive,但预测错误了

    FN:真实值是1,预测值是0,预测是negative,但预测错误了

    TP:真实值是1,预测值是1,预测是positive,预测正确了

    混淆矩阵的信息更多谢,提供更多一些指示的指标。

    精准率和召回率

    预测值0 预测值1
    真实值0 9978(TN) 12(FP)
    真实值1 2(FN) 8(TP)

    precision=TPTP+FP8/(8+12)=40 精准率 precision = \frac{TP}{TP+FP},即精准率为8/(8+12)=40%

    所谓的精准率是:分母为所有预测为1的个数,分子是其中预测对了的个数。即预测值为1,且预测对了的比例。

    现实情况中我们更关注“患病”、“有风险”,且在“有风险”中且判断正确的概率。如在100次结果为患病的预测中,平均有40次是预测对的。精准率是我们关注的那个事件,预测的有多准。
    recall=TPTP+FN=88+2=80 召回率:recall=\frac{TP}{TP+FN}=\frac{8}{8+2}=80%
    召回率:所有真实值为1的数据中,预测对了的个数。每当有100个癌症患者,算法可以成功的预测出8个。是在我们关注的那个事件真实的发生情况下,我们成功预测的比例是多少。

    举个例子在知道算法准确率的情况下,为什么需要精准率和召回率

    预测值0 预测值1
    真实值0 9978 12
    真实值1 2 8

    如果简单粗暴认为所有人都是健康的,那么算法准确率是99.78%,这个数据应该是毫无意义。如果算精准率则是40%,召回率是80%。也就是说在预测有病的人中预测准的概率是40%,在实际有病的人中成功预测的有80%。这两个指标在本次分类中才能看出比准确率要实用。

    总之:

    精准率:我们关注的那件事,预测的有多准。

    召回率:我们关注的那个事件真实的发生情况下,我们成功预测的比例是。

  7. 评价分类结果(下):F1 Score、ROC、AUC

    当精准率和召回率一个高一个低时该如何选择取舍那?适场景而定

    在实际业务场景中,有很多没有明显的选择,该如何在精准率和召回率中平衡,可以使用一个新的指标:F1 SCORE

    F1 SCORE: 是精准率和召回率的调和平均值:
    F1=2precisionrecallprecision+recall F1=\frac{2*precision*recall}{precision+recall}
    调和平均值,当其中某一个值高、另一个值低时,得到的Fs1 score也低;只有而且都非常高,F1才会高。

    import numpy  as np
    def f1_score(precision, recall):
        try:
            return 2*precision*recall /(precision + recall)
        except:
            return 0.0
    

    0.9 0.1 --》 0.18…

    ROC曲线

    分类阈值、TPR和FPR

    分类阈值:设置判断样本为正例的阈值threshold ?

    如果某个逻辑回归模型预测得到一个分数,但是这个分数是不是归到一个类中,它需要一个判断的条件,或者说是一个阈值,大于它,或者是小于他。

    sklearn中有一个方法:decision_function

    decision_scores = log_reg.decision_function(X_test)

    y_predict = np.array(decision_scores >=5, dtype = ‘int’)

    img

    TPR :预测为1,且预测对了的数量,占真实值为1的数据百分比。(召回率)
    TPR=recall=TPTP+FN TPR=recall=\frac{TP}{TP+FN}

img

FPR:预测值为1,单预测错了的数量
FPR=FPTN+FP FPR=\frac{FP}{TN+FP}
ROC曲线:receiver operation characteristic Cureve,描述TPR和FPR之间的关系。x座标轴是FPR,y座标轴是TPR

TPR是所有真实值为1中被判断为1的比例;FPR是真实值为0中被错误判断为1的概率。理想情况下TPR=1 ,FPR=0。阈值取得最小正例预测值与最大负例预测值之间的值即可。
在这里插入图片描述

根据ROC曲线,x座标轴是FPR,y座标轴是TPR,我们想要真实值1中预测的越准确越好,即TPR越高越好,FPR是负例中错误的预测 成正确,这个值应该越少越好。因此也就是在左上角,说明效果越好。

当TPR=1,FPR=0,称为完美分类。当两个算法的ROC曲线中随便一点的TPR都要比两外一个算法好,此时可以任务好的那个算法是两个算法中我们想要的。

当两个算法有交叉,这时无法判断那个分类器较好时,按照饼干哥的文章中说的可以计算曲线显得面积AUC,作为性能度量。???如何计算,请看AUC

AUC,在ROC曲线中,曲线下面的面试称为area under curve 。这个面积是小于1的。

AUC可以看做是由多个梯形的面积的和。

AUC=1,完美分类器;(0.5,1)优于随机猜测。需要选取一个合适的阈值。AUC=0.5模型没有预测价值;AUC<0.5 比随机预测还差。

sklearn.metrics  roc_auc_score
roc_auc_score(y_test,decision_scores)
  1. 线性回归的评价指标:MSE、RMSE、MAE、R Squared

    线性回归模型,在使用最小二乘法,使训练集与预测值的差的平方和最小,求出截距和系数,之后使用测试集进行测试,以此来衡量算法的好坏。

    均方误差MSE:在最小二乘法的计算过程中均方和往往在项数越多时,结果越相差很大。因此需要将测试的数据集的个数进行抵消,因此:mean squared error
    1mi=1m(ytestiy^testi)2 \frac{1}{m}\sum_{i=1}^{m}(y_{test}^{i}-\hat{y}_{test}^{i})^2
    均方根误差RMSE(解决量纲问题):root mean squarde error
    1mi=1m(ytestiy^testi)2=MSEtest \sqrt{\frac{1}{m}\sum_{i=1}^{m}(y_{test}^{i}-\hat{y}_{test}^{i})^2}=\sqrt{MSE_{test}}
    平均绝对误差MAE: mean absolute error
    1mi=1mytest(i)y^test(i) \frac{1}{m}\sum_{i=1}^{m}|y_{test}^{(i)}-\hat{y}_{test}^{(i)}|
    R Square
    R2=1SSresidualSStotal=1(y^(i)y(i))2(yy(i))2 R^2=1-\frac{SS_{residual}}{SS_{total}}=1-\frac{\sum{(\hat{y}^{(i)}-y^{(i)})^2}}{\sum{(\overline{y}-y^{(i)})^2}}
    R Square <=1,

    它越大越好,因为减数的分子这样就越小,即预测值和真实值之差的平方和越小,错误率低。其最大值是1.

    当为0时,则是基准模型,什么叫做基准模型???

四、总结

​ 本文中分别介绍了:数据拆分,将数据拆分为训练数据集&测试数据集。同时对于分类KNN,介绍了结果的评价指标,他们分别是精准度、混淆矩阵、精准率、召回率、F1 Score、ROC曲线等,各类指标都有其应用的场景。最后介绍了线性回归算法的评价,可以用MSE、RMSE、MAE、R Squared,其中效果最好的是R Squared。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章