python 數據分析實踐--(1)收入預測分析


收入分析預測

    • 說明:

      1. 預覽數據集,明確分析目的

      1. 導入數據集,預處理數據

      1. 探索數據背后的特征

      • 3.1 數值型變量統計描述

      • 3.2 離散型變量統計描述

      • 3 .3 了解數據的分布形狀

      1. 數據建模

      • 4.1 對離散變量重編碼

      • 4.2 拆分數據集

      • 4.3 搭建模型

      • 4.4 模型網格搜索法,探尋模型最佳參數

      • 4.5 模型預測與評估

        • 4.5.1 K近鄰模型在測試集上的預測

        • 4.5.2 K近鄰網格搜索模型在測試集上的預測

        • 4.5.3 GBDT模型在測試集上的預測

        • 4.5.4 網格搜索的GBDT模型在測試集上的預測

    • 5.實驗總結

說明:

本文用途只做學習記錄:

首先看一下劉老師介紹的數據分析和數據挖掘的區別: 在這里插入圖片描述

1. 預覽數據集,明確分析目的

通過Excel工具打開income文件,可發現該數據集一共有 32 561 條樣本數據,共有15個數據變量,其中9個離散型變量,6個數值型變量。數據項主要包括:年齡,工作類型,受教育程度,收入等,具體可見下面兩個圖: 在這里插入圖片描述 在這里插入圖片描述 拿到上面的數據集,我們觀察這些數據都有什么用,想一想這張數據表中income比較特殊,有分析價值,其他變量的不同會對income產生一定的影響。

實驗目的:因此,基於上面的數據集,需要預測居民的年收入是否會超過5萬美元?

2. 導入數據集,預處理數據

在jupyter notebook中導入相應包,讀取數據,進行預處理。在上述數據集中,有許多變量都是離散型的,如受教育程度、婚姻狀態、職業、性別等。通常數據拿到手后,都需要對其進行清洗,例如檢查數據中是否存在重復觀測、缺失值、異常值等,而且,如果建模的話,還需要對字符型的離散變量做相應的重編碼。

    import numpy as np
  import pandas as pd
  import seaborn as sns
   
  # 下載的數據集存放的路徑:
  income = pd.read_excel(r'E:\Data\1\income.xlsx')
   
  # 查看數據集是否存在缺失
  income.apply(lambda x:np.sum(x.isnull()))
    age                  0
  workclass         1836
  fnlwgt               0
  education           0
  education-num       0
  marital-status       0
  occupation       1843
  relationship         0
  race                 0
  sex                 0
  capital-gain         0
  capital-loss         0
  hours-per-week       0
  native-country     583
  income               0
  dtype: int64

從上面的結果可以發現,居民的收入數據集中有3個變量存在數值缺失,分別是居民的工作類型(離散型)缺1836、職業(離散型)缺1843和國籍(離散型)缺583。缺失值的存在一般都會影響分析或建模的結果,所以需要對缺失數值做相應的處理。 缺失值的處理一般采用三種方法:

  • 1.刪除法,缺失的數據較少時適用;

  • 2.替換法,用常數替換缺失變量,離散變量用 眾數 ,數值變量用 均值或中位數

  • 3.插補法:用未缺失的預測該缺失變量。

根據上述方法,三個缺失變量都為離散型,可用眾數替換。pandas中fillna()方法,能夠使用指定的方法填充NA/NaN值。 函數形式: fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None, **kwargs) 參數:

  • value:用於填充的空值的值。(該處為字典)

  • method: {‘backfill’, ‘bfill’, ‘pad’, ‘ffill’, None}, default None。定義了填充空值的方法, pad/ffill表示用前面行/列的值,填充當前行/列的空值, backfill / bfill表示用后面行/列的值,填充當前行/列的空值。

  • axis:軸。0或’index’,表示按行刪除;1或’columns’,表示按列刪除。

  • inplace:是否原地替換。布爾值,默認為False。如果為True,則在原DataFrame上進行操作,返回值為None。

  • limit:int,default None。如果method被指定,對於連續的空值,這段連續區域,最多填充前 limit 個空值(如果存在多段連續區域,每段最多填充前 limit 個空值)。如果method未被指定, 在該axis下,最多填充前 limit 個空值(不論空值連續區間是否間斷)

  • downcast:dict, default is None,字典中的項,為類型向下轉換規則。或者為字符串“infer”,此時會在合適的等價類型之間進行向下轉換,比如float64 to int64 if possible。

    # 缺失值處理,采用眾數替換法(mode()方法取眾數)
  income.fillna(value={'workclass':income['workclass'].mode()[0],
                        'ouccupation':income['occupation'].mode()[0],
                        'native-country':income['native-country'].mode()[0]},
                inplace = True)

3. 探索數據背后的特征

對缺失值采用了替換處理的方法,接下來對居民收入數據集做簡單的探索性分析,目的是了解數據背后的特征如數據的集中趨勢、離散趨勢、數據形狀和變量間的關系等。首先,需要知道每個變量的基本統計值,如均值、中位數、眾數等,只有了解了所需處理的數據特征,才能做到“心中有數”。

3.1 數值型變量統計描述

    # 3.1 數值型變量統計描述
  income.describe()

| age | fnlwgt | education-num | capital-gain | capital-loss | hours-per-week ---|---|---|---|---|---|--- count | 32561.000000 | 3.256100e+04 | 32561.000000 | 32561.000000 | 32561.000000 | 32561.000000 mean | 38.581647 | 1.897784e+05 | 10.080679 | 1077.648844 | 87.303830 | 40.437456 std | 13.640433 | 1.055500e+05 | 2.572720 | 7385.292085 | 402.960219 | 12.347429 min | 17.000000 | 1.228500e+04 | 1.000000 | 0.000000 | 0.000000 | 1.000000 25% | 28.000000 | 1.178270e+05 | 9.000000 | 0.000000 | 0.000000 | 40.000000 50% | 37.000000 | 1.783560e+05 | 10.000000 | 0.000000 | 0.000000 | 40.000000 75% | 48.000000 | 2.370510e+05 | 12.000000 | 0.000000 | 0.000000 | 45.000000 max | 90.000000 | 1.484705e+06 | 16.000000 | 99999.000000 | 4356.000000 | 99.000000

上面的結果描述了有關數值型變量的簡單統計值,包括非缺失觀測的個數(count)、平均值(mean)、標准差(std)、最小值(min)、下四分位數(25%)、中位數(50%)、上四分位數(75%)和最大值(max)。

3.2 離散型變量統計描述

    # 2. 離散型變量統計描述
  income.describe(include= ['object'])

| workclass | education | marital-status | occupation | relationship

  race sex native-country income
count 32561 32561 32561 30718
32561 32561      
unique 8 16 7 14
top Private HS-grad Married-civ-spouse Prof-specialty
Husband White Male United-States <=50K
freq 24532 10501 14976 4140
29753 24720      

上面為離散變量的統計值,包含每個變量非缺失觀測的數量(count)、不同離散值的個數(unique)、出現頻次最高的離散值(top)和最高頻次數(freq)。以受教育水平變量為例,一共有16種不同的教育水平;3萬多居民中,高中畢業的學歷是出現最多的;並且一共有10 501名。

3 .3 了解數據的分布形狀

為了了解數據的分布形狀(如偏度、峰度等)可以通過 可視化 的方法進行展現。

    #以被調查居民的年齡和每周工作小時數為例,繪制各自的分布形狀圖:
  import matplotlib.pyplot as plt
   
  # 設置繪圖風格
  plt.style.use('ggplot')
   
  # 設置多圖形的組合
  fig, axes = plt.subplots(3,1)
   
  # 繪制不同收入水平下的年齡核密度圖,觀察連續型變量的分布情況
  income.age[income.income == ' <=50K'].plot(kind = 'kde', label = '<=50K', ax = axes[0], legend = True, linestyle = '-')
  income.age[income.income == ' >50K'].plot(kind = 'kde', label = '>50K', ax = axes[0], legend = True, linestyle = '--')
   
  # 繪制不同收入水平下的周工作小時核密度圖
  income['hours-per-week'][income.income == ' <=50K'].plot(kind = 'kde', label = '<=50K', ax = axes[1], legend = True, linestyle = '-')
  income['hours-per-week'][income.income == ' >50K'].plot(kind = 'kde', label = '>50K', ax = axes[1], legend = True, linestyle = '--')
   
   
  # 繪制不同收入水平下的受教育時長核密度圖
  income['education-num'][income.income == ' <=50K'].plot(kind = 'kde', label = '<=50K', ax = axes[2], legend = True, linestyle = '-')
  income['education-num'][income.income == ' >50K'].plot(kind = 'kde', label = '>50K', ax = axes[2], legend = True, linestyle = '--')

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- kJH5APNo-1585580374849)(output_25_1.png)]](https://img- blog.csdnimg.cn/20200330230555736.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70) 第一幅圖展現的是,在不同收入水平下,年齡的核密度分布圖,對於年收入超過5萬美元的居民來說,他們的年齡幾乎呈現正態分布,而收入低於5萬美元的居民,年齡呈現右偏特征,即年齡偏大的居民人數要比年齡偏小的人數多。

第二幅圖展現了不同收入水平下,周工作小時數的核密度圖,很明顯,兩者的分布趨勢非常相似,並且出現局部峰值。

第三幅圖展現了不同收入水平下,教育時長的核密度圖,很明顯,兩者的分布趨勢非常相似,並且也多次出現局部峰值。 針對離散型變量,對比居民的收入水平高低在性別、種族狀態、家庭關系等方面的差異,進而可以發現這些離散變量是否影響收入水平:

    # 構造不同收入水平下各種族人數的數據
  race = pd.DataFrame(income.groupby(by = ['race','income']).aggregate(np.size).loc[:,'age'])
  #print(race)
   
  # 重設行索引
  race = race.reset_index()
  #print(race)
   
  # 變量重命名
  race.rename(columns={'age':'counts'}, inplace=True)
  #print(race)
   
  # 排序
  race.sort_values(by = ['race','counts'], ascending=False, inplace=True)
  #print(race)
   
  # 構造不同收入水平下各家庭關系人數的數據
  relationship = pd.DataFrame(income.groupby(by = ['relationship','income']).aggregate(np.size).loc[:,'age'])
  relationship = relationship.reset_index()
  relationship.rename(columns={'age':'counts'}, inplace=True)
  relationship.sort_values(by = ['relationship','counts'], ascending=False, inplace=True)
   
  # 構造不同收入水平下各男女人數的數據
  sex = pd.DataFrame(income.groupby(by = ['sex','income']).aggregate(np.size).loc[:,'age'])
  sex = sex.reset_index()
  sex.rename(columns={'age':'counts'}, inplace=True)
  sex.sort_values(by = ['sex','counts'], ascending=False, inplace=True)
   
  # 設置圖框比例,並繪圖
  plt.figure(figsize=(9,5))
  sns.barplot(x="race", y="counts", hue = 'income', data=race)
  plt.show()
   
  plt.figure(figsize=(9,5))
  sns.barplot(x="relationship", y="counts", hue = 'income', data=relationship)
  plt.show()
   
  plt.figure(figsize=(9,5))
  sns.barplot(x="sex", y="counts", hue = 'income', data=sex)
  plt.show()

在這里插入圖片描述 ![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- mOnATOXC-1585580374849)(output_28_1.png)]](https://img- blog.csdnimg.cn/20200330230802422.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70) ![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- Cv6i9X3x-1585580374850)(output_28_2.png)]](https://img- blog.csdnimg.cn/20200330230851358.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70)

圖一、反映的是相同的種族下,居民年收入水平高低的人數差異;圖二、反映的是相同的家庭成員關系下,居民年收入水平高低的人數差異。但無論怎么比較,都發現一個規律,即在某一個相同的水平下(如白種人或未結婚人群中),年收入低於5萬美元的人數都要比年收入高於5萬美元的人數多,這個應該是抽樣導致的差異(數據集中年收入低於5萬和高於5萬的居民比例大致在75%:25%)。圖三、反映的是相同的性別下,居民收入水平高低人數的差異;其中,女性收入低於5萬美元的人數比高於5萬美元人數的差異比男性更嚴重,比例大致為90%:10%, 男性大致為70%:30%。

4. 數據建模

4.1 對離散變量重編碼

由於數據集中有很多離散型變量,這些變量的值為字符串,不利於建模,因此,需要先對這些變量進行重新編碼。編碼的方法有很多種:

  • 將字符型的值轉換為整數型的值

  • 啞變量處理(0-1變量)

  • One-Hot熱編碼(類似於啞變量)

在本案例中,將采用“字符轉數值”的方法對離散型變量進行重編碼

    # 離散型變量的重編碼
  for feature in income.columns:
      if income[feature].dtype == 'object':
          income[feature] = pd.Categorical(income[feature]).codes
  income.head(10)

| age | workclass | fnlwgt | education | education-num | marital- status | occupation | relationship | race | sex | capital-gain |

capital-loss hours-per-week native-country income
0 39 6 77516
  0 40 38
1 50 5 83311
0 13 38 0
2 38 3 215646
0 40 38 0
3 53 3 234721
0 40 38 0
4 28 3 338409
0 40 4 0
5 37 3 284582
0 40 38 0
6 49 3 160187
0 16 22 0
7 52 5 209642
0 45 38 1
8 31 3 45781
  0 50 38
9 42 3 159449
  0 40 38

對字符型離散變量的重編碼效果,所有的字符型變量都變成了整數型變量,接下來就基於這個處理好的數據集對收入水平income進行預測。 在原本的居民收入數據集中,關於受教育程度的有兩個變量,一個是education(教育水平),另一個是education- num(受教育時長),而且這兩個變量的值都是一一對應的,只不過一個是字符型,另一個是對應的數值型,如果將這兩個變量都包含在模型中的話,就會產生信息的冗余;fnlwgt變量代表的是一種序號,其對收入水平的高低並沒有實際意義。故為了避免冗余信息和無意義變量對模型的影響,考慮將education變量和fnlwgt變量從數據集中刪除。

    income.drop(['education','fnlwgt'], axis=1, inplace=True)
    income.head(10)

| age | workclass | education-num | marital-status | occupation | relationship | race | sex | capital-gain | capital-loss | hours-per-

week native-country income
0 39 6
38 0  
1 50 5
  0  
2 38 3
  0  
3 53 3
  0  
4 28 3
  0  
5 37 3
  0  
6 49 3
  0  
7 52 5
  1  
8 31 3
38 1  
9 42 3
38 1  

上面表格呈現的就是經處理“干凈”的數據集,所要預測的變量就是income,該變量是二元變量,對其預測的實質就是對年收入水平的 分類 (一個新樣本進來,通過分類模型,可以將該樣本分為哪一種收入水平)。

關於 分類模型 有很多種:

  • Logistic模型

  • 決策樹

  • K近鄰

  • 朴素貝葉斯模型

  • 支持向量機

  • 隨機森林

  • 梯度提升樹GBDT模型 等。

本案例將對比使用 K近鄰GBDT 兩種分類器,因為通常情況下,都會選用多個模型作為備選,通過對比才能得知哪種模型可以更好地擬合數據。

4.2 拆分數據集

接下來就進一步說明如何針對 分類 問題,從零開始完成建模的步驟。基於上面的“干凈”數據集, 需要將其拆分為兩個部分,一部分用於分類器模型的構建,另一部分用於分類器模型的評估,這樣做的目的是避免分類器模型過擬合或欠擬合。 如果模型在訓練集上表現很好,而在測試集中表現很差,則說明分類器模型屬於過擬合狀態;如果模型在訓練過程中都不能很好地擬合數據,那說明模型屬於欠擬合狀態。通常情況下,會把訓練集和測試集的比例分配為** 75%和25% ** 。

    # 導入sklearn包中的函數
    from sklearn.model_selection import train_test_split
    
    # 拆分數據
    X_train, X_test, y_train, y_test = train_test_split(income.loc[:,'age':'native-country'],
                                                       income['income'],train_size = 0.75,test_size=0.25, random_state = 1234)
    # print(X_train)
    # print(y_train)
    print("訓練數據集中共有 %d 條觀測"  %X_train.shape[0])
    print("測試數據集中共有 %d 條測試" %X_test.shape[0])
    訓練數據集中共有 24420 條觀測
    測試數據集中共有 8141 條測試

上面的結果,運用隨機抽樣的方法,將數據集拆分為兩部分,其中訓練數據集包含24 420條樣本,測試數據集包含8 141條樣本,下面將運用拆分好的訓練數據集開始構建K近鄰和GBDT兩種分類器。

4.3 搭建模型

    # 導入K近鄰模型的類
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.ensemble import GradientBoostingClassifier 
    
    # 構件K近鄰模型
    kn = KNeighborsClassifier()
    kn.fit(X_train,y_train)
    print(kn)
    
    #構件GBDT模型
    gbdt = GradientBoostingClassifier()
    gbdt.fit(X_train, y_train)
    print(gbdt)
    KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
               metric_params=None, n_jobs=1, n_neighbors=5, p=2,
               weights='uniform')
    GradientBoostingClassifier(criterion='friedman_mse', init=None,
                  learning_rate=0.1, loss='deviance', max_depth=3,
                  max_features=None, max_leaf_nodes=None,
                  min_impurity_decrease=0.0, min_impurity_split=None,
                  min_samples_leaf=1, min_samples_split=2,
                  min_weight_fraction_leaf=0.0, n_estimators=100,
                  presort='auto', random_state=None, subsample=1.0, verbose=0,
                  warm_start=False)

首先,針對K近鄰模型,這里直接調用sklearn子模塊neighbors中的KNeighborsClassifier類,並且使用模型的默認參數,即讓K近鄰模型自動挑選最佳的搜尋近鄰算法(algorithm=‘auto’)、使用歐氏距離公式計算樣本間的距離(p=2)、指定未知分類樣本的近鄰個數為5(n_neighbors=5)而且所有近鄰樣本的權重都相等(weights=‘uniform’)。其次,針對GBDT模型,可以調用sklearn子模塊ensemble中的GradientBoostingClassifier類,同樣先嘗試使用該模型的默認參數,即讓模型的學習率(迭代步長)為0.1(learning_rate=0.1)、損失函數使用的是對數損失函數(loss=‘deviance’)、生成100棵基礎決策樹(n_estimators=100),並且每棵基礎決策樹的最大深度為3(max_depth=3),中間節點(非葉節點)的最小樣本量為2(min_samples_split=2),葉節點的最小樣本量為1(min_samples_leaf=1),每一棵樹的訓練都不會基於上一棵樹的結果(warm_start=False)。

4.4 模型網格搜索法,探尋模型最佳參數

    # K 近鄰模型網格搜索法
    # 導入網格搜索函數
    from sklearn.grid_search import GridSearchCV
    # 選擇不同的參數
    k_options = list(range(1,12))
    parameters = {'n_neighbors':k_options}
    # 搜索不同的K值
    grid_kn = GridSearchCV(estimator= KNeighborsClassifier(), param_grid=parameters, cv=10, scoring='accuracy')
    grid_kn.fit(X_train, y_train)
    # 結果輸出
    grid_kn.grid_scores_, grid_kn.best_params_, grid_kn.best_score_
    ([mean: 0.81507, std: 0.00711, params: {'n_neighbors': 1},
      mean: 0.83882, std: 0.00696, params: {'n_neighbors': 2},
      mean: 0.83722, std: 0.00843, params: {'n_neighbors': 3},
      mean: 0.84586, std: 0.01039, params: {'n_neighbors': 4},
      mean: 0.84222, std: 0.00916, params: {'n_neighbors': 5},
      mean: 0.84713, std: 0.00900, params: {'n_neighbors': 6},
      mean: 0.84316, std: 0.00719, params: {'n_neighbors': 7},
      mean: 0.84525, std: 0.00629, params: {'n_neighbors': 8},
      mean: 0.84394, std: 0.00678, params: {'n_neighbors': 9},
      mean: 0.84570, std: 0.00534, params: {'n_neighbors': 10},
      mean: 0.84464, std: 0.00444, params: {'n_neighbors': 11}],
     {'n_neighbors': 6},
     0.8471334971334972)

GridSearchCV函數中的幾個參數含義,estimator參數接受一個指定的模型,這里為K近鄰模型的類;param_grid用來指定模型需要搜索的參數列表對象,這里是K近鄰模型中n_neighbors參數的11種可能值;cv是指網格搜索需要經過10重交叉驗證;scoring指定模型評估的度量值,這里選用的是模型預測的准確率。通過網格搜索的計算,得到三部分的結果,第一部分包含了11種K值下的平均准確率(因為做了10重交叉驗證);第二部分選擇出了最佳的K值,K值為6;第三部分是當K值為6時模型的最佳平均准確率,且准確率為84.78%。

    # GBDT 模型的網格搜索法
    # 選擇不同的參數
    from sklearn.grid_search import GridSearchCV
    learning_rate_options = [0.01, 0.05, 0.1]
    max_depth_options = [3,5,7,9]
    n_estimators_options = [100, 300, 500]
    
    parameters = {'learning_rate':learning_rate_options,
                 'max_depth':max_depth_options,
                 'n_estimators':n_estimators_options}
    
    grid_gbdt = GridSearchCV(estimator= GradientBoostingClassifier(),param_grid=parameters,cv=10,scoring='accuracy')
    grid_gbdt.fit(X_train, y_train)
    
    # 結果輸出
    grid_gbdt.grid_scores_,grid_gbdt.best_params_, grid_gbdt.best_score_
    ([mean: 0.84267, std: 0.00727, params: {'learning_rate': 0.01, 'max_depth': 3, 'n_estimators': 100},
      mean: 0.85360, std: 0.00837, params: {'learning_rate': 0.01, 'max_depth': 3, 'n_estimators': 300},
      mean: 0.85930, std: 0.00746, params: {'learning_rate': 0.01, 'max_depth': 3, 'n_estimators': 500},
      mean: 0.85213, std: 0.00821, params: {'learning_rate': 0.01, 'max_depth': 5, 'n_estimators': 100},
      mean: 0.86327, std: 0.00751, params: {'learning_rate': 0.01, 'max_depth': 5, 'n_estimators': 300},
      mean: 0.86929, std: 0.00767, params: {'learning_rate': 0.01, 'max_depth': 5, 'n_estimators': 500},
      mean: 0.85459, std: 0.00767, params: {'learning_rate': 0.01, 'max_depth': 7, 'n_estimators': 100},
      mean: 0.87076, std: 0.00920, params: {'learning_rate': 0.01, 'max_depth': 7, 'n_estimators': 300},
      mean: 0.87342, std: 0.00983, params: {'learning_rate': 0.01, 'max_depth': 7, 'n_estimators': 500},
      mean: 0.85438, std: 0.00801, params: {'learning_rate': 0.01, 'max_depth': 9, 'n_estimators': 100},
      mean: 0.86855, std: 0.00907, params: {'learning_rate': 0.01, 'max_depth': 9, 'n_estimators': 300},
      mean: 0.87248, std: 0.00974, params: {'learning_rate': 0.01, 'max_depth': 9, 'n_estimators': 500},
      mean: 0.85962, std: 0.00778, params: {'learning_rate': 0.05, 'max_depth': 3, 'n_estimators': 100},
      mean: 0.86974, std: 0.00689, params: {'learning_rate': 0.05, 'max_depth': 3, 'n_estimators': 300},
      mean: 0.87326, std: 0.00697, params: {'learning_rate': 0.05, 'max_depth': 3, 'n_estimators': 500},
      mean: 0.86978, std: 0.00790, params: {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 100},
      mean: 0.87543, std: 0.00897, params: {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 300},
      mean: 0.87445, std: 0.00962, params: {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 500},
      mean: 0.87338, std: 0.00927, params: {'learning_rate': 0.05, 'max_depth': 7, 'n_estimators': 100},
      mean: 0.87391, std: 0.00964, params: {'learning_rate': 0.05, 'max_depth': 7, 'n_estimators': 300},
      mean: 0.87072, std: 0.01012, params: {'learning_rate': 0.05, 'max_depth': 7, 'n_estimators': 500},
      mean: 0.87211, std: 0.00989, params: {'learning_rate': 0.05, 'max_depth': 9, 'n_estimators': 100},
      mean: 0.86851, std: 0.01048, params: {'learning_rate': 0.05, 'max_depth': 9, 'n_estimators': 300},
      mean: 0.86229, std: 0.00857, params: {'learning_rate': 0.05, 'max_depth': 9, 'n_estimators': 500},
      mean: 0.86626, std: 0.00660, params: {'learning_rate': 0.1, 'max_depth': 3, 'n_estimators': 100},
      mean: 0.87355, std: 0.00802, params: {'learning_rate': 0.1, 'max_depth': 3, 'n_estimators': 300},
      mean: 0.87449, std: 0.00842, params: {'learning_rate': 0.1, 'max_depth': 3, 'n_estimators': 500},
      mean: 0.87383, std: 0.00878, params: {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 100},
      mean: 0.87310, std: 0.01001, params: {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 300},
      mean: 0.87236, std: 0.00939, params: {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 500},
      mean: 0.87502, std: 0.01037, params: {'learning_rate': 0.1, 'max_depth': 7, 'n_estimators': 100},
      mean: 0.86953, std: 0.00873, params: {'learning_rate': 0.1, 'max_depth': 7, 'n_estimators': 300},
      mean: 0.86192, std: 0.00823, params: {'learning_rate': 0.1, 'max_depth': 7, 'n_estimators': 500},
      mean: 0.87154, std: 0.01075, params: {'learning_rate': 0.1, 'max_depth': 9, 'n_estimators': 100},
      mean: 0.85995, std: 0.00848, params: {'learning_rate': 0.1, 'max_depth': 9, 'n_estimators': 300},
      mean: 0.85328, std: 0.00828, params: {'learning_rate': 0.1, 'max_depth': 9, 'n_estimators': 500}],
     {'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 300},
     0.8754299754299755)

4.5 模型預測與評估

在模型構建好后,下一步做的就是使用得到的分類器對測試數據集進行預測,進而驗證模型在樣本外的表現能力。通常,驗證模型好壞的方法有多種。

  • 對於預測的連續變量來說,常用的衡量指標有均方誤差(MSE)和均方根誤差(RMSE);

  • 對於預測的分類變量來說,常用的衡量指標有混淆矩陣中的准確率、ROC曲線下的面積AUC、K-S值等。

接下來,依次對上文中構建的四種模型(K近鄰、K近鄰網格搜索法、GDBT模型、GDBT網格搜索法模型)進行預測和評估。

4.5.1 K近鄰模型在測試集上的預測

    kn_pred = kn.predict(X_test)
    print(pd.crosstab(kn_pred, y_test))
    
    # 模型得分
    print("模型在訓練集上的准確率為%f" % kn.score(X_train, y_train))
    print("模型在測試集上的准確率為%f" % kn.score(X_test, y_test))
    income     0     1
    row_0             
    0       5644   725
    1        582  1190
    模型在訓練集上的准確率為0.889844
    模型在測試集上的准確率為0.839455

第一部分是混淆矩陣,矩陣中的行是模型的預測值,矩陣中的列是測試集的實際值,主對角線就是模型預測正確的數量(income <=50K有 5644 人 和 income >50K 有 1190人),582和725就是模型預測錯誤的數量。經過計算,得到第二部分的結論,即模型在訓練集中的准確率為88.9%,但在測試集上的錯誤率超過16%(1-0.839),說明默認參數下的KNN模型可能存在過擬合的風險。 模型的准確率就是基於混淆矩陣計算的,但是該方法存在一定的弊端,即如果數據本身存在一定的不平衡時(正負樣本的比例差異較大),一定會導致准確率很高,但並不一定說明模型就是理想的。所以可以繪制ROC曲線,並計算曲線下的面積AUC值

    from sklearn import metrics
    
    # 計算ROC曲線的x軸 和 y軸數據
    fpr, tpr, _ = metrics.roc_curve(y_test, kn.predict_proba(X_test)[:,1])
    # 繪制ROC曲線
    plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
    # 添加陰影
    plt.stackplot(fpr, tpr, color='steelblue')
    # 繪制參考線
    plt.plot([0,1],[0,1],linestyle='dashed', color='black')
    # 添加文本
    plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
    plt.show()

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- RGSHpCE8-1585580374852)(output_58_0.png)]](https://img- blog.csdnimg.cn/20200330230940325.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70)

上圖繪制了模型的ROC曲線,經計算得知,該曲線下的面積AUC為0.864。使用AUC來評估模型的好壞,那應該希望AUC越大越好。一般而言,當AUC的值超過 0.8 時,基本上就可以認為模型比較合理。所以,基於默認參數的K近鄰模型在居民收入數據集上的表現還算理想。

4.5.2 K近鄰網格搜索模型在測試集上的預測

    from sklearn import metrics
    
    grid_kn_pred = grid_kn.predict(X_test)
    print(pd.crosstab(grid_kn_pred, y_test))
    
    # 模型得分
    print("模型在訓練集上的准確率為%f" % grid_kn.score(X_train, y_train))
    print("模型在測試集上的准確率為%f" % grid_kn.score(X_test, y_test))
    
    fpr, tpr, _ = metrics.roc_curve(y_test, grid_kn.predict_proba(X_test)[:,1])
    plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
    plt.stackplot(fpr, tpr, color='steelblue')
    plt.plot([0,1],[0,1],linestyle='dashed', color='black')
    plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
    plt.show()
    income     0     1
    row_0             
    0       5838   861
    1        388  1054
    模型在訓練集上的准確率為0.882924
    模型在測試集上的准確率為0.846579

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- cT0VhIYl-1585580374853)(output_61_1.png)]](https://img- blog.csdnimg.cn/20200330231008759.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70)

相比於默認參數的K近鄰模型來說,經過網格搜索后的模型在訓練數據集上的准確率下降了,但在測試數據集上的准確率提高了,這也是我們所期望的,說明優化后的模型在預測效果上更加優秀,並且兩者差異的縮小也能夠降低模型過擬合的可能。再來看看ROC曲線下的面積,網格搜索后的K近鄰模型所對應的AUC為0.87,相比於原先的KNN模型提高了一點。所以,從模型的穩定性來看,網格搜索后的K近鄰模型比原始的K近鄰模型更加優秀。

4.5.3 GBDT模型在測試集上的預測

    from sklearn import metrics
    
    gbdt_pred = gdbt.predict(X_test)
    print(pd.crosstab(gbdt_pred, y_test))
    
    # 模型得分
    print("模型在訓練集上的准確率為%f" % gbdt.score(X_train, y_train))
    print("模型在測試集上的准確率為%f" % gbdt.score(X_test, y_test))
    
    fpr, tpr, _ = metrics.roc_curve(y_test, gbdt.predict_proba(X_test)[:,1])
    plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
    plt.stackplot(fpr, tpr, color='steelblue')
    plt.plot([0,1],[0,1],linestyle='dashed', color='black')
    plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
    plt.show()
    income     0     1
    row_0             
    0       5869   780
    1        357  1135
    模型在訓練集上的准確率為0.869861
    模型在測試集上的准確率為0.860337

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- AipV9iKg-1585580374853)(output_64_1.png)]](https://img- blog.csdnimg.cn/20200330231055405.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70)

集成算法GBDT在測試集上的表現明顯要比K近鄰算法優秀,這就是基於多棵決策樹進行投票的優點。該模型在訓練集和測試集上的表現都非常好,准確率均超過85%,而且AUC值也是前面兩種模型中最高的,達到了0.914。

4.5.4 網格搜索的GBDT模型在測試集上的預測

    from sklearn import metrics
    
    grid_gbdt_pred = grid_gbdt.predict(X_test)
    print(pd.crosstab(grid_gbdt_pred , y_test))
    
    # 模型得分
    print("模型在訓練集上的准確率為%f" % grid_gbdt.score(X_train, y_train))
    print("模型在測試集上的准確率為%f" % grid_gbdt.score(X_test, y_test))
    
    fpr, tpr, _ = metrics.roc_curve(y_test, grid_gbdt.predict_proba(X_test)[:,1])
    plt.plot(fpr, tpr, linestyle = 'solid', color ='red')
    plt.stackplot(fpr, tpr, color='steelblue')
    plt.plot([0,1],[0,1],linestyle='dashed', color='black')
    plt.text(0.6, 0.4, 'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict=dict(size =16))
    plt.show()
    income     0     1
    row_0             
    0       5835   669
    1        391  1246
    模型在訓練集上的准確率為0.889271
    模型在測試集上的准確率為0.869795

![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img- yFeUmkP8-1585580374853)(output_67_1.png)]](https://img- blog.csdnimg.cn/20200330231121761.png?x-oss- process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjQ5Mzg4,size_16,color_FFFFFF,t_70)

基於網格搜索后的GBDT模型的表現,從准確率來看,是4個模型中表現最佳的,該模型在訓練集上的准確率接近89%,同時,在測試集上的准確率也超過86%;從繪制的ROC曲線來看,AUC的值也是最高的,超過0.92。 不論是K近鄰模型,還是梯度提升樹GBDT模型,都可以通過網格搜索法找到各自的最佳模型參數,而且這些最佳參數的組合一般都會使模型比較優秀和健壯。所以,縱向比較默認參數的模型和網格搜索后的最佳參數模型,后者可能是比較好的選擇(盡管后者可能會花費更多的運行時間);橫向比較單一模型和集成模型,集成模型一般會比單一模型表現優秀。

5.實驗總結

本次收入預測問題屬於機器學習中的聚類問題,主要是通過數據集中的income變量將數據分成了兩個類別(年收入大於5W和年收入小於等於5W)。

本次實驗主要收獲是:

  • 1.熟悉了數據挖掘的重要流程(預覽數據集,明確分析的目的–導入數據並數據預處理–探索數據特征–清洗數據,構建模型–模型預測,模型評估)。

  • 2.初步了解了python sklearn中的兩個機器學習模型K近鄰和GBDT(梯度提升樹)和用網格搜索法改進模型參數優化模型。

  • 3.本章還學習到了pands,matplotlib。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM