用scikit-learn实现朴素贝叶斯分类器

朴素贝叶斯(Naive Bayes Classifier)是一种「天真」的算法(假定所有特征发生概率是独立的),同时也是一种简单有效的常用分类算法。关于它的原理,参见朴素贝叶斯分类器的应用
scikit-learn是一个广泛应用的机器学习Python库,它封装了包括朴素贝叶斯在内的若干基础算法。在这篇博客里,我们希望用朴素贝叶斯实现对短文本(新闻标题)的分类。

朴素贝叶斯属于有监督分类,需要获取一批已标注的数据作为训练和测试分类器的样本。样本的获取,一般通过人工标注或网页抓取的方式。这里先准备好2w条已标注的新闻标题(共10个标签,每个标签下的样本是均衡的),并以3:1的比例切分训练集和测试集。文本的格式如下:

娱乐\t组图:刘亦菲短裙秀腿 浓妆变冷艳时髦女

其次,将短文本转化为一个多维向量,这涉及到两个问题:一是分词,由于scikit-learn内置的分词器并不支持中文,所以需要指定一个tokenizer(推荐结巴分词);二是文本到向量的转化方法,考虑到后续可能加大训练集的数量,而短文本的特征相对稀疏,采用了内存占用率较低、仅计算词频的HashingVectorizer

这里要注意两点:

  • 测试集和训练集只有共用一个vectorizer才能共享vocabulary,避免特征表达不一致的问题。
  • fit_transform如果用toarray()转化为dense矩阵,计算至少慢两倍以上。
1
2
3
4
# non_negative=True -- 模型仅包含非负值
vectorizer = HashingVectorizer(tokenizer=comma_tokenizer, non_negative=True)
train_data = vectorizer.fit_transform(train_words)
test_data = vectorizer.fit_transform(test_words)

然后,我们创建一个多项式的朴素贝叶斯分类器(适用于离散特征的分类),分别输入训练集的文本和标签(要求都为numpy矩阵)进行训练,训练好的分类器再用在测试集文本的分类,以检验分类器的性能。

1
2
3
4
# alpha -- 模型的平滑参数
clf = MultinomialNB(alpha=0.01)
clf.fit(train_data, numpy.asarray(train_tags))
pred = clf.predict(test_data)

最后,比较分类器的预测结果和测试集的真实标签,得到分类器的准确率和召回率。

1
2
m_precision = metrics.precision_score(actual, pred)
m_recall = metrics.recall_score(actual, pred)

完整代码请见sci_classifier.py
从测试的结果来看,准确率和召回率均在8成以上,当然,分类器的性能也依赖于输入数据对应不同类别的可识别度是否足够强。