Аналогично: Конвейер не работает с Label Encoder
Я хотел бы иметь объект, который обрабатывает кодирование меток (в моем случае с LabelEncoder
), преобразование и оценку. Для меня важно, чтобы все эти функции можно было выполнять через только один объект.
Я пробовал использовать конвейер таким образом:
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
# mock training dataset
X = np.random.rand(1000, 100)
y = np.concatenate([["label1"] * 300, ["label2"] * 300, ["label3"] * 400])
le = LabelEncoder()
ss = StandardScaler()
clf = MyClassifier()
pl = Pipeline([('encoder', le),
('scaler', ss),
('clf', clf)])
pl.fit(X, y)
Который дает :
File "sklearn/pipeline.py", line 581, in _fit_transform_one
res = transformer.fit_transform(X, y, **fit_params)
TypeError: fit_transform() takes exactly 2 arguments (3 given)
Разъяснения:
X
и y
- мои наборы данных для обучения, X
- значения, а y
- целевые метки.
X
- это numpy.ndarray
формы (n_sample, n_features) и типа float, значения от 0 до 1.
y
- это numpy.ndarray
формы (n_sample,) и строки типа
Я ожидаю, что LabelEncoder
будет кодировать y
, а не X
.
Мне нужен y
только для MyClassifier
, и мне нужно, чтобы он закодирован в целые числа
для работы MyClassifier
.
После некоторых размышлений и столкновения с ошибкой, описанной выше, мне показалось, что было наивно думать, что Pipeline
сможет с этим справиться. Я понял, что Pipeline
может очень хорошо справиться с моим преобразованием и классификатором вместе, но это была часть кодирования меток, которая потерпела неудачу.
Как правильно достичь того, чего я хочу? Под «правильным» я подразумеваю сделать что-то, что обеспечило бы возможность повторного использования и некоторую согласованность с sklearn
. Есть ли в библиотеке sklearn
класс, который делает то, что я хочу?
Я очень удивлен, что не нашел ответа в Интернете, потому что мне кажется, что то, что я делаю, нет ничего необычного. Я могу что-то здесь упустить.
Да, я хочу кодировать y с помощью LabelEncoder
, а не X. X - это numpy.ndarray
формы (n_sample, n_features) и типа float, y - это numpy.ndarray
формы (n_sample,) и строки типа. Мне нужен y для MyClassifier
. StandardScaler
не нуждается в y, но принимает его и игнорирует, только обработка на X. LabelEncoder
не принимает 2 параметра, как вы заявили. Я отредактирую свой вопрос, чтобы добавить эти пояснения.
Что касается фиктивной версии X и y, это должно помочь: X = np.random.rand(1000, 100)
и y = np.concatenate([["label1"] * 300, ["label2"] * 300, ["label3"] * 400])
Отлично, я что-нибудь придумаю. Я могу предвидеть, что вам все равно нужно будет вытащить LabelEncoder из конвейера, ни в коем случае. Не могли бы вы добавить фиктивный ввод в вопрос, чтобы другие могли его найти?
Да, мне нужен LabelEncoder из конвейера. Я также добавил фиктивный ввод в вопрос. Спасибо.
LabelEncoder будет автоматически вызван на y
, когда вы вызовете clf.fit()
. Так что вам не нужно об этом беспокоиться. y
может иметь целые числа, строки как классы, которые будут правильно обрабатываться оценщиками в scikit. Таким образом, нет необходимости включать LabelEncoder в конвейер для работы с y
.
Я реализовал категориальное кодирование с помощью панд, а в качестве классификатора использовал SGDClassifier. Ваш код выше вызывает MyClassifier()
, но он не определен внутри самого кода.
import numpy as np
import pandas as pd
# from sklearn.preprocessing import LabelEncoder # No longer used
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.linear_model import SGDClassifier
X = np.random.randn(1000, 10)
y_initial = np.concatenate([["label1"] * 300, ["label2"] * 300, ["label3"] * 400])
df = pd.DataFrame({'y':y_initial})
df['y'] = df['y'].astype('category') # Same as the output of LabelEncoder
ss = StandardScaler()
clf = SGDClassifier()
y = df['y']
pl = Pipeline([('scaler', ss),
('clf', clf)])
pl.fit(X,y)
Результатом является подходящий объект конвейера:
Pipeline(memory=None,
steps=[('scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('clf', SGDClassifier(alpha=0.0001, average=False, class_weight=None, epsilon=0.1,
eta0=0.0, fit_intercept=True, l1_ratio=0.15,
learning_rate='optimal', loss='hinge', max_iter=None, n_iter=None,
n_jobs=1, penalty='l2', power_t=0.5, random_state=None,
shuffle=True, tol=None, verbose=0, warm_start=False))])
Спасибо, я вижу, что я недостаточно подчеркнул тот факт, что я хотел бы иметь все эти функции в только один объект, для концептуальных целей. Здесь декодирование прогнозируемых значений Pipeline
pl
не могло быть выполнено только с информацией только о Pipeline
pl
. Я хотел бы иметь объект, у которого есть данные, необходимые для обработки кодирования, преобразования, прогнозирования и декодирования.
Я не уверен, что это возможно только с одним объектом, вам нужно написать функцию, которая последовательно кодирует цели, передает цели и входные переменные обучения в конвейер, а затем подходит. Затем другой, который передает входные переменные теста в pipeline.predict () и декодирует результат. Pipeline obj не позволяет делать то, что вы хотите, сам по себе. Однако я думаю: не могли бы вы изменить задачу с классификации на кластеризацию? В этом случае вы можете LabelEncode все X и Y после вызова numpy.hstack(X,y)
, и это может быть передано в объект конвейера.
Спасибо за отзыв, это полезная информация. Однако я не думаю, что процесс, который вы описали во второй части вашего комментария, мог бы сработать для меня.
Позвольте мне попытаться кое-что поднять, однако имейте в виду, что, поскольку ваши X не являются категориальными переменными, а скорее непрерывными значениями, результат кластеризации, выполненной на .hstack (X, y), не будет каким-либо образом интерпретирован людьми. . Также StandardScaler придется вытащить из конвейера.
Неважно, после тестирования похоже, что LabelEncoder не выполняет кодирование по столбцам для 2D-массивов. Возможно, вам придется перейти на keras, sklearn не дает возможности делать то, что вы хотите.
Проблема в том, насколько я понимаю Pipeline
, я мог бы создать собственный LabelEncoder
, который принимал бы 2 аргумента в fit
, но Pipeline
не предоставляет способа преобразовать y и передать преобразованное y через конвейер. Если я прав, можно изменить только X. При просмотре fit
из LabelEncoder
я должен сделать «обмен», который я сделать не могу.
Я пытался создать собственный класс в соответствии со строками упомянутого выше сообщения, а также безуспешно пытался скопировать и переписать LabelEncoder
: я продолжал получать указанную вами ошибку. Теперь я думаю: что, если мы передадим переменные конвейеру в обратном порядке, например, (y, X)
, и добавим класс между кодировщиком и классификатором, единственная цель которого - вернуть два массива в обратном порядке, таким образом поменяв их местами? Постараюсь это реализовать.
Я думаю, что здесь мы наткнемся на стену, поскольку transform
возвращает только одно значение, X или y, но не оба сразу. Мой план состоял в том, чтобы создать класс, унаследованный от Pipeline
, но для этого потребуется кодировщик (например, LabelEncoder
) для кодирования y. Затем я перезапишу методы (например, fit
, transform
, predict
и т. д.) Для кодирования y, а затем вызову метод суперкласса (в данном случае Pipeline
).
Позвольте нам продолжить обсуждение в чате.
Я считаю, что это невозможно.
Во-первых, все трансформаторы наследуются от sklearn.base.TransformerMixin. Метод fit_transform
принимает аргументы X
и, необязательно, y
, но возвращает только X_new
. scikit-learn не предназначен для такого рода преобразований.
Во-вторых, LabelEncoder завершится ошибкой в конвейере, потому что fit
и transform
принимают только один аргумент, y
, а не X, y
.
В конце концов, я написал функцию для поиска в Enum
, отображающую строковые метки в целочисленные метки. По крайней мере, тогда преобразование происходит в коде и отслеживается с помощью контроля версий.
Спасибо за ответ. Я тоже так подумал. Однако Вивек Кумар указал на кое-что полезное в комментариях, что LabelEncoder должен автоматически вызываться, если y
не является целым числом. Зная это, я напишу ответ, используя этот небольшой трюк.
Как написал Вивек Кумар в комментариях:
LabelEncoder will be automatically called on y when you call clf.fit(). So you dont need to worry about it. y can have integers, strings as classes, that will be handled correctly by the estimators in scikit. So there is no need to include LabelEncoder in the pipeline to work on y.
Итак, вот решение моей проблемы:
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# mock training dataset
X = np.random.rand(1000, 100)
y = np.concatenate([["label1"] * 300, ["label2"] * 300, ["label3"] * 400])
ss = StandardScaler()
clf = MyClassifier() # my own classifier
pl = Pipeline([('scaler', ss),
('clf', clf)])
pl.fit(X, y)
Единственное отличие: теперь pl.predict(X)
будет возвращать массив строк, содержащих значения «label1», «label2» или «label3» (что имеет смысл, поскольку это то, чем мы его кормили).
При необходимости, чтобы вернуть LabelEncoder, который автоматически используется sklearn.pipeline
, вы можете сделать:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder(pl.classes_)
Это даст мне копию кодировщика этикеток, используемого конвейером pl
.
Что вы хотите кодировать, X или Y? Скорее всего y, но подтвердите, пожалуйста. Обратите внимание, однако, что вы передаете оба значения в LabelEncoder.fit_transform (), X в качестве первого значения и y в качестве второго. LabelEncoder.fit_transform () принимает входной массив только один, поэтому неясно, что происходит с
y
, отсюда и ошибка. Кроме того, какие данные вы вводите в качестве входных? Это массив numpy или фреймворк pandas? Если вы дадите имитацию X и y, я могу написать вам решение.