1. 程式人生 > >(原創)一個完整的機器學習項目是怎麽建立起來的

(原創)一個完整的機器學習項目是怎麽建立起來的

時間 port 所有 散點 平均值 註意 sklearn 重復數 因此

在這篇文章中,將介紹機器學習項目的流程

明確問題

首先,我們需要預覽這個項目。項目的目的是什麽,以房價預測為例,數據為StatLib的加州房產數據,那麽目的就是預測街區的房產價格中位數。

劃定問題及分析

要知道商業目的是什麽,畢竟建立模型不是最終目的。比如說,目的是輸出一個價格傳給另一套系統來判斷是否值得投資以及利潤是多少。
要知道現在的解決方案效果怎麽樣,比如會給出一個現解決方案的誤差率是alpha。
現在我們可以進一步研究問題,明確這個問題是監督/非監督,還是強化模型?是分類/回歸,還是聚類等其他。要使用批量學習還是線上學習?
分析,我們有房價的值,所以是一個監督問題;我們最終是要預測得到房價中位數,因此是一個回歸問題,而且是一個多變量預測回歸,因為有很多影響參數;另外,沒有連續的數據流入,沒有特別需求需要對數據變動作出快速適應。數據量不大可以放到內存中,因此批量學習就可以。【如果數據量很大,你可以要麽在多個服務器上對批量學習做拆分(使用 MapReduce 技術,後面會看到),或是使用線上學習】

選擇性能指標

在這裏我們需要選擇一個評價指標,回歸問題的典型指標是均方根誤差RMSE,它表征的是系統預測誤差的標準差。
另外,也可以使用差平方絕對誤差。

核實假設

再一次核實之前的分析是否準確,需要聯系下遊的處理進行檢查。

獲取數據

創建工作空間

比如python jupyter及相應的庫文件(如numpy, pandas, scipy, 及sklearn等)和框架(tf等)

下載數據

一般來說,可以從數據庫中下載數據,但是對於數據庫一般需要密碼及權限。在這裏,我們可以直接從網頁數據,當然,這是具體問題具體分析的。

查看數據

下載好數據後,我們需要查看一下數據的結果,預覽一下。基本用到以下代碼

import pandas as pd
data=pd.read_csv(‘路徑‘)
data.head()
data.info()#返回特征的數量及類型
data.describe()#返回數量、均值、標準差、最值等信息

另外也可以使用柱狀圖通過可視化查看數據的分布,代碼:

%matplotlib inline   # only in a Jupyter notebook
import matplotlib.pyplot as plt
data.hist(bins=50, figsize=(20,15))
plt.show()#在jupyter中可以不加這條語句

hist()方法依賴於 Matplotlib,後者依賴於用戶指定的圖形後端以打印到屏幕上。因此在畫圖之前,你要指定 Matplotlib 要使用的後端。
最簡單的方法是使用 Jupyter 的魔術命令%matplotlib inline。它會告訴 Jupyter 設定好 Matplotlib,以使用 Jupyter 自己的後端。繪圖就會在 notebook 中渲染了。

創建測試集

在查看數據前,最好先創建一下測試集,以免查看數據後因為思維定勢影響測試集的選擇。
一種方法是可以隨機選擇測試集,比如隨機選擇20%的數據作為測試集,但是這樣當數據集更新時,測試集會變化,我們可以使用隨機數處理。代碼:

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

另外,隨機取數有可能丟失掉關鍵特征的分布。比如,有一個特征A對最終標簽的貢獻很大(兩者之間相關性很強),
那麽我們也應該在測試集中保證A的分布符合原數據集的分布趨勢。這時可以使用分層采樣。代碼:

data["A_new"] = np.ceil(data["A"] / 1.5)
data["A_new"].where(data["A_new"] < 5, 5.0, inplace=True)#這兩條語句是對數據生成一個新的標簽,表征A的分層(分布),這要具體問題具體分析
from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)

for train_index, test_index in split.split(data, data["A_new"]):#按照A_new對data進行劃分測試集訓練集
    strat_train_set = data.loc[train_index]
    strat_test_set = data.loc[test_index]

可通過以下代碼檢查分層采樣的結果:

data["A_new"].value_counts() / len(data)
strat_test_set["A_new"].value_counts() / len(strat_test_set)

註意最後需要將生成的A_new標簽刪除,用drop命令,代碼:

for set in (strat_train_set, strat_test_set):
    set.drop(["A_new"], axis=1, inplace=True)

這樣我們就生成了兩組訓練-測試集,隨機的和分層的。

數據可視化及數據探索

數據可視化

經常性的,我們需要首先對數據進行一下觀察,可以判斷特征與標簽的關系以及哪些特征的作用或者影響更大。在這裏使用matplotlib庫即可,如:

housing.plot(kind="scatter", x="longitude", y="latitude")#散點圖看分布

查找關聯

可以使用corr()方法計算每對屬性間的標準相關系數,如:

corr_m=housing.corr()
print(corr_m[‘median_house_value‘].sort_values(ascending=True))

註意corr()只能表征線性關系,對於非線性關系直接忽視,因此參考性有局限。還有一種檢測屬性間相關系數的方法時pandas的scatter_matrix函數,如:

from pandas.tools.plotting import scatter_matrix
attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]#只表示這四個的相關性
scatter_matrix(housing[attributes], figsize=(12, 8))

屬性組合試驗

有時候僅僅使用原有的特征數據的效果並不好,這是可以考慮一下將一些特征組合產生新的特征,比如將人數/家庭,得到每戶的人數這樣一個特征。如:

housing[‘population_per_household‘]=housing[‘population‘]/housing[‘households‘]

然後可以重新觀察相關性。

為機器學習準備數據

不要手工來做,你需要寫一些函數,理由如下:
函數可以讓你在任何數據集上(比如,你下一次獲取的是一個新的數據集)方便地進行重復數據轉換。
你能慢慢建立一個轉換函數庫,可以在未來的項目中復用。
在將數據傳給算法之前,你可以在實時系統中使用這些函數。
這可以讓你方便地嘗試多種數據轉換,查看哪些轉換方法結合起來效果最好。
有一點需要註意,那就是要時刻記得復制數據,保證後面的數據處理盡量不要影響最初的數據。做好標記。

數據清洗

原數據中會存在缺失值等問題,因此需要對數據進行清洗。這一非常關鍵的一步。
對於缺失值的處理,有三種方式
1、直接刪掉缺失值所在的行;2、如果一個特征的缺失值太多,那麽直接刪掉該特征;3、對缺失位置進行賦值(用0、中位數或者平均值等)。如:

housing.dropna(housing[‘total_bedrooms‘])
housing.drop(‘total_bedrooms‘,axis=1)
housing[‘total_bedrooms‘].fillna(median)

盡量采用第三種方式,這樣可以充分利用原數據。可以使用sklearn的Imputer類來處理缺失值。

from sklearn.preprocessing import Imputer
imputer = Imputer(strategy="median")#創建一個Imputer類
housing_num = housing.drop("ocean_proximity", axis=1)#創建沒有文本屬性的數據副本
imputer.fit(housing_num)#將類應用於數據,求出median
X = imputer.transform(housing_num)#對數據進行轉換,填充缺失值,得到一個numpy數組
housing_tr = pd.DataFrame(X, columns=housing_num.columns)#數組轉化為DataFrame格式

處理文本和類別屬性

數據中會有一些文本類型,在處理時我們可以使用one-hot對其進行重新編碼,這需要兩個轉換(文本分類到整數分類,再到one-hot向量)
可以用sklearn的LabelBinarizer實現這兩個轉換

from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()
housing_cat_1hot = encoder.fit_transform(housing_cat)#得到one-hot向量
print(housing_cat_1hot)

但是,上面的類也應用於標簽列的轉換,正確的做法時用sklearn即將提供的CategoricalEncoder類,如:

cat_encoder = CategoricalEncoder()
housing_cat_reshaped = housing_cat.values.reshape(-1, 1)
housing_cat_1hot = cat_encoder.fit_transform(housing_cat_reshaped)
print(housing_cat_1hot)

自定義轉換器

轉換器的作用是將一些數據處理的操作集中在一起執行,比如前面敘述的清洗、屬性組合等,另外可以將自制的轉換器與sklearn的流水線無縫銜接工作。這一部分的示例代碼可以查看自己寫的文件(備註:)。這一部分可以將屬性組合寫在裏面。
註意這裏可以為屬性設置一些超參數,檢查這個屬性是否地ML的算法有幫助。

特征縮放

這個步驟很重要,針對的是輸入數值屬性量度的不同問題。比如,年齡屬性在20~50,而收入分布在5000~100000,這樣的數據應用於算法的性能不會太好。通常情況下不要對目標值進行縮放。
兩種方式:
線性函數歸一化(min-max-scaling)-減去最小值,再除以最大值與最小值的差值,sklearn的MinMaxScaler
標準化(standardization)-減去平均值,再除以方差,得到的分布具有單位方差。sklearn的StandardScaler
註:所有的數據轉換等操作都要分別作用於訓練集和測試集,不要向完成的數據集使用。

轉換流水線

流水線的作用時創建一種模式,使得數據可以按照一定順序進行處理和轉化。例如下面是一個完整的處理數值和類別屬性的流水線:

from sklearn.pipeline import FeatureUnion
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]

num_pipeline = Pipeline([
        (‘selector‘, DataFrameSelector(num_attribs)),
        (‘imputer‘, Imputer(strategy="median")),
        (‘attribs_adder‘, CombinedAttributesAdder()),
        (‘std_scaler‘, StandardScaler()),
    ])

cat_pipeline = Pipeline([
        (‘selector‘, DataFrameSelector(cat_attribs)),
        (‘cat_encoder‘, CategoricalEncoder(encoding="onehot-dense")),
    ])

full_pipeline = FeatureUnion(transformer_list=[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])

使用時調用:

housing_prepared=full_pipeline.fit_transform(housing)#其中housing是分層抽樣並drop掉標簽值的分好的訓練集。

這樣調用步驟:num_pipeline->DataFrameSelector->Imputer->CombinedAttributesAdder->StandardScaler->cat_pipeline->DataFrameSelector->CategoricalEncoder,得到處理好的訓練集。
其表示分別為:子流水線數據操作-》選擇轉化器-》缺失值處理-》屬性組合-》標準化-》子流水線分類處理-》選擇轉化器-》分類標記為one-hot向量
對於選擇轉換器的解釋:通過選擇對應的屬性(數值或分類)、丟棄其它的,來轉換數據,並將輸出DataFrame轉變成一個 NumPy 數組。Scikit-Learn 沒有工具來處理 PandasDataFrame,因此我們需要寫一個簡單的自定義轉換器來做這項工作:

#這一部分最好寫在前面
from sklearn.base import BaseEstimator, TransformerMixin
class DataFrameSelector(BaseEstimator, TransformerMixin):
    def __init__(self, attribute_names):
        self.attribute_names = attribute_names
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return X[self.attribute_names].values

至此,我們得到了處理後的訓練集(不帶標簽)housing_prepared,標簽集housing_labels,測試集(未經處理,並帶標簽)。

選擇並訓練模型

在訓練集上訓練和評估

到這裏我們就可以選擇算法模型對數據進行訓練學習(其實我們可以發現大多數的工作都集中在數據的預處理上,包括清洗可視化文類屬性轉化等)。

from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(housing_prepared,housing_labels)

評價模型,可以使用均方根誤差。

from from sklearn.metrics import mean_squared_error
housing_predictions = lin_reg.predict(housing_prepared)
lin_mse = mean_squared_error(housing_labels, housing_predictions)
lin_rmse = np.sqrt(lin_mse)

當然不僅是線性回歸,同樣可以使用其他模型,比如決策樹模型、隨機森林模型,步驟同上。

使用交叉驗證做更佳的評價

另外,我們可以使用交叉驗證來驗證模型,以決策樹為例:

from sklearn.model_selection import cross_val_score
scores = cross_val_score(tree_reg, housing_prepared, housing_labels,scoring="neg_mean_squared_error", cv=10)
rmse_scores = np.sqrt(-scores)

以上,隨機地將訓練集分成十個不同的子集,成為“折”,然後訓練評估決策樹模型 10 次,每次選一個不用的折來做評估,用其它 9 個來做訓練。結果是一個包含 10 個評分的數組。
Scikit-Learn 交叉驗證功能期望的是效用函數(越大越好)而不是損失函數(越低越好),因此得分函數實際上與 MSE 相反(即負值),這就是為什麽前面的代碼在計算平方根之前先計算-scores。
結果查看:

def display_scores(scores):
...     print("Scores:", scores)
...     print("Mean:", scores.mean())
...     print("Standard deviation:", scores.std())
display_scores(tree_rmse_scores)

當然也可以將線性回歸和隨機森林適用到交叉驗證上。另外還可以使用其他算法,比如神經網絡、不同核心的支持向量機等。不要將太多時間花在調參上。
到這兒的目標是先列出幾個合適的模型列表。

模型微調

網格搜索

使用 Scikit-Learn 的GridSearchCV方法。以針對隨機森林為例。

from sklearn.model_selection import GridSearchCV
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)

分析可知,以上訓練總共有18 × 5 = 90輪。
參數的最佳組合及最佳估計器:

print(grid_search.best_params_)
print(grid_search.best_estimator_)

隨機搜索

當超參數的搜索空間很大時,最好使用RandomizedSearchCV。這個類的使用方法和類GridSearchCV很相似,但它不是嘗試所有可能的組合,而是通過選擇每個超參數的一個隨機值的特定數量的隨機組合。

集成方法

另一種微調系統的方法是將表現最好的模型組合起來。

分析最佳模型和它們的誤差

通過分析最佳模型,常常可以獲得對問題更深的了解。比如,RandomForestRegressor可以指出每個屬性對於做出準確預測的相對重要性:

feature_importances = grid_search.best_estimator_.feature_importances_
print(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)#輸出重要性和對應的屬性名

根據以上重要性分數,我們可以舍棄一些不重要的屬性等

用測試集評估系統

終於調試完模型,接下來我們需要用測試集來測試,註意,我們之前分割數據之後,測試集一直沒用,這時我們需要先對測試集進行一下處理,比如丟掉標簽、流水線處理等。然後再將我們的模型應用上去。

final_model = grid_search.best_estimator_
X_test = strat_test_set.drop("median_house_value", axis=1)
y_test = strat_test_set["median_house_value"].copy()
X_test_prepared = full_pipeline.transform(X_test)
final_predictions = final_model.predict(X_test_prepared)
final_mse = mean_squared_error(y_test, final_predictions)
final_rmse = np.sqrt(final_mse) 
print(final_rmse)

到這,我們就基本完成了模型的創建和測試,接下來就需要將結果或者結論展示出來。

啟動、監控和維護系統

將以上模型植入公司系統,實現自動化運行。

實踐

不要浪費在高級算法上,會使用就可以了。重點在於理解業務和數據,以及數據的處理。

註:此文章參考了GitHub-PeterHo的文章,感謝。

(原創)一個完整的機器學習項目是怎麽建立起來的