我开始使用scikit-learn做一些NLP。我已经使用了NLTK的一些分类器,现在我想尝试在scikit-learn中实现的分类器。
我的数据基本上是句子,我从这些句子的某些单词中提取特征来做一些分类任务。我的大多数功能都是名义上的:单词的词性(POS),左到右的单词,左到右的单词,右到右的单词,POS单词到单词。 - 正确的,从一个词到另一个词的句法关系等等。
当我使用NLTK分类器(决策树,朴素贝叶斯)进行一些实验时,特征集只是一个字典,其中包含特征的相应值:标称值。例如:[{“postag”:“noun”,“wleft”:“house”,“path”:“VPNPNP”,...},....]。我只需将它传递给分类器,他们就完成了自己的工作。
这是所用代码的一部分:
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
因此,显然,包装器不能创建稀疏矩阵,因为这些特征是名义上的。然后,我回到原来的问题。
答案 0 :(得分:4)
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
看一下文件的结尾。
答案 1 :(得分:4)
用于scikit-learn的NLTK包装器的问题在于它实际上想要将功能名称映射到数值,因此在这种情况下不会解决问题。 DictVectorizer
是scikit-learn更复杂,因为它在遇到字符串特征值时会执行“一个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分类器。 {@ 1}} 不这样做,正如@ogrisel已经指出的那样。对于NLP任务,您需要使用GaussianNB
或MultinomialNB
,因为这些是专为离散数据设计的。