nltk 使用SklearnClassifier和ClassifierBasedPOSTagger构建自己的分类器,

abithluo  于 5个月前  发布在  其他
关注(0)|答案(2)|浏览(83)

我正在尝试使用SklearnClassifierClassifierBasedPOSTagger构建自己的基于词性标注的分类器。我已经尝试过的代码如下:

from nltk.corpus import treebank
nltk.download('treebank')

data = treebank.tagged_sents()
train_data = data[:3500]
test_data = data[3500:]
from nltk.classify import SklearnClassifier
from sklearn.naive_bayes import BernoulliNB
from nltk.tag.sequential import ClassifierBasedPOSTagger

bnb = SklearnClassifier(BernoulliNB())
bnb_tagger = ClassifierBasedPOSTagger(train=train_data,
                                      classifier_builder=bnb.train)

# evaluate tagger on test data and sample sentence
print(bnb_tagger.evaluate(test_data))

# see results on our previously defined sentence
print(bnb_tagger.tag(nltk.word_tokenize(sentence)))

这段代码产生了以下错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
C:\Users\ABDULL~1.IMR\AppData\Local\Temp/ipykernel_6580/266992580.py in <module>
      4 
      5 bnb = SklearnClassifier(BernoulliNB())
----> 6 bnb_tagger = ClassifierBasedPOSTagger(train=train_data,
      7                                       classifier_builder=bnb.train)
      8 

~\Miniconda3\envs\nlp_course\lib\site-packages\nltk\tag\sequential.py in __init__(self, feature_detector, train, classifier_builder, classifier, backoff, cutoff_prob, verbose)
    637 
    638         if train:
--> 639             self._train(train, classifier_builder, verbose)
    640 
    641     def choose_tag(self, tokens, index, history):

~\Miniconda3\envs\nlp_course\lib\site-packages\nltk\tag\sequential.py in _train(self, tagged_corpus, classifier_builder, verbose)
    673         if verbose:
    674             print("Training classifier ({} instances)".format(len(classifier_corpus)))
--> 675         self._classifier = classifier_builder(classifier_corpus)
    676 
    677     def __repr__(self):

~\Miniconda3\envs\nlp_course\lib\site-packages\nltk\classify\scikitlearn.py in train(self, labeled_featuresets)
    110 
    111         X, y = list(zip(*labeled_featuresets))
--> 112         X = self._vectorizer.fit_transform(X)
    113         y = self._encoder.fit_transform(y)
    114         self._clf.fit(X, y)

~\Miniconda3\envs\nlp_course\lib\site-packages\sklearn\feature_extraction\_dict_vectorizer.py in fit_transform(self, X, y)
    288             Feature vectors; always 2-d.
    289         """
--> 290         return self._transform(X, fitting=True)
    291 
    292     def inverse_transform(self, X, dict_type=dict):

~\Miniconda3\envs\nlp_course\lib\site-packages\sklearn\feature_extraction\_dict_vectorizer.py in _transform(self, X, fitting)
    233                     if feature_name in vocab:
    234                         indices.append(vocab[feature_name])
--> 235                         values.append(self.dtype(v))
    236 
    237             indptr.append(len(indices))

TypeError: float() argument must be a string or a number, not 'NoneType'

如何正确实现?

y3bcpkx1

y3bcpkx11#

这是一个scikit-learn的bug。让我向您展示:
ClassifierBasedPOSTagger(及其超类ClassifierBasedTagger)使用了一个feature_detector方法,该方法给定一些参数(标记、索引、历史记录)会产生一个类似于以下的字典:

{
    "prevtag": prevtag,
    "prevprevtag": prevprevtag,
    "word": word,
    "word.lower": word.lower(),
    "suffix3": word.lower()[-3:],
    "suffix2": word.lower()[-2:],
    "suffix1": word.lower()[-1:],
    "prevprevword": prevprevword,
    "prevword": prevword,
    "prevtag+word": f"{prevtag}+{word.lower()}",
    "prevprevtag+word": f"{prevprevtag}+{word.lower()}",
    "prevword+word": f"{prevword}+{word.lower()}",
    "shape": shape,
}

在某些情况下,例如对于句子中的第一个单词,前一个标签根本不存在,即None。例如,如果我们取样训练句子:

[('Pierre', 'NNP'), ('Vinken', 'NNP'), (',', ','), ('61', 'CD'), ('years', 'NNS'), ('old', 'JJ'), (',', ','), ('will', 'MD'), ('join', 'VB'), ('the', 'DT'), ('board', 'NN'), ('as', 'IN'), ('a', 'DT'), ('nonexecutive', 'JJ'), ('director', 'NN'), ('Nov.', 'NNP'), ('29', 'CD'), ('.', '.')]

那么ClassifierBasedPOSTagger将在_train中调用feature_detector,产生:

{'prevprevtag': None,
 'prevprevtag+word': 'None+pierre',
 'prevprevword': None,
 'prevtag': None,
 'prevtag+word': 'None+pierre',
 'prevword': None,
 'prevword+word': 'None+pierre',
 'shape': 'upcase',
 'suffix1': 'e',
 'suffix2': 're',
 'suffix3': 'rre',
 'word': 'Pierre',
 'word.lower': 'pierre'}

因此,从特征名称到特征值的Map具有预期的类型Dict[str, Optional[str]]
这个Map沿着链传递,到达sklearn\feature_extraction\_dict_vectorizer.py,到达_transformDictVectorizer的方法。使用像最后一个示例中的x特征字典,以下代码段将运行:
https://github.com/scikit-learn/scikit-learn/blob/e64714637d8cc9f4724ae21ea500e4bdc57b0a39/sklearn/feature_extraction/_dict_vectorizer.py#L223-L255
第227行负责处理特征值为None的情况,将特征名称设置为例如"prevprevtag"。然而,在第255行,我们的特征值的默认值dtype被取走,这产生了np.float64(None)。这会抛出您正在经历的TypeError。
简而言之-scikit-learn的_transform方法在输入参数包含Map到None时失败。
我的建议是将其报告为问题。您可以在此消息上链接。
也许在此期间,您可以使用以下代码片段:
这将防止特征值为None,而bug仍然存在。相反,它使用'None'。或者,您可以想出要使用的任何令牌来替换None,例如key: value if value else 'my_custom_value_which_represents_none'
我没有时间为您实际训练更大的东西,所以我快速地用三个句子进行了训练:
这输出:
(显然这个评估很糟糕,但重点是它不再崩溃了)
快乐标记,

  • Tom Aarsen
ejk8hzay

ejk8hzay2#

@larsmans - 如果你对scikit-learn中可能存在的bug有任何看法

相关问题