【问题标题】:Classifying Documents into Categories将文档分类
【发布时间】:2011-03-08 00:08:11
【问题描述】:

我在 Postgres 数据库中存储了大约 30 万个文档,这些文档带​​有主题类别(总共大约 150 个类别)。我还有另外 150k 文档还没有类别。我正在尝试找到以编程方式对它们进行分类的最佳方法。

我一直在探索NLTK 及其朴素贝叶斯分类器。似乎是一个很好的起点(如果您能为这项任务提出更好的分类算法,我会全力以赴)。

我的问题是我没有足够的 RAM 来一次在所有 150 个类别/300k 文档上训练 NaiveBayesClassifier(5 个类别的训练使用 8GB)。此外,随着我​​对更多类别进行训练,分类器的准确率似乎会下降(2 个类别的准确率为 90%,5 个类别为 81%,10 个类别为 61%)。

我是否应该一次只训练 5 个类别的分类器,然后通过分类器运行所有 150k 文档以查看是否有匹配项?似乎这会起作用,除了会有很多误报,其中与任何类别不真正匹配的文档会被分类器硬塞进分类器,只是因为它是可用的最佳匹配......有吗一种为分类器提供“以上都不是”选项的方法,以防文档不适合任何类别?

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

【问题讨论】:

标签: python machine-learning nlp nltk naivebayes


【解决方案1】:

你应该首先将你的文档转换成TF-log(1 + IDF) vectors:词频是稀疏的,所以你应该使用python dict,将词作为键并计数作为值,然后除以总计数以获得全局频率。

另一个解决方案是使用 abs(hash(term)) 例如作为正整数键。然后你使用 scipy.sparse 向量,它比 python dict 更方便、更有效地执行线性代数运算。

还通过对属于同一类别的所有标记文档的频率进行平均来构建 150 个频率向量。然后对于要标记的新文档,您可以计算文档向量和每个类别向量之间的cosine similarity,并选择最相似的类别作为您的文档的标签。

如果这还不够好,那么您应该尝试使用 L1 惩罚来训练逻辑回归模型,如 this examplescikit-learn 中所述(这是 liblinear 的包装器,如 @ephes 所述)。用于训练逻辑回归模型的向量应该是之前引入的 TD-log(1+IDF) 向量,以获得良好的性能(精度和召回率)。 scikit learn lib 提供了一个 sklearn.metrics 模块,其中包含用于计算给定模型和给定数据集的分数的例程。

对于较大的数据集:您应该尝试vowpal wabbit,它可能是地球上解决大规模文档分类问题最快的兔子(但不容易使用 python 包装器 AFAIK)。

【讨论】:

  • Vowpal wabbit 很快。但是我们仍然使用批量训练而不是在线学习算法,因为 liblinear(经过适当优化)只需几分钟即可处理数百万个文档(我们映射(共享)特征向量,以便新的训练或分类过程不必解析文件,但只在主内存上循环)并且它的性能更好(我现在没有数字......)。
  • 同意,当数据流是无限的并且不再适合内存时,vowpal wabbit 真的很有趣,例如当来自流行的网络邮件提供商的“报告垃圾邮件”按钮时:)
  • 此外...质心分类并不比朴素贝叶斯好多少。这篇论文www2009.org/proceedings/pdf/p201.pdf 是错误的。我们告诉他们,他们使用测试数据进行训练(由于一个错误),但讨论没有结果……线性 SVM 仍然是最先进的。
  • 在numpy/scipy中是否有处理稀疏向量的便捷方法?我的意思是不将其转换为一些 lil/csc-matrix - 许多算法不会采用矩阵......也许我只是愚蠢,但我没有发现任何表现得像 ((dim, value), ...) scipy 中的元组...
  • @OP 请参阅stackoverflow.com/questions/2380394/…,了解海报所说的使用 NLTK 和 PyLucene 的实现。另外,我认为 SVM 对此非常过分。
【解决方案2】:

您的文档有多大(字数)? 150K 训练文档的内存消耗应该不是问题。

朴素贝叶斯是一个不错的选择,尤其是当您有许多类别但只有少量训练示例或非常嘈杂的训练数据时。但总的来说,线性支持向量机的性能要好得多。

您的问题是多类(文档仅属于一个类别)还是多标签(文档属于一个或多个类别)?

准确度是判断分类器性能的一个糟糕选择。您应该使用精确率与召回率、精确率召回盈亏平衡点 (prbp)、f1、auc 并且必须查看精确率与召回率曲线,其中召回率 (x) 根据您的置信度阈值与精度 (y) 进行对比(无论文档是否属于某个类别)。通常,您会为每个类别构建一个二元分类器(一个类别的正面训练示例与不属于您当前类别的所有其他训练示例)。您必须为每个类别选择一个最佳置信度阈值。如果您想将每个类别的这些单一衡量标准组合成一个全局绩效衡量标准,您必须微观(总结所有真阳性、假阳性、假阴性和真阴性并计算综合得分)或宏观(计算每个类别的得分和然后平均所有类别的分数)平均值。

我们拥有数千万个文档、数百万个训练示例和数千个类别(多标签)的语料库。由于我们面临严重的训练时间问题(每天新增、更新或删除的文档数量非常多),我们使用liblinear 的修改版本。但是对于较小的问题,使用 liblinear 周围的 python 包装器之一(liblinear2scipyscikit-learn)应该可以正常工作。

【讨论】:

  • 平均文档大约 500-1000 字。文档可以是“多标签”。
  • 好的,然后使用@ogrisel 建议的稀疏 tfidf 向量(我忘了提)和每个类别一个二元分类器。也许您的文档中有一些非序数(数字)特征 - 您必须适当地对它们进行分类。
  • 您使用了哪个修改版本的 liblinear?或者你自己修改了什么?
  • +1 建议将精度/召回率作为分类器质量的衡量标准
  • 绝对召回/精度/ f-measure 来衡量性能。在信息学领域相当标准。 (en.wikipedia.org/wiki/F-measure)。还建议使用 k 折交叉验证 (en.wikipedia.org/wiki/…) 进行测量。我也同意你的表现会更好地进行二进制分类(无论是 X 还是不是)然后尝试一次性标记所有内容。
【解决方案3】:

有没有办法让“没有 上面“分类器的选项只是 如果文件不适合 任何类别?

您可能只需每次都训练“以上都不是”伪类别即可获得此效果。如果您可以训练的最大类别是 5 个类别(尽管我不确定为什么它会占用如此多的 RAM),则从它们的实际 2K 文档中分别训练 4 个实际类别,并使用其 2K 文档训练一个“以上都不是”的类别从所有其他 146 个类别中随机抽取(如果您想要“分层抽样”方法,每个类别大约 13-14 个,这可能更合理)。

仍然感觉有点杂乱无章,使用完全不同的方法可能会更好 - 找到一个多维文档度量,将 300K 预标记文档定义为 150 个可合理分离的集群,然后分配每个其他尚未标记的文档到如此确定的适当集群。我不认为 N​​LTK 有任何直接可用的东西来支持这种事情,但是,嘿,NLTK 的发展如此之快,以至于我很可能错过了一些东西......;-)

【讨论】:

  • 我们知道无法正确分类的特殊文档类别。这有点杂乱无章,但效果很好。
猜你喜欢
  • 2012-10-02
  • 2014-01-06
  • 2017-11-28
  • 2013-11-07
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
相关资源
最近更新 更多