機器學習(二)——端到端的機器學習項目

不離不棄

芳齡永繼

概述

第二章假設(shè)我們將親自實踐一個端到端的機器學習項目寡润,體會真正的機器學習工程項目的過程吆玖。


實驗流程

實驗數(shù)據(jù)

StatLib 庫中選擇了加州住房價格的數(shù)據(jù)集闸与,該數(shù)據(jù)集基于1990年加州人口普查的數(shù)據(jù)。數(shù)據(jù)集下載地址:Dataset

image

下載并預覽數(shù)據(jù)

下載并解壓 housing.tgz

image

預覽數(shù)據(jù)
image

每一行代表一個區(qū)域眶熬,每個區(qū)域有10個相關(guān)屬性: longitude, latitude, housing_median_age, total_rooms, total_bed rooms, population, households, median_income, median_house_value, ocean_proximity

讀取并初步分析數(shù)據(jù)

  1. 讀取數(shù)據(jù)
  2. 查看數(shù)據(jù)結(jié)構(gòu)和描述
    image

    可知大部分屬性是float64類型的亡笑,ocean_proximity則是object们镜,該數(shù)據(jù)集中包含20640個實例,total_bedrooms只有20433個實例的妖,即207個區(qū)域缺失了這個特征
  3. 查看數(shù)據(jù)基本情況
    由上一步可知,ocean_proximity則是object足陨,預覽數(shù)據(jù)時可知嫂粟,其是文本類型數(shù)據(jù),查看其存在多少種分類
    image

    可知其存在5種分類
    image

    繪制各屬性的頻數(shù)分布直方圖觀察數(shù)據(jù)分布情況
    image

    發(fā)現(xiàn)房價墨缘、房齡兩個屬性被設(shè)置了上限星虹,大量超過限制的實例被壓縮到末尾區(qū)間內(nèi),以及部分屬性重尾(右側(cè)延伸大于其左側(cè)延伸)镊讼,之后需要注意這些問題
import pandas as pd
import matplotlib.pyplot as plt

def load_housing_data(housing_path):
    return pd.read_csv(housing_path)

if __name__ == '__main__':
    housing = load_housing_data('housing.csv')
    housing.head()
    housing.info()
    housing['ocean_proximity'].value_counts()
    housing.describe()

    housing.hist(bins=50, figsize=(20,15))
    plt.savefig('housing_distribution.png')

創(chuàng)建測試集

選取數(shù)據(jù)集的20%作為測試集宽涌,首先我們?yōu)榕懦藶橛绊懀S機選取數(shù)據(jù)的20%作為測試集狠毯,其分布如下

image

思索一下护糖,發(fā)現(xiàn)問題似乎沒有這么簡單,隨機抽樣固然排除了人為因素嚼松,但是容易造成抽樣偏差嫡良,假設(shè)一個地區(qū)有70%的男生,30%的女生献酗,完全隨機抽樣有可能全部抽出男生寝受,那么此時測試集就不能代表整體了。因而我們?yōu)楸WC測試集具有代表性罕偎,會選擇分層抽樣很澄。此例中我們按照地區(qū)收入中位數(shù)進行分層抽樣:

  1. 首先觀察收入中位數(shù)的分布,發(fā)現(xiàn)大部分人處于2~3w左右
  2. 收入數(shù)據(jù)為連續(xù)值颜及,我們四舍五入對其取整甩苛,將其離散化
  3. 為減少離散值類數(shù)目,將大于5w的部分歸于5w類目中
  4. 按此比例對原數(shù)據(jù)進行分層抽樣
    image
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

housing['income_cat'] = np.ceil(housing['median_income'] / 1.5)
housing['income_cat'].where(housing['income_cat'] < 5, 5.0, inplace=True)

spliter = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in spliter.split(housing,housing['income_cat']):
    strat_train_set = housing.loc[train_index]
    strat_test_set = housing.loc[test_index]
for data_set in (strat_train_set, strat_test_set):
    data_set.drop(['income_cat'], axis=1 ,inplace=True)

PS:測試集的構(gòu)建很重要俏站,卻往往被忽視讯蒲,因此機器學習項目中需要注意

數(shù)據(jù)探索和可視化

首先將測試集放在一邊,我們只能夠在訓練集上進行數(shù)據(jù)探索(如果數(shù)據(jù)量比較大肄扎,可以先提取出一個較小的探索集)墨林,這樣才不會損壞訓練集。

  1. 將地理數(shù)據(jù)可視化

    image

    可見與加州地圖輪廓類似犯祠,但也看不出什么了旭等,因而改變alpha參數(shù),觀察實例分布密度
    image

    可見有明顯的高密度區(qū)域衡载,就是灣區(qū)搔耕、洛杉磯等地。

  2. 將人口月劈、房價信息可視化

    image

    圖中每個圓的半徑代表人口數(shù)量度迂,顏色代表房價高低(藍色為低藤乙,紅色為高),可見人口與房價的關(guān)系惭墓。

  3. 尋找特征之間的相關(guān)性
    查看各屬性與房價之間的皮爾森相關(guān)性系數(shù)

    image

    相關(guān)性從-1~1坛梁。越接近1,就有越強的正相關(guān)腊凶;越接近-1划咐,就有越強的負相關(guān)。從圖中可知钧萍,一個地區(qū)收入中位數(shù)越高褐缠,房價越高;而越往北风瘦,房價越低队魏。而與房價相關(guān)性最高的特征是收入。

  4. 繪制收入万搔、房價散點圖

    image

    二者相關(guān)性的確很強胡桨,但是50w以上的部分是一條直線,因為我們將50w以上的實例全部歸類于50w這一類了瞬雹。仔細觀察昧谊,發(fā)現(xiàn)45w、35w酗捌、23w附近貌似也有這樣的直線呢诬,值得注意。

  5. 嘗試不同屬性的組合
    比如目前知道每個地區(qū)的房間數(shù)胖缤,但并沒有什么用尚镰,如果知道了每個家庭的房間數(shù),可能就大大不同了哪廓,因此可以對屬性進行組合钓猬。

    image

    發(fā)現(xiàn)每個家庭的房間數(shù)、每個房間的臥室數(shù)與房價也很有相關(guān)性

    housing = strat_train_set.copy()
    housing.plot(kind='scatter', x='longitude', y='latitude')
    plt.savefig('gregrophy.png')
    housing.plot(kind='scatter', x='longitude', y='latitude', alpha=0.1)
    plt.savefig('gregrophy_more.png')
    
    fig = plt.scatter(x=housing['longitude'], y=housing['latitude'], alpha=0.4, \
        s=housing['population']/100, label='population', \
        c=housing['median_house_value'], cmap=plt.get_cmap('jet'))
    plt.colorbar(fig)
    plt.legend()
    plt.savefig('gregrophy_population_value.png')
    
    corr_matrix = housing.corr()
    corr_matrix['median_house_value'].sort_values(ascending=False)
    
    housing.plot(kind='scatter', x='median_income', y='median_house_value', alpha=0.1)
    plt.savefig('income_value.png')
    
    housing['rooms_per_household'] = housing['total_rooms']/housing['households']
    housing['bedrooms_per_room'] = housing['total_bedrooms']/housing['total_rooms']
    housing['population_per_household'] = housing['population']/housing['households']
    corr_matrix = housing.corr()
    corr_matrix['median_house_value'].sort_values(ascending=False)
    

數(shù)據(jù)準備

  1. 準備干凈的數(shù)據(jù)集撩独,將預測器與標簽分開

  2. 數(shù)據(jù)清洗

    • 放棄這些相應(yīng)的地區(qū)
    • 放棄這個屬性
    • 將缺失的值設(shè)為某個值(0、平均數(shù)或中位數(shù)等)
      我選擇用中位數(shù)填補缺失值
      image

      填補之后账月,可見數(shù)據(jù)集已經(jīng)沒有缺失部分了
    housing = strat_train_set.drop('median_house_value', axis=1)
    housing_labels = strat_train_set['median_house_value'].copy()
    
    imputer = Imputer(strategy='median')
    housing_num = housing.drop('ocean_proximity', axis=1)
    imputer.fit(housing_num)
    imputer.statistics_
    X = imputer.transform(housing_num)
    housing_tr = pd.DataFrame(X, columns=housing_num.columns)
    
  3. 處理文本與分類屬性
    之前ocean_proximity為文本屬性综膀,無法進行數(shù)值計算,因此我們需要將其轉(zhuǎn)化為數(shù)字局齿,但是此時算法會將其理解為純數(shù)值進行運算剧劝,這是不對的,我們的原始數(shù)據(jù)是分類數(shù)據(jù)抓歼,是類別讥此,不能用于加減乘除等運算拢锹,因此我們需要將其轉(zhuǎn)換成獨熱編碼。

    image

    encoder = LabelEncoder()
    housing_cat = housing['ocean_proximity']
    housing_cat_encoded = encoder.fit_transform(housing_cat)
    
    encoder = OneHotEncoder()
    housing_cat_1hot = encoder.fit_transform(housing_cat_encoded.reshape(-1,1))
    
    encoder = LabelBinarizer()
    housing_cat_1hot = encoder.fit_transform(housing_cat)
    
  4. 自定義轉(zhuǎn)換器
    之前討論過組合屬性形成新的屬性萄喳,我們也希望可以引入新的屬性卒稳,但是又希望這個過程可以與Scikit-Learn的流水線無縫銜接,因此可以自定義轉(zhuǎn)換器他巨。所需要的只是新創(chuàng)建一個類充坑,應(yīng)用fit()、transform()染突、fit_transform()就可以了捻爷。如果添加TransformerMixin作為基類,可以直接獲得最后一個方法份企;添加BaseEstimator作為基類也榄,則可以獲得兩個自動調(diào)整超參數(shù)的方法get_params()和set_params()。
    增加前:

    image

    增加后:
    image

    新增加了每間房房間數(shù)量和每戶家庭人口數(shù)兩個組合屬性

    class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    """docstring for CombinedAttributesAdder"""
    def __init__(self, add_bedrooms_per_room=True):
        self.add_bedrooms_per_room = add_bedrooms_per_room
    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        rooms_per_household = X[:,rooms_ix] / X[:,household_ix]
        population_per_household = X[:,population_ix] / X[:,household_ix]
        if self.add_bedrooms_per_room:
            bedrooms_per_room = X[:,bedrooms_ix] / X[:,rooms_ix]
            return np.c_[X, rooms_per_household, population_per_household, bedrooms_per_room]
        else:
            return np.c_[X, rooms_per_household, population_per_household]
    
  5. 轉(zhuǎn)換流水線
    許多數(shù)據(jù)轉(zhuǎn)換需要正確的步驟來運行司志,因此需要設(shè)置流水線工作甜紫。我仿照書上寫這段代碼時,發(fā)現(xiàn)由于版本更新俐芯,這里失效了:

    image

    經(jīng)檢查是LabelBinarizer更新后只接收兩個參數(shù)棵介,因此重寫了一個自己的MyLabelBinarizer

    class MyLabelBinarizer(BaseEstimator, TransformerMixin):
    """docstring for DataFrameSelector"""
    def __init__(self):
        self.encoder = LabelBinarizer()
    def fit(self, X, y=None):
        return encoder.fit(X)
    def transform(self, X, y=None):
        return encoder.transform(X)
    

    image

    準備數(shù)據(jù)成功

選擇和訓練模型

  1. 培訓和評估訓練集
    使用線性回歸模型,看看預測情況如何

    #train the model
    lin_reg = LinearRegression()
    lin_reg.fit(housing_prepared, housing_labels)
    some_data = housing.iloc[:5]
    some_labels = housing_labels.iloc[:5]
    some_data_prepared = full_pipeline.transform(some_data)
    print("Predictions: ", lin_reg.predict(some_data_prepared))
    print("Labels: ", list(some_labels))
    housing_predictions = lin_reg.predict(housing_prepared)
    lin_mse = mean_squared_error(housing_labels, housing_predictions)
    lin_rmse = np.sqrt(lin_mse)
    

    image

    部分預測結(jié)果吧史,效果不盡如人意邮辽,利用RMSE進行評估
    image

    效果確實不行!

    使用決策樹模型贸营,看看情況如何

tree_reg = DecisionTreeRegressor()
tree_reg.fit(housing_prepared,housing_labels)
housing_predictions = tree_reg.predict(housing_prepared)
tree_mse = mean_squared_error(housing_labels, housing_predictions)
tree_rmse = np.sqrt(tree_mse)
```

image

情況喜人6质觥!但是轉(zhuǎn)念一想钞脂,100%正確揣云,這不是過擬合了吧?進行模型驗證評估1小邓夕!

  1. 驗證模型
    選擇交叉驗證方法驗證模型

    image

    效果真的爛,保險起見阎毅,檢查一下線性回歸模型
    image

    也很差勁焚刚,換模型!扇调!

    #valid models
    tree_scores = cross_val_score(tree_reg, housing_prepared, housing_labels, scoring='neg_mean_squared_error', cv=10)
    tree_rmse_scores = np.sqrt(-tree_scores)
    display_scores(tree_rmse_scores)
    
    lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, scoring='neg_mean_squared_error', cv=10)
    lin_rmse_scores = np.sqrt(-lin_scores)
    display_scores(lin_rmse_scores)
    
  2. 使用隨機森林

    image

    情況爛到家了矿咕,理論上來說,不應(yīng)該,選其他模型或者調(diào)參數(shù)

  3. 調(diào)參
    利用網(wǎng)格搜索使用不同參數(shù)組合碳柱,找出最好參數(shù)結(jié)果

    image

    查看整體情況
    image

    此時隨機森林的最佳參數(shù)是max_features為6捡絮,n_estimators為30時,RMSE=50019

    #improve the models
    param_grid = [
        {'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
        {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]}
    ]
    
    forest_reg = RandomForestRegressor()
    grid_search = GridSearchCV(forest_reg, param_grid, cv=5, scoring='neg_mean_squared_error')
    grid_search.fit(housing_prepared,housing_labels)
    grid_search.best_params_
    cvres = grid_search.cv_results_
    for mean_score, params in zip(cvres['mean_test_score'], cvres['params']):
        print(np.sqrt(-mean_score), params)
    
  4. 分析最佳模型及其錯誤
    查看每個特征的重要性

    image

    image

    可見收入是最重要的特征

    #analyse the model
    feature_importances = grid_search.best_estimator_.feature_importances_
    extra_attribs = ['rooms_per_hhold', 'pop_per_hhold', 'bedrooms_per_room']
    cat_one_hot_attribs = list(encoder.classes_)
    attributes = num_attribs + extra_attribs + cat_one_hot_attribs
    sorted(zip(feature_importances,attributes),reverse=True)
    
  5. 通過測試集評估系統(tǒng)

    image

    最終誤差可以接受

    #final model
    final_model = grid_search.best_estimator_
    
    X_test = strat_test_set.drop('median_house_value', axis=1)
    y_teat = strat_test_set['median_house_value'].copy()
    X_test_prepared = full_pipeline.fit_transform(X_test)
    final_predictons = final_model.predict(X_test_prepared)
    final_mse = mean_squared_error(y_teat, final_predictons)
    final_rmse = np.sqrt(final_mse)
    
  6. 啟動莲镣、監(jiān)控和維護系統(tǒng)


后記

性能指標

回歸問題的典型性能衡量指標是均方根誤差(RMSE)福稳,它測量的是預測過程中預測錯誤的標準偏差。

image

當數(shù)據(jù)離群點很多時剥悟,可以考慮使用平均絕對誤差(MAE
image

特征縮放

如果輸入的數(shù)據(jù)有較大的比例差異灵寺,可以考慮對特征數(shù)值進行縮放,通常有兩種方法:最大-最小縮放区岗、標準化
最大-最小縮放:歸一化略板,將數(shù)值縮放到0~1之間,實現(xiàn)方法是將值減去最小值并除以最大值和最小值的差
標準化:首先減去平均值慈缔,再除以方差

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叮称,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子藐鹤,更是在濱河造成了極大的恐慌瓤檐,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娱节,死亡現(xiàn)場離奇詭異挠蛉,居然都是意外死亡,警方通過查閱死者的電腦和手機肄满,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門谴古,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稠歉,你說我怎么就攤上這事掰担。” “怎么了怒炸?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵带饱,是天一觀的道長。 經(jīng)常有香客問我阅羹,道長勺疼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任捏鱼,我火速辦了婚禮恢口,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘穷躁。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布问潭。 她就那樣靜靜地躺著猿诸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狡忙。 梳的紋絲不亂的頭發(fā)上梳虽,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音灾茁,去河邊找鬼窜觉。 笑死,一個胖子當著我的面吹牛北专,可吹牛的內(nèi)容都是我干的禀挫。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼拓颓,長吁一口氣:“原來是場噩夢啊……” “哼语婴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驶睦,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤砰左,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后场航,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缠导,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年溉痢,在試婚紗的時候發(fā)現(xiàn)自己被綠了僻造。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡适室,死狀恐怖嫡意,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捣辆,我是刑警寧澤蔬螟,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站汽畴,受9級特大地震影響旧巾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忍些,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一鲁猩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罢坝,春花似錦廓握、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽男应。三九已至,卻和暖如春娱仔,著一層夾襖步出監(jiān)牢的瞬間沐飘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工牲迫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耐朴,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓盹憎,卻偏偏與公主長得像筛峭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子脚乡,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容