scikit learn中不同数据类型的自定义管道

Posted

技术标签:

【中文标题】scikit learn中不同数据类型的自定义管道【英文标题】:Custom pipeline for different data type in scikit learn 【发布时间】:2017-12-16 08:19:04 【问题描述】:

我目前正试图根据一堆整数和一些文本特征来预测一个 kickstarter 项目是否会成功。我正在考虑构建一个看起来像这样的管道

参考:http://scikit-learn.org/stable/auto_examples/hetero_feature_union.html#sphx-glr-auto-examples-hetero-feature-union-py

这是我的 ItemSelector 和管道代码

class ItemSelector(BaseEstimator, TransformerMixin):    
    def __init__(self, keys):
        self.keys = keys

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

    def transform(self, data_dict):
        return data_dict[self.keys]

我验证了 ItemSelector 正在按预期工作

t = ItemSelector(['cleaned_text'])
t.transform(df)

And it extract the necessary columns

管道

pipeline = Pipeline([
    # Use FeatureUnion to combine the features from subject and body
    ('union', FeatureUnion(
        transformer_list=[
            # Pipeline for pulling features from the post's subject line
            ('text', Pipeline([
                ('selector', ItemSelector(['cleaned_text'])),
                ('counts', CountVectorizer()),
                ('tf_idf', TfidfTransformer())
            ])),

            # Pipeline for pulling ad hoc features from post's body
            ('integer_features', ItemSelector(int_features)),
        ]
    )),

    # Use a SVC classifier on the combined features
    ('svc', SVC(kernel='linear')),
])

但是当我运行 pipeline.fit(X_train, y_train) 时,我收到了这个错误。知道如何解决这个问题吗?

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-27-317e1c402966> in <module>()
----> 1 pipeline.fit(X_train, y_train)

~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/sklearn/pipeline.py in fit(self, X, y, **fit_params)
    266             This estimator
    267         """
--> 268         Xt, fit_params = self._fit(X, y, **fit_params)
    269         if self._final_estimator is not None:
    270             self._final_estimator.fit(Xt, y, **fit_params)

~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/sklearn/pipeline.py in _fit(self, X, y, **fit_params)
    232                 pass
    233             elif hasattr(transform, "fit_transform"):
--> 234                 Xt = transform.fit_transform(Xt, y, **fit_params_steps[name])
    235             else:
    236                 Xt = transform.fit(Xt, y, **fit_params_steps[name]) \

~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/sklearn/pipeline.py in fit_transform(self, X, y, **fit_params)
    740         self._update_transformer_list(transformers)
    741         if any(sparse.issparse(f) for f in Xs):
--> 742             Xs = sparse.hstack(Xs).tocsr()
    743         else:
    744             Xs = np.hstack(Xs)

~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/scipy/sparse/construct.py in hstack(blocks, format, dtype)
    456 
    457     """
--> 458     return bmat([blocks], format=format, dtype=dtype)
    459 
    460 

~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/scipy/sparse/construct.py in bmat(blocks, format, dtype)
    577                                                     exp=brow_lengths[i],
    578                                                     got=A.shape[0]))
--> 579                     raise ValueError(msg)
    580 
    581                 if bcol_lengths[j] == 0:

ValueError: blocks[0,:] has incompatible row dimensions. Got blocks[0,1].shape[0] == 81096, expected 1.

【问题讨论】:

您应该发布完整的错误堆栈跟踪。您也可以单独使用 TfidfVectorizer 代替 CountVectorizer 和 TfidfTransformer。还有一件事,确保 ItemSelector 返回的数据是二维形状(n_samples,n_features)。 你能发布一些重现错误的示例数据吗? 另外,integer_featuresItemSelector 的输出形状是什么?好像有问题 gist.github.com/sarchak/19a59c9eb5f9e9956924b29b1adf5ee7/… 这些是测试列车分裂前的形状(108129, 7). 【参考方案1】:

ItemSelector 返回的是一个数据框,而不是一个数组。这就是scipy.hstack 抛出错误的原因。更改 ItemSelector 如下:

class ItemSelector(BaseEstimator, TransformerMixin):    
    ....
    ....
    ....

    def transform(self, data_dict):
        return data_dict[self.keys].as_matrix()

错误发生在管道的integer_features 部分。对于第一部分text,ItemSelector 下方的转换器支持 Dataframe,因此可以正确地将其转换为数组。但第二部分只有 ItemSelector 并返回 Dataframe。

更新

在评论中,您提到要对从 ItemSelector 返回的结果 Dataframe 执行一些操作。因此,您可以创建一个新的 Transformer 并将其附加到管道的第二部分,而不是修改 ItemSelector 的转换方法。

class DataFrameToArrayTransformer(BaseEstimator, TransformerMixin):    
    def __init__(self):

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

    def transform(self, X):
        return X.as_matrix()

那么你的管道应该是这样的:

pipeline = Pipeline([
    # Use FeatureUnion to combine the features from subject and body
    ('union', FeatureUnion(
        transformer_list=[
            # Pipeline for pulling features from the post's subject line
            ('text', Pipeline([
                ('selector', ItemSelector(['cleaned_text'])),
                ('counts', CountVectorizer()),
                ('tf_idf', TfidfTransformer())
            ])),

            # Pipeline for pulling ad hoc features from post's body
            ('integer', Pipeline([
                ('integer_features', ItemSelector(int_features)),
                ('array', DataFrameToArrayTransformer()),
            ])),
        ]
    )),

    # Use a SVC classifier on the combined features
    ('svc', SVC(kernel='linear')),
])

这里要理解的主要内容是,FeatureUnion 在组合它们时只会处理二维数组,因此像 DataFrame 这样的任何其他类型都可能会出现问题。

【讨论】:

管道将尝试对传入的数据应用 lower 方法并返回 ndarray 实际上会给出此错误~/Anaconda/anaconda/envs/ds/lib/python3.5/site-packages/sklearn/feature_extraction/text.py in &lt;lambda&gt;(x) 205 206 if self.lowercase: --&gt; 207 return lambda x: strip_accents(x.lower()) 208 else: 209 return strip_accents AttributeError: 'numpy.ndarray' object has no attribute 'lower' 我也遇到了数据框和 scikit-learn 的问题。但是,让它们一起工作并不难,正如您所见here。特别是,你会在那里找到一个 DataFrameFeatureUnion-Transformer

以上是关于scikit learn中不同数据类型的自定义管道的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Scikit-learn 的管道中创建我们的自定义特征提取器函数并将其与 countvectorizer 一起使用

Scikit-learn 管道类型错误:zip 参数 #2 必须支持迭代

管道中的 Scikit-Learn FunctionTransformer 没有其他功能 - 不返回原始数据?

如何在 scikit-learn 中使用管道调整自定义内核函数的参数

如何将功能管道从 scikit-learn V0.21 移植到 V0.24

Scikit-learn 分类器具有依赖于训练特征的自定义记分器