Трубопровод Scikit-Learn: была передана разреженная матрица, но требуются плотные данные
Мне трудно понять, как исправить созданный мной конвейер (читай: в основном вставлено из учебника). Это питон 3.4.2:
df = pd.DataFrame
df = DataFrame.from_records(train)
test = [blah1, blah2, blah3]
pipeline = Pipeline([('vectorizer', CountVectorizer()), ('classifier', RandomForestClassifier())])
pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1]))
predicted = pipeline.predict(test)
Когда я запускаю его, я получаю:
TypeError: A sparse matrix was passed, but dense data is required. Use X.toarray() to convert to a dense numpy array.
Это для линии pipeline.fit(numpy.asarray(df[0]), numpy.asarray(df[1]))
,
Я много экспериментировал с решениями через numpy, scipy и так далее, но я до сих пор не знаю, как это исправить. И да, подобные вопросы возникали раньше, но не внутри конвейера. Где это я должен подать заявку toarray
или же todense
?
6 ответов
К сожалению, эти два несовместимы. CountVectorizer
создает разреженную матрицу, а для RandomForestClassifier требуется плотная матрица. Можно конвертировать используя X.todense()
, Это существенно увеличит объем памяти.
Ниже приведен пример кода для этого, который я нашел по адресу http://zacstewart.com/2014/08/05/pipelines-of-featureunions-of-pipelines.html который позволяет вам вызывать.todense() на стадии конвейера.
class DenseTransformer(TransformerMixin):
def transform(self, X, y=None, **fit_params):
return X.todense()
def fit_transform(self, X, y=None, **fit_params):
self.fit(X, y, **fit_params)
return self.transform(X)
def fit(self, X, y=None, **fit_params):
return self
Как только у вас есть DenseTransformer
Вы можете добавить его как шаг конвейера.
pipeline = Pipeline([
('vectorizer', CountVectorizer()),
('to_dense', DenseTransformer()),
('classifier', RandomForestClassifier())
])
Другим вариантом будет использование классификатора, предназначенного для разреженных данных, таких как LinearSVC
,
from sklearn.svm import LinearSVC
pipeline = Pipeline([('vectorizer', CountVectorizer()), ('classifier', LinearSVC())])
Самое чистое решение будет использовать FunctionTransformer
преобразовать в плотный: это будет автоматически реализовывать fit
, transform
а также fit_transform
методы, как в ответе Дэвида. Кроме того, если мне не нужны специальные имена для шагов конвейера, я бы хотел использовать sklearn.pipeline.make_pipeline
удобная функция для включения более минималистичного языка для описания модели:
from sklearn.preprocessing import FunctionTransformer
pipeline = make_pipeline(
CountVectorizer(),
FunctionTransformer(lambda x: x.todense(), accept_sparse=True),
RandomForestClassifier()
)
Случайные леса в 0.16 dev теперь принимают разреженные данные.
Вы можете изменить панд Series
для массивов с использованием .values
метод.
pipeline.fit(df[0].values, df[1].values)
Однако я думаю, что проблема здесь происходит потому, что CountVectorizer()
по умолчанию возвращает разреженную матрицу и не может быть передан в классификатор RF. CountVectorizer()
есть dtype
параметр для указания типа возвращаемого массива. Тем не менее, как правило, вам нужно сделать какое-то уменьшение размерности, чтобы использовать случайные леса для классификации текста, потому что векторы мешков слов очень длинные
Я обнаружил, что FunctionTransformer и использование x.toarray() вместо x.todense() мне помогли.
'pipeline': Pipeline(
[
('vect', TfidfVectorizer()),
('dense', FunctionTransformer(lambda x: x.toarray(), accept_sparse=True)),
('clf', GaussianProcessClassifier())
]
)
С этим конвейером добавим TfidTransformer plus
pipelinex = Pipeline([('bow',vectorizer),
('tfidf',TfidfTransformer()),
('to_dense', DenseTransformer()),
('classifier',classifier)])