sklearn——转换器(Transformer)与预估器(estimator)

sklearn——转换器(Transformer)与预估器(estimator)

文章目录

在我之前接触的sklearn中,有 SimpleImputerOrdinalEncoderOneHotEncoder,他们的共同点是都用于对数据特征预处理。

在对他们的使用中都无法避免遇到:fit_transformtransform。初时,还没有充分了解的我只是记住了这一特征,最后了解到 sklearnTransformerestimator的概念,认为有必要整合一下。

转换器 Transformer

由于在进行模型fit之前,需要先对数据集进行预处理,这里包括对空值的处理、对object变量的预处理。

可以看作需要对数据集进行一个转换,则这个时候就需要用上Transformer了,本质上SimpleImputerOrdinalEncoderOneHotEncoder 都属于 Transformer。

例如,原数据 X_train 经过一个SimpleImputer 后,得到了经过空值插补的新数据集 X_train_new

fit 与 fit_transform 与 transform

SimpleImputer 为例,若需要均值插补,那么在插补之前首先需要知道空值对应列的均值是多少。

fit 是先计算矩阵空值(缺失值)对应列的相关值大小,如均值、中位数、频率最高的数。

transform 则在根据fit 计算的值对输入的数据集进行变换。

故,在对一个新的数据集处理之前,需要先fit 一次,得到相关值,在transform 进行转换。但一般情况下,我们会直接使用 fit_transform

fit_transform 内部是先对数据集进行 fit,在顺势对其调用transform

>>>import numpy as np
>>>from sklearn.impute import SimpleImputer
>>>imp = SimpleImputer(missing_values=np.nan, strategy='mean')
>>>imp.fit([[1, 8], [np.nan, 4], [7, 6]])
SimpleImputer()
>>>X
array([[nan,  2.],
 [ 6., nan],
 [ 7., nan]])
>>>print(imp.transform(X))
[[4. 2.]
 [6. 6.]
 [7. 6.]]
>>> print(imp.fit_transform(X))
[[6.5 2. ]
 [6.  2. ]
 [7.  2. ]]

第10行是根据提前输入的数据进行fit, 再根据fit 保存的均值,来对X进行 transform。输入的数组第一列平均值为 (1 + 7)/2 = 4 ,第二列的均值是 (8 + 4 + 6)/3 = 6,即除数是非空值的个数。 得到均值后再直接插入X的空值部分,不考虑 X 的数据。

第14行,对 X 直接进行fit_transform 即对X 先 fit ,再对X 进行 transform ,道理同上。

值得注意的是

在对数据集中, 训练集和验证机进行转换的时候,只需要对训练集调用fit即可,对于验证机直接调用transform(),因为为数据集的一致性,对验证集的转换最好同训练集一致。 (例如对训练集进行OrdinalEncoder 编码, 若对验证机也调用 fit_transform,很有可能训练集验证集中,对于同一个 object值的编码不同,比如对训练集中的球的颜色红色编码为 3 ,验证集中却对其编码为 6)

扒拉了下源码(可以不看这部分,看上面结论就够了)

翻了下源码,SimpleImputerOrdinalEncoderOneHotEncoder都有基类class _BaseEncoder(TransformerMixin, BaseEstimator) ,虽然它也有基类TransformerMixin,但是这个代码我没看大明白, 只能看出return self.fit(X, **fit_params).transform(X), 先 fit 了一次,再直接调用了一次 transform

所以简单来说 fit_transform 即 先调用了一次 fit 再调用了一次 transform

class TransformerMixin:
    """Mixin class for all transformers in scikit-learn."""

    def fit_transform(self, X, y=None, **fit_params):
        """
        Fit to data, then transform it.
        拟合数据并转换它
        Fits transformer to `X` and `y` with optional parameters `fit_params`
        and returns a transformed version of `X`.
        Parameters
        ----------
        X : array-like of shape (n_samples, n_features)
            Input samples.
        y :  array-like of shape (n_samples,) or (n_samples, n_outputs), \
                default=None
            Target values (None for unsupervised transformations).
        **fit_params : dict
            Additional fit parameters.
        Returns
        -------
        X_new : ndarray array of shape (n_samples, n_features_new)
            Transformed array.
        """
        # non-optimized default implementation; override when a better
        # method is possible for a given clustering algorithm
        if y is None:
            # fit method of arity 1 (unsupervised transformation)
            return self.fit(X, **fit_params).transform(X)
        else:
            # fit method of arity 2 (supervised transformation)
            return self.fit(X, y, **fit_params).transform(X)

OneHotEncoder 为例,直接看它的,fit 和 transform, 会发现他们都调用了基类_BaseEncoder的 _fit() 和 transform()。

    def _fit(self, X, handle_unknown="error", force_all_finite=True):
        self._check_n_features(X, reset=True)
        self._check_feature_names(X, reset=True)
        X_list, n_samples, n_features = self._check_X(
            X, force_all_finite=force_all_finite
        )
        self.n_features_in_ = n_features

        if self.categories != "auto":
            if len(self.categories) != n_features:
                raise ValueError(
                    "Shape mismatch: if categories is an array,"
                    " it has to be of shape (n_features,)."
                )

        self.categories_ = []

        for i in range(n_features):
            Xi = X_list[i]
            if self.categories == "auto":
                cats = _unique(Xi)
            else:
                cats = np.array(self.categories[i], dtype=Xi.dtype)
                if Xi.dtype.kind not in "OUS":
                    sorted_cats = np.sort(cats)
                    error_msg = (
                        "Unsorted categories are not supported for numerical categories"
                    )
                    # if there are nans, nan should be the last element
                    stop_idx = -1 if np.isnan(sorted_cats[-1]) else None
                    if np.any(sorted_cats[:stop_idx] != cats[:stop_idx]) or (
                        np.isnan(sorted_cats[-1]) and not np.isnan(sorted_cats[-1])
                    ):
                        raise ValueError(error_msg)

                if handle_unknown == "error":
                    diff = _check_unknown(Xi, cats)
                    if diff:
                        msg = (
                            "Found unknown categories {0} in column {1}"
                            " during fit".format(diff, i)
                        )
                        raise ValueError(msg)
            self.categories_.append(cats)

    def _transform(
        self, X, handle_unknown="error", force_all_finite=True, warn_on_unknown=False
    ):
        self._check_feature_names(X, reset=False)
        self._check_n_features(X, reset=False)
        X_list, n_samples, n_features = self._check_X(
            X, force_all_finite=force_all_finite
        )

        X_int = np.zeros((n_samples, n_features), dtype=int)
        X_mask = np.ones((n_samples, n_features), dtype=bool)

        columns_with_unknown = []
        for i in range(n_features):
            Xi = X_list[i]
            diff, valid_mask = _check_unknown(Xi, self.categories_[i], return_mask=True)

            if not np.all(valid_mask):
                if handle_unknown == "error":
                    msg = (
                        "Found unknown categories {0} in column {1}"
                        " during transform".format(diff, i)
                    )
                    raise ValueError(msg)
                else:
                    if warn_on_unknown:
                        columns_with_unknown.append(i)
                    # Set the problematic rows to an acceptable value and
                    # continue `The rows are marked `X_mask` and will be
                    # removed later.
                    X_mask[:, i] = valid_mask
                    # cast Xi into the largest string type necessary
                    # to handle different lengths of numpy strings
                    if (
                        self.categories_[i].dtype.kind in ("U", "S")
                        and self.categories_[i].itemsize > Xi.itemsize
                    ):
                        Xi = Xi.astype(self.categories_[i].dtype)
                    elif self.categories_[i].dtype.kind == "O" and Xi.dtype.kind == "U":
                        # categories are objects and Xi are numpy strings.
                        # Cast Xi to an object dtype to prevent truncation
                        # when setting invalid values.
                        Xi = Xi.astype("O")
                    else:
                        Xi = Xi.copy()

                    Xi[~valid_mask] = self.categories_[i][0]
            # We use check_unknown=False, since _check_unknown was
            # already called above.
            X_int[:, i] = _encode(Xi, uniques=self.categories_[i], check_unknown=False)
        if columns_with_unknown:
            warnings.warn(
                "Found unknown categories in columns "
                f"{columns_with_unknown} during transform. These "
                "unknown categories will be encoded as all zeros",
                UserWarning,
            )

大致读了以下,说实话没有看太明白,但是很容易看出,_fit 和 _transform 在主要的循环部分都多次用到,self.categories_ 这个变量,而这个变量来自于sef._fit() 。 即transform 会直接使用 fit 中存储好的预设值。

预估器 Estimator

即 sklearn 中带有的各种算法模型。无论分类器或者回归都属于此类。

常见基础的如决策树、随机森林。

# 决策树
from sklearn.tree import DecisionTreeRegressor		// 用于回归问题
from sklearn.tree import DecisionTreeClassifier		// 用于分类问题
# 随机胜率
from sklearn.ensemble import RandomForestRegressor
# XGBoost
from xgboost import XGBRegressor

这些预估器在使用上大同小异,以决策树为例。

  • 先实例化一个 esitimator
  • 再对他进行 fit 拟合,注意这里的 fit 是 esitimator 自带的 fit。
  • 再调用 predict 进行预测、预估。
from sklearn.tree import DecisionTreeRegressor		// 用于回归问题
from sklearn.tree import DecisionTreeClassifier		// 用于分类问题

melbourne_model = DecisionTreeRegressor(random_state = 1)

#Fit model
melbourne_model.fit(X,y)

melbourne_model.predict(X.head())   // 进行模型预测

各模型的比较和详细解释不在这里赘述。

谨记,ML的基础永远是数学基础。

上一篇:springboot-knife4j


下一篇:随机森林-sklearn