第五章:文本聚类和主题建模

目录

一、概述

二、获取数据集

三、文本聚类的通用流程

3.1  文本转embedding

3.2 降低嵌入的维度

3.3 使用聚类算法

3.4 检查聚类

四、从文本聚类到主题建模

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

4.2 BERTopic的模块化

4.3 添加一个特殊的乐高积木

4.3.1 KeyBERTInspired

4.3.2 MMR(Maximal Marginal Relevance)

4.3.3 文本生成乐高积木

4.3.4 可视化

无、总结


第四章:文本分类-优快云博客https://blog.youkuaiyun.com/m0_67804957/article/details/145822178

一、概述

文本聚类:根据文本的语义内容、含义和关系将相似的文本分组。

如下图所示,语义相似的文档聚类不仅有助于高效地对大量非结构化文本进行分类,还能实现快速的探索性数据分析。

聚类非结构化文本数据

近年来,语言模型的发展使得文本能够以上下文和语义的方式进行表示,这增强了文本聚类的有效性。

文本聚类是一种无监督无标注任务,允许创造性地提出解决方案,并且有多种应用,例如发现异常值、加速标注以及发现错误标注的数据。

主题建模:从文本数据中发现抽象主题,通常使用关键词或关键短语描述,并且理想情况下有一个总体的标签。

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

在本章中,我们将首先探索如何使用嵌入模型进行聚类,然后转向一种受文本聚类启发的主题建模方法,即BERTopic。

文本聚类和主题建模探索了将各种不同的语言模型结合起来的创造性方法。我们将探索如何将仅编码器(嵌入)、仅解码器(生成)甚至经典方法(词袋模型)结合起来,从而产生令人惊叹的新技术和流程。

二、获取数据集

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

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

# 从Hugging Face加载数据
from datasets import load_dataset
dataset = load_dataset("maartengr/arxiv_nlp")["train"]

# 提取元数据
abstracts = dataset["Abstracts"]
titles = dataset["Titles"]

三、文本聚类的通用流程

文本聚类允许发现你可能熟悉或不熟悉的模式。它可以帮助直观地理解任务(例如分类任务)及其复杂性。因此,文本聚类不仅仅是一种快速的探索性数据分析方法。

尽管文本聚类有多种方法,从基于图的神经网络到基于质心的聚类技术,但一种流行的通用流程涉及三个步骤和算法:

  1. 使用嵌入模型将输入文档转换为嵌入向量。

  2. 使用降维模型降低嵌入向量的维度。

  3. 使用聚类模型找到语义相似的文档组。

3.1  文本转embedding

第一步是将我们的文本数据转换为嵌入,如下图所示。回想一下前面章节的内容,嵌入是文本的数值表示,旨在捕捉其含义。

第一步:我们使用嵌入模型将文档转换为嵌入

选择针对语义相似性任务优化的嵌入模型对于聚类尤为重要,因为我们试图找到语义相似的文档组。

正如我们在前一章所做的那样,我们将使用MTEB排行榜来选择嵌入模型。我们需要一个在聚类任务上得分不错且足够小以快速运行的嵌入模型。我们不再使用前一章中使用的“sentence-transformers/all-mpnet-base-v2”模型,而是改用“thenlper/gte-small”模型。这是一个更新的模型,在聚类任务上优于之前的模型,并且由于其较小的尺寸,推理速度更快。

from sentence_transformers import SentenceTransformer

# 为每个摘要创建嵌入
embedding_model = SentenceTransformer("thenlper/gte-small")
embeddings = embedding_model.encode(abstracts, show_progress_bar=True)

我们来检查一下每个文档嵌入包含多少个值:

# 检查生成的嵌入的维度
embeddings.shape

>> 输出结果为 (44949, 384)

每个嵌入包含384个值,这些值共同表示文档的语义表示。你可以将这些嵌入视为我们想要聚类的特征。

3.2 降低嵌入的维度

在聚类嵌入之前,我们需要考虑其高维度。随着维度数量的增加,每个维度中可能的值呈指数级增长。在每个维度中找到所有子空间变得越来越复杂。

因此,高维数据对于许多聚类技术来说可能是一个问题,因为识别有意义的聚类变得更加困难。

相反,我们可以利用降维技术。如下图所示,这种技术允许我们将数据从高维空间压缩到低维空间。降维技术的目标是通过找到低维表示来保留高维数据的全局结构。

降维允许将高维空间中的数据压缩到低维表示

请注意,这是一种压缩技术,底层算法并不是随意删除维度。为了帮助聚类模型创建有意义的聚类,降维是我们聚类流程的第二步,如下图所示。

第二步:使用降维技术将嵌入压缩到低维空间

常见的降维方法有主成分分析(PCA)和均匀流形近似与投影(UMAP)。对于这个流程,我们选择UMAP,因为它比PCA更好地处理非线性关系和结构。

注意

降维技术并非完美无缺。它们无法完美地将高维数据压缩到低维表示中。这个过程总会丢失一些信息。在降低维度和保留尽可能多的信息之间需要找到平衡。

为了执行降维,我们需要实例化UMAP类并将生成的嵌入传递给它:

from umap import UMAP

# 将输入嵌入从384维降低到5维
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设置为cosine,因为基于欧几里得距离的方法在处理高维数据时存在问题。

需要注意的是,在UMAP中设置random_state可以使结果在不同会话中可复现,但会禁用并行化,从而减慢训练速度。

3.3 使用聚类算法

第三步是聚类降维后的嵌入,如下图所示。

第三步:我们使用降维后的嵌入对文档进行聚类

常见聚类算法

虽然常见的选择是基于质心的算法(如k-means),它需要生成一组聚类,但我们事先不知道聚类的数量。

相反,基于密度的算法可以自由计算聚类的数量,并且不会强迫所有数据点都属于某个聚类,如下图所示。

聚类算法不仅影响聚类的生成方式,还影响其呈现方式

一种常见的基于密度的模型是HDBSCAN。如下图所示

HDBSCAN是DBSCAN聚类算法的层次变体,允许在不需要明确指定聚类数量的情况下找到密集的(微观)聚类。作为一种基于密度的方法,HDBSCAN还可以检测数据中的离群点,这些离群点不属于任何聚类。换句话说,它们会被忽略。由于arXiv文章可能包含一些小众论文,使用能够检测离群点的模型可能会很有帮助。

与前面的包一样,使用HDBSCAN非常简单。我们只需要实例化模型并将降维后的嵌入传递给它:

from hdbscan import HDBSCAN

# 我们拟合模型并提取聚类
hdbscan_model = HDBSCAN(
    min_cluster_size=50, metric="euclidean", cluster_selection_method="eom"
).fit(reduced_embeddings)
clusters = hdbscan_model.labels_

# 我们生成了多少个聚类?
len(set(clusters))

>> 输出结果为 156

使用HDBSCAN,我们在数据集中生成了156个聚类。如果想生成更多聚类,我们需要降低min_cluster_size的值,因为它表示聚类可以接受的最小大小。

3.4 检查聚类

现在我们已经生成了聚类,可以手动检查每个聚类并探索分配的文档,以了解其内容。例如,我们从聚类0中随机选取几篇文档:

import numpy as np

# 打印聚类0中的前三个文档
cluster = 0
for index in np.where(clusters==cluster)[0][:3]:
    print(abstracts[index][:300] + "... \n")

>> 输出结果为:

这项工作旨在设计一种从英语文本到美国手语(ASL)的统计机器翻译。该系统基于Moses工具并进行了部分修改,结果通过3D虚拟形象进行合成以供解读。首先,我们将输入文本翻译成手语词汇……

关于手语的研究仍然强烈区分与音韵学和语音学相关的语言问题,以及用于识别和合成的手势研究。本文关注动作与意义的交织,用于对手语的分析、合成和评估……

现代计算语言学软件无法生成手语翻译的重要方面。根据一些研究,我们推断大多数自动手语翻译系统在生成动画时忽略了诸多方面,因此解读失去了真实信息……

从这些文档来看,这个聚类似乎主要包含关于手语翻译的文档,这很有趣!

我们可以更进一步,尝试将结果可视化,而不是手动检查所有文档。为此,我们需要将文档嵌入从384维降低到二维,因为这允许我们将文档绘制在x/y平面上:

import pandas as pd

# 将384维嵌入降低到二维以便于可视化
reduced_embeddings = UMAP(
    n_components=2, min_dist=0.0, metric="cosine", random_state=42
).fit_transform(embeddings)

# 创建数据框
df = pd.DataFrame(reduced_embeddings, columns=["x", "y"])
df["title"] = titles
df["cluster"] = [str(c) for c in clusters]

# 分别选择离群点和非离群点(聚类)
to_plot = df.loc[df.cluster != "-1", :]
outliers = df.loc[df.cluster == "-1", :]

我们还分别为聚类(clusters_df)和离群点(outliers_df)创建了一个数据框,因为我们通常希望专注于聚类并突出显示它们。

注意

使用任何降维技术进行可视化都会造成信息丢失。这只是我们原始嵌入的近似表示。虽然它具有信息性,但可能会将聚类推得更近或更远,而不是它们实际的位置。因此,人工评估、亲自检查聚类是聚类分析的关键组成部分!

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

import matplotlib.pyplot as plt

# 分别绘制离群点和非离群点
plt.scatter(outliers.x, outliers.y, alpha=0.05, s=2, c="grey")
plt.scatter(
    to_plot.x, to_plot.y, c=to_plot.cluster.astype(int),
    alpha=0.6, s=2, cmap="tab20b"
)
plt.axis("off")

如下图所示,它能够很好地捕捉主要的聚类。注意,点群被涂上了相同的颜色,这表明HDBSCAN将它们归为一个组。由于我们有大量的聚类,绘图库会在聚类之间循环使用颜色,所以不要认为所有绿色的点都属于同一个聚类

生成的聚类(彩色)和离群点(灰色)以二维可视化形式呈现

这种可视化在视觉上很吸引人,但还不允许我们看到聚类内部发生了什么。相反,我们可以通过从文本聚类扩展到主题建模来进一步扩展这种可视化。

四、从文本聚类到主题建模

文本聚类是一种强大的工具,用于在大量文档集合中发现结构。在我们之前的例子中,我们可以通过手动检查每个聚类并根据其包含的文档集合对其进行识别。例如,我们探索了一个包含关于手语的文档的聚类。我们可以将该聚类的主题定义为“手语”。

这种在文本数据集合中发现主题或潜在主题的想法通常被称为主题建模。传统上,它涉及找到一组关键词或短语,这些关键词或短语最能代表并捕捉主题的含义,如下图所示。

传统上,主题通过一组关键词来表示,但也可以采取其他形式

而不是将主题标记为“手语”,这些技术使用诸如“手语”、“语言”和“翻译”等关键词来描述主题。因此,它不会给主题一个单一的标签,而是要求用户通过这些关键词来理解主题的含义。

经典方法,如潜在狄利克雷分配(Latent Dirichlet Allocation, LDA),假设每个主题通过语料库词汇表中单词的概率分布来表征。下图展示了如何对词汇表中的每个单词根据其与主题的相关性进行评分。

基于单词在单个主题上的分布来提取关键词

这些方法通常使用词袋模型(Bag-of-Words)作为文本数据的主要特征,这种方法不考虑单词和短语的上下文或含义。相比之下,我们在文本聚类示例中考虑了上下文和语义相似性,因为它依赖于基于Transformer的嵌入,这些嵌入通过注意力机制优化了语义相似性和上下文含义。

下边将通过一个高度模块化的文本聚类和主题建模框架——BERTopic,将文本聚类扩展到主题建模领域。

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

BERTopic是一种主题建模技术,它利用语义相似的文本聚类来提取各种类型的主题表示。

其底层算法可以分为两个步骤:

  1. 首先,如下图所示,我们遵循与之前文本聚类示例相同的流程:嵌入文档,降低其维度,最后对降维后的嵌入进行聚类,以创建语义相似的文档组。
    BERTopic流程的第一部分是创建语义相似的文档聚类
  2. 其次,它通过利用经典的词袋模型方法来对语料库词汇表中的单词进行分布建模。正如我们在第1章中简要讨论的并如下图所示,词袋模型正如其名字所暗示的那样,统计每个单词在文档中出现的次数。这种表示可以用来提取文档中出现最频繁的单词。
    词袋模型统计每个单词在文档中出现的次数

然而,有两个需要注意的地方:

  1. 首先,这是一种基于文档层面的表示,而我们感兴趣的是基于聚类层面的视角。为了解决这个问题,单词的频率是在整个聚类中而不是仅在单个文档中计算的,如下图所示。
    通过计算每个聚类中单词的频率而不是每个文档中的频率来生成c-TF(聚类词频)
  2. 其次,像“the”和“I”这样的停用词通常在文档中频繁出现,但对文档的实际含义贡献甚微。BERTopic使用一种基于类别的词频-逆文档频率(c-TF-IDF)变体,以增加对聚类更有意义的单词的权重,并减少在所有聚类中广泛使用的单词的权重。

在c-TF-IDF中,词袋模型中的每个单词(即c-TF)都会乘以其IDF值。如下图所示,IDF值是通过对所有单词在所有聚类中的平均频率与每个单词的总频率的比值取对数来计算的。

创建权重方案

结果是为每个单词生成一个权重(“IDF”),我们可以将其与它们的频率(“c-TF”)相乘,以获得加权值(“c-TF-IDF”)。

这个过程的第二部分,如下图所示,允许我们生成之前看到的单词分布。我们可以使用scikit-learn的CountVectorizer来生成词袋(或词频)表示。在这里,每个聚类被视为一个具有特定词汇表排名的主题。

BERTopic流程的第二部分是表示主题:计算类c中术语x的权重

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

BERTopic的完整流程大致包括两个步骤:聚类和主题表示

这个流程的一个主要优势是,聚类和主题表示这两个步骤彼此相对独立。例如,使用c-TF-IDF时,我们不依赖于用于聚类文档的模型。这使得整个流程的每个组件都具有高度的模块化。正如我们将在本章后面探讨的那样,它是一个微调主题表示的绝佳起点。

如下图所示,尽管sentence-transformers被用作默认的嵌入模型,但我们可以将其替换为任何其他嵌入技术。同样的规则也适用于所有其他步骤。如果你不想使用HDBSCAN生成离群点,你可以改用k-means。

BERTopic的模块化是其关键组成部分,允许你根据需要构建自己的主题模型

你可以将这种模块化视为用乐高积木搭建:流程的每个部分都可以被另一种类似的算法完全替换。通过这种模块化,新发布的模型可以集成到其架构中。

4.2 BERTopic的模块化

BERTopic的模块化还有另一个优势:它允许使用相同的底层模型将其用于和适应不同的用例。例如,BERTopic支持多种算法变体:

  • 指导性主题建模(Guided topic modeling)

  • (半)监督主题建模((Semi-)supervised topic modeling)

  • 层次主题建模(Hierarchical topic modeling)

  • 动态主题建模(Dynamic topic modeling)

  • 多模态主题建模(Multimodal topic modeling)

  • 多方面主题建模(Multi-aspect topic modeling)

  • 在线和增量主题建模(Online and incremental topic modeling)

  • 零样本主题建模(Zero-shot topic modeling)

  • ...

模块化和算法灵活性是作者将BERTopic打造为主题建模一站式解决方案的基础。你可以在其文档或代码仓库中找到其功能的完整概述。

要使用我们的arXiv数据集运行BERTopic,我们可以使用之前定义的模型和嵌入(尽管这不是强制性的):

from bertopic import BERTopic

# 使用之前定义的模型训练我们的模型
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()
TopicCountNameRepresentation
-114520-1_the_of_and_to[the, of, and, to, in, we, that, language, for...]
022900_speech_asr_recognition_end[speech, asr, recognition, end, acoustic, speaker...]
114031_medical_clinical_biomedical_patient[medical, clinical, biomedical, patient, health...]
211562_sentiment_aspect_analysis_reviews[sentiment, aspect, analysis, reviews, opinion...]
39863_translation_nmt_machine_neural[translation, nmt, machine, neural, bleu, english...]
............
15054150_coherence_discourse_paragraph_text[coherence, discourse, paragraph, text, cohesion...]
15154151_prompt_prompts_optimization_prompting[prompt, prompts, optimization, prompting, llm...]
15253152_sentence_sts_embeddings_similarity[sentence, sts, embeddings, similarity, embedding...]
15353153_counseling_mental_health_therapy[counseling, mental, health, therapy, 

每个主题由多个关键词表示,这些关键词在“Name”列中用“_”连接。这个“Name”列使我们能够快速了解主题的内容,因为它展示了四个最能代表该主题的关键词。

注意
你可能还注意到,第一个主题被标记为-1。该主题包含所有无法归入其他主题的文档,并被视为离群点。这是聚类算法HDBSCAN的结果,它不会强迫所有点都被聚类。要移除离群点,我们可以使用非离群算法(如k-means),或者使用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,我们可以创建一个交互式变体,允许我们快速探索存在的主题及其包含的文档。

这样做需要我们使用之前用UMAP创建的二维嵌入reduced_embeddings。此外,当我们悬停在文档上时,我们将显示标题而不是摘要,以便快速了解主题中的文档:

# 可视化主题和文档
fig = topic_model.visualize_documents(
    titles, 
    reduced_embeddings=reduced_embeddings, 
    width=1200, 
    hide_annotations=True
)

# 更新图例的字体以便于可视化
fig.update_layout(font=dict(size=16))

如下图所示,这个交互式图快速给我们提供了创建的主题的感觉。你可以放大查看单个文档,或者双击右侧的主题以仅查看该主题。

可视化文档和主题时的输出

在BERTopic中有多种可视化选项。以下三种值得探索,以了解主题之间的关系:

# 可视化带有排名关键词的条形图
topic_model.visualize_barchart()

# 可视化主题之间的关系
topic_model.visualize_heatmap(n_clusters=30)

# 可视化主题的潜在层次结构
topic_model.visualize_hierarchy()

4.3 添加一个特殊的乐高积木

到目前为止,我们探索的BERTopic流程虽然快速且模块化,但有一个缺点:它仍然通过词袋模型来表示主题,而没有考虑语义结构。

解决方案是利用词袋模型表示的优势——快速生成有意义的表示。我们可以使用这种初始的有意义表示,并通过更强大但速度较慢的技术(如嵌入模型)对其进行调整。如下图所示,我们可以通过重新排序初始单词分布来改进最终的表示。请注意,这种重新排序初始结果集的思想是神经搜索中的一个核心概念,我们在第8章中会详细介绍。

通过重新排序原始c-TF-IDF分布来优化主题表示

因此,我们可以设计一个新的“乐高积木”,如下图所示,它接受这种初始主题表示,并输出一个改进的表示。

重新排序器(表示)模块位于c-TF-IDF表示之上

在BERTopic中,这种重新排序模型被称为表示模型(representation models)。这种方法的一个主要好处是,主题表示的优化只需要针对每个主题进行一次,而不是针对每个文档。例如,如果我们有数百万篇文档和一百个主题,表示模块只需要对每个主题应用一次,而不是对每篇文档都应用。

如下图所示,BERTopic已经设计了多种表示模块,允许你对表示进行微调。表示模块甚至可以多次堆叠,使用不同的方法来优化表示。

在应用c-TF-IDF加权后,可以使用多种表示模型对主题进行微调,其中许多是大型语言模型

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

# 保存原始表示
from copy import deepcopy
original_topics = deepcopy(topic_model.topic_representations_)

其次,我们创建一个简单的包装器,用于快速可视化主题词的差异,以便比较使用表示模型前后的结果:

def topic_differences(model, original_topics, nr_topics=5):
    """展示两个模型之间主题表示的差异"""
    df = pd.DataFrame(columns=["Topic", "Original", "Updated"])
    for topic in range(nr_topics):
        # 提取每个模型中每个主题的前5个关键词
        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

4.3.1 KeyBERTInspired

第一个表示模块是KeyBERTInspired。KeyBERTInspired是一种受KeyBERT关键词提取包启发的方法。KeyBERT通过比较单词和文档嵌入之间的余弦相似性从文本中提取关键词。

BERTopic采用了类似的方法。KeyBERTInspired使用c-TF-IDF通过计算文档的c-TF-IDF值与对应主题之间的相似性,提取每个主题中最具代表性的文档。如下图所示,每个主题的平均文档嵌入被计算出来,并与候选关键词的嵌入进行比较,以重新排序关键词。

KeyBERTInspired表示模型的流程

由于BERTopic的模块化特性,我们可以在不需要重新执行降维和聚类步骤的情况下,使用KeyBERTInspired更新初始主题表示:

from bertopic.representation import KeyBERTInspired

# 使用KeyBERTInspired更新主题表示
representation_model = KeyBERTInspired()
topic_model.update_topics(abstracts, representation_model=representation_model)

# 展示主题差异
topic_differences(topic_model, original_topics)
TopicOriginalUpdated
0speechasrrecognitionendacousticspeechencoderphoneticlanguagetrans...
1medicalclinicalbiomedicalpatienthe...nlpehrclinicalbiomedicallanguage
2sentimentaspectanalysisreviewsopinionaspectsentimentaspectssentimentscl...
3translationnmtmachineneuralbleutranslationtranslatingtranslatetransl...
4summarizationsummariessummaryabstract...summarizationsummarizerssummariessumm...

更新后的模型显示,主题的可读性比原始模型更强。它还展示了使用基于嵌入的技术的缺点。例如,原始模型中的某些单词(如主题3中的“nmt”,代表神经机器翻译)被移除,因为模型无法正确表示该实体。对于领域专家来说,这些缩写是非常有信息量的。

4.3.2 MMR(Maximal Marginal Relevance)

尽管使用了c-TF-IDF和前面提到的KeyBERTInspired技术,我们仍然在主题表示中存在显著的冗余。例如,在一个主题表示中同时出现“summaries”和“summary”会引入冗余,因为它们非常相似。

我们可以使用最大边际相关性(MMR)来多样化我们的主题表示。该算法试图找到一组彼此多样化的关键词,同时这些关键词仍然与它们所比较的文档相关。它通过嵌入一组候选关键词并迭代计算下一个最佳关键词来实现这一点。这需要设置一个多样性参数,以指示关键词需要有多大的多样性。

在BERTopic中,我们使用MMR从一组初始关键词(例如30个)中筛选出更小但更多样化的关键词集合(例如10个)。它会过滤掉冗余的单词,只保留对主题表示有新贡献的单词。

操作起来非常简单:

from bertopic.representation import MaximalMarginalRelevance

# 使用最大边际相关性更新主题表示
representation_model = MaximalMarginalRelevance(diversity=0.2)
topic_model.update_topics(abstracts, representation_model=representation_model)

# 展示主题差异
topic_differences(topic_model, original_topics)
TopicOriginalUpdated
0speechasrrecognitionendacousticspeechasrerrormodeltraining
1medicalclinicalbiomedicalpatienthe...clinicalbiomedicalpatienthealthcare...
2sentimentaspectanalysisreviewsopinionsentimentanalysisreviewsabsapolarity
3translationnmtmachineneuralbleutranslationnmtbleuparallelmultilin...
4summarizationsummariessummaryabstract...summarizationdocumentextractiverouge ...

更新后的主题在表示上更具多样性。例如,主题4只保留了一个与“summary”相关的单词,并增加了其他可能对整体表示更有贡献的单词。

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

4.3.3 文本生成乐高积木

在前面的例子中,BERTopic的表示模块主要作为重新排序模块。然而,正如我们在上一章中已经探讨的,生成模型在各种任务中具有巨大潜力。

我们可以通过遵循部分重新排序流程,在BERTopic中高效地使用生成模型。与其使用生成模型为所有文档(可能有数百万篇)识别主题,我们不如让模型为我们的主题生成一个标签。如下图所示,与其生成或重新排序关键词,我们不如让模型基于之前生成的关键词和一组代表性文档生成一个简短的标签。

使用文本生成型LLM和提示工程,从每个主题的关键词和相关文档中创建主题标签

图中所示的提示包含两个部分。首先,使用[DOCUMENTS]标签插入的文档是一个小的文档子集,通常是四个,这些文档最能代表主题。选择与主题的c-TF-IDF值具有最高余弦相似性的文档。其次,构成主题的关键词也传递给提示,并通过[KEYWORDS]标签引用。这些关键词可以由c-TF-IDF或我们之前讨论的任何其他表示生成。

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

我们创建一个适合该模型的提示,并通过representation_model参数在BERTopic中使用:

from transformers import pipeline
from bertopic.representation import TextGeneration

prompt = """我有一个主题,包含以下文档: 
[DOCUMENTS]

该主题由以下关键词描述:'[KEYWORDS]'.

根据文档和关键词,这个主题是关于什么的?"""

# 使用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)

# 展示主题差异
topic_differences(topic_model, original_topics)
TopicOriginalUpdated
0speechasrrecognitionendacousticSpeech-to-description
1medicalclinicalbiomedicalpatienthe...Science/Tech
2sentimentaspectanalysisreviewsopinionReview
3translationnmtmachineneuralbleuAttention-based neural machine translation
4summarizationsummariessummaryabstract...Summarization

一些标签(如“Summarization”)在与原始表示比较时似乎是合理的。然而,其他标签(如“Science/Tech”)似乎过于宽泛,并没有很好地反映原始主题。让我们探索一下OpenAI的GPT-3.5的表现,考虑到该模型不仅更大,而且预期具有更强的语言能力。

import openai
from bertopic.representation import OpenAI

prompt = """
我有一个主题,包含以下文档:
[DOCUMENTS]

该主题由以下关键词描述:[KEYWORDS]

根据上述信息,提取一个简短的主题标签,格式如下:
topic: <简短的主题标签>
"""

# 使用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)

# 展示主题差异
topic_differences(topic_model, original_topics)
TopicOriginalUpdated
0speechasrrecognitionendacoustic利用外部数据改进低资源语言的语音识别
1medicalclinicalbiomedicalpatienthe...改进生物医学领域的表示学习
2sentimentaspectanalysisreviewsopinion基于方面的观点情感分析的进展
3translationnmtmachineneuralbleu神经机器翻译的增强
4summarizationsummariessummaryabstract...文档摘要技术

生成的标签非常令人印象深刻!我们甚至没有使用GPT-4,而生成的标签似乎比之前的例子更有信息量。请注意,BERTopic并不局限于使用OpenAI的产品,它也有本地后端。

提示
尽管看起来我们不再需要关键词了,但它们仍然是输入文档的代表。没有模型是完美的,通常建议生成多个主题表示。BERTopic允许所有主题通过不同的表示来呈现。例如,你可以同时使用KeyBERTInspired、MMR和GPT-3.5,以从不同角度看待同一主题。

4.3.4 可视化

使用这些由GPT-3.5生成的标签,我们可以使用datamapplot包创建精美的图表:

# 可视化主题和文档
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,
)
前20个主题的可视化

无、总结

在本章中,我们探讨了大型语言模型(LLMs)——无论是生成型还是表示型——如何在无监督学习领域中发挥作用。尽管近年来分类等有监督方法占据主导地位,但文本聚类等无监督方法因其能够在没有预先标记的情况下根据语义内容对文本进行分组而具有巨大的潜力。

我们介绍了一种用于聚类文本文档的常见流程,该流程从将输入文本转换为数值表示(即嵌入)开始。然后,对这些嵌入进行降维处理,以简化高维数据,从而获得更好的聚类结果。最后,我们在降维后的嵌入上应用聚类算法以对输入文本进行聚类。手动检查这些聚类帮助我们了解它们包含哪些文档以及如何解释这些聚类。

为了摆脱这种手动检查的方式,我们探讨了BERTopic如何通过一种自动表示聚类的方法扩展文本聚类流程。这种方法通常被称为主题建模,它试图在大量文档中发现主题。BERTopic通过一种增强的词袋模型方法生成这些主题表示,该方法结合了基于类别的词频-逆文档频率(c-TF-IDF),根据单词在聚类中的相关性和频率对单词进行加权。

BERTopic的一个主要优势是其模块化特性。在BERTopic中,你可以选择流程中的任何模型,从而为同一主题创建多种表示,形成不同的视角。我们探索了最大边际相关性(MMR)和KeyBERT启发方法,以微调通过c-TF-IDF生成的主题表示。此外,我们还使用了上一章中提到的相同生成型LLM(Flan-T5和GPT-3.5),通过生成高度可解释的标签进一步提高主题的可解释性。

在下一章中,我们将转移注意力,探讨一种改进生成模型输出的常见方法,即提示工程

第六章:提示词工程-优快云博客https://blog.youkuaiyun.com/m0_67804957/article/details/145852541

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值