将文档分类到类别中

34 投票
3 回答
14850 浏览
提问于 2025-04-16 00:24

我在一个Postgres数据库里存了大约30万个文档,这些文档都有主题分类(总共有大约150个分类)。另外还有15万个文档还没有分类。我想找个好方法来给这些文档自动分类。

我在研究NLTK和它的朴素贝叶斯分类器。这个方法看起来是个不错的起点(如果你有更好的分类算法推荐,我非常乐意听听)。

我的问题是,我的电脑内存不够,无法一次性用所有150个分类和30万个文档来训练朴素贝叶斯分类器(训练5个分类就用了8GB内存)。而且,随着我训练的分类数量增加,分类器的准确率似乎在下降(用2个分类的准确率是90%,5个分类是81%,10个分类则降到61%)。

我是不是应该每次只训练5个分类,然后把所有15万个文档都放进分类器里看看有没有匹配的?这样做似乎可行,但问题是会出现很多误判,也就是一些实际上不符合任何分类的文档,被分类器强行归入某个分类,因为那是它能找到的最匹配的分类……有没有办法让分类器有一个“以上皆非”的选项,以防文档不适合任何分类?

这是我的测试类http://gist.github.com/451880

3 个回答

2

有没有办法让分类器有一个“以上皆非”的选项,以防文档不适合任何类别呢?

你可以通过每次训练一个“以上皆非”的伪类别来实现这个效果。如果你最多只能训练5个类别(虽然我不太明白为什么会占用那么多内存),那么可以从实际的2000个文档中训练4个真实的类别,再加上一个“以上皆非”的类别,这个类别的2000个文档可以随机从其他146个类别中选取(如果你想用“分层抽样”的方法,大约从每个类别中选取13-14个文档,这样可能更合理)。

不过,这感觉有点像是临时拼凑的解决方案,或许你可以尝试完全不同的方法——找到一种多维度的文档度量,把你30万条预标记的文档分成150个相对独立的簇,然后把其他尚未标记的文档分配到相应的簇中。我觉得NLTK没有直接支持这种方法的工具,但嘿,NLTK发展得这么快,我可能错过了什么...;-)

11

你的文档有多大(字数)?如果有15万份训练文档,内存消耗应该不是问题。

朴素贝叶斯是个不错的选择,特别是当你有很多类别但每个类别的训练样本很少,或者训练数据非常嘈杂的时候。不过一般来说,线性支持向量机的表现会更好。

你的问题是多类分类(每个文档只能属于一个类别)还是多标签分类(一个文档可以属于一个或多个类别)?

用准确率来评估分类器的表现并不是个好主意。你应该使用精确率和召回率、精确率召回率平衡点(prbp)、F1值、AUC等指标,并查看精确率和召回率的曲线,其中召回率(x轴)与精确率(y轴)根据你的置信阈值(文档是否属于某个类别)进行绘制。通常,你会为每个类别建立一个二分类器(一个类别的正样本与所有不属于该类别的样本进行对比)。你需要为每个类别选择一个最佳的置信阈值。如果你想把每个类别的单独指标合并成一个整体的表现指标,你可以选择微平均(把所有的真阳性、假阳性、假阴性和真阴性加起来,计算综合得分)或宏平均(计算每个类别的得分,然后对所有类别的得分取平均)。

我们有数千万份文档,数百万个训练样本和数千个类别(多标签)。由于我们面临严重的训练时间问题(每天新增、更新或删除的文档数量相当高),我们使用了一个修改版的 liblinear。但对于较小的问题,使用liblinear的Python封装(如liblinear2scipyscikit-learn)应该也能很好地工作。

33

你应该先把你的文档转换成 TF-log(1 + IDF) 向量。因为词频数据比较稀疏,所以可以用 Python 的字典来存储,字典的键是词,值是词的出现次数,然后用这个次数除以总的词数,得到全局频率。

另外一个方法是用 abs(hash(term)) 作为正整数的键。这样你可以使用 scipy.sparse 向量,这种方式在进行线性代数运算时比 Python 字典更方便、更高效。

还可以通过计算所有属于同一类别的标记文档的频率平均值,来构建150个频率向量。然后,对于需要标记的新文档,你可以计算这个文档向量和每个类别向量之间的 余弦相似度,选择最相似的类别作为你文档的标签。

如果效果还不够好,你可以尝试训练一个逻辑回归模型,使用 L1 惩罚,具体可以参考 这个例子,它是关于 scikit-learn 的(这是一个 liblinear 的封装,正如 @ephes 所解释的)。用于训练逻辑回归模型的向量应该是之前提到的 TD-log(1+IDF) 向量,这样才能获得好的表现(准确率和召回率)。scikit-learn 库提供了一个 sklearn.metrics 模块,可以用来计算给定模型和数据集的评分。

对于更大的数据集,你可以尝试 vowpal wabbit,这可能是地球上处理大规模文档分类问题最快的工具(但据我所知,使用 Python 封装并不容易)。

撰写回答