6.1 管道和復合估算器?
轉換器(Transformers )通常與分類器,回歸器或其他估計器組合在一起,以構建復合估計器。最常用的工具是 管道(Pipeline)。管道通常與FeatureUnion結合使用, FeatureUnion將轉換器的輸出連接到復合特征空間中。 TransformedTargetRegressor處理轉換目標 (即log-transform y)。否則,Pipelines僅變換觀察到的數據(X)。
6.1.1 管道(Pipeline):鏈式估計器
Pipeline
可用于將多個估計器鏈接為一個。這很有用,因為在處理數據時通常會有固定的步驟順序,例如特征選擇,歸一化和分類。Pipeline
在這里有多種用途:
便捷和封裝
聯合參數選擇
您可以對管道中估計器的所有參數進行一次網格搜索。
安全
轉換器( transformers)和預測器(predictors)使用相同的樣本訓練,管道有助于避免將統計數據從測試數據泄漏到經過交叉驗證訓練的模型中。
除最后一個管道外,管道中的所有估計器都必須是轉換器(即必須具有轉換(transform)方法)。最后的估計器可以是任何類型(轉換器,分類器等)。
6.1.1.1 用法
6.1.1.1.1 構造
管道是由包含(鍵,值)對的列表構建的,其中鍵是包含此步驟名稱的字符串,而值是估計器對象:
>>> from sklearn.pipeline import Pipeline
>>> from sklearn.svm import SVC
>>> from sklearn.decomposition import PCA
>>> estimators = [('reduce_dim', PCA()), ('clf', SVC())]
>>> pipe = Pipeline(estimators)
>>> pipe
Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC())])
功能函數make_pipeline
是構造管道的簡寫。它使用可變數量的估計器并返回管道,而且自動填充名稱:
>>> from sklearn.pipeline import make_pipeline
>>> from sklearn.naive_bayes import MultinomialNB
>>> from sklearn.preprocessing import Binarizer
>>> make_pipeline(Binarizer(), MultinomialNB())
Pipeline(steps=[('binarizer', Binarizer()), ('multinomialnb', MultinomialNB())])
6.1.1.1.2 訪問步驟
管道的估計器在steps
屬性中以列表形式存儲,但是可以對管道建立索引并通過索引或名稱(通過[idx]
)來訪問管道:
>>> pipe.steps[0]
('reduce_dim', PCA())
>>> pipe[0]
PCA()
>>> pipe['reduce_dim']
PCA()
管道的named_steps
屬性允許在交互式環境中按名稱和制表符(tab)補全的方式訪問步驟:
>>> pipe.named_steps.reduce_dim is pipe['reduce_dim']
True
也可以使用通常用于Python序列(例如列表或字符串)的切片方法來提取子管道(盡管步長只能為1)。 這對于僅執行某些轉換(或其逆轉換)是很方便的:
>>> pipe[:1]
Pipeline(steps=[('reduce_dim', PCA())])
>>> pipe[-1:]
Pipeline(steps=[('clf', SVC())])
6.1.1.1.3 嵌套參數
可以使用<estimator>__<parameter>
語法訪問管道中估計器的參數 :
>>> pipe.set_params(clf__C=10)
Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC(C=10))])
這對進行網格搜索特別重要:
>>> from sklearn.model_selection import GridSearchCV
>>> param_grid = dict(reduce_dim__n_components=[2, 5, 10],
... clf__C=[0.1, 10, 100])
>>> grid_search = GridSearchCV(pipe, param_grid=param_grid)
各個單獨的步驟可以替換為多個參數,并將非最終步驟設置為'passthrough'
:
>>> from sklearn.linear_model import LogisticRegression
>>> param_grid = dict(reduce_dim=['passthrough', PCA(5), PCA(10)],
... clf=[SVC(), LogisticRegression()],
... clf__C=[0.1, 10, 100])
>>> grid_search = GridSearchCV(pipe, param_grid=param_grid)
管道的估算器可以通過索引檢索:
>>> pipe[0]
PCA()
或通過名稱:
>>> pipe['reduce_dim']
PCA()
示例
也可以參閱:
6.1.1.2 注釋
管道調用fit
方法與依次調用每個估計器的fit
方法效果相同(transform
輸入并將其傳遞到下一步)。管道具有管道中最后一個估計器的所有方法,即,如果最后一個估計器是一個分類器,則Pipeline
可以用作分類器。如果最后一個估計器是轉換器,那么管道可以用作轉換器。
6.1.1.3 緩存轉換器:避免重復計算
適配轉換器很耗費計算資源,通過設置memory
參數, Pipeline
將在調用fit
方法后緩存每個轉換器。如果參數和輸入數據一致,則此功能可避免重復計算適配管道內的轉換器。一個典型的例子是網格搜索中,轉換器只需適配一次即可應用于每種配置。
memory
參數用以緩存轉換器。 memory
可以是包含緩存轉換器的目錄的字符串,也可以是一個joblib.Memory 對象:
>>> from tempfile import mkdtemp
>>> from shutil import rmtree
>>> from sklearn.decomposition import PCA
>>> from sklearn.svm import SVC
>>> from sklearn.pipeline import Pipeline
>>> estimators = [('reduce_dim', PCA()), ('clf', SVC())]
>>> cachedir = mkdtemp()
>>> pipe = Pipeline(estimators, memory=cachedir)
>>> pipe
Pipeline(memory=...,
steps=[('reduce_dim', PCA()), ('clf', SVC())])
>>> # Clear the cache directory when you don't need it anymore
>>> rmtree(cachedir)
警告:緩存轉換器的副作用
使用未啟用緩存功能的
Pipeline
,可以檢查原始實例,例如:
>>> from sklearn.datasets import load_digits
>>> X_digits, y_digits = load_digits(return_X_y=True)
>>> pca1 = PCA()
>>> svm1 = SVC()
>>> pipe = Pipeline([('reduce_dim', pca1), ('clf', svm1)])
>>> pipe.fit(X_digits, y_digits)
Pipeline(steps=[('reduce_dim', PCA()), ('clf', SVC())])
>>> # The pca instance can be inspected directly
>>> print(pca1.components_)
[[-1.77484909e-19 ... 4.07058917e-18]]啟用緩存會在適配前觸發轉換器的克隆。因此,管道的轉換器實例不能直接進行查看。在下面的示例中,訪問
PCA
實例pca2
將引發AttributeError
,因為pca2
是未進行適配的轉換器,應該使用屬性named_steps
檢查管道中的評估器:
>>> cachedir = mkdtemp()
>>> pca2 = PCA()
>>> svm2 = SVC()
>>> cached_pipe = Pipeline([('reduce_dim', pca2), ('clf', svm2)],
... memory=cachedir)
>>> cached_pipe.fit(X_digits, y_digits)
Pipeline(memory=...,
steps=[('reduce_dim', PCA()), ('clf', SVC())])
>>> print(cached_pipe.named_steps['reduce_dim'].components_)
[[-1.77484909e-19 ... 4.07058917e-18]]
>>> # Remove the cache directory
>>> rmtree(cachedir)
示例:
6.1.2 回歸中轉換目標
TransformedTargetRegressor
在擬合回歸模型之前先轉換目標y
。通過逆變換將預測映射回原始空間。它以將用于預測的回歸器和將應用于目標變量的轉換器作為參數:
>>> import numpy as np
>>> from sklearn.datasets import load_boston
>>> from sklearn.compose import TransformedTargetRegressor
>>> from sklearn.preprocessing import QuantileTransformer
>>> from sklearn.linear_model import LinearRegression
>>> from sklearn.model_selection import train_test_split
>>> X, y = load_boston(return_X_y=True)
>>> transformer = QuantileTransformer(output_distribution='normal')
>>> regressor = LinearRegression()
>>> regr = TransformedTargetRegressor(regressor=regressor,
... transformer=transformer)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
>>> regr.fit(X_train, y_train)
TransformedTargetRegressor(...)
>>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test)))
R2 score: 0.67
>>> raw_target_regr = LinearRegression().fit(X_train, y_train)
>>> print('R2 score: {0:.2f}'.format(raw_target_regr.score(X_test, y_test)))
R2 score: 0.64
對于簡單的轉換,可以傳遞一對函數,而不是Transformer對象,來定義轉換及其逆映射:
>>> def func(x):
... return np.log(x)
>>> def inverse_func(x):
... return np.exp(x)
隨后,該對象創建為:
>>> regr = TransformedTargetRegressor(regressor=regressor,
... func=func,
... inverse_func=inverse_func)
>>> regr.fit(X_train, y_train)
TransformedTargetRegressor(...)
>>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test)))
R2 score: 0.65
默認情況下,所提供的函數在每次擬合時都檢查是否彼此相反。但是,可以通過設置check_inverse
為False
來繞過此檢查 :
>>> def inverse_func(x):
... return x
>>> regr = TransformedTargetRegressor(regressor=regressor,
... func=func,
... inverse_func=inverse_func,
... check_inverse=False)
>>> regr.fit(X_train, y_train)
TransformedTargetRegressor(...)
>>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test)))
R2 score: -4.50
**注意:**所述轉化可以通過設定任一方式觸發,
transformer
或雙功能參數func
以及inverse_func
。但是,同時設置這兩個選項將引發錯誤。示例
6.1.3 聯合特征(FeatureUnion):復合特征空間
FeatureUnion
將多個轉換器對象組合為一個新的轉換器,該轉換器將多個轉換器的輸出合并在一起。
一個 FeatureUnion
包含一個轉換器對象列表。在擬合期間,每個參數都獨立地適配數據。并行應用這些轉換器,并將它們輸出的特征矩陣并排連接成一個更大的矩陣。
如果您想對數據的每個字段應用不同的轉換,請參閱相關的類sklearn.compose.ColumnTransformer
(請參閱用戶指南)。
FeatureUnion
具有與Pipeline
相同的目的--便利性及聯合參數估計和驗證。
結合FeatureUnion
和Pipeline
可以創建復雜的模型。
(一個FeatureUnion
無法檢查兩個轉換器是否可能產生相同的功能。僅當特征集合不相交時才產生并集,并確保是調用者的責任。)
6.1.3.1 用法
FeatureUnion是使用(key, value)
對的列表構建的,其中key
是要為給定的轉換指定的名稱(任意字符串;它僅用作標識符),而value1
是估計對象:
>>> from sklearn.pipeline import FeatureUnion
>>> from sklearn.decomposition import PCA
>>> from sklearn.decomposition import KernelPCA
>>> estimators = [('linear_pca', PCA()), ('kernel_pca', KernelPCA())]
>>> combined = FeatureUnion(estimators)
>>> combined
FeatureUnion(transformer_list=[('linear_pca', PCA()),
('kernel_pca', KernelPCA())])
像管道一樣,特征聯合也有一個簡稱的構造函數:make_union
,該構造函數不需要顯式命名組件。
像Pipeline
一樣,可以使用set_params
替換各個單獨的步驟,并通過設置'drop'
來忽略它們:
>>> combined.set_params(kernel_pca='drop')
FeatureUnion(transformer_list=[('linear_pca', PCA()),
('kernel_pca', 'drop')])
示例:
6.1.4 異構數據的列轉換器
許多數據集包含不同類型的特征,例如文本,浮點數和日期,其中每種類型的特征都需要單獨的預處理或特征提取步驟。通常,在應用scikit-learn方法之前,對數據進行預處理最容易,例如使用pandas。由于下列原因之一,在將數據傳遞給scikit-learn之前進行處理可能會出現問題:
將測試數據中的統計信息合并到預處理器中,會使交叉驗證得分不可靠(稱為數據泄漏),例如在定標器或估算缺失值的情況下。 您可能希望通過參數搜索調整預處理器中的參數
ColumnTransformer
有助于在管道內對數據的不同列進行不同的變換,以防止數據泄漏并且可以對其進行參數化。ColumnTransformer
適用于數組,稀疏矩陣和 pandas DataFrames。
可以對每列應用不同的轉換,例如預處理或特定的特征提取方法:
>>> import pandas as pd
>>> X = pd.DataFrame(
... {'city': ['London', 'London', 'Paris', 'Sallisaw'],
... 'title': ["His Last Bow", "How Watson Learned the Trick",
... "A Moveable Feast", "The Grapes of Wrath"],
... 'expert_rating': [5, 3, 4, 5],
... 'user_rating': [4, 5, 4, 3]})
對于此數據,我們可能希望使用preprocessing.OneHotEncoder
將city
列編碼為類別變量,同時將feature_extraction.text.CountVectorizer
應用于“title
列。由于我們可能在同一列上使用多種特征提取方法,因此我們給每個轉換器一個唯一的名稱,例如'city_category'
和'title_bow'
。默認情況下,其余的評分列將被忽略(remainder='drop'
):
>>> from sklearn.compose import ColumnTransformer
>>> from sklearn.feature_extraction.text import CountVectorizer
>>> from sklearn.preprocessing import OneHotEncoder
>>> column_trans = ColumnTransformer(
... [('city_category', OneHotEncoder(dtype='int'),['city']),
... ('title_bow', CountVectorizer(), 'title')],
... remainder='drop')
>>> column_trans.fit(X)
ColumnTransformer(transformers=[('city_category', OneHotEncoder(dtype='int'),
['city']),
('title_bow', CountVectorizer(), 'title')])
>>> column_trans.get_feature_names()
['city_category__x0_London', 'city_category__x0_Paris', 'city_category__x0_Sallisaw',
'title_bow__bow', 'title_bow__feast', 'title_bow__grapes', 'title_bow__his',
'title_bow__how', 'title_bow__last', 'title_bow__learned', 'title_bow__moveable',
'title_bow__of', 'title_bow__the', 'title_bow__trick', 'title_bow__watson',
'title_bow__wrath']
>>> column_trans.transform(X).toarray()
array([[1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1]]...)
在上面的示例中, CountVectorizer
期望將一維數組作為輸入,因此將列指定為字符串('title'
)。然而,由于preprocessing.OneHotEncoder
像其他大多數轉換器一樣,都希望使用2D數據,因此在這種情況下,您需要將列指定為字符串列表(['city']
)。
除了標量或單個項目列表之外,可以將列選擇指定為多個項目的列表,整數數組,切片,布爾掩碼或使用make_column_selector
。make_column_selector
用于根據數據類型或列名稱來選擇列:
>>> from sklearn.preprocessing import StandardScaler
>>> from sklearn.compose import make_column_selector
>>> ct = ColumnTransformer([
... ('scale', StandardScaler(),
... make_column_selector(dtype_include=np.number)),
... ('onehot',
... OneHotEncoder(),
... make_column_selector(pattern='city', dtype_include=object))])
>>> ct.fit_transform(X)
array([[ 0.904..., 0. , 1. , 0. , 0. ],
[-1.507..., 1.414..., 1. , 0. , 0. ],
[-0.301..., 0. , 0. , 1. , 0. ],
[ 0.904..., -1.414..., 0. , 0. , 1. ]])
如果輸入是DataFrame,則字符串可以引用列,整數始終被解釋為位置列。
通過設置,我們可以通過設置 remainder='passthrough'
保留其余的評分欄。這些值將附加到轉換的末尾:
>>> column_trans = ColumnTransformer(
... [('city_category', OneHotEncoder(dtype='int'),['city']),
... ('title_bow', CountVectorizer(), 'title')],
... remainder='passthrough')
>>> column_trans.fit_transform(X)
array([[1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 5, 4],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 3, 5],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, 4],
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 5, 3]]...)
通過remainder
參數設置來估計改造其余評級列。轉換后的值將附加到轉換的末尾:
>>> from sklearn.preprocessing import MinMaxScaler
>>> column_trans = ColumnTransformer(
... [('city_category', OneHotEncoder(), ['city']),
... ('title_bow', CountVectorizer(), 'title')],
... remainder=MinMaxScaler())
>>> column_trans.fit_transform(X)[:, -2:]
array([[1. , 0.5],
[0. , 1. ],
[0.5, 0.5],
[1. , 0. ]])
make_column_transformer
函數可用于更輕松地創建ColumnTransformer
對象。具體來說,名稱將自動給出。以上示例的等效項為:
>>> from sklearn.compose import make_column_transformer
>>> column_trans = make_column_transformer(
... (OneHotEncoder(), ['city']),
... (CountVectorizer(), 'title'),
... remainder=MinMaxScaler())
>>> column_trans
ColumnTransformer(remainder=MinMaxScaler(),
transformers=[('onehotencoder', OneHotEncoder(), ['city']),
('countvectorizer', CountVectorizer(),
'title')])
6.1.5 可視化復合估計器
在jupyter notebook中顯示估算器時,可以用HTML形式顯示估算器。這對于許多使用估算器診斷或可視化管道很有用。通過在 sklearn.set_config
中設置display
選項激活此可視化:
>>> from sklearn import set_config
>>> set_config(display='diagram')
>>> # diplays HTML representation in a jupyter context
>>> column_trans
HTML輸出的示例可以在具有混合類型的Column Transformer的輸出為HTML形式的Pipeline中看到 。或者,可以使用以下命令將HTML寫入文件 :estimator_html_repr
>>> from sklearn.utils import estimator_html_repr
>>> with open('my_estimator.html', 'w') as f:
... f.write(estimator_html_repr(clf))
示例: