在我之前的几篇文章中,我研究了几种应用机器学习的方法,包括监督学习和无监督学习。这一次,我想让您关注机器学习一个非常简单但功能强大且应用广泛的用途,即文档分类。

您几乎肯定在日常生活中见过这项技术的使用。实际上,您可能没有亲眼见到它在起作用,但您肯定从中受益,例如电子邮件垃圾邮件过滤器。您可能还记得,在垃圾邮件过滤器的早期,您需要“训练”您的电子邮件程序,以便它知道您的真实电子邮件是什么样的。嗯,那是一个正在运行的机器学习模型,它被告知“好”文档是什么样的,而不是“坏”文档。当然,现在的垃圾邮件过滤器比这复杂得多,但正如您将在本文中看到的那样,垃圾邮件发送者在垃圾邮件文本中包含看似无辜(且与其业务无关)的词语是有逻辑原因的。

文本分类是许多企业和组织必须处理的问题。无论是对法律文件、医疗记录还是推文进行分类,机器学习都可以帮助您浏览大量文本,并将其分成不同的组。

现在,文本分类比处理纯数字数据需要更高的复杂度。特别是,它要求您花费一些时间收集和组织数据,使其成为模型可以处理的格式。幸运的是,Python 的 scikit-learn 附带了许多工具,可以轻松实现这一目标。

组织数据

许多文本分类案例都是监督学习问题——也就是说,您将训练模型,为其提供输入(例如,文本文档)以及每个输入的“正确”输出(例如,类别)。在 scikit-learn 中,监督学习的通用模板是


model = CLASS()
model.fit(X, y)
model.predict(new_data_X)

CLASS 是 scikit-learn 附带的 30 个左右的 Python 类之一,每个类都实现了一种不同类型的“估计器”——一种机器学习算法。一些估计器最适合监督分类问题,一些适用于监督回归问题,还有一些适用于聚类(即,无监督分类)问题。您通常可以从几种不同的估计器中进行选择,但通用格式保持不变。

创建估计器的实例后,您需要对其进行训练。这是使用 “fit” 方法完成的,您需要向其提供 X(输入,作为二维 NumPy 数组或 Pandas 数据帧)和 y(一维 NumPy 数组或 Pandas 系列)。模型训练完成后,您可以调用其 “predict” 方法,并将 new_data_X(另一个二维 NumPy 数组或 Pandas 数据帧)传递给它。结果是一个 NumPy 数组,其中列出了输入应分类到的(数字)类别。

我最喜欢使用 scikit-learn 的地方之一是它的大部分内容都使用相同的 API。无论您使用哪种模型,您几乎总是会在模型上使用 “fit” 和 “predict” 的某种组合。

作为一般规则,机器学习模型要求输入是数字。因此,您将类别名称转换为数字,国家名称转换为数字,颜色名称转换为数字——基本上,一切都必须是数字。

那么,您如何处理文本数据呢?确实,字节是数字,但这在这里并没有真正帮助;您想处理单词和句子,而不是单个字符。

答案是将文档转换为 DTM——“文档-词项矩阵”,其中列是文档中使用的词,行指示该词是否(以及多少次)出现在文档中。

例如,以下三个句子

  • 我饿了,需要吃午饭。

  • 给我打电话,我们去吃饭。

  • 你需要吃饭吗?

让我们将以上内容转换为 DTM


i'm hungry and need to eat lunch call me we'll go do you
1     1     1    1  1    1   1   0    0     0  0   0  0
0     0     1    0  0    1   0   1    1     1  1   0  0
0     0     0    1  1    1   0   0    0     0  0   1  1

现在,这个 DTM 肯定很好地总结了哪些词出现在哪些文档中。但是,仅仅使用我构建的三个具有重叠词汇的短句,它已经开始变得相当宽泛。想象一下,如果您要对大量文档进行分类会发生什么;DTM 将会非常庞大!此外,DTM 主要由零组成。

因此,DTM 通常以“稀疏矩阵”的形式实现,列出非零值的坐标。这往往会大大缩小其大小,从而减少处理时间。

您将把这个 DTM 馈送到您的模型中。由于它是数字的,模型可以处理它——因此,可以进行预测。请注意,您实际上需要制作两个不同的 DTM:一个用于训练模型,另一个用于处理您要分类的文本。

创建 DTM

我决定做一个简短的实验,看看我是否可以创建一个机器学习模型,该模型知道如何区分 Python 代码和 Ruby 代码。我不仅在我的电脑上有相当多的此类代码,而且这两种语言具有相似的词汇,我想知道模型实际上可以多准确地进行一些分类。

因此,首要任务是创建一个 Python 文本列表,以及一个平行的数字类别列表。我使用了一些列表推导式来完成此操作,如下所示


from glob import glob

# read Ruby files
ruby_files = [open(filename).read()
              for filename in glob("Programs/*.rb")]

# read Python files
python_files = [open(filename).read()
                for filename in glob("Programs/*.py")]

# all input files
input_text = ruby_files + python_files

# set up categories
input_text_categories = [0] * len(ruby_files) + [1]
 ↪* len(python_files)

运行此代码后,我有一个字符串列表 (input_text) 和另一个整数列表 (input_text_categories),它们表示这些字符串应分类到的两个类别。

现在我必须将这个字符串列表转换为 DTM。幸运的是,scikit-learn 附带了许多“特征提取”工具,可以轻松实现这一点


from sklearn.feature_extraction.text import CountVectorizer

cv = CountVectorizer()
cv_dtm = cv.fit_transform(input_text)

CountVectorizer 不是创建 DTM 的唯一方法。实际上,您可以使用不同的策略。除其他外,一个单词而不是多个单词的粒度可能不适合您的文本。

请注意,我使用了 cv.fit_transform。这既教会向量化器词汇表(“fit”),又生成 DTM。我可以使用相同的词汇表创建新的 DTM,只需使用 “transform”——当我想要进行一两个预测时,我确实会这样做。

创建模型

现在我的输入格式可以用于创建模型了!您可能会使用多种算法,但最常见(且出奇地准确)的算法之一是朴素贝叶斯。Scikit-learn 实际上附带了几个不同版本的朴素贝叶斯。我在这里使用的是 MultinomialNB;它非常适合这种文本数据。(但是,当然,通常最好测试您的模型,甚至调整输入和参数以从中获得更好的结果。)以下是我创建然后训练模型的方式


from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()
nb.fit(input_text_dtm, input_text_categories)

请注意,我现在已经使用了两次 “fit”:一次(在 CountVectorizer 上)从输入文本训练和创建 DTM,然后(在 MultinomialNB 上)基于该 DTM 训练模型。

模型现在已全部设置完毕!现在我可以进行一些预测了。我将创建一些新文档


docs_new = ['class Foo(object):\nprint "Hello, {}".format(self.name)\n',
            'x = [10, 20, 30]\n',
           '10.times do {|i| puts i}']

docs_new 变量包含三个字符串:第一个是 Python,第二个可以是 Ruby 或 Python,第三个是 Ruby。

为了查看模型如何对它们进行分类,我首先需要从这些文档中创建一个 DTM。请注意,我将重用 cv,即 CountVectorizer 对象。但是,我不会使用 “fit” 方法来使用新词汇表训练它。相反,我将使用 “transform” 将现有词汇表与新文档一起使用。这将允许模型将文档与之前的文档进行比较


docs_new_dtm = cv.transform(docs_new)

现在进行预测


nb.predict(docs_new_dtm)

输出为


array([1, 1, 0])

换句话说,前两个文档被视为 Python,第三个被视为 Ruby——对于如此小的训练集来说,还不错。您可以想象,您用于训练的文档越多,您的分类就可能越准确。

我尝试了上述代码的一个略微变体,使用了 “20 newsgroups” 数据集,其中包含来自 20 个不同 Usenet 论坛帖子的 20,000 个帖子。在使用 CountVectorizerMultinomialNB(就像我在这里所做的那样)之后,该模型能够以惊人的高精度预测各种句子和段落最合适的新闻组。

当然,与包括机器学习在内的所有统计数据一样,成功率永远不会达到 100%。实际上,您可以(并且可能想要)更新模型,调整输入和模型的超参数,以尝试进一步改进它。

总结

文档分类是机器学习的一个实际应用,许多组织都在使用它——不仅在垃圾邮件过滤器中,而且还用于对大量文本进行排序。如您所见,设置这样的模型并非特别困难,scikit-learn 提供了大量的向量化器、特征提取工具和估计器,您可以用来创建它们。

资源

我在本文中使用了 Python 和 SciPy 堆栈的许多部分(NumPy、SciPy、Pandas、Matplotlib 和 scikit-learn)。所有这些都可以从 PyPIScyPy.org 获得。

Reuven Lerner 在世界各地的公司教授 Python、数据科学和 Git。您可以订阅他的免费每周 “更好的开发者” 电子邮件列表,并从他的书籍和课程中学习,网址为 http://lerner.co.il。Reuven 与他的妻子和孩子住在以色列的莫迪因。

加载 Disqus 评论