Hands-on Machine Learning with Scikit Learn, Keras & TensorFlow
Charpter two 学习笔记(小白的心酸)
Custom transformers(定制变换器)
尽管scikit-learn提供了许多有用的变换器,但是有时我们也需要为一些任务编写自己的变换器,比如自定义清理操作或组合特定属性。你会希望你的转换器能够无缝地使用scikit-learn功能(比如pipelines),而且由于scikit-learn依赖于duck类型(而不是inheritance),你所需要做的就是创建一个类并实现三个方法:
fit(), transform(), fit_transform()
您可以通过简单地添加TransformerMixin作为基类来获得fit_transform()。(如果使用TransformerMixin作为基类,只需定义fit()和tramsform()即可自动实现fit_transform()函数)如果您如果添加BaseEstimator作为基类,,注意此时__init__函数不能接受*args和 **kwargs,还可以使用两个额外的方法(get_params()和set_params()),这将有助于自动超参数调优。
例如下面这个小的transformer类, 它添加了我们之前讨论过的组合属性。
from sklearn.base import BaseEstimator, TransformerMixin
# get the right column indices: safer than hard-coding indices 3, 4, 5, 6
rooms_ix, bedrooms_ix, population_ix, household_ix = [
list(housing.columns).index(col)
for col in ("total_rooms", "total_bedrooms", "population", "households")]
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
def __init__(self, add_bedrooms_per_room): # no *args or **kwargs
self.add_bedrooms_per_room = add_bedrooms_per_room
def fit(self, X, y=None):
return self # nothing else to do
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]
调用这个transformer时只需要
attr_adder = CombinedAttributesAdder(add_bedrooms_per_room=False)
housing_extra_attribs = attr_adder.fit_transform(housing.values)
这个例子中,transformer 只有一个超参数,add_bedrooms_per_room, 默认设置为True,超参数可以让你很容易发现添加这个属性是否有助于机器学习算法。更一般的,您可以添加一个超参数排除任何你不能100%确定的数据准备步骤。这些数据准备步骤的自动化程度越高,可以自动尝试的组合就越多,从而更有可能找到理想的组合(并为您节省大量时间)。
Transformation Pipeline(变换器 pipeline)
有许多数据转换步骤需要按正确的顺序执行。幸运的是,Scikit-Learn提供了Pipeline类来帮助处理这样的转换序列。下面是一个用于数值属性的小pipeline:
from sklearn.pipeline import Pipeline
num_pipeline = Pipeline([
('imputer', SimpleImputer(strategy="median")),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler()),
])
housing_num_tr = num_pipeline.fit_transform(housing_num)
Pipeline构造函数接受定义一系列步骤的名称/估计器对的列表。除最后一个估计器外的所有估计器都必须是transformer(即它们必须具有fit_transform()方法)。名称可以是你想要的任何名称(只要它们是唯一的并且不包含双下划线);它们将在以后的超参数调优中派上用场。
当您调用pipeline的fit()方法时,它会依次对所有转换器调用fit_transform(),将每次调用的输出作为参数传递给下一次调用,直到它到达最终的估计值,并为此调用fit()方法。
pipeline公开与最终评估器相同的方法。在本例中,最后一个估计器是StandardScaler,它是一个转换器,因此管道有一个transform()方法,它按顺序将所有转换应用到数据(当然还有一个fit_transform()方法)。
到目前为止,我们已经分别处理了分类属性的列(text attribute)和数字属性的列(numerical attribute)。如果使用一个转换器来处理所有列,并对每个列应用适当的转换,将会更加方便。在0.20版本中,Scikit-Learn为此引入了ColumnTransformer,其优点是它可以很好地处理pandas DataFrames。
from sklearn.compose import ColumnTransformer
num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]
full_pipeline = ColumnTransformer([
("num", num_pipeline, num_attribs),
("cat", OneHotEncoder(), cat_attribs),
])
housing_prepared = full_pipeline.fit_transform(housing)
首先我们导入columnTransform类,然后获得数字列名和分类列名的列表,columnTransform类即创建完成。
constructor 包括一个元组列表,其中每个元组包含一个名称、一个transformer和要用到该transformer的目标的列名。
在本例中,我们指定数字列由num_pipeline(转换器为文中前面声明)进行转换,指定分类列由OneHotEncoder转换。
注意,OneHotEncoder返回一个稀疏矩阵,num_pipeline返回密集矩阵。当存在这种稀疏矩阵与密集矩阵混合的情况时,如果密度低于给定的阈值时(默认情况下,稀疏阈值为0.3),则返回稀疏矩阵,本例返回密集矩阵。
如果你使用的是scikit-learn 0.19或更早的版本,那么您可以使用第三方库,比如sklearn-pandas, 或者你可以推出自己定义的transformer,以获得与ColumnTransformer相同的功能。或者您可以使用FeatureUnion类,它可以应用不同的transformer并将其输出连接起来。但是您不能为每个transformer指定不同的列,它们都适用于整个数据。可以使用用于列选择的自定义transformer来解决这个限制(请参阅Jupyter notebook中的示例)。