第5章 文本聚类与主题建模


尽管近年来像分类这样的监督技术在行业中占据主导地位,但文本聚类等无监督技术的潜力仍不可小觑。文本聚类的目标是根据语义内容、含义和关系对相似文本进行分组。如图5-1所示,由语义相似文档形成的聚类结果不仅能够高效地对大量非结构化文本进行分类,还为快速探索性数据分析提供了可能。


图5-1. 对无结构文本数据进行聚类

近期语言模型的发展——这些模型能够对文本进行上下文感知和语义表征——显著提升了文本聚类的有效性。语言远非简单的词袋组合,而最新语言模型已证明其确实能够有效捕捉这一本质特征。无需监督的文本聚类方法为创造性解决方案和多样化应用提供了可能,例如异常值检测、加速标注流程以及发现错误词元数据等场景。

文本聚类技术同样在主题建模领域大放异彩。在该领域中,我们的目标是从海量文本数据中发现(抽象的)主题概念。如图5-2所示,我们通常采用关键词或短语来描述主题,理想情况下还应为其赋予一个统领性的标签。


图5-2主题建模是一种为文本文档聚类赋予意义的方法

在本章中,我们将首先探索如何使用嵌入模型进行聚类,然后过渡到一种受文本聚类启发的主题建模方法——BERTopic。文本聚类和主题建模在本书中具有重要地位,因为它们探索了结合多种不同语言模型的创新方式。我们将深入研究如何通过融合仅编码器(嵌入模型)、仅解码器(生成式模型)甚至传统方法(词袋模型)来催生令人惊叹的新技术与应用流程。

ArXiv文章:计算和语言

本章中,我们将在ArXiv文章上运行聚类和主题建模算法。ArXiv是一个开放获取的学术文章平台,主要涵盖计算机科学、数学和物理学等领域的研究成果。为了与本书主题保持一致,我们将探索计算机科学-计算与语言(cs.CL)领域的文章。该数据集包含来自ArXiv cs.CL板块的44,949篇摘要(时间跨度为1991年至2024年)。

我们将加载数据,并为每篇文章的摘要、标题和年份创建单独的变量:

# Load data from Hugging Face

from datasets import load_dataset

dataset = load_dataset("maartengr/arxiv_nlp")["train"]

# Extract metadata

abstracts = dataset["Abstracts"]

titles = dataset["Titles"]

一个用于文本集群的公共管道

文本聚类能够发现你可能熟悉或不熟悉的数据中的模式。它不仅有助于直观理解任务(例如分类任务),还能让你认识到任务的复杂性。因此,文本聚类可以超越单纯的探索性数据分析快速方法。

虽然文本聚类存在多种方法——从基于图的神经网络到质心聚类技术——但一个广受欢迎的三步通用流程包含以下算法:

1.使用嵌入模型将输入文档转换为嵌入表示

2.通过降维模型对嵌入进行维度缩减

3.利用聚类模型寻找语义相似文档的群组

文档嵌入

第一步是将我们的文本数据转换为嵌入,如图5-3所示。回想前几章的内容可知,嵌入是文本的数值表示形式,旨在捕捉其核心含义


图5-3 步骤1:我们使用嵌入模型将文档转换为嵌入

选择针对语义相似性任务优化的嵌入模型对于聚类任务尤为重要,因为我们需要找到由语义相似文档组成的群组。幸运的是,在本文撰写时,大多数嵌入模型都专注于实现这一目标——语义相似性。

与上一章的做法相同,我们将使用MTEB排行榜来选择嵌入模型。我们需要一个在聚类任务上有不错得分,同时体积足够小、能快速运行的嵌入模型。不同于上一章使用的"sentence-transformers/all-mpnet-base-v2"模型,这次我们改用"thenlper/gte-small"模型。这是一个更新颖的模型,在聚类任务上的表现优于前一个模型,由于体积小巧,在推理时甚至更快。不过,如果有时间的话,也可以尝试一下自那以后发布的新模型!(注:该模型已开源并可通过Hugging Face Transformers平台直接调用)

from sentence_transformers import SentenceTransformer

# Create an embedding for each abstract

embedding_model = SentenceTransformer("thenlper/gte-small")

embeddings = embedding_model.encode(abstracts, show_progress_bar=True)

Let’s check how many values each document embedding contains:

# Check the dimensions of the resulting embeddings

embeddings.shape

让我们查看每个文档嵌入包含多少个值:

(44949, 384)

降低嵌入的维度

在我们对嵌入进行聚类之前,首先需要考虑其高维度特性。随着维度数量的增加,每个维度内可能取值的数量会呈指数级增长。找出每个维度内的所有潜在子空间变得越来越复杂。

因此,对于许多聚类技术来说,高维数据会带来挑战,因为识别有意义的聚类变得更加困难。此时我们可以采用降维技术。如图5-4所示,该技术允许我们缩小维度空间的规模,用更少的维度表示相同的数据。降维技术的目标是通过寻找低维表示来保留高维数据的全局结构。


图5-4 降维技术允许将高维空间中的数据映射到低维表示形式,从而实现对数据的有效压缩

请注意,这是一种压缩技术,其底层算法并非随意移除维度。为了帮助聚类模型创建有意义的簇,因此我们聚类流程中的第二步是降维处理,具体过程如图5-5所示。


图5-5步骤2:通过降维方法将嵌入向量映射到低维空间

常用的降维方法包括主成分分析(PCA)¹ 和统一流形近似与投影(UMAP)²。在本文本聚类的通用流程中,我们选择UMAP,因为它在处理非线性关系和数据结构方面相较于PCA具有更强的能力,尤其擅长捕捉复杂的高维数据中的非线性模式,同时在保持数据局部邻域特征方面表现更优。


1 Harold Hotelling. “Analysis of a complex of statistical variables into principal components.” Journal of Educa‐ tional Psychology 24.6 (1933): 417.

2 Leland McInnes, John Healy, and James Melville. “UMAP: Uniform Manifold Approximation and Projection for dimension reduction.” arXiv preprint arXiv:1802.03426 (2018)

降维技术并非完美无缺。它们无法在低维表示中完美重现高维数据。采用这种方法时,信息始终会有所损失。在降低维度与尽可能保留更多信息之间,需要寻求平衡

为了进行降维,我们需要实例化UMAP类并将生成的嵌入传入其中:

from umap import UMAP

# We reduce the input embeddings from 384 dimensions to 5 dimensions

umap_model = UMAP(

n_components=5, min_dist=0.0, metric='cosine', random_state=42

)

reduced_embeddings = umap_model.fit_transform(embeddings)

我们可以使用n_components参数来决定低维空间的形状,即设置为5个维度。通常情况下,5到10之间的值能够较好地捕捉高维数据的全局结构特征。min_dist参数表示嵌入点之间的最小距离,我们将其设置为0,因为这通常会导致更紧密的聚类效果。我们将metric参数设为"余弦",因为基于欧氏距离的方法在高维数据处理中存在局限性。

需要注意的是,在UMAP中设置random_state参数会使结果在不同会话中保持可复现性,但同时会禁用并行计算功能,从而导致模型训练速度下降。这个权衡需要根据具体应用场景进行取舍:当需要稳定的实验复现时,建议保留random_state设置;而在追求训练效率的场景下,则可以暂时关闭该参数以利用多核并行加速。

对降维后的嵌入进行聚类

第三步是对降维后的嵌入进行聚类,如图5-6所示。


图5-6. 步骤3:我们采用降维后的嵌入表示对文档进行聚类

虽然基于质心的算法(如k均值)是常见选择,这类算法需要预先生成一组簇,但我们事先并不知道簇的数量。相反,密度聚类算法能够自动计算簇的数量,并且不会强制所有数据点都必须属于某个簇,如图5-7所示。


图5-7聚类算法不仅影响聚类的生成方式,也影响着它们的呈现方式

一种常见的基于密度的模型是带有噪声的应用程序层次密度空间聚类(HDBSCAN)[3]。HDBSCAN是DBSCAN聚类算法的层次化变体,它允许在不显式指定聚类数量的情况下发现密集的(微观)聚类[4]。作为基于密度的方法,HDBSCAN还能够检测数据中的离群点——即不属于任何聚类的数据点。这些离群点不会被分配或强制归入任何聚类,换句话说就会被忽略。由于ArXiv文章可能包含某些小众论文,使用能够检测离群点的模型会很有帮助。

与之前的工具包一样,HDBSCAN的使用非常简单。我们只需要实例化该模型并将降维后的嵌入结果传递给它即可


3 Leland McInnes, John Healy, and Steve Astels. “hdbscan: Hierarchical density based clustering.” J. Open Source Softw. 2.11 (2017): 205.

4 Martin Ester et al. “A density-based algorithm for discovering clusters in large spatial databases with noise.” KDD’96, Aug. 1996: 226–231.

from hdbscan import HDBSCAN

# We fit the model and extract the clusters

hdbscan_model = HDBSCAN(

min_cluster_size=50, metric="euclidean", cluster_selection_method="eom"

).fit(reduced_embeddings)

clusters = hdbscan_model.labels_

# How many clusters did we generate?

len(set(clusters))

使用HDBSCAN算法,我们在数据集中生成了156个聚类。若要创建更多聚类,需要减小min_cluster_size参数的值,因为该参数代表聚类可取的最小大小。

检查集群

现在我们已经生成了集群,我们可以手动检查每个集群,并探索分配的文档, 以了解其内容。例如,让我们从集群0中随机抽取一些文档:

import numpy as np

# Print first three documents in cluster 0

cluster = 0

for index in np.where(clusters==cluster)[0][:3]:

print(abstracts[index][:300] + "... \n")

This works aims to design a statistical machine translation from English text

to American Sign Language (ASL). The system is based on Moses tool with some

modifications and the results are synthesized through a 3D avatar for

interpretation. First, we translate the input text to gloss, a written fo...

Researches on signed languages still strongly dissociate lin- guistic issues

related on phonological and phonetic aspects, and gesture studies for

recognition and synthesis purposes. This paper focuses on the imbrication of

motion and meaning for the analysis, synthesis and evaluation of sign lang...

Modern computational linguistic software cannot produce important aspects of

sign language translation. Using some researches we deduce that the majority of

automatic sign language translation systems ignore many aspects when they

generate animation; therefore the interpretation lost the truth inf...

从这些文档来看,这个聚类主要包含与手语互译相关的文档,有趣的是!
我们可以更进一步,尝试通过可视化结果来替代手动浏览所有文档。为此,我们需要将文档嵌入降维到二维空间,因为这样可以在x/y平面上绘制出文档的分布情况:

import pandas as pd

# Reduce 384-dimensional embeddings to two dimensions for easier visualization

reduced_embeddings = UMAP(

n_components=2, min_dist=0.0, metric="cosine", random_state=42

).fit_transform(embeddings)

# Create dataframe

df = pd.DataFrame(reduced_embeddings, columns=["x", "y"])

df["title"] = titles

df["cluster"] = [str(c) for c in clusters]

# Select outliers and non-outliers (clusters)

to_plot = df.loc[df.cluster != "-1", :]

outliers = df.loc[df.cluster == "-1", :]

我们还分别为聚类(clusters_df)和离群值(outliers_df)创建了独立的数据框,因为我们通常需要聚焦于聚类结果并对其重点突出显示。

出于可视化目的使用任何降维技术都会造成信息损失。这仅仅是原始嵌入特征的一种近似呈现。虽然这种可视化手段具有启发性,但它可能将本应聚集的簇强制拉近,或将实际分散的簇人为推远。因此,人工评估——即我们亲自检验这些聚类结果——始终是聚类分析过程中不可或缺的关键环节!

要生成静态图,我们将使用著名的绘图库matplotlib

import matplotlib.pyplot as plt

# Plot outliers and non-outliers separately

plt.scatter(outliers_df.x, outliers_df.y, alpha=0.05, s=2, c="grey")

plt.scatter(

clusters_df.x, clusters_df.y, c=clusters_df.cluster.astype(int),

alpha=0.6, s=2, cmap="tab20b"

)

plt.axis("off")

如图5-8所示,该算法倾向于相当有效地捕捉到主要簇结构。注意图中各点簇被染成了相同的颜色,这表明HDBSCAN算法将它们归入同一类别。由于我们处理的簇数量较多,绘图库会在不同簇之间循环使用颜色方案,因此需要注意不能简单认为所有绿色点都属于同一个簇(例如)。


图5-8 生成的聚类(彩色词元)与离群点(灰色词元)通过二维可视化方式进行呈现

这在视觉上颇具吸引力,但尚未揭示群集内部正在发生的真实情况。为此,我们可以通过将文本聚类升级为话题建模来增强这种可视化效果。

从文本聚类到主题建模

文本聚类是一种在大规模文档集合中发现结构的强大工具。在我们之前的示例中,我们可以手动检查每个聚类,并根据其文档集合来识别它们。例如,我们探索了一个包含手语相关文档的聚类。我们可以说该聚类的主题是"手语"。

在文本数据集合中寻找主题或潜在主题的想法通常被称为主题建模。传统上,这包括找到一组最能代表和捕捉主题含义的关键词或短语,如图5-9所示。图中展示了通过提取关键词"手语教学"、"聋哑人群"、"手势交流"等代表性词汇,来表征手语主题的典型方法


图5-9 传统上,主题通常通过多个关键词来表示,但也可以采用其他形式

这些技术并非将主题词元为"手,而是使用"手势"、"语言"和"翻译"等关键词来描述主题。因此,这种方法并不会为某个主题赋予单一标签,而是需要用户通过这些关键词来理解主题的含义。传统方法如潜在狄利克雷分配(LDA)假设每个主题都通过语料库词汇表中词语的概率分布来表征。如图5-10所示,该过程会为词汇表中的每个词语计算其相对于各个主题的相关性得分。


图5-10 关键字是根据它们在单一主题上的分布来提取的


5 David M. Blei, Andrew Y. Ng, and Michael I. Jordan. “Latent Dirichlet allocation.” Journal of Machine Learning Research 3. Jan (2003): 993–1022.

这些方法通常采用词袋技术作为文本数据的主要特征,但既不考虑词语的上下文,也不考虑其语义和短语含义。相比之下,我们的文本聚类示例通过基于Transformer的嵌入表示(优化了注意力机制下的语义相似性和上下文含义)实现了对这两方面的考量。在本节中,我们将通过一个高度模块化的文本聚类与主题建模框架——BERTopic,将文本聚类扩展到主题建模领域。

BERTopic:一个模块化的主题建模框架

BERTopic是一种基于语义相似文本簇的主题建模技术,可通过聚类分析提取多种类型的主题表征6。其底层算法可分为两个步骤:

如图5-11所示,第一步采用与文本聚类案例完全相同的处理流程:首先对文档进行嵌入表示,随后通过降维方法压缩特征维度,最终对降维后的嵌入向量进行聚类操作,从而形成具有语义相似性的文档组。


图5-11 BERTopic流程的第一部分是创建语义相似文档的聚类。

其次,它通过利用一种经典方法——词袋(bag-of-words)——对语料库词汇表中的词汇分布进行建模。正如我们在第1章简要讨论的并在图5-12中展示的那样,词袋模型正如其名称所暗示的那样,会统计文档中每个单词出现的次数。生成的表示(representation)可用于提取文档中最频繁的单词。


6 Maarten Grootendorst. “BERTopic: Neural topic modeling with a class-based TF-IDF procedure.” arXiv preprint arXiv:2203.05794 (2022)


图5-12 袋装词方法会统计每个单词在文档中出现的次数

不过有两个注意事项需要说明。首先,这是一种文档级别的表示方式,而我们关注的是集群级别的视角。为解决这一问题,如图5-13所示,单词的频率计算方式调整为在整个集群范围内进行,而非仅限单个文档内部。


图5-13. 通过统计每个聚类内单词的频率(而非单个文档)生成c-TF

其次,"the"和"I"这类停用词在文档中频繁出现,但对实际文档内容缺乏实质性意义。BERTopic采用基于类的词频-逆文档频率(c-TF-IDF)变体,通过赋予对聚类更具意义的词汇更高权重,同时对跨所有聚类使用的通用词汇降低权重的方式来实现这一点。

在词袋模型中,每个词的c-TF(即c-TF-IDF中的词频部分)都会乘以该词的IDF值。如图5-14所示,IDF值的计算方法为:首先取所有聚类中各个单词的平均频率,然后用该平均频率除以每个单词的总频率,最后对这个商值取对数。


图5-14. 创建加权方案

结果是为每个词计算出一个权重值("IDF"),我们可以将其与词频("c-TF")相乘得到加权值("c-TF-IDF")。如图5-15所示的流程第二部分,这使我们能够生成如前所述的词分布模型。我们可以使用scikit-learn的CountVectorizer工具来创建词袋模型(bag-of-words)或词频表征。在此过程中,每个聚类都会被视为一个主题,该主题对语料库词汇表中的词语具有特定的排序权重。

图5-15 BERTopic流水线的第二部分是主题表征:即计算术语x在类别c中的权重

将这两个步骤——聚类和主题表示——结合起来,就形成了如图5-16所示的BERTopic完整流程。通过这一流程,我们可以对语义相似的文档进行聚类,并从这些聚类中生成由若干关键词表示的主题。在某个主题中,词语的权重越高,其对主题的代表性就越强。


图5-16 BERTopic的完整流程大致可分为两个步骤:聚类和主题表征

该管道的主要优势在于,其两个核心步骤——聚类(clustering)与主题表示(topic representation)——在很大程度上相互独立。例如,通过c-TF-IDF方法,我们无需依赖文档聚类阶段所使用的具体模型。这种设计使得整个管道的每个组件都具有高度的模块化特性。正如本章后续内容将探讨的,它也为主题表示的微调提供了极佳的起点。

如图5-17所示,虽然默认使用sentence-transformers作为嵌入模型,但我们可以将其替换为任何其他嵌入技术。这一原则同样适用于管道中的其他步骤。若您不希望使用HDBSCAN算法生成离群点,则可以选择k-means聚类方法作为替代方案。


图5-17 BERTopic的模块化设计是其核心优势,使您能够完全自主地构建个性化主题模型

你可以将这种模块化设计想象成用乐高积木搭建——管道的每个组成部分都能被完全替换为另一种类似的算法。通过这种模块化设计,新发布的模型可以无缝集成到其架构体系中。随着语言人工智能领域的不断发展壮大,BERTopic也在持续进化升级!

BERTopic的模块化设计

BERTopic的模块化设计还具备另一项优势:它允许使用同一基础模型进行多种场景的适配与应用。例如,BERTopic支持以下丰富的算法变体:

• 引导式主题建模
• (半)监督主题建模
• 层次化主题建模
• 动态主题建模
• 多模态主题建模
• 多维度主题建模
• 在线和增量式主题建模
• 零样本主题建模
• 等等

模块化设计与算法灵活性是作者打造BERTopic成为主题建模一站式解决方案的核心基础。您可以在官方文档或开源仓库中获取完整的功能说明与实现细节

要使用我们的ArXiv数据集运行BERTopic,我们可以采用先前定义的模型和词嵌入(尽管这并非强制要求)

from bertopic import BERTopic

# Train our model with our previously defined models

topic_model = BERTopic(

embedding_model=embedding_model,

umap_model=umap_model,

hdbscan_model=hdbscan_model,

verbose=True

).fit(abstracts, embeddings)

让我们先从探索已创建的主题开始。get_topic_info() 方法可用于快速获取我们发现的主题的简要描述:

topic_model.get_topic_info()

话题

计数

名称

表示

-1

14520

-1_the_of_and_to

[the, of, and, to, in, we, that, language, for...

0

2290

0_speech_asr_recognition_end

[speech, asr, recognition, end, acoustic, spea...

1

1403

1_medical_clinical_biomedical_patient

[medical, clinical, biomedical, patient, healt...

2

1156

2_sentiment_aspect_analysis_reviews

[sentiment, aspect, analysis, reviews, opinion...

3

986

3_translation_nmt_machine_neural

[translation, nmt, machine, neural, bleu, engl...

...

...

...

...

150

54

150_coherence_discourse_paragraph_text

[coherence, discourse, paragraph, text, cohesi...

151

54

151_prompt_prompts_optimization_prompting

[prompt, prompts, optimization, prompting, llm...

152

53

152_sentence_sts_embeddings_similarity

[sentence, sts, embeddings, similarity, embedd...

153

53

153_counseling_mental_health_therapy

[counseling, mental, health, therapy, psychoth...

154

50

154_backdoor_attacks_attack_triggers

[backdoor, attacks, attack, triggers, poisoned..

这些主题中的每一个都由多个关键词表示,这些关键词在"名称"列中用"_"连接。该名称列通过展示最能代表主题的四个关键词,使我们能够快速了解该主题的核心内容

你可能还注意到,第一个主题的标签被词元为-1。该主题包含了所有无法被归类到任何主题的文档,这些文档被视为异常值。这是聚类算法HDBSCAN的结果,因为HDBSCAN不会强制所有数据点都必须被聚类。若要移除这些异常值,我们可以采用两种方法:

1.使用不会生成离群值的算法(如k-means);

2.或者使用BERTopic提供的reduce_outliers()函数,将异常值重新分配到现有主题中。

我们可以使用get_topic函数检查单个主题,并探索哪些关键词最能代表它们。例如,主题0包含以下关键词:

topic_model.get_topic(0)

[('speech', 0.028177697715245358),

('asr', 0.018971184497453525),

('recognition', 0.013457745472471012),

('end', 0.00980445092749381),

('acoustic', 0.009452082794507863),

('speaker', 0.0068822647060204885),

('audio', 0.006807649923681604),

('the', 0.0063343444687017645),

('error', 0.006320144717019838),

('automatic', 0.006290216996043161)]

例如,主题0包含关键词"speech"(语音)、"asr"(语音识别)和"recognition"(识别)。根据这些关键词,似乎该主题与自动语音识别(ASR)相关。我们可以使用find_topics()函数根据搜索词来查找特定主题。让我们搜索一个与主题建模相关的主题:

topic_model.find_topics("topic modeling")

([22, -1, 1, 47, 32],

[0.95456535, 0.91173744, 0.9074769, 0.9067007, 0.90510106])

这表明主题22与我们的搜索词具有较高的相似度(0.95)。如果我们进一步查看该主题,可以发现它确实是一个关于主题建模的主题:

topic_model.get_topic(22)

[('topic', 0.06634619076655907),

('topics', 0.035308535091932707),

('lda', 0.016386314730705634),

('latent', 0.013372311924864435),

('document', 0.012973600191120576),

('documents', 0.012383715497143821),

('modeling', 0.011978375291037142),

('dirichlet', 0.010078277589545706),

('word', 0.008505619415413312),

('allocation', 0.007930890698168108)]

尽管我们知道这个话题是关于主题建模的,但我们还是来看看BERTopic的摘要是否也被归类到这一主题下

topic_model.topics_[titles.index("BERTopic: Neural topic modeling with a class based TF-IDF procedure")]

22

没错!这些功能使我们能够快速找到我们感兴趣的话题。

BERTopic的模块化设计为用户提供了丰富的选择,但提供的选择过多可能会让用户感到应接不暇。为此,作者专门创建了一份最佳实践指南,其中详细介绍了加速训练流程、优化文本表征质量等常见实用方法

为了更轻松地探索主题,我们可以回顾之前的文本聚类示例。在那里,我们创建了静态可视化来观察生成主题的总体结构。使用BERTopic时,我们可以创建交互式版本,允许我们快速探索存在的主题及其包含的文档。

为此,我们需要使用通过UMAP创建的二维嵌入表示(即reduced_embeddings)。此外,当悬停于文档上时,我们将显示文档标题而非摘要,以便快速了解主题中的文档内容:

# Visualize topics and documents

fig = topic_model.visualize_documents(

titles,

reduced_embeddings=reduced_embeddings,

width=1200,

hide_annotations=True

)

# Update fonts of legend for easier visualization

fig.update_layout(font=dict(size=16))

如图5-18所示,这个交互式图表能让我们快速了解生成的主题。您可以通过缩放功能查看具体文档,或通过双击右侧面板中的主题标签实现针对性查看


图5-18. 可视化文档与主题时的输出结果

BERTopic 提供了丰富的可视化选项。以下三种图表尤其值得探索,以便了解主题之间的关系:

# Visualize barchart with ranked keywords

topic_model.visualize_barchart()

# Visualize relationships between topics

topic_model.visualize_heatmap(n_clusters=30)

# Visualize the potential hierarchical structure of topics

topic_model.visualize_hierarchy()
添加一个特殊乐高积木块

到目前为止我们探索的BERTopic管道尽管快速且模块化,但仍存在一个缺陷:它依然通过词袋模型(bag-of-words)来表征主题,而未考虑语义结构。解决方案在于充分发挥词袋模型表示法的优势——其生成有意义表示的速度优势。我们可以利用这种初始有意义表示,再通过更强大但计算成本更高的技术(如嵌入模型)进行优化调整。如图5-19所示,我们可以通过重新排序单词的初始分布来改进最终表征效果。值得注意的是,这种对初始结果集进行重新排序的想法,正是神经搜索(neural search)领域的核心方法论之一,该主题我们将在第8章详细探讨。


图5-19. 通过重新排序原始c-TF-IDF分布来微调主题表征

因此,我们可以设计如图5-20所示的新型乐高积木模块,该模块接收这种初始主题表示作为输入,并输出改进后的表示形式。在BERTopic框架中,这类重排序模型被称为表示模型。这种方法的核心优势在于,主题表示的优化仅需进行与主题数量相等的次数。例如,当我们面对百万级文档和百个主题时,表示模块只需对每个主题而非每个文档进行一次应用。

如图5-21所示,BERTopic开发者设计了多种表示模块来支持表示的微调训练。这些模块具有高度的灵活性,允许采用不同的方法论进行多层次优化。特别值得注意的是,表示模块可以通过多层堆叠的方式应用,即使用不同的技术方法对表示进行迭代精炼。这种架构设计使得系统能够通过组合多种优化策略,在保持计算效率的同时实现表示质量的显著提升。

图5-20 重排序器(表示)模块位于c-TF-IDF表示层之上

图5-21 在应用了c-TF-IDF加权后,主题可通过多种表示模型进行微调,其中许多为大型语言模型

在我们探索如何使用这些表示块之前,我们首先需要做两件事。首先,我们将 保存我们的原始主题表示,以便更容易与有无表示模型进行比较:

# Save original representations

from copy import deepcopy

original_topics = deepcopy(topic_model.topic_representations_)

其次,让我们创建一个简短的包装器,以便快速可视化在比较使用与不使用表示模型时主题词的差异:

def topic_differences(model, original_topics, nr_topics=5):

"""Show the differences in topic representations between two models """

df = pd.DataFrame(columns=["Topic", "Original", "Updated"])

for topic in range(nr_topics):

# Extract top 5 words per topic per model

og_words = " | ".join(list(zip(*original_topics[topic]))[0][:5])

new_words = " | ".join(list(zip(*model.get_topic(topic)))[0][:5])

df.loc[len(df)] = [topic, og_words, new_words]

return df
受KeyBERT启发的

我们将要探索的第一个表示块是KeyBERTInspired。正如您可能已经猜到的,KeyBERTInspired是一种受关键词提取包KeyBERT启发而开发的方法7

KeyBERT通过计算词与文档嵌入之间的余弦相似度来从文本中提取关键词。BERTopic采用了类似的方法。KeyBERTInspired则使用c-TF-IDF,通过计算文档的c-TF-IDF值与其所属主题的c-TF-IDF值之间的相似度,来提取每个主题最具代表性的文档。如图5-22所示,首先会计算每个主题的平均文档嵌入,然后将这些平均嵌入与候选关键词的嵌入进行对比,从而对关键词进行重新排序。


图5-22. 基于KeyBERT的表征模型构建流程

由于BERTopic的模块化特性,我们可以使用KeyBERTInspired方法来更新我们最初的主题表示,而无需执行降维和聚类步骤


7 Maarten Grootendorst. “KeyBERT: Minimal keyword extraction with BERT.” (2020).

from bertopic.representation import KeyBERTInspired

# Update our topic representations using KeyBERTInspired

representation_model = KeyBERTInspired()

topic_model.update_topics(abstracts, representation_model=representation_model)

# Show topic differences

topic_differences(topic_model, original_topics)

话题

原始的

更新后的

0

speech | asr | recognition | end | acoustic

speech | encoder | phonetic | language | trans...

1

medical | clinical | biomedical | patient | he...

nlp | ehr | clinical | biomedical | language

2

sentiment | aspect | analysis | reviews | opinion

[medical, clinical, biomedical, patient, healt...

3

translation | nmt | machine | neural | bleu

translation | translating | translate | transl...

4

summarization | summaries | summary | abstract...

summarization | summarizers | summaries | summ..

更新后的模型显示,相较于原始模型,主题内容更易于阅读。它还揭示了基于嵌入技术的弊端:原始模型中像"nmt"(主题3,代表神经机器翻译)这类词汇由于模型无法有效表征实体而被移除。对于领域专家而言,这类缩写具有高度信息密度。

最大边际相关性

尽管使用了c-TF-IDF和之前提到的KeyBERT启发式技术,我们在生成的主题表征中仍然存在显著的冗余问题。例如,在主题表示中同时包含"summaries"和"summary"这两个词就会引入冗余,因为它们具有高度相似性。

我们可以采用最大边际相关性(Maximal Marginal Relevance, MMR)算法来增强主题表征的多样性。该算法通过嵌入候选关键词集合,迭代计算下一个最佳关键词来实现目标:在保持与待比较文档相关性的同时,确保关键词之间的差异性。这需要设定一个多样性参数,用于控制关键词之间所需的差异程度。

在BERTopic框架中,我们应用MMR技术从初始的30个关键词进行筛选,最终得到一个更精炼(10个关键词)且更具多样性的集合。该过程会过滤掉冗余词汇,仅保留那些能为话题表征带来新信息的关键词。具体而言,算法通过以下步骤实现优化:

1.建立候选关键词的嵌入表示

2.计算当前集合中各关键词的边际相关性

3.选择能带来最大边际增益且与现有集合差异最大的关键词

4.重复上述过程直至达到预定数量

5.最终输出经过多样性校验的主题关键词集合

这种基于MMR的优化机制有效解决了传统主题建模中"关键词通货膨胀"的问题,通过动态平衡相关性与多样性,显著提升了主题表征的质量和可解释性。在BERTopic的具体实现中,我们还结合了预训练语言模型的上下文感知能力,使关键词的选择更能反映深层语义特征而非表面形式特征。

这样做相当直接:

from bertopic.representation import MaximalMarginalRelevance

# Update our topic representations to MaximalMarginalRelevance

representation_model = MaximalMarginalRelevance(diversity=0.2)

topic_model.update_topics(abstracts, representation_model=representation_model)

# Show topic differences

topic_differences(topic_model, original_topics)

话题

原始的

更新后的

0

speech | asr | recognition | end | acoustic

speech | asr | error | model | training

1

medical | clinical | biomedical | patient | he...

clinical | biomedical | patient | healthcare |...

2

sentiment | aspect | analysis | reviews | opinion

sentiment | analysis | reviews | absa | polarity

3

translation | nmt | machine | neural | bleu

translation | nmt | bleu | parallel | multilin...

4

summarization | summaries | summary | abstract...

summarization | document | extractive | rouge ...

生成的主题在表征上展现出更多的多样性。例如,主题4仅呈现了一个类似"摘要"的词汇,而是转而添加了其他可能对整体表征更具贡献性的词语

KeyBERTInspired和MMR都是提升初始主题表示的令人惊叹的技术。KeyBERTInspired尤其倾向于移除几乎所有停用词,因为它专注于词语与文档之间的语义关系。

文本生成的乐高积木

在之前的示例中,BERTopic的表示块一直充当着重新排序模块的角色。然而正如我们在前文探讨的那样,生成式模型在各类任务中都具有巨大的应用潜力。我们可以通过采用重新排序流程中的一部分方法,在BERTopic中实现生成式模型的高效应用。与其用生成式模型为数以百万计的文档识别主题(这可能存在潜在需求),我们将改用该模型来为主题生成简短标签。如图5-23所示,我们的做法并非生成或重新排序关键词,而是基于先前生成的关键词以及少量代表性文档,要求模型生成简短标签。


图5-23. 使用文本生成式大语言模型(LLMs)和提示工程,从与每个主题相关的关键词和文档中创建主题标签

如图所示的提示符有两个组件。首先,使用[文档]词元插入的文档是文档的一 个小子集,通常是四个,最好地代表主题。选择其c-TF-IDF值与主题值余弦相 似度最高的文档。其次,组成一个主题的关键字也会被传递给提示符,并使用 [关键字]标签进行引用。关键字可以由c-TF-IDF或我们到目前为止讨论的任何 其他代表生成。

因此,我们只需要对每个主题使用一次生成模型,其中可能有数百个,而不是 对每个文档使用一次,其中可能有数百万个。有许多生成模型我们可以选择, 包括开源的和专有的。让我们从我们在上一章中探索过的一个模型开始,即 Flan-T5模型。

我们创建了一个与模型兼容的提示符,并通过表示_model参数在BERTopic中使 用它:

from transformers import pipeline

from bertopic.representation import TextGeneration

prompt = """I have a topic that contains the following documents:

[DOCUMENTS]

The topic is described by the following keywords: '[KEYWORDS]'.

Based on the documents and keywords, what is this topic about?"""

# Update our topic representations using Flan-T5

generator = pipeline("text2text-generation", model="google/flan-t5-small")

representation_model = TextGeneration(

generator, prompt=prompt, doc_length=50, tokenizer="whitespace"

)

topic_model.update_topics(abstracts, representation_model=representation_model)

# Show topic differences

topic_differences(topic_model, original_topics)

话题

原始的

更新后的

0

speech | asr | recognition | end | acoustic

Speech-to-description

1

medical | clinical | biomedical | patient | he...

Science/Tech

2

sentiment | aspect | analysis | reviews | opinion

Review

3

translation | nmt | machine | neural | bleu

Attention-based neural machine translation

4

summarization | summaries | summary | abstract...

Summarization

其中一些标签,例如"摘要",在与原始表述形式进行对比时看似合理。但其他标签如"科技"则显得过于宽泛,未能准确反映原始主题的实质。我们不妨转而探讨OpenAI的GPT-3.5模型会展现出怎样的表现——该模型不仅规模更为庞大,而且预计具备更强的语言处理能力:

import openai

from bertopic.representation import OpenAI

prompt = """

I have a topic that contains the following documents:

[DOCUMENTS]

The topic is described by the following keywords: [KEYWORDS]

Based on the information above, extract a short topic label in the following

format:

topic: <short topic label>

"""

# Update our topic representations using GPT-3.5

client = openai.OpenAI(api_key="YOUR_KEY_HERE")

representation_model = OpenAI(

client, model="gpt-3.5-turbo", exponential_backoff=True, chat=True,

prompt=prompt

)

topic_model.update_topics(abstracts, representation_model=representation_model)

# Show topic differences

topic_differences(topic_model, original_topics)

话题

原始的

更新后的

0

speech | asr | recognition | end | acoustic

Leveraging External Data for Improving Low-Res...

1

medical | clinical | biomedical | patient | he...

Improved Representation Learning for Biomedica...

2

sentiment | aspect | analysis | reviews | opinion

Advancements in Aspect-Based Sentiment Analys...

3

translation | nmt | machine | neural | bleu

Neural Machine Translation Enhancements

4

summarization | summaries | summary | abstract...

Document Summarization Techniques

生成的标签效果非常出色!我们甚至没有使用GPT-4,但生成的标签似乎比之前的示例更具信息量。值得注意的是,BERTopic不仅限于使用OpenAI提供的服务,还支持本地后端部署。

尽管看起来我们不再需要关键词,但它们仍然能够代表输入文档的核心内容。没有模型是完美的,通常建议生成多个主题表征。BERTopic允许所有主题都通过不同的表征形式呈现。例如,您可以同时使用KeyBERT启发式、最大边际相关性(MMR)和GPT-3.5等方法,从而获得同一主题的不同视角。

与这些GPT-3。5个生成的标签,我们可以使用数据曲线图包创建漂亮的插图 ( 图5-24):

# Visualize topics and documents

fig = topic_model.visualize_document_datamap(

titles,

topics=list(range(20)),

reduced_embeddings=reduced_embeddings,

width=1200,

label_font_size=11,

label_wrap_width=20,

use_medoids=True,

)

图5-24 可视化呈现的前20个主题

总结

本章中,我们探讨了生成式和表征式大语言模型(LLMs)如何应用于无监督学习领域。尽管近年来监督学习方法(如分类任务)占据主导地位,但文本聚类等无监督方法仍具有巨大潜力——它们能够基于语义内容对文本进行分组,而无需预先标注。

我们介绍了一个通用的文本文档聚类流程:首先将输入文本转换为数值表示形式(即我们称之为"嵌入表示"的向量),接着对嵌入向量进行降维处理以简化高维数据,从而获得更优的聚类效果。最后,在降维后的嵌入空间上应用聚类算法完成文本分组。通过手动检查聚类结果,我们得以理解各个聚类包含的文档类型,并学习了如何对这些聚类结果进行有效解读。

为了摆脱这种人工检查模式,我们探索了BERTopic如何通过自动表示聚类的方法扩展文本聚类管道。这种方法通常被称为主题建模,旨在从大量文档中挖掘潜在主题。BERTopic通过结合词袋方法和c-TF-IDF(一种根据词语在所有聚类中的相关性和频率进行加权的方法)生成主题表征。

BERTopic的主要优势在于其模块化设计。用户可以选择管道中的任意模型,这允许创建同一主题的多种视角表示。我们探索了最大边际相关性(Maximal Marginal Relevance)和KeyBERTInspired方法来微调基于c-TF-IDF生成的主题表征。此外,我们还使用了前章相同的生成式大语言模型(Flan-T5和GPT-3.5),通过生成高度可解释的标签来进一步提升主题的可解释性。

在下一章中,我们将转换焦点,探讨改进生成模型输出结果的常见方法——提示工程(Prompt Engineering)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡攀峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值