5.1 實驗介紹
實驗背景
在這個項目中,您將使用1994年美國人口普查收集的數據,選用幾個監督學習算法以準確地建模被調查者的收入。然后,您將根據初步結果從中選擇出最佳的候選算法,并進一步優化該算法以最好地建模這些數據。你的目標是建立一個能夠準確地預測被調查者年收入是否超過50000美元的模型。這種類型的任務會出現在那些依賴于捐款而存在的非營利性組織。了解人群的收入情況可以幫助一個非營利性的機構更好地了解他們要多大的捐贈,或是否他們應該接觸這些人。雖然我們很難直接從公開的資源中推斷出一個人的一般收入階層,但是我們可以(也正是我們將要做的)從其他的一些公開的可獲得的資源中獲得一些特征從而推斷出該值。
這個項目的數據集來自UCI機器學習知識庫。這個數據集是由Ron Kohavi和Barry Becker在發表文章"Scale Up the Accuracy of Naive-Bayes Classifiers: A Decision-Tree Hybrid"之后捐贈的,你可以在Ron Kohavi提供的在線版本中找到這個文章。我們在這里探索的數據集相比于原有的數據集有一些小小的改變,比如說移除了特征 以及一些遺失的或者是格式不正確的記錄。'fnlwgt'
5.2 數據探索
導入數據
運行下面的代碼單元以載入需要的Python庫并導入人口普查數據。
注意數據集的最后一列 將是我們需要預測的列(表示被調查者的年收入會大于或者是最多50,000美元),人口普查數據中的每一列都將是關于被調查者的特征。'income'
#
引入此項目需要的庫
import numpy as np
import pandas as pd
from time import time
import matplotlib as mpl
#
使用
display()
顯示
DataFrame
from IPython.display import display
#
引入可視化代碼
visuals.py
import visuals as vs
#
讓可視化更方便更美觀
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
#
設置
matplotlib
中文顯示
mpl.rcParams['font.family'] = 'SimHei'
mpl.rcParams['axes.unicode_minus'] = False
#
載入人口普查數據
try:
??? data = pd.read_csv('census.csv')
??? print('
數據集有
{}
個樣本,每個樣本有
{}
個特征。
'.format(
??????? *data.shape))
??? display(data.head())
except:
??? print('
數據集無法加載,數據集丟失了嗎?
')
數據集有 45222 個樣本,每個樣本有 14 個特征
特征
- age: 一個整數,表示被調查者的年齡。
- workclass: 一個類別變量表示被調查者的通常勞動類型,允許的值有 {Private, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, Without-pay, Never-work}
- education_level: 一個類別變量表示教育程度,允許的值有 {Bachelors, Some-college, 11th, HS-grad, Prof-school, Assoc-acdm, Assoc-voc, 9th, 7th-8th, 12th, Masters, 1st-4th, 10th, Doctorate, 5th-6th, Preschool}
- education-num: 一個整數表示在學校學習了多少年
- 婚姻狀態: 一個類別變量,允許的值有 {已婚-公民-配偶, 離婚, 從未結婚, 分居, 寡居, 已婚-配偶-缺席, 已婚-AF-配偶}
- 職業: 一個類別變量表示一般的職業領域,允許的值有 {Tech-support, Craft-repair, Other-service, Sales, Exec-Managemential, Prof-specialty, Handlers-cleaners, Machine-op-inspct, Adm-clerical, Farming-fishing , Transport-move, Priv-house-serv, Protective-serv, Armed Forces}
- relationship: 一個類別變量表示家庭情況,允許的值有
- race: 一個類別變量表示人種,允許的值有 {White, Asia-Pac-Islander, Amer-Indian-Eskimo, Other, Black}
- :一個類別變量表示性別,允許的值有 { Female , Male }
- 資本收益:連續值。
- 資本損失: 連續值。
- 小時/周: 連續值。
- native-country: 一個類別變量表示原始的國家,允許的值有 {美國, 柬埔寨, 英國, 波多黎各, 加拿大, 德國, Outlying-US(關島-USVI-etc), 印度, 日本, 希臘, 南部, 中國, 古巴, 伊朗, 洪都拉斯, 菲律賓, 意大利, 波蘭, 牙買加, 越南, 墨西哥, 葡萄牙, 愛爾蘭, 法國, 多米尼加共和國, 老撾, 厄瓜多爾, 臺灣, 海地, 哥倫比亞, 匈牙利, 危地馬拉, 尼加拉瓜, 蘇格蘭, 泰國, 南斯拉夫, 薩爾瓦多, 特里納達德和多巴哥, 秘魯, 洪, 荷蘭荷蘭}
數據探索
首先我們對數據集進行一個粗略的探索,我們將看看每一個類別里會有多少被調查者?并且告訴我們這些里面多大比例是年收入大于 50,000 美元的。在下面的代碼單元中,你將需要計算以下量:
- 總的記錄數量,
'n_records'
- 年收入大于50,000美元的人數,.
'n_greater_50k'
- 年收入最多為50,000美元的人數?.
'n_at_most_50k'
- 年收入大于50,000美元的人所占的比例,?.
'greater_percent'
**提示:**您可能需要查看上面的生成的表,以了解 條目的格式是什么樣的。'income'
# TODO:
總記錄數
n_records = len(data.index)
# TODO:
個人收入超過
50,000
美元
n_greater_50k = len(data[data['income'] != '<=50K'].index)
# TODO:
個人最多可獲得
50,000
美元
n_at_most_50k = len(data[data['income'] == '<=50K'].index)
# TODO:
個人收入超過
5
萬美元的百分比
greater_percent = float(n_greater_50k) / n_records * 100
#
輸出結果
print('
總記錄數:
{}'.format(n_records))
print('
個人收入超過
50,000
美元:
{}'.format(n_greater_50k))
print('
個人最多可獲得
50,000
美元:
{}'.format(n_at_most_50k))
print('
個人收入超過
5
萬美元的百分比:
{:.2f}
%
'.format(greater_percent))
總記錄數:45222
個人收入超過50,000美元:11208
個人最多可獲得50,000美元:34014
個人收入超過5萬美元的百分比:24.78%
5.3 數據預處理
準備數據
在數據能夠被作為輸入提供給機器學習算法之前,它經常需要被清洗、格式化和重新組織 - 這通常被叫做預處理。
幸運的是,對于這個數據集,沒有我們必須處理的無效或丟失的條目,然而,由于某一些特征存在的特性我們必須進行一定的調整。這個預處理都可以極大地幫助我們提升幾乎所有的學習算法的結果和預測能力。
display(data.dtypes)
data.describe()
import seaborn as sns
display(data.select_dtypes(include=['int64','float64']).columns)
sns.pairplot(data.select_dtypes(include=['int64','float64']),diag_kind='kde')
轉換傾斜的連續特征
一個數據集有時可能包含至少一個靠近某個數字的特征,但有時也會有一些相對來說存在極大值或者極小值的不平凡分布的的特征。算法對這種分布的數據會十分敏感,并且如果這種數據沒有能夠很好地歸一化處理會使得算法表現不佳。
在人口普查數據集的兩個特征符合這個描述:?和?。'capital-gain''capital-loss'
運行下面的代碼單元以創建一個關于這兩個特征的條形圖。請注意當前的值的范圍和它們是如何分布的。
# 將數據切分成特征和對應的標簽
income_raw = data['income']
features_raw = data.drop('income', axis = 1)
# 可視化原來數據的傾斜的連續特征
vs.distribution(data)
# TODO: 用 MatplotLib 和 Seaborn 實現可視化
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(10,5))
plt.subplot(121)
sns.distplot(data['capital-gain'])
plt.subplot(122)
sns.distplot(data['capital-loss'])
對于高度傾斜分布的特征如和,常見的做法是對數據施加一個對數轉換,將數據轉換成對數,這樣非常大和非常小的值不會對學習算法產生負面的影響。并且使用對數變換顯著降低了由于異常值所造成的數據范圍異常。'capital-gain''capital-loss'
但是在應用這個變換時必須小心:因為0的對數是沒有定義的,所以我們必須先將數據處理成一個比0稍微大一點的數以成功完成對數轉換。
運行下面的代碼單元來執行數據的轉換和可視化結果。再次,注意值的范圍和它們是如何分布的。
# 對于傾斜的數據使用 np.log() 函數轉換
skewed = ['capital-gain', 'capital-loss']
features_log_transformed = pd.DataFrame(data = features_raw)
features_log_transformed[skewed] = features_raw[skewed].apply(lambda x: np.log(x + 1))
# 可視化對數轉換后的數據分布
vs.distribution(features_log_transformed, transformed=True)
plt.figure(figsize=(10,5))
plt.subplot(121)
sns.distplot(features_log_transformed['capital-gain'])
plt.subplot(122)
sns.distplot(features_log_transformed['capital-loss'])
歸一化數字特征
除了對于高度傾斜的特征施加轉換,對數值特征施加一些形式的縮放通常會是一個好的習慣。
在數據上面施加一個縮放并不會改變數據分布的形式(比如上面說的 或 );但是歸一化保證了每一個特征在使用監督學習器的時候能夠被平等的對待。'capital-gain''capital-loss'
注意一旦使用了縮放,觀察數據的原始形式不再具有它本來的意義了,就像下面的例子展示的。
運行下面的代碼單元來規一化每一個數字特征。我們將使用sklearn.preprocessing.MinMaxScaler來完成這個任務。
# 引入 sklearn.preprocessing 的 MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# 初始化一個 scaler,然后將其應用于特征
scaler = MinMaxScaler()
numerical = ['age', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week']
features_log_minmax_transform = pd.DataFrame(data = features_log_transformed)
features_log_minmax_transform[numerical] = scaler.fit_transform(features_log_transformed[numerical])
# 顯示應用了 scaler 的樣本示例
display(features_log_minmax_transform.head(n = 5))
for key in numerical:
??? sns.distplot(features_log_transformed[key])
??? plt.show()
數據預處理
從上面的數據探索中的表中,我們可以看到有幾個屬性的每一條記錄都是非數字的。通常情況下,學習算法期望輸入是數字的,這要求非數字的特征(稱為類別變量)被轉換。轉換類別變量的一種流行的方法是使用獨熱編碼 (One-Hot編碼)方案。
獨熱編碼為每一個非數字特征的每一個可能的類別創建一個"虛擬"變量。例如,假設 有三個可能的取值 , 或者 。我們將把這個特征編碼成 , 和 。someFeatureABCsomeFeature_AsomeFeature_BsomeFeature_C
特征 | 特征_A | 特征_B | 特征_C | ||
0 | B | 0 | 1 | 0 | |
1 | C | –>一熱編碼 --> | 0 | 0 | 1 |
2 | 一個 | 1 | 0 | 0 |
此外,對于非數字的特征,我們需要將非數字的標簽 轉換成數值以保證學習算法能夠正常工作。因為這個標簽只有兩種可能的類別("<=50K" 和 ">50K"),我們不必要使用獨熱編碼,可以直接將他們編碼分別成兩個類 和 ,在下面的代碼單元中你將實現以下功能:'income'01
- 使用pandas.get_dummies()對 數據進行獨熱編碼'features_raw'
- 將目標標簽 轉換成數字'income_raw'
-
- 將 "<=50K" 轉換成0
- 將 ">50K" 轉換成1
# TODO: 使用 pandas.get_dummies() 對 'features_log_minmax_transform' 數據進行獨熱編碼
feature_object = features_log_minmax_transform.select_dtypes(include=['object'])
features_log_minmax_transform[feature_object.columns] = feature_object.apply(
??? lambda x: x.str.strip())
features_final = pd.get_dummies(features_log_minmax_transform)
# TODO: 將 'income_raw' 編碼成為數字
income = (income_raw == '>50K').astype(np.uint8)
# 輸出經過獨熱編碼之后的特征數量
encoded = list(features_final.columns)
print('獨熱編碼之后共有 {} 個全部特征'.format(len(encoded)))
# 移除下面一行的注釋以觀察編碼后的特征名稱
print(encoded)
獨熱編碼之后共有 103 個
全部特征 ['age', 'education-num', 'capital-gain', 'capital-loss', '每周小時數', 'workclass_Federal-gov', 'workclass_Local-gov', 'workclass_Private', 'workclass_Self-emp-inc', 'workclass_Self workclass_State-nmp-inc', 'workclass_State-gov', 'workclass_Without-pay', 'education_level_10th', 'education_level_11th', 'education_level_12th', 'education_level_1st-4th', 'education_level_5th-6th', 'education_level_7th-8th', 'education_level_9th', 'education_level_Assoc-acdm', 'education_level_Assoc-voc', 'education_level_Bachelors', 'education_level_Doctorate', 'education_level_HS-grad', 'education_level_Masters', 'education_level_Preschool', 'education_level_Prof-school', 'education_level_Some-College', 'marital-status_Divorced', 'marital-status_Married-AF-配偶', '婚姻-status_Married-civ-配偶', '婚姻-status_Married-配偶-缺席', '婚姻-status_Never-已婚', '婚姻-status_Separated', '婚姻-status_喪偶"、"occupation_Adm文職人員"、"occupation_Armed部隊"、"occupation_Craft修理"、"occupation_Exec管理"、"occupation_Farming捕魚"、"occupation_Handlers清潔工"、"occupation_Machine行動者"、"occupation_Other服務"、"occupation_Priv服務"、"occupation_Prof專業"、"occupation_Protective服務"、"occupation_Sales"、"occupation_Tech支持"、"occupation_Transport移動"、"relationship_Husband"、"家庭relationship_Not"、"家庭relationship_Not"、""relationship _ Other 親戚、 relationship _ Own 兒童、 relationship _ Unmarried 、 relationship _ Wife 、 race _ Amer - 印度 - 愛斯基摩人、 race _ Asian - 太平洋島民、 race _ Black 、 race _ Other 、 race _ White 、 _ Female 、 _ Male 、本土 country _ Cambodia 、本土 country _ Canada 、本土 country _ China 、本土 country _ Columbia 、本土 country _ Cuba 、本土 country _ Dominican 共和國、本土 country _ Ecuador 、本土 country _ El 薩爾瓦多、本土 country _ England 、"本地-country_France"、"本地-country_Germany"、"本地-country_Greece"、"本地-country_Guatemala"、"本地-country_Haiti"、"本地-country_Holand-荷蘭"、"本地-country_Honduras"、"本地-country_Hong"、"本地-country_Hungary"、"本地-country_India"、"本地-country_Iran"、"本地-country_Ireland"、"本地-country_Italy"、"本地-country_Jamaica"、"本地-country_Japan"、"本地-country_Laos"、"本地-country_Mexico"、"本地-country_Nicaragua', "本地country_Outlying-美國(關島-USVI-等)"、"本地country_Peru"、"本地country_Philippines"、"本地country_Poland"、"本地country_Portugal"、"本地country_Puerto-Rico"、"本地country_Scotland"、"本地country_South"、"本地country_Taiwan"、"本地country_Thailand"、"本地country_Trinadad和多巴哥"、"本地country_United國"、"本地國家_越南"、"本地country_Yugoslavia"]
混洗和切分數據
現在所有的類別變量已被轉換成數值特征,而且所有的數值特征已被規一化。和我們一般情況下做的一樣,我們現在將數據(包括特征和它們的標簽)切分成訓練和測試集。其中 80% 的數據將用于訓練和 20% 的數據用于測試。
運行下面的代碼單元來完成切分。
# 引入 sklearn.model_selection 的 train_test_split
from sklearn.model_selection import train_test_split
# 將 'features' 和 'income' 數據切分成訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(
??? features_final,income,test_size=0.2,random_state=0)
# 顯示切分的結果
print('訓練集有 {} 個樣本。'.format(X_train.shape[0]))
print('測試集有 {} 個樣本。'.format(X_test.shape[0]))
訓練集有 36177 個樣本。
測試集有 9045 個樣本。
5.4 評價模型性能
在這一部分中,我們將嘗試四種不同的算法,并確定哪一個能夠最好地建模數據。
這里面的三個將是你選擇的監督學習器,而第四種算法被稱為一個樸素的預測器。
評價方法和樸素的預測器
UdacityML通過他們的研究人員知道被調查者的年收入大于 $50,000 最有可能向他們捐款。因為這個原因UdacityML對于準確預測誰能夠獲得 $50,000 以上收入尤其有興趣。這樣看起來使用準確率作為評價模型的標準是合適的。
另外,把沒有收入大于 $50,000 的人識別成年收入大于 $50,000 對于UdacityML來說是有害的,因為他想要找到的是有意愿捐款的用戶。
這樣,我們期望的模型具有準確預測那些能夠年收入大于 $50,000 的能力比模型去召回這些被調查者更重要。我們能夠使用F-beta score作為評價指標,這樣能夠同時考慮準確率和召回率:
F_{\beta}=(1+\beta^2)\cdot\frac{precision\ \cdot\ recall}{\left(\beta^2\ \cdot\ precision\right)+recall}Fβ?=(1+β2)?(β2??precision?)+recallprecision?recall???
尤其是,當\beta =0.5β=0.5 個的時候跟多的強調準確率,這叫做F_{0.5}0.5 個?得分(或者為了簡單叫做 F-score)。
通過查看不同類別的數據分布(那些最多賺 $50,000 和那些能夠賺更多的),我們能發現:很明顯的是很多的被調查者年收入沒有超過 $50,000。這點會顯著地影響準確率,因為我們可以簡單地預測說"這個人的收入沒有超過$50,000",這樣我們甚至不用看數據就能做到我們的預測在一般情況下是正確的!
做這樣一個預測被稱作是樸素的,因為我們沒有任何信息去證實這種說法。通常考慮對你的數據使用一個樸素的預測器是十分重要的,這樣能夠幫助我們建立一個模型的表現是否好的基準。那有人說,使用這樣一個預測是沒有意義的:如果我們預測所有人的收入都低于 $50,000,那么UdacityML就不會有人捐款了。
注意: 準確、精密 和 召回 概要
準確度 (accuracy)衡量分類器進行正確預測的頻率。它是正確預測數量與預測總數(測試數據點數量)的比率。
精確度 (precision)告訴我們,我們將哪些郵件歸類為垃圾郵件,實際上是垃圾郵件。這是一個真正的肯定比例(被分類為垃圾郵件,實際上是垃圾郵件)與所有正面比率(所有被歸類為垃圾郵件,無論這是否正確的分類)。
換句話說:[ 真實肯定 / ( 真實肯定 + 錯誤肯定) ]
召回度(敏感度) (recall)告訴我們實際上垃圾郵件的比例是由我們分類為垃圾郵件。這是真正的肯定比例(被分類為垃圾郵件,實際上是垃圾郵件)與所有實際上是垃圾郵件的比率。
換句話說:[ 真正肯定 / (真正肯定 + 錯誤否定)]
對于像我們這樣的分類分布有偏差的分類問題,例如,如果我們有100條短信,只有2條是垃圾信息,其余98條不是,則準確度本身并不是一個很好的指標。
我們可以將90封郵件歸類為非垃圾郵件(包括2封垃圾郵件,但我們將其歸類為非垃圾郵件,因此它們將是錯誤的否定信息),10封為垃圾郵件(全部10個誤報),仍然得到相當好的準確性分數。
對于這種情況,精確度和召回率非常方便。這兩個指標可以合并得到F_1F1?分數,這是精度和召回分數的加權平均值(調和平均值)。這個分數可以從0到1,其中1是最好的F_1F1?分數(當我們處理比率時,我們采用調和平均值)。
樸素預測器的性能
如果我們選擇一個無論什么情況都預測被調查者年收入大于 $50,000 的模型,那么這個模型在這個數據集上的準確率和F-score是多少?
**注意:**你必須使用下面的代碼單元將你的計算結果賦值給 和 ,這些值會在后面被使用,請注意這里不能使用scikit-learn,你需要根據公式自己實現相關計算。'accuracy''fscore'
# TODO:
計算準確率
accuracy = income.mean()
# TODO:
使用上面的公式,并設置
`beta=0.5`
計算
F-score
prediction = np.ones(len(income))
beta2 = 0.5 ** 2
recall = 1
precision = accuracy
fscore = (1 + beta2) * precision * recall / (beta2 * precision + recall)
#
輸出結果
print('
樸素預測器得分:
[
準確度
: {:.4f}
,
F-score: {:.4f}]'.format(
??? accuracy, fscore))
樸素預測器得分:
[
準確度
: 0.2478
,
F-score: 0.2917]
5.5 監督學習模型
下面的監督學習模型是現在在scikit-learn中你能夠選擇的模型
- 樸素貝葉斯 (GaussianNB)
- 決策樹
- 集成方法 (Bagging, AdaBoost, Random Forest, Gradient Boosting)
- K近鄰 (KNeighbors)
- 隨機梯度下降分類器 (SGDC)
- 支撐向量機 (SVM)
- 物流回歸
創建一個訓練和預測的管道
為了正確評估你選擇的每一個模型的性能,創建一個能夠幫助你快速有效地使用不同大小的訓練集并在測試集上做預測的訓練和測試的流水線是十分重要的。
你在這里實現的功能將會在接下來的部分中被用到。在下面的代碼單元中,你將實現以下功能:
- 從sklearn.metrics中導入 和fbeta_scoreaccuracy_score
- 用樣例訓練集擬合學習器,并記錄訓練時間
- 用學習器來對訓練集進行預測并記錄預測時間
- 在最前面的 500 個訓練數據上做預測
- 計算訓練數據和測試數據的準確率
- 計算訓練數據和測試數據的 F-score
# TODO: 從 sklearn.metrics 中導入 fbeta_score 和 accuracy_score
from sklearn.metrics import fbeta_score, accuracy_score
def train_predict(learner, sample_size, X_train, y_train, X_test, y_test):
??? '''
??? 輸入:
??????? - learner:????? 要訓練和預測的機器學習算法
??????? - sample_size:? 從訓練集中抽取的樣本(數量)的大小
??????? - X_train:????? 特征訓練集
??????? - y_train:????? 目標培訓集
??????? - X_test:?????? 特征測試集
??????? - y_test:?????? 目標測試集
??? '''
???
??? results = {}
???
??? # TODO: 使用 sample_size 大小的訓練數據來擬合 learner
??? start = time() # 獲取開始時間
??? learn = learner.fit(X_train[:sample_size], y_train[:sample_size])
??? end = time() # 獲取結束時間
???
??? # TODO: 計算訓練時間
??? results['train_time'] = end - start
???
??? # TODO: 獲得測試集 (X_test) 的預測結果,
??? #?????? 然后使用 .predict() 獲得前300個訓練樣本 (X_train) 的預測值。
??? start = time() # 獲取開始時間
??? predictions_test = learner.predict(X_test)
??? predictions_train = learner.predict(X_train[:300])
??? end = time() # 獲取結束時間
???
??? # TODO: 計算預測用時
??? results['pred_time'] = end - start
???
??? # TODO: 計算前300個訓練樣本的準確率
??? results['acc_train'] = accuracy_score(y_train[:300], predictions_train)
???
??? # TODO: 計算測試集的準確率
??? results['acc_test'] = accuracy_score(y_test, predictions_test)
???
??? # TODO: 計算前300個訓練樣本的準確率
??? results['f_train'] = fbeta_score(y_train[:300], predictions_train, 0.5)
???
??? # TODO: 計算測試集的F-score
??? results['f_test'] = fbeta_score(y_test, predictions_test, 0.5)
???
??? # 成功
??? print(' {} 對 {} 個樣本進行了訓練。'.format(
??????? learner.__class__.__name__, sample_size))
???
??? # 返回結果
??? return results
初始模型的評估
在下面的代碼單元中,您將需要實現以下功能:
- 導入你在前面的討論的三個監督學習模型。
- 初始化三模型并存儲在 , 和 中。'clf_A''clf_B''clf_C'
-
- 如果可能對每一個模型都設置一個?。random_state
- **注意:**這里先使用每一個模型的默認參數,在接下來的部分中你將需要對某一個模型的參數進行調整。
- 計算記錄的數目等于 1%,10% 和 100% 的訓練數據,并將這些值存儲在 中'samples'
**注意:**取決于你選擇的算法,下面實現的代碼可能需要一些時間來運行!
# TODO: 從 sklearn 中導入三個監督學習模型
# 下面是所有算法的示例
# 1. 樸素貝葉斯 (GaussianNB)
# 2. 決策樹
# 3. 集成方法 (AdaBoost)
# 4. 集成方法 (Random Forest)
# 5. 集成方法 (Gradient Boosting)
# 6. K近鄰 (KNeighbors)
# 7. 隨機梯度下降分類器 (SGDC)
# 8. 支撐向量機 (SVM)
# 9. Logistic回歸
# 10. XGBClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import SGDClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
# TODO: 初始化三個模型
clf_A = GaussianNB()
clf_B = DecisionTreeClassifier(random_state=42)
clf_C = AdaBoostClassifier(random_state=42)
clf_D = RandomForestClassifier(random_state=42)
clf_E = GradientBoostingClassifier(random_state=42)
clf_F = KNeighborsClassifier()
clf_G = SGDClassifier(max_iter=1000, tol=1e-3,random_state=42)
clf_H = SVC(random_state=42)
clf_I = LogisticRegression(random_state=42)
# TODO: 計算 1%,10%,100% 的訓練數據分別對應多少個數據點
samples_1 = int(0.01*len(X_train))
samples_10 = int(0.1*len(X_train))
samples_100 = len(X_train)
# 收集學習器的結果
results = {}
for clf in [clf_D, clf_C, clf_E]: #[clf_A, clf_B, clf_C, clf_D, clf_E, clf_F, clf_G, clf_H, clf_I, clf_J]:
??? clf_name = clf.__class__.__name__
??? results[clf_name] = {}
???
??? for i, samples in enumerate([samples_1, samples_10, samples_100]):
??????? results[clf_name][i] = train_predict(
??????????? clf, samples, X_train, y_train, X_test, y_test)
# 對選擇的三個模型得到的評價結果進行可視化
vs.evaluate(results, accuracy, fscore)
RandomForestClassifier 對 361 個樣本進行了訓練。
RandomForestClassifier 對 3617 個樣本進行了訓練。
RandomForestClassifier 對 36177 個樣本進行了訓練。
AdaBoostClassifier 對 361 個樣本進行了訓練。
AdaBoostClassifier 對 3617 個樣本進行了訓練。
AdaBoostClassifier 對 36177 個樣本進行了訓練。
GradientBoostingClassifier 對 361 個樣本進行了訓練。
GradientBoostingClassifier 對 3617 個樣本進行了訓練。
GradientBoostingClassifier 對 36177 個樣本進行了訓練。
提高效果
在這最后一節中,您將從三個有監督的學習模型中選擇 最好的模型來使用數據。
你將在整個訓練集(?和?)上通過使用網格搜索優化至少調節一個參數以獲得一個比沒有調節之前更好的 F-score。X_trainy_train
模型調優
調節選擇的模型的參數。使用網格搜索 來至少調整模型的重要參數(至少調整一個),這個參數至少需給出并嘗試3個不同的值。你要使用整個訓練集來完成這個過程。在接下來的代碼單元中,你需要實現以下功能:'GridSearchCV'
- 導入sklearn.grid_search.gridSearchCV和sklearn.metrics.make_scorer
- 初始化你選擇的分類器,并將其存儲在 中。clf
-
- 如果能夠設置的話,設置random_state
- 創建一個對于這個模型你希望調整參數的字典。
-
- 例如:?。parameters = {'parameter' : [list of values]}
- **注意:**如果你的學習器(學習者)有 參數,請不要調節它!max_features
- 使用 來創建一個 評分對象(設置make_scorerfbeta_score\beta =0.5β=0.5 個)。
- 在分類器 上用 作為評價函數運行網格搜索,并將結果存儲在 中。clf'scorer'grid_obj
- 用訓練集(, )訓練 ,并將結果存儲在 中。X_trainy_traingrid_objgrid_fit
**注意:**取決于你選擇的參數列表,下面實現的代碼可能需要花一些時間運行!
# TODO: 導入 GridSearchCV, make_scorer
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
from sklearn.metrics import fbeta_score
from sklearn.metrics import make_scorer
# TODO: 初始化分類器
clf_lr = LogisticRegression(random_state=42)
clf_ada = AdaBoostClassifier(random_state=42)
clf_gb = GradientBoostingClassifier(random_state=42)
# TODO: 創建您希望調優的參數列表
# LogisticRegression 參數列表
solver_lr = 'newton-cg liblinear'.split()
c_lr = np.logspace(-2,1,4)
param_grid_lr = dict(
??? solver=solver_lr,
??? C=c_lr
)
# AdaBoostClassifier 參數列表
n_estimators_ada = range(150,180,10)
learning_rate_ada = [x/10 for x in range(13,16)]
param_grid_ada = dict(
??? n_estimators=n_estimators_ada,
??? learning_rate=learning_rate_ada
)
# GradientBoostingClassifier 參數列表
max_depth_gb = range(5,8)
learning_rate_gb = np.logspace(-2,0,3)
n_estimators_gb = range(120,150,10)
param_grid_gb = dict(
??? n_estimators=n_estimators_gb,
??? learning_rate=learning_rate_gb,
??? max_depth=max_depth_gb
)
# TODO: 創建一個 fbeta_score 打分對象
scorer = make_scorer(fbeta_score)
# TODO: 在分類上使用網格搜索,使用 scorer 作為評價函數
# 使用 LogisticRegression 分類器
# grid_obj = GridSearchCV(clf_lr,param_grid_lr)
# 未優化的模型
# ------
# 測試數據的準確性評分:0.8630
# 測試數據的F得分:0.7395
# 使用 AdaBoostClassifier 分類器
grid_obj = GridSearchCV(clf_ada,param_grid_ada)
# 優化模型
# ------
# 測試數據的最終準確度分數:0.8649
# 測試數據的最終F得分:0.7398
# 使用 GradientBoostingClassifier 分類器
# grid_obj = GridSearchCV(clf_gb,param_grid_gb)
# 優化模型
# ------
# 測試數據的最終準確度分數:0.8706
# 測試數據的最終F得分:0.7511
# TODO: 用訓練數據擬合網格搜索對象并找出最佳參數
grid_fit = grid_obj.fit(X_train, y_train)
# 得到最佳參數
best_clf = grid_fit.best_estimator_
# 使用沒有調優的模型進行預測
predictions = (clf.fit(X_train, y_train)).predict(X_test)
best_predictions = best_clf.predict(X_test)
print('未優化的模型\n------')
print('測試數據的準確性評分:{:.4f}'.format(
??? accuracy_score(y_test, predictions)))
print('測試數據的F得分:{:.4f}'.format(
??? fbeta_score(y_test, predictions, beta=0.5)))
print('\n優化模型\n------')
print('測試數據的最終準確度分數:{:.4f}'.format(
??? accuracy_score(y_test, best_predictions)))
print('測試數據的最終F得分:{:.4f}'.format(
??? fbeta_score(y_test, best_predictions, beta=0.5)))
print('\n調優后的參數\n------')
print(grid_fit.best_params_)
print(best_clf.get_params())
未優化的模型
測試數據的準確性評分:0.8630
測試數據的F得分:0.7395
優化模型
測試數據的最終準確度分數:0.8649
測試數據的最終F得分:0.7398
調優后的參數
{'learning_rate': 1.4, 'n_estimators': 160}
{'algorithm': 'SAMME.R', 'base_estimator': None, 'learning_rate': 1.4, 'n_estimators': 160, 'random_state': 42}
5.6 最終模型評估
結果
評價指標 | 樸素預測器 | 未優化模型 | 優化后模型 |
準確率 | 0.2478 | 0.8630 | 0.8649 |
F得分 | 0.2917 | 0.7395 | 0.7398 |
特征的重要性
在數據上(比如我們這里使用的人口普查的數據)使用監督學習算法的一個重要的任務是決定哪些特征能夠提供最強的預測能力。
通過專注于一些少量的有效特征和標簽之間的關系,我們能夠更加簡單地理解這些現象,這在很多情況下都是十分有用的。在這個項目的情境下這表示我們希望選擇一小部分特征,這些特征能夠在預測被調查者是否年收入大于 $50,000 這個問題上有很強的預測能力。
選擇一個有 屬性(這是一個根據這個選擇的分類器來對特征的重要性進行排序的函數)的scikit學習分類器(例如,AdaBoost,隨機森林)。在下一個Python代碼單元中用這個分類器擬合訓練集數據并使用這個屬性來決定這個人口普查數據中最重要的5個特征。feature_importance_
提取特征重要性
選擇一個 中有 屬性的監督學習分類器分類器,這個屬性是一個在做預測的時候根據所選擇的算法來對特征重要性進行排序的功能。scikit-learnfeature_importance_
在下面的代碼單元中,你將要實現以下功能:
- 如果這個模型和你前面使用的三個模型不一樣的話從 sklearn 中導入一個監督學習模型。
- 在整個訓練集上訓練一個監督學習模型。
- 使用模型中的 提取特征的重要性。.feature_importances_
# TODO: 導入一個擁有 'feature_importances_' 的監督學習模型
from xgboost import XGBClassifier
# TODO: 在訓練集上建立一個監督學習模型
model = XGBClassifier()
model.fit(X_train, y_train)
# TODO: 提取特征重要性
importances = model.feature_importances_
# 繪制特征重要性
vs.feature_plot(importances, X_train, y_train)
pd.Series(importances,index=X_train.columns).sort_values(ascending=False)[:10]
特征選擇
如果我們只是用可用特征的一個子集的話模型表現會怎么樣?
通過使用更少的特征來訓練,在評價指標的角度來看我們的期望是訓練和預測的時間會更少。
從上面的可視化來看,我們可以看到前五個最重要的特征貢獻了數據中所有特征中超過一半的重要性。
這提示我們可以嘗試去減小特征空間,并簡化模型需要學習的信息。下面代碼單元將使用你前面發現的優化模型,并只使用五個最重要的特征在相同的訓練集上訓練模型。
np.argsort(importances)[::-1][:5]
array([2, 0, 3, 1, 4])
# 導入模型克隆功能
from sklearn.base import clone
# 減小特征空間
X_train_reduced = X_train[X_train.columns.values[(np.argsort(importances)[::-1])[:5]]]
X_test_reduced = X_test[X_test.columns.values[(np.argsort(importances)[::-1])[:5]]]
# 克隆之前得到的最優模型繼續訓練
clf = (clone(best_clf)).fit(X_train_reduced, y_train)
# 做出一個新的預測
reduced_predictions = clf.predict(X_test_reduced)
# 輸出每一個版本的數據應用最終模型的分數
print('最終模型接受完整數據的訓練\n------')
print('測試數據的準確性:{:.4f}'.format(
??? accuracy_score(y_test,best_predictions)))
print('測試數據的F分數:{:.4f}'.format(
??? fbeta_score(y_test,reduced_predictions,0.5)))
print('\n最終模型接受壓縮數據的訓練\n------')
print('測試數據的準確性:{:.4f}'.format(
??? accuracy_score(y_test,best_predictions)))
print('測試數據的F分數:{:.4f}'.format(
??? fbeta_score(y_test,reduced_predictions,0.5)))
最終模型接受完整數據的訓練
測試數據的準確性:0.8649
測試數據的F分數:0.6855
最終模型接受壓縮數據的訓練
測試數據的準確性:0.8649
測試數據的F分數:0.6855