更深层次的含义:Python 中的主题建模

已发表: 2022-03-11

计算机和为其提供动力的处理器是为处理数字而设计的。 相比之下,电子邮件和社交媒体帖子的日常语言结构松散,不适合计算。

这就是自然语言处理 (NLP) 的用武之地。NLP 是计算机科学的一个分支,它通过应用计算技术(即人工智能)来分析自然语言和语音,与语言学重叠。 主题建模侧重于了解给定文本的主题。 主题建模使开发人员可以实现有用的功能,例如检测社交媒体上的突发新闻、推荐个性化消息、检测虚假用户和表征信息流。

开发人员如何哄骗专注于计算的计算机在这些复杂级别上理解人类通信?

一袋词

要回答这个问题,我们需要能够以数学方式描述文本。 我们将从最简单的方法开始我们的主题建模 Python 教程:词袋。

此方法将文本表示为一组单词。 例如,句子This is an example可以描述为一组单词,使用这些单词出现的频率:

 {"an": 1, "example": 1, "is": 1, "this": 1}

请注意此方法如何忽略词序。 举这些例子:

  • “我喜欢星球大战,但我不喜欢哈利波特。”
  • “我喜欢哈利波特,但我不喜欢星球大战。”

这些情感用相同的词来表示,但它们具有相反的含义。 然而,为了分析文本的主题,这些差异并不重要。 在这两种情况下,我们都在谈论哈利波特和星球大战的口味,无论这些口味是什么。 因此,词序无关紧要。

当我们有多个文本并试图理解它们之间的差异时,我们需要为我们的整个语料库单独考虑每个文本的数学表示。 为此,我们可以使用矩阵,其中每一列代表一个单词或术语,每一行代表一个文本。 语料库的可能表示包括在每个单元格中记录给定单词(列)在特定文本(行)中使用的频率。

在我们的示例中,语料库由两个句子(我们的矩阵行)组成:

 ["I like Harry Potter", "I like Star Wars"]

我们按照遇到的顺序列出了这个语料库中的单词: I , like , Harry , Potter , Star , Wars 。 这些对应于我们的矩阵列。

矩阵中的值表示给定单词在每个短语中使用的次数:

 [[1,1,1,1,0,0], [1,1,0,0,1,1]] 

图像左侧显示两行文字:我喜欢哈利波特和我喜欢星球大战。然后这段文字在中间变成一袋单词,每个单词后面跟着该单词的使用次数:“我喜欢哈利波特”变成“{I: 1, like: 1, Harry: 1, Potter : 1, Star: 0, Wars: 0}”和“我喜欢星球大战”变成“{I: 1, like: 1, Harry: 0, Potter: 0, Star: 1, Wars: 1}” 向右,然后这些数字以矩阵表示形式排列:前者变为“1 1 1 1 0 0”行,后者变为“1 1 0 0 1 1”。
文本转换为矩阵表示

请注意,矩阵的大小是通过将文本数量乘以至少一个文本中出现的不同单词的数量来确定的。 后者通常不必要地大并且可以减小。 例如,一个矩阵可能包含两列共轭动词,例如“play”和“played”,不管它们的含义是否相似。

但是描述新概念的栏目可能会丢失。 例如,“古典”和“音乐”各有不同的含义,但当它们组合在一起时——“古典音乐”——它们又有不同的含义。

由于这些问题,有必要对文本进行预处理以获得良好的结果。

预处理和主题聚类模型

为了获得最佳结果,有必要使用多种预处理技术。 以下是一些最常用的:

  1. 小写字母。 使所有单词小写。 使所有单词小写。 无论单词在句子中的位置如何,单词的含义都不会改变。
  2. n-克。 将一行中的所有n 个单词组视为新术语,称为n-grams 。 这样,诸如“白宫”之类的案例将被考虑并添加到词汇表中。
  3. 词干。 识别单词的前缀和后缀,将它们与词根隔离开来。 这样,“play”、“played”或“player”之类的词就可以用“play”这个词来表示了。 词干提取有助于减少词汇表中的单词数量,同时保留其含义,但它会大大减慢预处理,因为它必须应用于语料库中的每个单词。
  4. 停止的话。 不要考虑缺乏意义或实用性的词组。 这些包括冠词和介词,但也可能包括对我们的特定案例研究无用的词,例如某些常用动词。
  5. 词频-逆文档频率(tf-idf)。 使用 tf–idf 的系数,而不是注意矩阵每个单元格中每个单词的频率。 它由两个数字相乘:
    • tf——文本中给定术语或单词的频率,以及
    • idf——文档总数除以包含该给定术语的文档数的对数。

    tf-idf 衡量一个词在语料库中的使用频率。 为了能够将单词细分为组,重要的是不仅要了解哪些单词出现在每个文本中,还要了解哪些单词在一个文本中经常出现,而在其他文本中根本不出现。

下图显示了这些预处理技术的一些简单示例,其中修改了语料库的原始文本以生成相关且可管理的单词列表。

“小写字母”技术转换了句子“The White House”。成一个词表:“the”、“white”、“house”。 “n-gram”技术将其转换为更长的列表:“the”、“white”、“house”、“the white”、“white house”。 “词干”技术转换了“足球运动员打得很好”这句话。进入这个列表:“the”、“football”、“play”、“a”、“good”、“game”。 “停用词”技术将其转换为更短的列表:“football”、“play”、“good”、“game”。
文本预处理技术示例

现在我们将演示如何在 Python 中应用其中一些技术。 一旦我们以数学方式表示了我们的语料库,我们需要通过应用无监督机器学习算法来识别正在讨论的主题。 在这种情况下,“无监督”意味着该算法没有任何预定义的主题标签,如“科幻小说”,可应用于其输出。

为了对我们的语料库进行聚类,我们可以从多种算法中进行选择,包括非负矩阵分解 (NMF)、稀疏主成分分析 (sparse PCA) 和潜在狄利克雷分配 (LDA)。 我们将重点关注 LDA,因为它在社交媒体、医学、政治学和软件工程方面取得了良好的效果,因此被科学界广泛使用。

LDA 是一种无监督主题分解模型:它根据文本包含的单词和单词属于某个主题的概率对文本进行分组。 LDA算法输出主题词分布。 有了这些信息,我们可以根据最有可能与之相关的词来定义主要主题。 一旦我们确定了主要主题及其相关词,我们就可以知道哪个或哪些主题适用于每个文本。

考虑以下由五个短句组成的语料库(均取自《纽约时报》的头条新闻):

 corpus = [ "Rafael Nadal Joins Roger Federer in Missing US Open", "Rafael Nadal Is Out of the Australian Open", "Biden Announces Virus Measures", "Biden's Virus Plans Meet Reality", "Where Biden's Virus Plan Stands"]

该算法应该清楚地识别出一个与政治和冠状病毒有关的话题,以及第二个与纳达尔和网球有关的话题。

在 Python 中应用策略

为了检测主题,我们必须导入必要的库。 Python 有一些用于 NLP 和机器学习的有用库,包括 NLTK 和 Scikit-learn (sklearn)。

 from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer from sklearn.decomposition import LatentDirichletAllocation as LDA from nltk.corpus import stopwords

使用CountVectorizer() ,我们使用CountVectorizer() () 生成表示每个文本单词频率的矩阵。 请注意,如果您包含参数,例如stop_words以包含停用词, ngram_range包含n -gram,或lowercase=True将所有字符转换为小写,则 CountVectorizer 允许进行预处理。

 count_vect = CountVectorizer(stop_words=stopwords.words('english'), lowercase=True) x_counts = count_vect.fit_transform(corpus) x_counts.todense() matrix([[0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1]], dtype=int64)

要定义我们语料库的词汇表,我们可以简单地使用属性.get_feature_names()

 count_vect.get_feature_names() ['announces', 'australian', 'biden', 'federer', 'joins', 'measures', 'meet', 'missing', 'nadal', 'open', 'plan', 'plans', 'rafael', 'reality', 'roger', 'stands', 'virus']

然后,我们使用 sklearn 函数执行 tf-idf 计算:

 tfidf_transformer = TfidfTransformer() x_tfidf = tfidf_transformer.fit_transform(x_counts)

为了执行 LDA 分解,我们必须定义主题的数量。 在这个简单的例子中,我们知道有两个主题或“维度”。 但在一般情况下,这是一个需要调整的超参数,可以使用随机搜索或网格搜索等算法来完成:

 dimension = 2 lda = LDA(n_components = dimension) lda_array = lda.fit_transform(x_tfidf) lda_array array([[0.8516198 , 0.1483802 ], [0.82359501, 0.17640499], [0.18072751, 0.81927249], [0.1695452 , 0.8304548 ], [0.18072805, 0.81927195]])

LDA 是一种概率方法。 在这里,我们可以看到五个标题中的每一个属于两个主题的概率。 我们可以看到,前两个文本属于第一个主题的概率更高,接下来的三个文本属于第二个主题的概率更高,正如预期的那样。

最后,如果我们想了解这两个主题是关于什么的,我们可以看到每个主题中最重要的词:

 components = [lda.components_[i] for i in range(len(lda.components_))] features = count_vect.get_feature_names() important_words = [sorted(features, key = lambda x: components[j][features.index(x)], reverse = True)[:3] for j in range(len(components))] important_words [['open', 'nadal', 'rafael'], ['virus', 'biden', 'measures']]

正如预期的那样,LDA 正确地将与网球锦标赛和纳达尔相关的词分配到第一个主题,将与拜登和病毒相关的词正确分配到第二个主题。

大规模分析和实际用例

本文对主题建模进行了大规模分析; 我研究了 2016 年美国总统大选期间的主要新闻话题,并观察了一些大众媒体——比如纽约时报和福克斯新闻——在他们的报道中包含的话题,比如腐败和移民。 在本文中,我还分析了大众媒体内容与选举结果之间的相关性和因果关系。

主题建模在学术界之外也被广泛用于发现大量文本中隐藏的主题模式。 例如,它可以用于推荐系统或确定客户/用户在调查、反馈表或社交媒体上谈论的内容。

Toptal 工程博客对 Juan Manuel Ortiz de Zarate 对本文中提供的代码示例的审阅表示感谢。

主题建模推荐读物

Twitter 中改进的主题建模
Albanese、Federico 和 Esteban Feuerstein。 “通过社区池改进 Twitter 中的主题建模。” (2021 年 12 月 20 日):arXiv:2201.00690 [cs.IR]

为公共卫生分析 Twitter
保罗、迈克尔和马克·德雷泽。 “你就是你的推文:分析 Twitter 以促进公共卫生。” 2021 年 8 月 3 日。

在 Twitter 上对政治倾向进行分类
科恩、拉维夫和德里克·鲁斯。 “在 Twitter 上对政治倾向进行分类:这并不容易!” 2021 年 8 月 3 日。

使用关系主题模型捕获耦合
盖瑟斯、马尔科姆和丹尼斯·波希瓦尼克。 “使用关系主题模型捕获面向对象软件系统中类之间的耦合。” 2010 年 10 月 25 日。