# 資料前處理 : 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()`。