Je suis totalement novice en apprentissage machine et je travaille avec une technique d'apprentissage non supervisée.
L'image montre mes exemples de données (après tout le nettoyage) Capture d'écran: Exemple de données
J'ai deux Pipline construit pour nettoyer les données:
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())
])
Ensuite, j'ai fait l'union de ces deux pipelines et le code correspondant est affiché ci-dessous:
from sklearn.pipeline import FeatureUnion
full_pipeline = FeatureUnion(transformer_list=[
("num_pipeline", num_pipeline),
("cat_pipeline", cat_pipeline),
])
Maintenant j'essaye de faire fit_transform sur le Data mais sa montre moi l'erreur.
Code de transformation:
housing_prepared = full_pipeline.fit_transform(housing)
housing_prepared
Message d'erreur: Fit_transform () prend 2 arguments de position mais 3 ont été donnés
Le problème:
Le pipeline suppose que la méthode fit_transform
de LabelBinarizer est définie pour prendre trois arguments de position:
def fit_transform(self, x, y)
...rest of the code
alors qu'il est défini pour ne prendre que deux:
def fit_transform(self, x):
...rest of the code
Solution possible:
Cela peut être résolu en créant un transformateur personnalisé pouvant gérer 3 arguments de position:
Importer et créer une nouvelle classe:
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)
Gardez votre code identique au lieu d'utiliser LabelBinarizer (), utilisez la classe que nous avons créée: MyLabelBinarizer ().
fit
: self.classes_, self.y_type_, self.sparse_input_ = self.encoder.classes_, self.encoder.y_type_, self.encoder.sparse_input_
Je crois que votre exemple est tiré du livre Hands-On Machine Learning avec Scikit-Learn & TensorFlow. Malheureusement, j'ai rencontré ce problème aussi. Une modification récente de scikit-learn
(0.19.0
) a modifié la méthode LabelBinarizer
de fit_transform
. Malheureusement, LabelBinarizer
n'a jamais été conçu pour que cet exemple l'utilise. Vous pouvez voir des informations sur le changement ici et ici .
Jusqu'à ce qu'ils trouvent une solution à cela, vous pouvez installer la version précédente (0.18.0
) comme suit:
$ pip install scikit-learn==0.18.0
Après avoir exécuté cela, votre code devrait être exécuté sans problème.
À l'avenir, il semblerait que la solution correcte consisterait à utiliser une classe CategoricalEncoder
ou quelque chose de similaire. Ils tentent apparemment de résoudre ce problème depuis des années. Vous pouvez voir la nouvelle classe ici et une discussion plus approfondie du problème ici .
LabelBinarizer n'autorisant pas plus de 2 arguments de position, vous devez créer votre binariseur personnalisé comme
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)
J'ai rencontré le même problème et je l'ai obtenu en appliquant la solution de contournement spécifiée dans le rapport Github du livre .
Avertissement: les versions précédentes du livre utilisaient la classe LabelBinarizer à ce point. Encore une fois, c'était incorrect: tout comme le LabelEncoder classe, la classe LabelBinarizer a été conçue pour pré-traiter les étiquettes, pas caractéristiques d'entrée. Une meilleure solution consiste à utiliser la prochaine version de Scikit-Learn Classe CategoricalEncoder: elle sera bientôt ajoutée à Scikit-Learn et à entre-temps, vous pouvez utiliser le code ci-dessous (copié à partir de Pull Request # 9151 ).
Pour contourner le problème, voici la solution: collez-le et exécutez-le dans une cellule précédente:
# 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
Simplement, vous pouvez définir la classe suivante juste avant votre pipeline:
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)
Ensuite, le reste du code est semblable à celui mentionné dans le livre avec une modification minime dans cat_pipeline
avant la concaténation de pipeline - procédez comme suit:
cat_pipeline = Pipeline([
("selector", DataFrameSelector(cat_attribs)),
("label_binarizer", NewLabelBinarizer())])
Vous avez fait!
J'ai eu le même problème et résolu en utilisant DataFrameMapper (besoin d'installer sklearn_pandas):
from sklearn_pandas import DataFrameMapper
cat_pipeline = Pipeline([
('label_binarizer', DataFrameMapper([(cat_attribs, LabelBinarizer())])),
])
Je pense que vous êtes en train de parcourir les exemples du livre: Apprentissage automatique avec Scikit Learn et Tensorflow . J'ai rencontré le même problème en reprenant l'exemple du chapitre 2.
Comme mentionné par d'autres personnes, le problème est lié à LabelBinarizer de Sklearn. Sa méthode fit_transform nécessite moins d'arguments que d'autres transformateurs en cours de développement. (seulement y lorsque d'autres transformateurs prennent normalement X et y, voir ici pour plus de détails). C'est pourquoi, lorsque nous exécutons pipeline.fit_transform, nous avons introduit plus d'arguments dans ce transformateur que nécessaire.
Une solution simple que j'ai utilisée consiste simplement à utiliser OneHotEncoder et à définir la valeur "sparse" sur False pour garantir que la sortie est un tableau numpy identique à la sortie num_pipeline. (De cette façon, vous n'avez pas besoin de coder votre propre encodeur personnalisé)
votre cat_pipeline d'origine:
cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('label_binarizer', LabelBinarizer())
])
vous pouvez simplement changer cette partie en:
cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('one_hot_encoder', OneHotEncoder(sparse=False))
])
Vous pouvez partir d'ici et tout devrait fonctionner.
J'ai fini par rouler le mien
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
Semble travailler
lbn = LabelBinarizer()
thingy = np.array([['male','male','female', 'male'], ['A', 'B', 'A', 'C']]).T
lbn.fit(thingy)
lbn.transform(thingy)
résultats
array([[1, 1, 0, 0],
[1, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 1]])
Vous pouvez créer un autre Transformer personnalisé qui effectue l’encodage à votre place.
class CustomLabelEncode(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
return LabelEncoder().fit_transform(X);
Dans cet exemple, nous avons fait LabelEncoding mais vous pouvez aussi utiliser LabelBinarizer
Oubliez LaberBinarizer et utilisez plutôt OneHotEncoder.
Si vous utilisez un LabelEncoder avant OneHotEncoder pour convertir des catégories en entiers, vous pouvez maintenant utiliser directement OneHotEncoder.