# 資料前處理 : sklearn 1. 資料拆分 2. 類別型欄位編碼 3. Pipeline 4. 微調模型 --- ## 1. 資料拆分 (1) 使用 numpy:更新資料集時會產生不同資料組,會使機器看到未訓練的部分。 ```python= import numpy as np def split_train_test(data, test_ratio): np.random.seed(42) # 回傳打亂後的 index,也可放 array 回傳 array shuffled_indices = np.random.permutation(len(data)) test_set_size = int(len(data) * test_ratio) test_indices = shuffled_indices[:test_set_size] train_indices = shuffled_indices[test_set_size:] return data.iloc[train_indices], data.iloc[test_indices] ``` <br> (2) 使用雜湊:使用個實例的識別碼來決定是否將牠放入測試組。例如:計算各實例的雜湊,若小於等於最大雜湊值的 20% 時放入測試組。 ```python= from zlib import crc32 def test_set_check(identifier, test_ratio): # crc32最大值介於 0 ~ 2**32 間 # np.int64 轉成 64 位元 # & 0xffffffff 確保相容 python2、python return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2**32 def split_train_test_by_id(data, test_ratio, id_column): #以data中的id_column計算crc32碼 ids = data[id_column] in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio)) return data.loc[~in_test_set], data.loc[in_test_set] ``` 需使用最不可能改變的特徵來建立唯一的識別碼,如經緯度。若使用索引,則必須確保新資料都加在資料組的最後面,且不刪除任何資料列。 <br> (3) 使用 sklearn: ```python from sklearn.model_selection import train_test_split train, test = train_test_split(data, test_size=0.2, random_state=42) ``` <br> (4) 使用分層抽樣: ```python= from sklearn.model_selection import StratifiedShuffleSplit split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42) # 依照 data['XXX'] 的值將 data 切成 test_size 比例的 data for train_index, test_index in split.split(data, data["XXX"]): strat_train_set = data.loc[train_index] strat_test_set = data.loc[test_index] ``` --- ## 2. 類別型欄位編碼(One-hot & Label Encoding & Embedding) - **Label Encoding** ```python= from sklearn.preprocessing import OrdinalEncoder ordinal_encoder = OrdinalEncoder() data_cata_encoded = ordinal_encoder.fit_transform(data_cata) ``` 適合有序分類如 bad, average, good 等等 - **One-hot Encoding** ```python= from sklearn.preprocessing import OneHotEncoder onehot_encoder = OneHotEncoder() data_cata = onehot_encoder.fit_transform(data_cata) ``` 此輸出是 SciPy 稀疏矩陣:指儲存非零元素的位置,不會使用大量記憶體儲存整個矩陣。若想轉換成 Numpy 陣列,可使用 `.toarray()`。 - Embedding 使用 keras 表徵學習將類別型欄位訓練成一個 embedding 矩陣 --- ## 3. Pipeline 將所有轉換步驟串成一個 Pipeline,由一系列轉換器(以某種方式轉換 Input)及一個估計器(以 Input 產生出一新值)組成,呼叫 pipeline 的 fit 時,他會依序呼叫所有轉換器的 `fit_transform()` 方法,並將呼叫得到的輸出當成參數傳給下一個呼叫式,直到到達最終的估計器為止,此時會呼叫`fit()`方法。 - 範例一 由兩個轉換器及一個估計器組成: ```python= from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler num_pipeline = Pipeline([ ('imputer', SimpleImputer(strategy="median")), ('attribs_adder', CombinedAttributesAdder()), ('std_scaler', StandardScaler()), ]) data_num_tr = num_pipeline.fit_transform(data_num) ``` - 範例二 ```python= polynomial_regression = Pipeline([ ("poly_features", PolynomialFeatures(degree=10, include_bias=False)), ("lin_reg", LinearRegression()), ]) ``` --- ## 4. 微調模型 - **K-fold** 隨機將資料拆成 `cv` 等分,並評估模型 `cv` 次,每次用不同的 fold 評估,並用其他 `cv-1` 個 fold 來訓練,故裡面有 `cv` 個評估分數 ```python= from sklearn.model_selection import cross_val_score # 訓練十次 scores = cross_val_score(model, data_prepared, data_labels, scoring="neg_mean_squared_error", cv=10) rmse_scores = np.sqrt(-scores) ``` Scikit-Learn 的交叉驗證期望收到一個效用函數(越大越好)而不是損失函數(越小越好),所以其實是 MSE 的相反。 - **網格搜索** ```python= from sklearn.model_selection import GridSearchCV param_grid = [ # 第一種組合 3 x 4 = 12 {'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]}, # 第二種組合 2 x 3 = 6 {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]}, ] forest_reg = RandomForestRegressor(random_state=42) # train across 5 folds, that's a total of (12+6)*5=90 rounds of training grid_search = GridSearchCV(forest_reg, param_grid, cv=5, scoring='neg_mean_squared_error', return_train_score=True) grid_search.fit(data_prepared, data_labels) ``` 可利用 `grid_search.best_params_` 找出最佳參數組合、`grid_search.best_estimator_` 找出最佳估計器、`grid_search.cv_results_`取得每個 iter 分數,亦或是初始化 `GridSearchCV`時加上 **`refit=True`**,即找到最佳估計器時,用整個訓練組重新訓練模型。 - **隨機搜索** 當超參數搜尋空間很大時,網格搜索通常較沒效率,所以通常會使用隨機搜索在每次迭代時隨機選擇一個隨機值 `RandomizedSearchCV()`。