IT박스

fit_transform ()은 2 개의 위치 인수를 사용하지만 3 개는 LabelBinarizer로 제공되었습니다.

itboxs 2020. 11. 14. 10:01
반응형

fit_transform ()은 2 개의 위치 인수를 사용하지만 3 개는 LabelBinarizer로 제공되었습니다.


저는 기계 학습을 처음 접했고 감독되지 않은 학습 기술로 작업 해 왔습니다.

이미지는 내 샘플 데이터를 보여줍니다 (모든 청소 후) 스크린 샷 : 샘플 데이터

데이터 정리를 위해 빌드 된이 두 개의 Pipline이 있습니다.

num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]

print(type(num_attribs))

num_pipeline = Pipeline([
    ('selector', DataFrameSelector(num_attribs)),
    ('imputer', Imputer(strategy="median")),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scaler', StandardScaler()),
])

cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(cat_attribs)),
    ('label_binarizer', LabelBinarizer())
])

그런 다음이 두 파이프 라인의 결합을 수행했으며 동일한 코드는 아래와 같습니다.

from sklearn.pipeline import FeatureUnion

full_pipeline = FeatureUnion(transformer_list=[
        ("num_pipeline", num_pipeline),
        ("cat_pipeline", cat_pipeline),
    ])

이제 데이터 에 대해 fit_transform을 시도하고 있지만 오류가 표시됩니다.

변환 코드 :

housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared

오류 메시지 : fit_transform ()은 2 개의 위치 인수를 사용하지만 3 개가 제공되었습니다.


문제 :

파이프 라인은 LabelBinarizer의 fit_transform메서드가 세 가지 위치 인수를 사용하도록 정의되어 있다고 가정합니다 .

def fit_transform(self, x, y)
    ...rest of the code

두 가지만 취하도록 정의되어 있습니다.

def fit_transform(self, x):
    ...rest of the code

가능한 해결책:

이는 3 개의 위치 인수를 처리 할 수있는 사용자 지정 변환기를 만들어 해결할 수 있습니다.

  1. 새 클래스 가져 오기 및 만들기 :

    from sklearn.base import TransformerMixin #gives fit_transform method for free
    class MyLabelBinarizer(TransformerMixin):
        def __init__(self, *args, **kwargs):
            self.encoder = LabelBinarizer(*args, **kwargs)
        def fit(self, x, y=0):
            self.encoder.fit(x)
            return self
        def transform(self, x, y=0):
            return self.encoder.transform(x)
    
  2. LabelBinarizer ()를 사용하는 대신 코드를 동일하게 유지하고 우리가 만든 클래스 인 MyLabelBinarizer ()를 사용하십시오.


참고 : LabelBinarizer 속성 (예 : classes_)에 액세스하려면 다음 줄을 fit메서드에 추가하십시오 .

    self.classes_, self.y_type_, self.sparse_input_ = self.encoder.classes_, self.encoder.y_type_, self.encoder.sparse_input_

귀하의 예는 Hands-On Machine Learning with Scikit-Learn & TensorFlow 책에서 나온 것이라고 생각합니다 . 불행히도이 문제도 발생했습니다. 최근 변경된 scikit-learn( 0.19.0) LabelBinarizerfit_transform방법이 변경되었습니다 . 불행히도 LabelBinarizer그 예제가 그것을 사용하는 방식으로 작동하도록 의도되지 않았습니다. 여기여기 에서 변경 사항에 대한 정보를 볼 수 있습니다 .

이에 대한 해결책을 찾을 때까지 0.18.0다음과 같이 이전 버전 ( )을 설치할 수 있습니다 .

$ pip install scikit-learn==0.18.0

이를 실행하면 코드가 문제없이 실행됩니다.

미래에는 CategoricalEncoder클래스 나 이와 유사한 것을 사용하는 것이 올바른 해결책이 될 것 같습니다 . 그들은 분명히 수년 동안이 문제를 해결하려고 노력해 왔습니다. 당신은 새로운 클래스 볼 수 있습니다 여기에 문제의 추가 논의를 여기에 .


LabelBinarizer는 2 개 이상의 위치 인수를 허용하지 않으므로 다음과 같은 사용자 지정 이진화기를 만들어야합니다.

class CustomLabelBinarizer(BaseEstimator, TransformerMixin):
    def __init__(self, sparse_output=False):
        self.sparse_output = sparse_output
    def fit(self, X, y=None):
        return self
    def transform(self, X, y=None):
        enc = LabelBinarizer(sparse_output=self.sparse_output)
        return enc.fit_transform(X)

num_attribs = list(housing_num)
cat_attribs = ['ocean_proximity']

num_pipeline = Pipeline([
    ('selector', DataFrameSelector(num_attribs)),
    ('imputer', Imputer(strategy='median')),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scalar', StandardScaler())
])

cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(cat_attribs)),
    ('label_binarizer', CustomLabelBinarizer())
])

full_pipeline = FeatureUnion(transformer_list=[
    ('num_pipeline', num_pipeline),
    ('cat_pipeline', cat_pipeline)
])

housing_prepared = full_pipeline.fit_transform(new_housing)

동일한 문제가 발생하여 책의 Github repo에 지정된 해결 방법을 적용하여 작동했습니다 .

경고 : 이전 버전의 책은이 시점에서 LabelBinarizer 클래스를 사용했습니다. 다시 말하지만 이것은 잘못된 것입니다. LabelEncoder 클래스와 마찬가지로 LabelBinarizer 클래스는 입력 기능이 아닌 레이블을 전처리하도록 설계되었습니다. 더 나은 솔루션은 Scikit-Learn의 곧 출시 될 CategoricalEncoder 클래스를 사용하는 것입니다. 곧 Scikit-Learn에 추가 될 예정이며 그 동안 아래 코드를 사용할 수 있습니다 (Pull Request # 9151 에서 복사 됨 ).

몇 가지 문제를 해결하려면 여기에 해결 방법이 있습니다. 이전 셀에 붙여넣고 실행하면됩니다.

# Definition of the CategoricalEncoder class, copied from PR #9151.
# Just run this cell, or copy it to your code, do not try to understand it (yet).

from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils import check_array
from sklearn.preprocessing import LabelEncoder
from scipy import sparse

class CategoricalEncoder(BaseEstimator, TransformerMixin):
    def __init__(self, encoding='onehot', categories='auto', dtype=np.float64,
                 handle_unknown='error'):
        self.encoding = encoding
        self.categories = categories
        self.dtype = dtype
        self.handle_unknown = handle_unknown

    def fit(self, X, y=None):
        """Fit the CategoricalEncoder to X.
        Parameters
        ----------
        X : array-like, shape [n_samples, n_feature]
            The data to determine the categories of each feature.
        Returns
        -------
        self
        """

        if self.encoding not in ['onehot', 'onehot-dense', 'ordinal']:
            template = ("encoding should be either 'onehot', 'onehot-dense' "
                        "or 'ordinal', got %s")
            raise ValueError(template % self.handle_unknown)

        if self.handle_unknown not in ['error', 'ignore']:
            template = ("handle_unknown should be either 'error' or "
                        "'ignore', got %s")
            raise ValueError(template % self.handle_unknown)

        if self.encoding == 'ordinal' and self.handle_unknown == 'ignore':
            raise ValueError("handle_unknown='ignore' is not supported for"
                             " encoding='ordinal'")

        X = check_array(X, dtype=np.object, accept_sparse='csc', copy=True)
        n_samples, n_features = X.shape

        self._label_encoders_ = [LabelEncoder() for _ in range(n_features)]

        for i in range(n_features):
            le = self._label_encoders_[i]
            Xi = X[:, i]
            if self.categories == 'auto':
                le.fit(Xi)
            else:
                valid_mask = np.in1d(Xi, self.categories[i])
                if not np.all(valid_mask):
                    if self.handle_unknown == 'error':
                        diff = np.unique(Xi[~valid_mask])
                        msg = ("Found unknown categories {0} in column {1}"
                               " during fit".format(diff, i))
                        raise ValueError(msg)
                le.classes_ = np.array(np.sort(self.categories[i]))

        self.categories_ = [le.classes_ for le in self._label_encoders_]

        return self

    def transform(self, X):
        """Transform X using one-hot encoding.
        Parameters
        ----------
        X : array-like, shape [n_samples, n_features]
            The data to encode.
        Returns
        -------
        X_out : sparse matrix or a 2-d array
            Transformed input.
        """
        X = check_array(X, accept_sparse='csc', dtype=np.object, copy=True)
        n_samples, n_features = X.shape
        X_int = np.zeros_like(X, dtype=np.int)
        X_mask = np.ones_like(X, dtype=np.bool)

        for i in range(n_features):
            valid_mask = np.in1d(X[:, i], self.categories_[i])

            if not np.all(valid_mask):
                if self.handle_unknown == 'error':
                    diff = np.unique(X[~valid_mask, i])
                    msg = ("Found unknown categories {0} in column {1}"
                           " during transform".format(diff, i))
                    raise ValueError(msg)
                else:
                    # 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
                    X[:, i][~valid_mask] = self.categories_[i][0]
            X_int[:, i] = self._label_encoders_[i].transform(X[:, i])

        if self.encoding == 'ordinal':
            return X_int.astype(self.dtype, copy=False)

        mask = X_mask.ravel()
        n_values = [cats.shape[0] for cats in self.categories_]
        n_values = np.array([0] + n_values)
        indices = np.cumsum(n_values)

        column_indices = (X_int + indices[:-1]).ravel()[mask]
        row_indices = np.repeat(np.arange(n_samples, dtype=np.int32),
                                n_features)[mask]
        data = np.ones(n_samples * n_features)[mask]

        out = sparse.csc_matrix((data, (row_indices, column_indices)),
                                shape=(n_samples, indices[-1]),
                                dtype=self.dtype).tocsr()
        if self.encoding == 'onehot-dense':
            return out.toarray()
        else:
            return out

Scikit Learn 및 Tensorflow를 사용한 Hands on Machine Learning 책의 예제를 살펴보고 있다고 생각합니다 . 2 장의 예제를 진행할 때 동일한 문제가 발생했습니다.

다른 사람들이 언급했듯이 문제는 sklearn의 LabelBinarizer와 관련이 있습니다. 파이프 라인의 다른 변환기에 비해 fit_transform 메서드에서 인수가 덜 걸립니다. (다른 트랜스포머가 일반적으로 X와 y를 모두 사용하는 경우에만 y, 자세한 내용은 여기 를 참조하십시오). 그래서 pipeline.fit_transform을 실행할 때 필요한 것보다 더 많은 인수를이 변환기에 입력했습니다.

내가 사용한 쉬운 수정은 OneHotEncoder를 사용하고 "sparse"를 False로 설정하여 출력이 num_pipeline 출력과 동일한 numpy 배열인지 확인하는 것입니다. (이렇게하면 사용자 지정 인코더를 코딩 할 필요가 없습니다)

원래 cat_pipeline :

cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('label_binarizer', LabelBinarizer())
])

이 부분을 다음과 같이 간단히 변경할 수 있습니다.

cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('one_hot_encoder', OneHotEncoder(sparse=False))
])

여기에서 갈 수 있으며 모든 것이 작동합니다.


간단히 말해, 파이프 라인 바로 전에 다음 클래스를 정의하면됩니다.

class NewLabelBinarizer(LabelBinarizer):
    def fit(self, X, y=None):
        return super(NewLabelBinarizer, self).fit(X)
    def transform(self, X, y=None):
        return super(NewLabelBinarizer, self).transform(X)
    def fit_transform(self, X, y=None):
        return super(NewLabelBinarizer, self).fit(X).transform(X)

그런 다음 나머지 코드는 cat_pipeline파이프 라인 연결 이전에 약간 수정하여 책에서 언급 한 것과 같습니다. 다음과 같이하십시오.

cat_pipeline = Pipeline([
    ("selector", DataFrameSelector(cat_attribs)),
    ("label_binarizer", NewLabelBinarizer())])

완료했습니다!


LaberBinarizer를 잊어 버리고 대신 OneHotEncoder를 사용하십시오.

OneHotEncoder 이전에 LabelEncoder를 사용하여 범주를 정수로 변환하는 경우 이제 OneHotEncoder를 직접 사용할 수 있습니다.


동일한 문제가 발생하여 DataFrameMapper를 사용하여 해결되었습니다 (sklearn_pandas를 설치해야 함).

from sklearn_pandas import DataFrameMapper
cat_pipeline = Pipeline([
    ('label_binarizer', DataFrameMapper([(cat_attribs, LabelBinarizer())])),
])

나는 결국 내 자신을 굴려

class LabelBinarizer(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        X = self.prep(X)
        unique_vals = []
        for column in X.T:
            unique_vals.append(np.unique(column))
        self.unique_vals = unique_vals
    def transform(self, X, y=None):
        X = self.prep(X)
        unique_vals = self.unique_vals
        new_columns = []
        for i, column in enumerate(X.T):
            num_uniq_vals = len(unique_vals[i])
            encoder_ring = dict(zip(unique_vals[i], range(len(unique_vals[i]))))
            f = lambda val: encoder_ring[val]
            f = np.vectorize(f, otypes=[np.int])
            new_column = np.array([f(column)])
            if num_uniq_vals <= 2:
                new_columns.append(new_column)
            else:
                one_hots = np.zeros([num_uniq_vals, len(column)], np.int)
                one_hots[new_column, range(len(column))]=1
                new_columns.append(one_hots)
        new_columns = np.concatenate(new_columns, axis=0).T        
        return new_columns

    def fit_transform(self, X, y=None):
        self.fit(X)
        return self.transform(X)

    @staticmethod
    def prep(X):
        shape = X.shape
        if len(shape) == 1:
            X = X.values.reshape(shape[0], 1)
        return X

일하는 것 같다

lbn = LabelBinarizer()
thingy = np.array([['male','male','female', 'male'], ['A', 'B', 'A', 'C']]).T
lbn.fit(thingy)
lbn.transform(thingy)

보고

array([[1, 1, 0, 0],
       [1, 0, 1, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 1]])

인코딩을 수행하는 사용자 지정 변환기를 하나 더 만들 수 있습니다.

class CustomLabelEncode(BaseEstimator, TransformerMixin):
    def fit(self, X, y=None):
        return self
    def transform(self, X):
        return LabelEncoder().fit_transform(X);

이 예에서는 LabelEncoding을 수행했지만 LabelBinarizer도 사용할 수 있습니다.


나는 또한 같은 문제에 직면했습니다. 다음 링크는이 문제를 해결하는 데 도움이되었습니다. https://github.com/ageron/handson-ml/issues/75

변경 사항 요약

1) Define following class in your notebook

class SupervisionFriendlyLabelBinarizer(LabelBinarizer):

   def fit_transform(self, X, y=None):

       return super(SupervisionFriendlyLabelBinarizer, self).fit_transform(X)

2) Modify following piece of code

  cat_pipeline = Pipeline([('selector', DataFrameSelector(cat_attribs)),
                     ('label_binarizer', SupervisionFriendlyLabelBinarizer()),])

3) Re-run the notebook. You will be able to run now


To perform one-hot encoding for multiple categorical features, we can create a new class which customizes our own multiple categorical features binarizer and plug it into categorical pipeline as follows.

Suppose CAT_FEATURES = ['cat_feature1', 'cat_feature2'] is a list of categorical features. The following scripts shall resolve the issue and produce what we want.

import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin

class CustomLabelBinarizer(BaseEstimator, TransformerMixin):
    """Perform one-hot encoding to categorical features."""
    def __init__(self, cat_features):
        self.cat_features = cat_features

    def fit(self, X_cat, y=None):
        return self

    def transform(self, X_cat):
        X_cat_df = pd.DataFrame(X_cat, columns=self.cat_features)
        X_onehot_df = pd.get_dummies(X_cat_df, columns=self.cat_features)
        return X_onehot_df.values

# Pipeline for categorical features.
cat_pipeline = Pipeline([
    ('selector', DataFrameSelector(CAT_FEATURES)),
    ('onehot_encoder', CustomLabelBinarizer(CAT_FEATURES))
])

참고URL : https://stackoverflow.com/questions/46162855/fit-transform-takes-2-positional-arguments-but-3-were-given-with-labelbinarize

반응형