我正在使用scikit对短语的含义进行文本分类。例如:
"Yes" - label.yes
"Yeah" - label.yes
...
"I don't know" - label.i_don't_know
"I am not sure" - label.i_don't_know
"I have no idea" - label.i_don't_know
使用TfidfVectorizer和MultinomialNB分类器,一切都很好。
当我添加新的文本/标签对时出现了问题:
"I" - label.i
预测"I"的类仍然返回label.I_don't_know,即使文本正好在这样的训练数据中,这可能是由于unigram"I"发生在标签.i.don't_know中的频率高于标签.i.
是否有一种分类器可以在该任务上提供相当或更好的性能,并保证正确返回训练数据元素的预测?
此代码进一步说明了问题:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
#instantiate classifier and vectorizer
clf=MultinomialNB(alpha=.01)
vectorizer =TfidfVectorizer(min_df=1,ngram_range=(1,2))
#Apply vectorizer to training data
traindata=['yes','yeah','i do not know','i am not sure','i have no idea','i'];
X_train=vectorizer.fit_transform(traindata)
#Label Ids
y_train=[0,0,1,1,1,2];
#Train classifier
clf.fit(X_train, y_train)
print clf.predict(vectorizer.transform(['i']))
代码输出标签1,但正确的分类是标签2。
问题不在于分类器,而在于矢量器。TfidfVectorizer
有一个参数token_pattern : string
,它是一个"正则表达式,表示"token"的组成部分,仅在tokenize=='word'时使用。默认regexp选择2个或更多字母字符的token(标点符号被完全忽略,始终被视为令牌分隔符)。"(增加了强调)。标记化器抛出单词i
,导致一个空文档。Naive Bayes将其分类为类别1
,因为这是训练数据中最频繁的类别。
根据数据,您可能需要考虑对Naive Bayes使用统一先验。
关于为什么事情可能不起作用的进一步提示:
您的管道设置方式可能还有其他一些奇怪之处。我发现检查每个阶段(标记器、矢量器、分类器等)的输入和输出很有用。从长远来看,在编写单元测试上投入一些时间将为您节省大量时间。
一旦你对一切都很满意,就试着根据测试数据评估你的分类器。我怀疑你们的课之间有相当大的重叠,特别是label.i_don't_know
和label.i
。如果是这种情况,分类器将表现不佳。