scikit-learn 中分类算法的文本特征输入格式

Posted

技术标签:

【中文标题】scikit-learn 中分类算法的文本特征输入格式【英文标题】:Text features input format for classification algorithms in scikit-learn 【发布时间】:2012-08-19 14:24:00 【问题描述】:

我开始使用 scikit-learn 来做一些 NLP。我已经使用了一些来自 NLTK 的分类器,现在我想尝试在 scikit-learn 中实现的分类器。

我的数据基本上是句子,我从这些句子的一些单词中提取特征来做一些分类任务。我的大部分特征都是名义上的:词的词性(POS),词到左,词性词到左,词到右,词性词到-对,从一个词到另一个词的句法关系路径,等等。

当我使用 NLTK 分类器(决策树、朴素贝叶斯)进行一些实验时,特征集只是一个字典,其中包含特征的相应值:名义值。如:[ "postag":"noun", "wleft":"house", "path":"***PNP",...,.... ]。我只需要将它传递给分类器,他们就完成了他们的工作。

这是使用的代码的一部分:

def train_classifier(self):
        if self.reader == None:
            raise ValueError("No reader was provided for accessing training instances.")

        # Get the argument candidates
        argcands = self.get_argcands(self.reader)

        # Extract the necessary features from the argument candidates
        training_argcands = []
        for argcand in argcands:
            if argcand["info"]["label"] == "NULL":
                training_argcands.append( (self.extract_features(argcand), "NULL") )
            else:
                training_argcands.append( (self.extract_features(argcand), "ARG") )

        # Train the appropriate supervised model
        self.classifier = DecisionTreeClassifier.train(training_argcands)

        return

这是提取的特征集之一的示例:

[('phrase': u'np', 'punct_right': 'NULL', 'phrase_left-sibling': 'NULL', 'subcat': 'fcl=np np vp np pu', 'pred_lemma': u'revelar', 'phrase_right-sibling': u'np', 'partial_path': 'vp fcl', 'first_word-postag': 'Bras\xc3\xadlia PROP', 'last_word-postag': 'Bras\xc3\xadlia PROP', 'phrase_parent': u'fcl', 'pred_context_right': u'um', 'pred_form': u'revela', 'punct_left': 'NULL', 'path': 'vp\xc2\xa1fcl!np', 'position': 0, 'pred_context_left_postag': u'ADV', 'voice': 0, 'pred_context_right_postag': u'ART', 'pred_context_left': u'hoje', 'NULL')]

正如我之前提到的,大多数特性都是名义上的(字符串值)。

现在,我想试试 scikit-learn 包中的分类器。据我了解,这种类型的特征集对于 sklearn 中实现的算法是不可接受的,因为所有特征值都必须是数字,并且它们必须在数组或矩阵中。因此,我使用 DictVectorizer 类转换了“原始”功能集。但是,当我传递这个转换后的向量时,会出现以下错误:

# With DecisionTreeClass
Traceback (most recent call last): 
.....
self.classifier.fit(train_argcands_feats,new_train_argcands_target)
  File "/usr/local/lib/python2.7/dist-packages/sklearn/tree/tree.py", line 458, in fit
    X = np.asarray(X, dtype=DTYPE, order='F')
  File "/usr/local/lib/python2.7/dist-packages/numpy/core/numeric.py", line 235, in asarray
    return array(a, dtype, copy=False, order=order)
TypeError: float() argument must be a string or a number


# With GaussianNB

Traceback (most recent call last):
....
self.classifier.fit(train_argcands_feats,new_train_argcands_target)
  File "/usr/local/lib/python2.7/dist-packages/sklearn/naive_bayes.py", line 156, in fit
    n_samples, n_features = X.shape
ValueError: need more than 0 values to unpack

当我只使用 DictVectorizer() 时出现这些错误。但是,如果我使用 DictVectorizer(sparse=False),我什至会在代码进入训练部分之前得到错误:

Traceback (most recent call last):
train_argcands_feats = self.feat_vectorizer.fit_transform(train_argcands_feats)
  File "/usr/local/lib/python2.7/dist-packages/sklearn/feature_extraction/dict_vectorizer.py", line 123, in fit_transform
    return self.transform(X)
  File "/usr/local/lib/python2.7/dist-packages/sklearn/feature_extraction/dict_vectorizer.py", line 212, in transform
    Xa = np.zeros((len(X), len(vocab)), dtype=dtype)
ValueError: array is too big.

由于这个错误,很明显必须使用稀疏表示。

所以问题是:如何转换我的标称特征以便使用 scikit-learn 提供的分类算法?

提前感谢您给予我的所有帮助。

更新

正如下面的答案所建议的,我尝试将 NLTK 包装器用于 scikit-learn。我只是更改了创建分类器的代码行:

self.classifier = SklearnClassifier(DecisionTreeClassifier())

然后,当我调用“train”方法时,我得到以下信息:

File "/usr/local/lib/python2.7/dist-packages/nltk/classify/scikitlearn.py", line 100, in train
    X = self._convert(featuresets)
  File "/usr/local/lib/python2.7/dist-packages/nltk/classify/scikitlearn.py", line 109, in _convert
    return self._featuresets_to_coo(featuresets)
  File "/usr/local/lib/python2.7/dist-packages/nltk/classify/scikitlearn.py", line 126, in _featuresets_to_coo
    values.append(self._dtype(v))
ValueError: could not convert string to float: np

因此,显然,包装器无法创建稀疏矩阵,因为特征是名义上的。然后,我回到原来的问题。

【问题讨论】:

你能发布一个代码 sn-p 来显示你正在尝试做什么吗?可能会让人们更容易帮助您找到错误... @JeffTratner 完成!如果您认为我应该提供其他任何东西,请告诉我。 【参考方案1】:

ValueError: array is too big. 非常明确:您不能在内存中分配 (n_samples, n_features) 的密集数组数据结构。在连续的内存块中存储那么多零是没有用的(在你的情况下也是不可能的)。请改用DictVectorizer documentation 中的稀疏数据结构。

另外,如果您更喜欢 NLTK API,您可以使用它的 scikit-learn 集成而不是使用 scikit-learn DictVectorizer

http://nltk.org/_modules/nltk/classify/scikitlearn.html

查看文件末尾。

【讨论】:

是的!我知道我必须使用稀疏表示。这就是为什么我的第一次尝试是使用具有默认参数的 DictVectorizer(创建一个稀疏矩阵)。但是,它抛出了指示的第一个错误。 好吧,我理解得更好,但仅使用堆栈跟踪很难跟踪:最好包含完整的脚本以了解正在发生的事情。所以无论如何:sklearn中的DecisionTree不支持高暗淡稀疏特征。 GaussianNB 是为不断量身定做的。对于 NLP 窗口特征或 TF-IDF 文档特征等高暗淡稀疏特征,通常最好使用线性模型或多项式或伯努利朴素贝叶斯模型。例如看一下这个例子:scikit-learn.org/stable/auto_examples/… 我同意错误信息并不清楚。 DecisionTree 和 GaussianNB 错误消息应明确声明不支持稀疏数据。请随意 2 open 2 github issues 来跟踪这个。 我已经用导致问题的主要代码部分更新了帖子,以及程序中使用的功能集示例。希望有帮助。我认为没有必要包含提取每个特征的代码。 忘了结束那句话:GaussianNB 是为连续变量量身定制的,因此不适用于 NLP 中通常使用的指标变量(它也不接受 scipy 稀疏矩阵作为输入)。【参考方案2】:

scikit-learn 的 NLTK 包装器的问题在于,它实际上需要将特征名称映射到数值的字典,因此在这种情况下,这并不能解决问题。 DictVectorizer 是 scikit-learn 更复杂的,因为它在遇到字符串特征值时会进行“one-of-K”编码;使用方法如下:

>>> data = [('first_word-postag': 'Bras\xc3\xadlia PROP',
   'last_word-postag': 'Bras\xc3\xadlia PROP',
   'partial_path': 'vp fcl',
   'path': 'vp\xc2\xa1fcl!np',
   'phrase': u'np',
   'phrase_left-sibling': 'NULL',
   'phrase_parent': u'fcl',
   'phrase_right-sibling': u'np',
   'position': 0,
   'pred_context_left': u'hoje',
   'pred_context_left_postag': u'ADV',
   'pred_context_right': u'um',
   'pred_context_right_postag': u'ART',
   'pred_form': u'revela',
   'pred_lemma': u'revelar',
   'punct_left': 'NULL',
   'punct_right': 'NULL',
   'subcat': 'fcl=np np vp np pu',
   'voice': 0,
  'NULL')]

将此列表分成两个列表,一个包含样本,另一个包含相应的标签:

>>> samples, labels = zip(*data)

将样本传递给DictVectorizer.fit(您也可以选择在单独的参数中传递标签,但它们将被忽略):

>>> v = DictVectorizer()
>>> X = v.fit_transform(samples)
>>> X
<1x19 sparse matrix of type '<type 'numpy.float64'>'
    with 19 stored elements in COOrdinate format>

然后,您应该能够将 X 传递给接受稀疏输入的 scikit-learn 分类器。 @ogrisel 已经指出,GaussianNB 会这样做。对于 NLP 任务,您需要使用 MultinomialNBBernoulliNB,因为它们是专门为离散数据设计的。

【讨论】:

谢谢!这基本上就是我最终做的事情。但由于与@ogrisel 的对话导致了该解决方案,因此我将他的答案标记为解决问题的答案。无论如何,也谢谢你!

以上是关于scikit-learn 中分类算法的文本特征输入格式的主要内容,如果未能解决你的问题,请参考以下文章

是否有可应用于分类数据输入的特征选择算法?

使用 scikit-learn 分类到多个类别

如何使用 scikit-learn 为机器学习准备文本数据

AI常用框架和工具丨5. 机器学习库Scikit-learn

AI常用框架和工具丨5. 机器学习库Scikit-learn

scikit-learn 为机器学习