zoukankan      html  css  js  c++  java
  • 模型选择与调优

    一、精确率与召回率

     1、精确率(Presicion)

    预测结果为正例样本中真实为正例的比例(查的准)。

    2、召回率(Recall)

     真实为正例的样本中预测结果为正例的比例(查的全,对正样本的区分能力)。

    3、精确率与召回率的理解

    • 混淆矩阵

    在分类任务下,预测结果(Predicted Condition)与正确标记(True Condition)之间存在四种不同的组合,构成混淆矩阵(适用于多分类)。

    例如:在100个人中有20人患病,80人健康,这是真实结果;预测结果可能是10人患病,90人正常。那么这个例子中,每一个样本有两种结果要么患病,要么正常,假设患病为正例,不患病(正常)为假例,那么:

    召回率为:

    (10/20)*100% = 50%

     显然,召回率的值越大越好,说明预测的就越贴近真实。

     4、其它指标(F1-score)

    反映模型的稳健性。

    [{F_1} = frac{{2TP}}{{2TP + FN + FP}} = frac{{2Pr ecision*{mathop{ m Re} olimits} call}}{{Pr ecision + {mathop{ m Re} olimits} call}}]

    该值越大,说明模型越稳健。

     5、分类模型评估API

    •  sklearn.metrics.classification_report(y_true, y_pred, target_names=None)
    y_true:真实目标值
    y_pred:估计器预测目标值
    target_names:目标类别名称
    return:每个类别精确率与召回率

    将其运用到之前的20篇文章分类中进行测试:

    from sklearn.datasets import fetch_20newsgroups
    from sklearn.model_selection import train_test_split
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.metrics import classification_report
    
    
    def naive_bytes():
        """
        朴素贝叶斯进行文本分类
        :return: None
        """
        #获取数据
        news = fetch_20newsgroups(subset='all')
    
        #进行数据分割
        x_train,x_test,y_train,y_test = train_test_split(news.data,news.target,test_size=0.25)
    
        #对数据集进行特征抽取
        tf = TfidfVectorizer()
    
        #以训练集当中的词的列表进行每篇文章重要性统计
        x_train = tf.fit_transform(x_train)
        print(tf.get_feature_names())  #特征值
        x_test = tf.transform(x_test)
    
        #进行朴素贝叶斯算法预测
        mlt = MultinomialNB(alpha=1.0)
        mlt.fit(x_train,y_train)
        y_predict = mlt.predict(x_test)
        print("文章的预测类别为:",y_predict)
    
        #准确率
        print("准确率为:",mlt.score(x_test,y_test))
    
        #每一个类别的精确率和召回率
        result = classification_report(y_test,y_predict,target_names=news.target_names)
        print(result)
    
        return None
    
    if __name__ == '__main__':
        naive_bytes()

    二、交叉验证与网格搜索

    (一)交叉验证 

    1、什么是交叉验证

    数据集我们一般分为训练集和测试集,交叉验证针对的就是训练集,将训练集的数据再次重新划分:

    • 训练集进行n等分
    • 其中一份作为验证集合
    • 其余各份都是作为训练集

    经过n组测试,每次更换不同的验证集,这样就会得到n组模型的结果,取所有模型结果的平均值作为最终的结果,该方法又被称为n折交叉验证,比如下面的四折交叉验证:

       将上述4个模型的准确率进行求平均值,从而得到最终的结果,其最明显的应用就是在k-近邻算法中k的取值默认为5,假如我们将k=1,6,8,11,每一个k值下进行4折验证取平均值,然后看哪一个k值下的效果更好就取哪一个k值,这样就更好。

    2、为什么进行交叉验证

    为了让被评估的模型更加精确、可信。

    (二)网格搜索(超参数搜索)

    1、什么是网格搜索

      网格搜索一般是和上述的交叉验证进行配合使用, 那么什么是网格搜索呢?通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。

      就像上面预设的k=1,6,8,11,然后对其进行4折验证取平均值,然后看每一个预设k值得效果,取最好的即可。

      如果出现超参数有多个,那么就会进行组合,比如x=[1,2,3],y=[1,2,3],z=[4,5,6],这样对27种组合,每一种进行10折验证取平均值,得到27种结果,取最好的组合结果即可。

    2、网格搜索API

    • sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)

    其中:
      estimator:估计器对象(knn对象)
      param_grid:估计器参数(dict){“n_neighbors”:[1,3,5]}
      cv:指定几折交叉验证
      fit:输入训练数据
      score:准确率
    当然,还有一些输出结果的参数可供查看:
      best_score_:在交叉验证中测试的最好结果
      best_estimator_:最好的参数模型(比如knn中k的值代表的模型)
      cv_results_:每次交叉验证后的测试集准确率结果和训练集准确率结果

    • 实例说明
    import pandas as pd
    from sklearn.model_selection import train_test_split,GridSearchCV
    from sklearn.preprocessing import StandardScaler
    from sklearn.neighbors import KNeighborsClassifier
    import numpy as np
    
    def knn():
        """
        近邻算法:预测入住位置
        :return:
        """
        # 读取训练集数据
        df = pd.read_csv("./data/k-近邻算法数据/train.csv")
    
        # 读取前5行数据
        # print(df.head(5))
    
        """
        进行数据处理:
        1、缩小数据范围
        2、时间戳处理
        3、删除少于指定位置的签到人数位置删除
        """
        # 1、缩小数据范围
        df = df.query("x>0 & x<1.2 & y>0 & y<1.23")
        #
        # 2、时间戳处理
        df_time = pd.to_datetime(df["time"], unit='s')
        # print(df_time)
        """
        132        1970-01-08 19:14:45
        142        1970-01-02 00:41:22
        ...
        """
        #   #把日期格式处理成字典格式
        time_dict = pd.DatetimeIndex(df_time)
        # print(time_dict)
        """
        DatetimeIndex(
        ['1970-01-08 19:14:45', '1970-01-02 00:41:22',
       '1970-01-07 06:32:23', '1970-01-02 18:59:24',...], 
        dtype='datetime64[ns]', name='time', length=417477, freq=None
        )
        """
        # 构造时间特征
        df["day"] = time_dict.day
        df["hour"] = time_dict.hour
        df["weekday"] = time_dict.weekday
    
        # 删除时间戳这一列
        df = df.drop(['time'], axis=1)
        # print(df)
        """
                        row_id       x       y   ...     day  hour  weekday
        132            132  0.1902  0.1510   ...       8    19        3
        142            142  0.1318  0.4975   ...       2     0        4
        149            149  0.0179  0.2321   ...       7     6        2
        ...
        """
        # 3、删除少于指定位置的签到人数位置删除
        place_count = df.groupby('place_id').count()
        # print(place_count)
        """
                        row_id    x    y  accuracy  day  hour  weekday
        place_id                                                  
        1000213704      22   22   22        22   22    22       22
        1000842315       6    6    6         6    6     6        6
        1002574526       1    1    1         1    1     1        1
        1002803051       1    1    1         1    1     1        1
        ...
        """
        # 过滤出少于指定位置的签到人数位置,通过reset_index将索引转成列进行操作
        pf = place_count[place_count["row_id"] > 3].reset_index()
        # print(pf)
        """
                   place_id  row_id    x    y  accuracy  day  hour  weekday
        0     1000213704      22   22   22        22   22    22       22
        1     1000842315       6    6    6         6    6     6        6
        ...
        """
        # 根据指定place_id进行过滤
        df = df[df['place_id'].isin(pf['place_id'])]
        # print(df)
    
        """
        获取特征值、目标值
        """
        # 1、获取特征值
        x = df.drop(['place_id'], axis=1)
    
        # 2、获取目标值
        y = df['place_id']
    
        """
        进行数据集分割,分成训练集和测试集
        """
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
        """
        特征工程:标准化
        """
        sd = StandardScaler()
        # 对训练集进行标准化
        x_train = sd.fit_transform(x_train.astype(np.float64))
        # 对测试集进行标准化
        x_test = sd.transform(x_test.astype(np.float64))
        """
        进行KNN算法预测
        fit predict score
        """
        knn = KNeighborsClassifier() #相当于网格搜索参数中的estimator
        #构造预设参数
        param = {"n_neighbors":[2,8,10,12]}
        #进行网格搜索
        gc = GridSearchCV(knn,param_grid=param,cv=2)
        gc.fit(x_train,y_train) #训练模型
        #输出结果
        print('测试集上的准确率:',gc.score(x_test,y_test))
        print('交叉验证当中最好的结果:',gc.best_score_) #比较每个超参数最后的2折平均值,选择最大的那个
        print('选择最好的模型是:',gc.best_estimator_) #选择出最合适的n_neighbors
        print('每个超参数每次交叉验证的结果:',gc.cv_results_) #每个超参数有两个结果
    
    
    if __name__ == '__main__':
        knn()
  • 相关阅读:
    hdu4846 最大子正方形(dp)
    hdu4847 水题
    hdu4847 水题
    hdu4848 DFS 暴搜+ 强剪枝
    hdu4848 DFS 暴搜+ 强剪枝
    洛谷 P4999 烦人的数学作业(数位DP)
    洛谷 P4317 花神的数论题(数位DP || 快速幂)
    洛谷 P2657 [SCOI2009]windy数(数位DP)
    洛谷 P2602 [ZJOI2010]数字计数(数位DP)
    HDU 2089 不要62(数位DP)
  • 原文地址:https://www.cnblogs.com/shenjianping/p/13138492.html
Copyright © 2011-2022 走看看