手里有三百份政策文件,或者四十份深度访谈的转写稿,第一个问题往往是这批材料到底在讲哪几件事。一份一份读能回答这个问题,只是很慢,读到后面,前面的印象也模糊了。主题模型(Topic Model,从大量文本里自动归纳话题的方法)就是用来回答这个问题的,它把文本归成若干主题,每个主题配一组关键词。
经典的 LDA(潜在狄利克雷分配,按词语一起出现的规律归纳主题)数的是词。【房价上涨让年轻人推迟结婚】和【住房成本高企,青年婚期后延】说的是一回事,切成词以后却没有一个词相同,LDA 很难把它们放到一起。BERTopic 是 Grootendorst 在 2022 年提出的主题模型。它先用语言模型把每段话变成一组表示意思的数字,意思相近的段落,这组数字也相近,于是能聚到一起,然后再从每一组里找出关键词。
在 TATOOLS 的【BERTopic 主题聚类】里【上传文档】,就能拿到主题清单,每个主题带关键词和一个概括出来的名字。报告还会画出主题之间的远近关系,列出每个主题的代表原文,统计主题随时间的变化。从切片段到算关键词,每一步的参数都在工具页上开放,可以按研究需要调整。结果出来以后,分得太细的主题可以合并,混在一起的可以拆开,每改一次生成一个新版本;也可以直接向这批材料提问,回答附原文出处。
这篇教程分三部分。第一部分顺着 BERTopic 的计算流程讲,从把长文切成片段一直讲到给主题起名字,也讲清工具页上每个参数改的是哪一步。第二部分讲结果出来以后怎样合并、拆分和移出主题,新版本重新算了什么。第三部分讲时间演化和追问,最后是论文里的写法。
01
BERTopic 分哪几步把文本变成主题
先看一个结果长什么样。一份 3,000 字的社区调研报告切成 10 个片段,6 个片段归入【住房负担】,3 个归入【养老服务】,剩下 1 个没有归入任何主题。从这组结果能读出,这份报告六成的篇幅在谈住房。
主题模型的输入是一组文本单位,输出是每个单位的主题归属和每个主题的关键词。在 BERTopic 里,一个单位只归入一个主题,或者被标为离群。用符号写出来是这样。
输入:片段集合 D = {d_1, d_2, …, d_n}
输出:每个片段的主题归属 z_i ∈ {−1, 0, 1, …, K − 1}
每个主题 k 的关键词表 (t_1, w_1), (t_2, w_2), …
z_i = −1 表示片段 d_i 没有归入任何主题,称为离群片段
LDA 假设每篇文档由几个主题按比例混合而成,一篇文档可以同时属于好几个主题。BERTopic 让每个片段只属于一个主题,一篇长文讲了几件事,靠把它切成片段来体现。上面那份调研报告就是这样,同一份报告的不同片段落进了不同主题。
Grootendorst 的论文把 BERTopic 写成一条流水线,前后几步各管一件事。判断两段话意思近不近,交给语义向量和聚类;主题用哪些词来描述,交给词频统计。两件事分开做,换掉其中一步,另一步照常工作。TATOOLS 按这条流水线实现,每一步都有对应的工具页设置和报告区块。
| 步骤 | 这一步做什么 | 工具页上的设置 |
|---|---|---|
| 切片段 | 把长文切成长度接近的片段 | 【文本分割窗口大小】 |
| 语义向量 | 把每个片段变成一个表示意思的向量 | 【嵌入距离】 |
| 降维 | 用 UMAP 把向量压到几维 | UMAP 降维参数 |
| 定主题数 | 决定分成几个主题 | 【主题数量设置】【参数预设】 |
| 聚类 | 用 HDBSCAN 按疏密把片段分组 | HDBSCAN 聚类参数 |
| 关键词 | 按 TF-IDF 给每个主题算关键词 | 文本向量化参数、停用词和词形合并 |
| 主题名 | 根据关键词和原文上下文起名字 | 【所属行业】 |
下面几节按这个顺序展开。
02
先把长文切成片段
一份 1,500 字的政策文件,前半部分谈财政补贴,后半部分谈监管要求。整篇当成一个单位,只能归进一个主题。按 300 字一段切开,大约得到 5 个片段,谈补贴的片段和谈监管的片段就能各自归进不同的主题。
分析单位(Unit of Analysis)是一次统计或判断针对的对象。BERTopic 在 TATOOLS 里的分析单位是文档片段,一份文件能切出多少片段,可以这样估算。
n_f ≈ L_f / w
n_f:第 f 份文件切出的片段数
L_f:这份文件清理后的字符数
w:文本分割窗口大小(字符数)
例:L_f = 1,500,w = 300,n_f ≈ 5
切点尽量落在窗口长度附近的句号、问号或叹号处,每个片段接近一个意思完整的小段。每份文件单独切,一个片段里不会混进两份文件的内容。每个片段都记下来源文件和它在文件里的顺序,报告里的每条原文都能找回出处。
窗口大小决定一个片段装多少内容。
| 窗口 | 片段大致是什么样 | 适合的材料 |
|---|---|---|
| 较小,100 到 200 | 一两句话,一段只说一件事 | 访谈、评论、短新闻 |
| 中等,200 到 350 | 一个自然段 | 政策文件、调研报告 |
| 较大,350 到 500 | 几段连在一起,语境更完整 | 论文、长篇评论 |
片段数是后面所有占比的分母。某个主题有 60 个片段,全部片段有 400 个,这个主题的占比就是 60 / 400 = 15%,离群片段也算在分母里。
TATOOLS 工具页上的【文本分割窗口大小(字符数)】可以在 100 到 500 之间调,默认 300。切片段之前,工具会先清理文本。中文模式只保留汉字、数字和最常用的几种标点,夹在中文里的英文字母会在这一步去掉,英文材料要在【文档主要语言】里选英文。目录、致谢和参考文献也会被切成片段参与聚类,上传前最好删掉。报告【主题建模分析概览】里的【文档片段数】,就是这次一共切出的片段数。
03
说法不同的两段话,怎么认出是一回事
【房价上涨让年轻人推迟结婚】和【住房成本高企,青年婚期后延】切成词以后,没有一个词相同。按词来比,这两句话毫不相干;按意思来比,它们说的几乎是同一件事。
语义嵌入(Sentence Embedding,把一段话变成一组表示意思的数字)把每个片段映射成 d 维空间里的一个向量 v。训练好的嵌入模型会让意思相近的话落在空间里相近的位置。Reimers 和 Gurevych 2019 年提出 Sentence-BERT,让这类句子向量可以大规模计算,BERTopic 的第一步用的就是这类向量。两个向量有多近,最常用余弦相似度来量。
cos(a, b) = (a · b) / (‖a‖ × ‖b‖)
余弦距离 = 1 − cos(a, b)
a · b:两个向量对应位置相乘再相加
‖a‖:向量 a 的长度,各分量平方和再开方
余弦相似度看的是两个向量的方向,取值在 −1 到 1 之间,越接近 1 方向越一致。用三个三维向量手算一遍,a = (1, 0, 1),b = (1, 1, 0),c = (2, 0, 2)。
| 比较 | 点积 | 长度相乘 | 余弦相似度 | 欧氏距离 |
|---|---|---|---|---|
| a 和 b | 1 | √2 × √2 = 2 | 0.5 | √2 ≈ 1.41 |
| a 和 c | 4 | √2 × √8 = 4 | 1 | √2 ≈ 1.41 |
a 和 c 方向完全相同,只是 c 长了一倍,余弦相似度是 1。按欧氏距离算,a 离 c 和 a 离 b 一样远。把向量先缩放到长度为 1,两种量法就对上了,单位向量之间的欧氏距离只由余弦决定。
‖â − b̂‖² = 2 − 2 × cos(a, b)
例:cos(a, b) = 0.5,距离 = √(2 − 1) = 1
cos(a, c) = 1,距离 = √(2 − 2) = 0
用词不同、意思相同的片段,靠语义向量才能聚到一起。访谈里的口语说法和政策文件里的书面说法谈的是同一件事,也能落进同一个主题。
TATOOLS 为每个片段计算一个语义向量。工具页【自定义参数】里的【嵌入距离】选余弦距离时,所有向量先缩放到长度 1 再进入下一步,后面比的就是方向;选欧氏距离,向量保留原来的长度。默认是余弦距离。
04
UMAP:向量太长,先压缩再分组
语义向量通常有几百甚至上千维。维度一高,任意两点之间的距离会变得差不多,最近的点和最远的点拉不开差距。Beyer 等人 1999 年专门分析过这个现象,它是维度灾难(Curse of Dimensionality,维度越高、距离越难区分远近)的一种表现。按疏密找分组的算法最怕这种情况,到处都一样稀,就找不出哪里密。
UMAP(统一流形逼近与投影,一种降维方法,由 McInnes、Healy 和 Melville 在 2018 年提出)先在原来的高维空间里给每个点找 k 个最近的邻居,连成一张邻居关系图;再在低维空间里摆放这些点,让邻居关系尽量保持原样。低维空间里两点的相似程度,用下面这条曲线计算。
q_ij = 1 / (1 + a × d_ij^(2b))
d_ij:点 i 和点 j 在低维空间里的距离
a、b:由最小距离 min_dist 和散布参数 spread 决定的两个常数
取 a = 1、b = 1 作为示例,距离 0.5 时 q = 1 / 1.25 = 0.8,距离 1 时 q = 0.5,距离 2 时 q = 1 / 5 = 0.2。距离越远,相似程度降得越快。min_dist 调大,曲线在近处变平,邻居之间允许留出一定间隔;调小,邻居会被挤得更紧,一簇一簇更分明。
TATOOLS 工具页的【UMAP(把高维数据压成少数几个维度)降维参数】有四个滑块,距离度量设置里另有【UMAP 距离】,可选余弦或欧氏。
| 设置 | 范围 | 调小 | 调大 |
|---|---|---|---|
| 邻居数量 n_neighbors | 2 到 50 | 更看重局部,小分组更容易显出来 | 更看重整体结构,相近的小分组容易连成大组 |
| 降维维度 n_components | 2 到 15 | 压缩得更多,疏密对比更明显 | 保留的信息更多,需要更多片段才能分出疏密 |
| 最小距离 min_dist | 0.001 到 0.5 | 点挤得更紧,簇更分明 | 点之间留出间隔,分布更均匀 |
| 散布参数 spread | 0.1 到 3.0 | 整体分布更收拢 | 整体分布更舒展 |
邻居数量可以按片段总数来想。400 个片段、邻居数量 15 时,每个片段只参考离它最近的 15 个片段,占其余片段的 15 / 399 ≈ 3.8%。邻居数量调到 40,参考范围扩大到 40 / 399 ≈ 10.0%,局部的小分组更容易被周围的大组吸收。
UMAP 算出的低维坐标会随任务一起保存。第二部分讲的拆分主题,就是在这组坐标上重新分组,整批材料不用重算。
05
HDBSCAN:扎堆的成主题,零散的单列
从高处看一片乡村,房子密集的地方是一个个村子,村子之间零星散着几户人家。把片段看成房子,村子就是主题,零星的几户就是离群片段。按疏密分组的算法做的就是这件事,而且事先不用说好有几个村子。
HDBSCAN(基于密度的层次聚类,Campello、Moulavi 和 Sander 2013 年提出)先给每个点量一个核心距离,再用它把稀疏处的距离拉长。
core_k(x) = x 到第 k 近的其他点的距离
d_mreach(x, y) = max{ core_k(x), core_k(y), d(x, y) }
k:最小样本数 min_samples
d(x, y):两点之间的原始距离
核心距离小,说明这个点周围很密。互达距离(Mutual Reachability Distance)取三个数里最大的一个,密集处的距离基本不变,稀疏处的点和别人的距离会被拉长。用一条直线上的四个点手算,A = 0,B = 1,C = 2,D = 10,k = 2。
| 点 | 离它最近的两个点 | 核心距离 |
|---|---|---|
| A | B(1)、C(2) | 2 |
| B | A(1)、C(1) | 1 |
| C | B(1)、A(2) | 2 |
| D | C(8)、B(9) | 9 |
A 和 B 的原始距离是 1,互达距离是 max{2, 1, 1} = 2。C 和 D 的原始距离是 8,互达距离是 max{2, 9, 8} = 9,D 被推得更远了。A、B 和 C 两两之间的互达距离都是 2,自成一组,D 离它们都在 9 以上。最小聚类大小设为 3 时,A、B 和 C 构成一个主题;D 单独一个点凑不成主题,标为离群,主题归属记作 −1。
算法接着把所有点按互达距离连成一棵树,从远到近逐级切断,得到一层套一层的分组。最小聚类大小决定多小的分组还算一个主题。最后要从这棵层次树里挑出最终的主题,EOM(超额质量法,偏向保留持续稳定的大组)和 Leaf(叶节点法,偏向切出最细的小组)是两种挑法。选择阈值 ε 让距离小于 ε 的相邻小组合并成一组。聚类结束后,HDBSCAN 还给每个片段算一个归属分数,取值 0 到 1,越接近 1,这个片段越靠近所在主题的密集核心。
TATOOLS 工具页【HDBSCAN(按疏密分层自动分类)聚类参数】开放了这一步的设置,【HDBSCAN 距离】决定原始距离 d(x, y) 怎么量。
| 设置 | 范围或选项 | 作用 |
|---|---|---|
| 最小聚类大小 min_cluster_size | 2 到 50 | 一个主题至少要有多少个片段 |
| 最小样本数 min_samples | 1 到 20,滑到 1 显示【自动】 | 核心距离里的 k,越大越保守,离群片段越多 |
| 选择阈值 cluster_selection_epsilon | 0 到 0.1 | 距离小于它的相邻小组合并 |
| 选择方法 | EOM、Leaf | 偏向稳定的大组,还是细分的小组 |
| HDBSCAN 距离 | 欧氏、曼哈顿、切比雪夫 | 原始距离的量法 |
| 计算概率 | 开或关 | 是否保留每个片段的归属分数 |
三种距离用两个点手算,x = (1, 2),y = (4, 6)。
欧氏距离 = √((4 − 1)² + (6 − 2)²) = √(9 + 16) = 5
曼哈顿距离 = |4 − 1| + |6 − 2| = 3 + 4 = 7
切比雪夫距离 = max{|4 − 1|, |6 − 2|} = 4
欧氏距离是两点之间的直线距离。曼哈顿距离把每一维的差加起来,每一维的差异都算数;切比雪夫距离只看差得最大的那一维。
主题数有了目标值 K 以后,TATOOLS 先用你在工具页上设的这些参数跑一遍 HDBSCAN,再把主题数调到 K。聚出来的组比 K 多,就反复合并中心离得最近的两组,距离的量法和【HDBSCAN 距离】一致;比 K 少,就用 K 均值把片段最多的一组分成两半,每半至少 2 个片段。离群片段只由 HDBSCAN 决定,调主题数时不增也不减。只有当前参数下全部片段都被判为离群、一个组都没有时,才改用 K 均值把全部片段分成 K 组,这时片段没有归属分数。报告的【主题原文片段】按归属分数给每个主题的片段排序。
06
主题数怎么定:手动指定和自动发现
400 个片段,分成 6 个主题还是 10 个主题,两种结果可能都说得通。6 个主题对应材料里的几大议题,10 个主题把其中几个议题又分出了子话题。主题数决定的是看材料的分辨率,要按研究问题来选。
TATOOLS 工具页的【主题数量设置】有两种方式。【手动指定】用滑块直接定主题数,范围 2 到 200,工具页提示设到 20 个以上时,实际得到的主题数可能略少。【自动发现】让工具从数据里读出主题数,候选范围有上限,需要几十个细分主题时用【手动指定】。
自动发现在降维后的坐标上,用 K 均值聚类(K-Means,把点分成 K 组、让每组尽量围着自己的中心)把候选的 K 逐个试一遍,每个 K 算四个读数。
组内平方和 W(K) = Σ_j Σ_{x∈C_j} ‖x − μ_j‖²
轮廓系数 s(i) = (b(i) − a(i)) / max{a(i), b(i)},整体取所有点的平均
CH 指数 CH(K) = [B(K) / (K − 1)] / [W(K) / (n − K)]
DB 指数 DB(K) = (1/K) × Σ_i max_{j≠i} (σ_i + σ_j) / d(μ_i, μ_j)
C_j 是第 j 组,μ_j 是它的中心。a(i) 是点 i 到同组其他点的平均距离,b(i) 是它到最近的另一组的平均距离。B(K) 是各组中心之间的离散程度,σ_i 是第 i 组的点到中心的平均距离。
| 读数 | 衡量什么 | 取哪个 K |
|---|---|---|
| 肘部 | 组内平方和随 K 下降,在哪里突然变缓 | 拐点处的 K |
| 轮廓系数(Rousseeuw,1987) | 每个点离自己组近、离别的组远的程度 | 最大值处的 K |
| CH 指数(Caliński 与 Harabasz,1974) | 组间分散和组内紧凑的比值 | 最大值处的 K |
| DB 指数(Davies 与 Bouldin,1979) | 每组和最像它的那组有多难分开 | 最小值处的 K |
手算两个读数。先看组内平方和从 K = 2 到 K = 6 怎样变化。
K 2 3 4 5 6
W(K) 1000 520 300 260 240
比上一个少 480 220 40 20
到 K = 4 以后下降突然变缓,肘部在 K = 4。再看轮廓系数,某个点到同组其他点的平均距离 a = 2,到最近的另一组平均距离 b = 6,它的轮廓系数是 (6 − 2) / 6 ≈ 0.67,这个点明显更靠近自己的组。
TATOOLS 把四个读数汇成两个数。主结构读数优先取肘部位置,肘部落在候选范围外时,改取几条曲线近优位置的中位数。细分读数取轮廓系数和 DB 指数两条曲线最优位置附近最大的 K,而且不小于主结构读数。【参数预设】里的三种策略,就是在这两个读数之间选。
| 预设 | 选哪个主题数 | 主结构 6、细分 10 时 | 主结构 6、细分 9 时 |
|---|---|---|---|
| 保守策略 | 主结构读数 | 6 | 6 |
| 平衡策略 | 两个读数之和除以 2,向下取整 | 8 | 7 |
| 激进策略 | 细分读数 | 10 | 9 |
三种预设还同时设定了降维、聚类和关键词的一组参数,选中以后,这些值会显示在【自定义参数】卡片里。
| 参数 | 保守策略 | 平衡策略 | 激进策略 |
|---|---|---|---|
| 邻居数量 | 20 | 15 | 10 |
| 降维维度 | 3 | 5 | 8 |
| 最小距离 | 0.2 | 0.1 | 0.05 |
| 最小聚类大小 | 15 | 10 | 5 |
| 选择阈值 | 0 | 0 | 0.02 |
| HDBSCAN 距离 | 欧氏 | 切比雪夫 | 曼哈顿 |
| 最频繁词汇数量 | 5,000 | 10,000 | 20,000 |
| 多样性参数 | 0.1 | 0.1 | 0.3 |
| 计算概率 | 关 | 开 | 开 |
保守策略看的是材料的主干,适合先摸清一批材料有哪几大块。已经知道有哪几块、想看细节时用激进策略,它会把子话题分出来。选【自定义参数】后,卡片里的每个设置都能自己改。TATOOLS 报告【主题建模分析概览】的【运行设置】记录了这次的主题数量模式和最终主题数。
07
关键词怎么算出来
一个谈住房的主题里,【我们】和【政策】几乎每个片段都有,【租金】【首付】【公积金】主要出现在这个主题的片段里。描述这个主题,后面三个词有用得多。算关键词要做的,就是给集中在本主题里的词打高分,给到处都有的词打低分。
TF-IDF(词频–逆文档频率,一个词在这段里常见、在别处少见的程度)是做这件事的经典办法。逆文档频率由 Sparck Jones 在 1972 年提出,Salton 和 Buckley 1988 年系统比较了各种加权方式。
tfidf(t, d) = tf(t, d) × idf(t)
idf(t) = log(n / df(t))
tf(t, d):词 t 在片段 d 里出现的次数
df(t):包含词 t 的片段数
n:片段总数
手算一个小例子。一共 4 个片段,片段 1 和片段 2 属于住房主题。【租金】在片段 1 出现 2 次,在片段 2 出现 1 次,别处没有,df = 2,idf = log₁₀(4 / 2) ≈ 0.301。【我们】在 4 个片段里各出现 1 次,df = 4,idf = log₁₀(4 / 4) = 0。
| 词 | 片段 1 | 片段 2 | 住房主题得分,两段平均 |
|---|---|---|---|
| 租金 | 2 × 0.301 = 0.602 | 1 × 0.301 = 0.301 | (0.602 + 0.301) / 2 ≈ 0.452 |
| 我们 | 1 × 0 = 0 | 1 × 0 = 0 | 0 |
BERTopic 原论文用的是类别级 TF-IDF(c-TF-IDF),把一个主题的所有片段当成一份大文档来算。
W(t, c) = tf(t, c) × log(1 + A / f(t))
tf(t, c):词 t 在主题 c 全部片段里的出现频率
f(t):词 t 在所有主题里的总出现次数
A:每个主题平均包含的词数
TATOOLS 先给每个片段算 TF-IDF 向量,再在主题内部取平均,得分最高的词排在最前面,上面的手算例子就是这个算法。实际计算用的是 TF-IDF 的常用改进版本,词频取对数,idf 做了平滑,每个片段的向量缩放到长度 1,长片段不会因为字多占便宜。【向量化类型】选 Count 时,改为把主题内各片段的词频直接相加。
工具页的【文本向量化参数】和上方的词表设置一起决定哪些词有资格当关键词。
| 设置 | 范围或选项 | 作用 |
|---|---|---|
| 向量化类型 | TF-IDF、Count | 按集中程度打分,或者按出现次数打分 |
| N-gram 范围 | 下限 1 到 3,上限 1 到 5,默认 1 到 2 | 关键词可以是单个词,也可以是连续几个词组成的短语 |
| 最小文档频率 min_df | 1 到 10 | 一个词至少要在多少个片段里出现才保留 |
| 最频繁词汇数量 max_features | 1,000 到 50,000 | 只保留出现最多的这么多个词参与计算 |
| 自定义停用词 | 自己填写 | 这些词不参与计算,留空时使用内置停用词表 |
| 词形合并 | 一行一组 | 分词后把几种写法并成一种再计数,原文不变 |
N-gram 上限调到 3,【住房 公积金 贷款】这样的三词短语也能成为关键词。词形合并写成【养老院:敬老院,老人院】,三种写法都按【养老院】计数,关键词表里只出现一次。最小文档频率设为 3,只在一两个片段里出现的生僻词会被排除,关键词更集中在反复出现的说法上。出现在绝大多数片段里的词也会被自动排除。
只按权重取词,排在前面的几个关键词可能意思差不多,比如【租金】【房租】【租房】挤在一起,关键词表读起来很重复。工具页【其他设置】里的【多样性参数】就是管这件事的。TATOOLS 先按权重给每个主题取 30 个候选词,再用最大边际相关(Maximal Marginal Relevance,MMR,一边看权重,一边避开和已选词重复)挑出 20 个。这个方法由 Carbonell 和 Goldstein 在 1998 年提出,BERTopic 的开源实现里也有同名的多样性参数。
MMR(w) = (1 − λ) × s(w) / s_max − λ × max_{v∈S} cos(e_w, e_v)
λ:多样性参数,取值 0 到 1
s(w):候选词 w 的权重,s_max 是本主题最高的权重
S:已经选中的词
e_w:词 w 的语义向量
每一步选 MMR 最高的候选词,选够 20 个为止。λ = 0 时第二项消失,就是直接取权重最高的 20 个。选中的词保留原来的权重,并按权重排序,报告里权重图和排名曲线的读法不变。
手算一步。【租金】权重最高,已经选中。剩下两个候选,【房租】的相对权重是 0.9,和【租金】的余弦相似度是 0.9;【首付】的相对权重是 0.8,和【租金】的相似度是 0.3。
| 候选词 | λ = 0 时的得分 | λ = 0.5 时的得分 |
|---|---|---|
| 房租 | 0.9 | 0.5 × 0.9 − 0.5 × 0.9 = 0 |
| 首付 | 0.8 | 0.5 × 0.8 − 0.5 × 0.3 = 0.25 |
λ = 0 时,下一个选的是【房租】;λ = 0.5 时,下一个选的是【首付】,意思和【租金】重复的【房租】往后排。三档预设里,保守策略和平衡策略的多样性参数是 0.1,激进策略是 0.3。
最小文档频率设得太高,可能没有词满足条件,计算就进行不下去。TATOOLS 遇到这种情况会自动调低最小文档频率重试,报告【主题建模分析概览】顶部会出现【参数已自动调整】,写明从几调到几、调了几次。关键词算好以后,报告的【主题关键词权重】【主题词权重排名】和【主题—关键词得分矩阵】分别用条形图、排名曲线和矩阵展示它们。
08
抽象标签:给每个主题起一个人读得懂的名字
关键词表是一串排好序的词,比如【租金 首付 公积金 通勤 学区 房东】。研究者读到这串词,会自己把它们归成一件事,也许叫【住房负担与居住选择】。这个归纳出来的名字,就是抽象标签。
主题标注(Topic Labeling)是主题模型研究里的一个专门问题。Mei、Shen 和 Zhai 2007 年最早系统研究自动给主题起名,先从语料里找出候选短语,再按候选短语和主题词分布的语义相关程度挑出最合适的一个。Lau 等人 2011 年把候选名扩展到维基百科的条目标题,再用排序模型挑选。大语言模型出现以后,直接读关键词和原文、概括出一个短名字,成了常见做法。
主题名把研究者的解读压缩成几个字,方便在报告、论文和编码表里指称一个主题。它本身就是一种解读,读的时候要和关键词、代表原文对照着看。关键词告诉你这个主题里常出现什么词,原文告诉你这些词在谈什么。
TATOOLS 给每个主题起名分三步。
取这个主题得分最高的几个关键词。
在原文里找到每个关键词出现的位置,截取它前后的一小段文字作为上下文。
把关键词、上下文和工具页选定的【所属行业】一起交给语言模型,要求写出一个简短的名字,概括这些词共同指向的意思,不能直接把关键词拼起来,也不能带【主题】二字。
所有主题同时起名。生成的名字不符合要求时,这个主题显示为【主题 N】。
【所属行业】给语言模型一个领域背景,工具页上有 13 个选项,从通用领域到政府与政策都有。同一组关键词放在不同领域里,会读出不同的意思。
| 关键词(示例) | 所属行业 | 可能起出的名字(示例) |
|---|---|---|
| 流量 转化 投放 复购 | 电商与零售 | 店铺引流与复购运营 |
| 流量 转化 投放 复购 | 文化创意与传媒 | 内容传播与受众转化 |
| 轮候 配租 保障 审核 | 政府与政策 | 公租房申请与配租 |
主题名在报告里多处出现。【这份结果说明了什么】列出前几个主题名,【主题建模分析概览】的【主题与关键词】把每个主题名和它的关键词放在一起。【主题分布散点图】和【文档片段分布图】勾选【显示主题标签】后,名字直接标在图上。追问时,TATOOLS 也靠主题名认出问题点到了哪个主题。
TATOOLS 还会为每个主题另起一个章节或编码名称,写在【章节或编码名称草案】表里,这个名字更适合直接用作论文小节标题或编码表条目。这张表同时按主题片段占全部片段的比例给出建议位置。
| 片段占比 | 按占比建议的位置 |
|---|---|
| 20% 及以上 | 报告重点章节 |
| 10% 及以上,不到 20% | 独立小节 |
| 5% 及以上,不到 10% | 合并讨论 |
| 不到 5% | 附录/补充 |
09
工具页上的设置分别改哪一步
TATOOLS 的【BERTopic 主题聚类】把流水线每一步的设置都放在了工具页上。常用的设置在页面上方,降维、聚类和关键词的细节参数在【自定义参数】卡片里,【参数预设】选中【自定义参数】后就能逐项修改。下表按工具页从上到下的顺序列出这些设置。
| 设置 | 范围或选项 | 作用在哪一步 |
|---|---|---|
| 文档主要语言 | 中文、英文 | 清理文本、分词和停用词表 |
| 主题时间演化基准 | 年月,可以不填 | 时间演化,文件里找不到时间锚点时兜底 |
| 自定义停用词 | 自己填写 | 关键词 |
| 词形合并 | 一行一组 | 关键词 |
| 所属行业 | 13 个领域 | 主题名和章节名 |
| 主题数量设置 | 手动指定、自动发现 | 定主题数 |
| 固定主题数量 | 2 到 200 | 定主题数,手动指定时使用 |
| 文本分割窗口大小 | 100 到 500 字符 | 切片段 |
| 参数预设 | 保守、平衡、激进、自定义 | 定主题数,并批量设定下方参数 |
| 嵌入距离 | 余弦、欧氏 | 语义向量 |
| UMAP 距离 | 余弦、欧氏 | 降维 |
| 邻居数量、降维维度、最小距离、散布参数 | 见 UMAP 一节 | 降维 |
| HDBSCAN 距离 | 欧氏、曼哈顿、切比雪夫 | 聚类 |
| 最小聚类大小、最小样本数、选择阈值、选择方法 | 见 HDBSCAN 一节 | 聚类 |
| 计算概率 | 开、关 | 归属分数 |
| 向量化类型、N-gram 范围、最小文档频率、最频繁词汇数量 | 见关键词一节 | 关键词 |
| 多样性参数 | 0 到 1 | 关键词 |
按研究需要调参数,可以从下面几种情况入手。
| 想要的效果 | 先调哪个设置 |
|---|---|
| 主题分得更粗,先看大块 | 【保守策略】,或【手动指定】一个较小的数 |
| 主题分得更细,看子话题 | 【激进策略】,或【手动指定】一个较大的数 |
| 一个片段里只说一件事 | 调小【文本分割窗口大小】 |
| 离群片段少一些 | 调小【最小样本数】或【最小聚类大小】 |
| 关键词里少出现泛泛的词 | 把这些词补进【自定义停用词】 |
| 同一个说法的几种写法合成一个关键词 | 【词形合并】 |
| 关键词里少出现意思相近的词 | 调大【多样性参数】 |
| 关键词里多出现固定搭配 | 把 N-gram 上限调到 3 |
| 主题名更贴近研究领域 | 选对【所属行业】 |
| 已经分好,只有个别主题不满意 | 在报告里合并、拆分或移出,第二部分会讲 |
同一批材料换一组设置再跑一次,会得到一份新的结果,比较几次运行时可以参考下一节的模型评估分数。
10
模型评估分数怎么读
TATOOLS 报告的【主题建模分析概览】给出【模型评估综合分】和【模型评估等级】,下面的【各项评分】列出六项分数。综合分是六项分数的加权平均。
S = Σ_d w_d × s_d / Σ_d w_d
s_d:第 d 项评分,取值 0 到 1
w_d:第 d 项的权重
| 评分项 | 看的是什么 | 权重 |
|---|---|---|
| 聚类质量 | 轮廓系数、CH 指数和 DB 指数,各主题大小是否均衡,离群片段多少 | 25% |
| 主题词连贯性 | 关键词得分是否集中,同一主题片段的语义向量是否紧凑,主题之间的关键词是否区分得开 | 25% |
| 主题差异度 | 词汇的丰富程度,主题大小的差异,主题之间的语义差异 | 15% |
| 参数匹配度 | 降维、聚类和关键词参数与材料规模是否相称 | 15% |
| 数据适配性 | 片段数和主题数是否合适,离群片段比例,片段覆盖情况 | 15% |
| 运行稳定性 | 计算过程是否稳定收敛 | 5% |
按上表的顺序给六项分数假设一组数值,代入公式手算一遍。
S = 0.25 × 0.62 + 0.25 × 0.70 + 0.15 × 0.55 + 0.15 × 0.80 + 0.15 × 0.66 + 0.05 × 0.90
= 0.155 + 0.175 + 0.0825 + 0.120 + 0.099 + 0.045
= 0.6765 ≈ 67.7%
| 综合分 | 模型评估等级 |
|---|---|
| 80% 及以上 | 优秀 |
| 60% 及以上,不到 80% | 良好 |
| 40% 及以上,不到 60% | 中等 |
| 20% 及以上,不到 40% | 较差 |
| 不到 20% | 差 |
67.7% 落在良好一档。这个分数衡量的是聚类结构和参数设置,同一批材料换几组设置各跑一次,可以用它比较哪次运行的结构更清楚。主题名起得贴不贴切、主题对研究问题有没有用,要回到代表原文里判断。
11
报告前半部分怎么读
TATOOLS 的报告从上到下排了十几个区块,主题编辑、时间演化和追问放在后面两部分讲,其余区块可以分五层读。
| 层次 | 区块 | 先看什么 |
|---|---|---|
| 总览 | 【这份结果说明了什么】【主题建模分析概览】 | 片段数、主题数、离群片段占比,主题名和关键词 |
| 主题之间 | 【主题分布散点图】【主题层级关系图】【不同层级的文档片段分布】 | 哪些主题挨得近,能不能归成更大的一类 |
| 主题内部 | 【主题关键词权重】【主题词权重排名】【主题原文片段】【主题文档片段占比分布】【主题—关键词得分矩阵】 | 每个主题靠哪些词撑起来,原文在谈什么,占多大比例 |
| 片段 | 【文档片段分布图】 | 每个片段落在哪里,离群片段在什么位置 |
| 写作 | 【章节或编码名称草案】【写作结构草案】 | 章节名、建议位置和章节结构 |
几个区块要多看一眼。
【主题分布散点图】里一个圆点是一个主题,圆点位置用来比较主题之间的相对远近,点击圆点可以看这个主题的详情。
【文档片段分布图】里一个圆点是一个片段,颜色表示所属主题,圆点大小和圆点距离都能调。
【主题层级关系图】用 Ward 层次聚合(Ward,1963,每次合并让组内离散程度增加最少的两组)把相近的主题一级一级合并。【不同层级的文档片段分布】里 L0 是原来的主题,层级越高,合并得越多。
【主题原文片段】每个主题最多列 5 个片段,按归属分数排序,每条最多显示 200 个字符,关键词在片段里标出。
【主题文档片段占比分布】按片段数排序,条形长度是这个主题的片段占全部片段的比例,分母包含离群片段。
【写作结构草案】根据主题名和关键词,结合片段数和截断后的原文片段生成,每节写明对应的主题编号和论述方向。
12
主题分得不满意,在报告里直接改
一份结果有 12 个主题,读完代表原文,研究者往往会有自己的判断。主题 2【保障房申请流程】和主题 5【公租房轮候】,对这项研究来说是同一件事;主题 8 里一半片段谈养老床位,一半谈医保报销;主题 11 全是会议通知里的套话。这三个问题可以直接在报告里处理,前面的计算都不用重做。
研究者看完模型给的结果,动手调整,再让模型重新算,是主题模型研究里专门讨论过的做法。Hu、Boyd-Graber、Satinoff 和 Smith 2014 年提出交互式主题模型,让使用者告诉模型哪些词应该放在一起。Smith 等人 2018 年做了一套可以合并和拆分主题的系统,还请用户试用,检验这些改法是否顺手。做质性研究的人对这件事更熟悉,编码的合并与拆分本来就是整理编码本的常规步骤,Saldaña 的《质性研究编码手册》里,第二轮编码的主要工作之一就是把第一轮的编码归并和重新分类。
TATOOLS 改主题时,已经生成的结果保持原样,每次改动生成一个新版本。
R_(v+1) = F(S_v, O)
S_v:第 v 版存下来的结果,包括片段、语义向量、降维坐标、主题归属和识别出的时间表达
O:这次要做的改动
F:按改动重新计算报告
新版本直接沿用上一版的语义向量、降维坐标和聚类结果,只按你的改动改写主题归属。没有被改的主题,片段一个都不变,两个版本之间的差别只来自你的改动。
合并
合并把几个主题的片段并成一个主题。
C_new = C_a ∪ C_b ∪ …
|C_new| = |C_a| + |C_b| + …
主题 2 有 48 个片段,主题 5 有 22 个,合并后的新主题有 48 + 22 = 70 个片段。合并至少要选 2 个主题。合并后的主题可以自己起名字,不起名字,TATOOLS 按合并后的关键词重新生成一个。
拆分
拆分在一个主题内部重新分组。TATOOLS 取这个主题所有片段的降维坐标,用 K 均值分成 p 组,p 可以是 2 到 5。K 均值要找一种分法,让每组片段到本组中心的距离平方和最小。
min Σ_{j=1..p} Σ_{x∈C_j} ‖x − μ_j‖²
μ_j:第 j 组片段坐标的平均值
用一维坐标手算,某个主题 6 个片段的坐标如下,拆成 2 组。
坐标:1, 2, 3, 10, 11, 12
分法一:{1, 2, 3} 和 {10, 11, 12}
中心 2 和 11,平方和 = (1 + 0 + 1) + (1 + 0 + 1) = 4
分法二:{1, 2, 3, 10} 和 {11, 12}
中心 4 和 11.5,平方和 = (9 + 4 + 1 + 36) + (0.25 + 0.25) = 50.5
分法一的平方和小得多,K 均值会选它。拆出来的每个子主题至少要有 2 个片段,所以一个主题至少要有 2p 个片段才能拆成 p 组,片段不够时,编辑栏会标出【片段不足】。拆分的起点是固定的,同一个主题拆成同样的组数,每次结果都一样。子主题的名字由 TATOOLS 重新生成。
移出
移出把一个主题的片段全部归入离群片段,适合处理套话、格式文字这类和研究无关的主题。
离群片段占比 = 离群片段数 / 片段总数
400 个片段里原有 32 个离群片段,占比 32 / 400 = 8.0%。移出一个有 15 个片段的主题以后,离群片段变成 47 个,占比 47 / 400 = 11.75%,报告显示为 11.8%。
合并、拆分和移出可以放在同一次里一起改,主题数按每项改动加加减减。一个主题一次只能用一种改法,也不能把主题全部移出。
开始:12 个主题
合并 2 个主题为 1 个:12 − 1 = 11
把 1 个主题拆成 3 个:11 + 2 = 13
移出 1 个主题:13 − 1 = 12
13
在报告底部的编辑栏里改
TATOOLS 报告页底部固定着一条编辑栏,左边是【报告版本】下拉框,右边是【编辑报告】和【展开编辑】按钮。展开以后有三个标签页,分别是【合并或移出】【拆分主题】和【时间锚点】,时间锚点放在第三部分讲。
改主题分四步。
在【合并或移出】里勾选主题,点【合并所选】或【移出所选】。在【拆分主题】里勾选主题,填好【每个主题拆为】几个子主题,点【拆分所选】。
要改的地方先列在【本次将执行】里。合并组可以在【合并后名称】里填名字,留空就按合并后的关键词重新命名,不想改了就点【撤销】。
编辑栏底部显示主题数从几变成几、受影响的片段数,以及这次基于哪个版本。
点【生成新版本】,新版本生成好以后,页面自动切换过去。
已经选过的主题会带上【组 1】【拆 3 份】或【移出】标记,不能再用别的改法,【清空】可以一次撤掉全部改动。新版本还可以接着改,下拉框里写明它基于哪个版本,一版接一版形成版本链。原来的版本一直保留,随时可以切回去看。
14
改完以后,报告里哪些内容会变
| 内容 | 新版本怎么处理 |
|---|---|
| 片段、语义向量、降维坐标 | 沿用上一版 |
| 主题归属 | 被改的主题按改动重新分,其余片段仍在原来的主题里 |
| 主题编号 | 按原来的顺序重新连续编号,拆出的子主题排在原主题的位置 |
| 关键词 | 所有主题按新的分组重新计算 |
| 主题名 | 没动的主题沿用;合并组填了名字就用你的名字,没填名字的合并组和拆出的子主题重新生成 |
| 章节或编码名称 | 没动的主题沿用,合并和拆分产生的主题重新生成 |
| 写作结构草案 | 整份重新生成 |
| 散点图、层级图、占比分布、得分矩阵、模型评估 | 按新的分组重新计算 |
| 主题时间演化 | 沿用上一版识别出的时间表达,按新的分组重新汇总 |
| 追问 | 片段的语义向量沿用,主题名和各主题的数字按新结果重新准备,按改过以后的主题回答 |
| 归属分数 | 这次有拆分时,原来的归属分数和子主题对不上,新版本不再沿用 |
没动的主题沿用原来的名字,你已经在笔记和编码表里用过的主题名,在新版本里还是同一个名字。质性研究讲究审计轨迹(Audit Trail,把每一次分析决定记录下来,供别人检查),Lincoln 和 Guba 1985 年把它列为检验研究可靠性和可确认性的手段,每一次分类调整都要留下记录。TATOOLS 为每个新版本保存一份变更摘要,承担的就是这份记录的作用。
15
改了什么,报告里都记着
新版本的报告靠上的位置多出一个【相对 vN 的变更】区块,N 是上一版的编号。
| 区块内容 | 写了什么 |
|---|---|
| 主题数 | 改之前和改之后的主题数 |
| 离群片段占比 | 改之前和改之后的占比 |
| 未改动主题 | 名称和章节名沿用上一版的主题个数 |
| 重新生成的主题 | 合并或拆分产生、重新命名的主题个数 |
| 合并 | 新主题,合并前的各个主题和片段数,命名来源 |
| 拆分 | 原主题,拆出来的各个主题和片段数 |
| 移出主题 | 被移出的主题和片段数 |
| 时间锚点 | 调整过锚点的文件,改前改后的锚点和来源 |
接着用前面的例子,一共 12 个主题和 400 个片段,其中 32 个是离群片段。同一次里合并主题 2 和主题 5,把主题 8 拆成 3 个,移出有 15 个片段的主题 11,变更摘要顶部的四个数字见下表。
| 指标 | 数值 | 怎么算 |
|---|---|---|
| 主题数 | 12 → 12 | 12 − 1 + 2 − 1 |
| 离群片段占比 | 8.0% → 11.8% | 32 / 400 和 47 / 400 |
| 未改动主题 | 8 | 12 个主题里有 4 个被改过 |
| 重新生成的主题 | 4 | 合并出 1 个,拆分出 3 个 |
合并表写明新主题由哪几个原主题组成、各有多少片段,命名来源一栏标出【用户命名】或【自动生成】。写论文时,TATOOLS 的这份摘要可以直接整理成附录里的主题调整记录。
16
主题怎样随时间变化:时间从原文里读出来
一份政策回顾里写着【2023 年第一季度,全市新开工保障房……】,一篇访谈里受访者说【去年 10 月我们递交了申请】。这些时间表达说明了片段在谈哪个时期的事。把每个主题的片段里提到的时间汇总起来,就能看出每个主题主要在讲哪段时期。
主题演化分析(Topic Evolution)研究主题怎样随时间出现、增强和消退。Blei 和 Lafferty 2006 年提出动态主题模型,让主题的词分布随时间片变化;Wang 和 McCallum 同年提出 Topics over Time(主题随时间模型),把时间戳当成和词一起生成的变量。这两种做法都要求每篇文档带一个时间戳。TATOOLS 走的是另一条路,从原文里的时间表达读出时间,再按主题汇总到月份。
识别时间表达并换算成标准日期,是自然语言处理里的一项基础任务,TimeML 标注规范(Pustejovsky 等,2003)把它分成识别和归一化两步。原文里的时间表达分三类,处理方式各不相同。
| 类型 | 例子 | 怎么处理 |
|---|---|---|
| 绝对时间 | 2023 年 3 月,2023 年第一季度 | 直接换算成月份 |
| 相对时间 | 去年 10 月,上个月 | 按所在文件的时间锚点换算 |
| 宽泛时间 | 本世纪,八十年代,近年来 | 不计入时间轴 |
时间锚点是换算相对时间用的参照日期。【去年 10 月】写在 2024 年的材料里是 2023 年 10 月,写在 2019 年的材料里是 2018 年 10 月。TATOOLS 给每份源文件定一个锚点,同一份文件的片段共用这个锚点,按四级顺序确定。
在文件开头部分找第一个带明确年份的日期。
找不到时,把开头部分切成小段,逐段识别其中的时间。
还找不到,由语言模型先判断文中有没有明确的时间,有的话读出年月。
前三步都没有结果,用工具页上填的【主题时间演化基准】。
四步都没能确定锚点的文件,里面的相对时间不计入时间轴。
每个时间表达换算成它覆盖的月份,权重平均分到这些月份上。把一个主题所有片段里的时间表达分到某个月的权重加起来,就是这个主题在这个月的时间权重。
W(k, m) = Σ_{e ∈ E_k} 1[m ∈ M(e)] / |M(e)|
E_k:主题 k 的片段里识别出的全部时间表达
M(e):时间表达 e 覆盖的月份集合
1[m ∈ M(e)]:月份 m 在 M(e) 里取 1,否则取 0
手算一个例子。主题 3 的片段里识别出三个时间表达,所在文件的锚点是 2024 年 6 月。
| 时间表达 | 覆盖的月份 | 每个月分到的权重 |
|---|---|---|
| 2023 年 3 月 | 2023-03 | 1 |
| 2023 年第一季度 | 2023-01 到 2023-03 | 1/3 |
| 去年 3 月 | 2023-03 | 1 |
W(3, 2023-01) = 1/3 ≈ 0.33
W(3, 2023-02) = 1/3 ≈ 0.33
W(3, 2023-03) = 1 + 1/3 + 1 ≈ 2.33
主题 3 的峰值在 2023 年 3 月。
这条曲线记录的是各主题的片段提到了哪些月份。回忆录、政策回顾和历史叙述把时间写在正文里,用这条曲线能看出每个主题讲的是哪段时期的事,也能看出不同主题在时间上怎样前后衔接。TATOOLS 把时间统一汇总到月份,跨度超过 60 个月的表达,比如【上世纪】,展开后会铺满几百个月,不计入时间轴。
17
时间演化报告怎么读
TATOOLS 报告的【主题时间演化(月份精度)】区块,最上面是四个统计卡片。
| 卡片 | 数的是什么 |
|---|---|
| 识别到时间的主题数 | 至少有一个时间表达计入时间轴的主题数,旁边写着主题总数 |
| 识别时间跨度 | 最早和最晚的月份 |
| 已确定时间锚点的源文件 | 有锚点的文件占全部源文件的比例 |
| 未计入的时间表达 | 相对时间缺少锚点的条数,加上时间范围过宽的条数 |
卡片下面是【时间锚点来源】条,按【自动确定】【全局基准】和【未确定】三段显示各有多少份文件。
曲线图上方可以在【主题筛选】里勾选要看的主题。【显示模式】有【偏移显示】【分离线图】和【堆叠图】三种,【时间轴】可以选【统一时间轴】或【各主题独立】。纵轴是时间权重,鼠标停在数据点上,能看到这个月计入了哪些时间表达,涉及几个片段。
【各主题时间分布】表每行一个主题,列出【出现时间跨度】【最高权重月份】【峰值位置】和【有记录月份数】。【原文中的时间表达明细】列出每个时间表达出现的次数、换算成的月份和处理状态,还有它前后 30 个字符的原文上下文。
| 状态 | 含义 |
|---|---|
| 已纳入 | 已经计入时间轴 |
| 无锚点丢弃 | 相对时间,所在文件没有锚点 |
| 跨度过大丢弃 | 换算后的时间范围太宽 |
| 宽泛时间丢弃 | 本世纪、年代、近年来这类说法 |
| 解析失败 | 识别出来了,但没能换算成日期 |
区块底部的【时间汇总口径】写明了这几条规则。核对某个月的峰值时,先在明细表里找到对应的时间表达,再看上下文里它指的是不是这个主题谈的那件事。
18
年份算错了,在报告里改
一份访谈稿开头写着【1998 年房改以后,单位就不再分房了】,访谈时间是 2024 年 6 月。按第一步的规则,这份文件的锚点定在了 1998 年,文中的【去年 10 月】被换算成了 1997 年 10 月。
打开编辑栏的【时间锚点】标签页,表格列出每份源文件的片段数、当前锚点和来源。
| 来源 | 含义 |
|---|---|
| 自动确定 | 前三步从原文里找到的锚点 |
| 全局基准 | 来自工具页或编辑栏设置的全局基准 |
| 手动设置 | 在编辑栏里给这份文件单独设的锚点 |
| 未确定 | 没有锚点 |
在这份文件的【设置为】里选 2024 年 6 月 15 日,生成新版本。新版本沿用已经识别出的时间表达,只按新锚点重新换算相对时间,【去年 10 月】变成 2023 年 10 月,绝对时间保持不变。表格上方的【全局基准】只作用于还没有锚点的文件。
变更摘要的【时间锚点】部分会列出改过锚点的文件,写明改前改后的锚点和来源。时间锚点可以和合并、拆分放在同一次里改,TATOOLS 一次算出新版本。
19
追问:直接向材料提问,回答附原文出处
读完报告,你手里往往还有很多具体问题。受访者谈公租房轮候时,最常抱怨的是什么?主题 3 在 2023 年 3 月前后集中谈了哪些事?这些问题可以直接在报告的【向这份材料提问】里问 TATOOLS。
直接问通用的 AI 助手,它靠训练时学到的知识作答,依据在哪说不清楚。检索增强生成(Retrieval-Augmented Generation,RAG,先找原文再回答)由 Lewis 等人 2020 年提出,先从材料里找出和问题相关的段落,再要求模型只照着这些段落作答,每句结论都能指回出处。TATOOLS 的追问就是这样做的,检索范围只限你这次上传的材料。
20
能问哪些问题
TATOOLS 在任务完成时,就把每个主题的数字和一句话内容概括准备好了。数字包括片段数、占比和排名,有时间数据的还有峰值月份;内容概括由语言模型读过关键词和最有代表性的几个片段后写成。全部原文片段也预先算好了语义向量,换一种说法提问也能搜到,离群片段同样在内。下面几类问题都能直接问。
| 想知道 | 可以这样问 | 回答里有什么 |
|---|---|---|
| 整体有哪几类 | 这批材料主要在讨论哪几类主题? | 各主题名和占比,逐个列出 |
| 哪个最大 | 占比最大的主题是什么,说了些什么? | 主题名、片段数和内容概括 |
| 某个主题谈什么 | 主题 3 主要在讨论什么? | 内容概括、关键词和占比,加几段原文 |
| 具体怎么说 | 受访者怎么谈公租房轮候? | 摘出原话,每句标出处 |
| 有没有提到 | 有没有人提到租金补贴? | 提到的原文;材料里没有就直接说没有,并指出最接近的内容 |
| 什么时候 | 各主题随时间有什么变化? | 各主题的峰值月份和先后顺序 |
| 两个主题的差别 | 主题 2 和主题 5 有什么不同? | 两个主题各自的数字和原文 |
同一个问题对应好几个主题或好几份文件时,回答会逐项列出,每项给出结论和数字。问数量和排名,回答靠准备好的数字;问具体内容,回答靠搜出来的原文。
一次问答大致是下面这样,材料和回答都是为说明而构造的示例。
| 示例 | 内容 |
|---|---|
| 问题 | 主题 3 里,受访者怎么谈公租房轮候? |
| 回答 | 受访者谈得最多的是轮候时间长,有人递交申请两年多还没排到[1];也有人说不知道自己排在第几位,只能隔段时间打电话去问[2]。 |
| 出处 [1] | 访谈-04:我们是前年递的申请,到现在两年多了还没轮到。 |
| 出处 [2] | 访谈-11:排到第几号也没人告诉,只能隔一段时间打电话去问。 |
报告还按这次的结果给出几个起手问题,比如【占比最大的主题是什么,说了些什么?】,有时间数据时会加上【各主题随时间有什么变化?】,点一下就能问。
21
回答里的编号是怎么来的
你问一句话,TATOOLS 在背后走四步。
先认主题。问题里直接出现主题名、【主题 N】或者某个关键词,就算点到了这个主题。主题名较长时,问题里出现了名字一半以上的两字组合,也算点到。其余主题再和问题比语义向量,意思相近的作为次要线索。
再取数字。点到了主题,就取这个主题的片段数、占比、排名和峰值月份;一个主题都没点到,就按和问题字面重合的程度,给全部主题的数字排序。
然后找原文。点到主题时,先取这个主题的片段,再补上次要线索主题的片段,以及全部材料里语义最相近的片段。没点到主题时,按语义相似度排序;材料来自多份文件而且问题没点名文件,先让每份文件各出一条最相关的片段。内容太短的片段排在有实质内容的片段后面,各主题的内容概括单独放一路,只作背景,不当出处。
最后写回答。语言模型只照着取到的原文、数字和内容概括作答,第一句直接给结论,用方括号编号标出每句话引用的原文片段。回答写完以后,没被引用的候选片段去掉,编号按在回答里第一次出现的顺序重新排。
编号指向的都是你上传的原文,数字都来自这次的分析结果,两样都能回到报告里核对。
22
怎么问,回答更准
| 这样问 | 为什么更准 |
|---|---|
| 直接写【主题 3】或主题名 | 第 1 步一下就能认出主题 |
| 用材料里的原话 | 更容易搜到对应的段落 |
| 一次只问一件事 | 找来的原文更集中 |
| 把【它】【这个】换成具体名字 | 每次提问相互独立,不记得上一句 |
主题名比较长时,问题里不必一字不差。第 1 步的两字组合可以手算。
主题名:住房负担与居住选择
两字组合:住房 房负 负担 居住 住选 选择,共 6 个(含【与】的组合不算)
问题:居住选择上遇到哪些难处
问题的两字组合:居住 住选 选择 择上 上遇 难处(含【到】【哪】【些】的组合不算)
重合:居住 住选 选择,共 3 个,达到名字的一半,算点到这个主题
最省事的办法是用【主题原文片段】里的【就这个主题提问】按钮,点一下,输入框自动填上【主题 N】。
每个版本有自己的提问记录。生成新版本时,片段的语义向量直接沿用,主题名和各主题的数字按新结果重新准备,在新版本上追问,用的就是改过以后的主题。TATOOLS 的公开示例任务里,访客能看到创建者问过的问题和回答,出处可以逐条展开核对。
23
把结果写进论文
TATOOLS 报告里有两份直接为写作准备的草案。【章节或编码名称草案】给每个主题一个适合当小节标题或编码条目的名字,附上关键词、片段数和按占比建议的位置。【写作结构草案】按主题组织出论文结果部分的章节结构,每节写明对应的主题编号和论述方向,引用前要回到原文核对。
方法部分要交代的参数,在【主题建模分析概览】的【运行设置】和【下载原始数据】里的参数表都能找到。下载的数据表还包括片段的主题归属和主题清单,章节或编码名称草案和主题原文片段也各有一张表。
下面是一段方法部分的写法示例,方括号里换成自己的数字。
本研究使用 TATOOLS 的 BERTopic 主题聚类工具(Grootendorst,2022)分析 [文件数] 份文本。文本按约 [窗口大小] 个字符切分为 [片段数] 个片段,计算语义向量后用 UMAP 降至 [降维维度] 维(邻居数量 [邻居数量],最小距离 [最小距离]),再用 HDBSCAN 聚类。主题数采用 [自动发现的平衡策略,或手动指定为 K 个],最终得到 [主题数] 个主题,离群片段占 [比例]。主题关键词取主题内各片段 TF-IDF 权重的平均值,主题名称由语言模型根据关键词及其原文语境生成,研究者对照代表原文逐一核对。研究者在初始结果上合并了 [a] 组主题,拆分 [b] 个主题,移出 [c] 个主题,形成第 [v] 版结果,调整记录见附录。主题的时间分布依据原文中的时间表达汇总到月份。
24
小结
BERTopic 先用语义向量判断片段之间意思远近,再用 UMAP 降维、HDBSCAN 按疏密聚类,最后用 TF-IDF 给主题算关键词,【多样性参数】可以压低意思重复的关键词。
片段是分析单位,【文本分割窗口大小】决定一个片段装多少内容,片段数是所有占比的分母。
主题数可以手动指定,也可以由肘部、轮廓系数、CH 指数和 DB 指数四个读数自动给出,三种预设分别取主结构、折中和细分。
抽象标签由关键词、关键词的原文上下文和【所属行业】生成,读主题名时对照代表原文。
TATOOLS 工具页开放了流水线每一步的设置,模型评估分数可以用来比较不同设置的运行结果。
合并、拆分和移出主题生成新版本,新版本沿用语义向量和降维坐标,没动的主题片段不变,变更摘要留下完整记录。
时间演化从原文的时间表达读出时间,相对时间按每份文件的锚点换算,锚点定错可以在报告里改。
追问能问整体有哪几类主题、哪个最大、某个主题谈什么、原文具体怎么说和各主题的时间先后,回答只引用原文片段,并用编号标出出处。
同一批材料想换一个角度看,可以交给 TATOOLS 的【高级LDA主题模型】,比较按词语共现归纳出的主题;想看材料里人物、机构和概念之间的关系,可以用【知识图谱】。
25
资料来源
Reimers 与 Gurevych:Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks,EMNLP-IJCNLP,2019
Beyer、Goldstein、Ramakrishnan 与 Shaft:When Is Nearest Neighbor Meaningful?,ICDT,1999
MacQueen:Some Methods for Classification and Analysis of Multivariate Observations,Proceedings of the Fifth Berkeley Symposium on Mathematical Statistics and Probability,1967
Mei、Shen 与 Zhai:Automatic Labeling of Multinomial Topic Models,KDD,2007
Lau、Grieser、Newman 与 Baldwin:Automatic Labelling of Topic Models,ACL,2011
Hu、Boyd-Graber、Satinoff 与 Smith:Interactive Topic Modeling,Machine Learning,第 95 卷,2014
Saldaña:The Coding Manual for Qualitative Researchers,第 2 版,SAGE,2013
Lincoln 与 Guba:Naturalistic Inquiry,SAGE,1985
Wang 与 McCallum:Topics over Time: A Non-Markov Continuous-Time Model of Topical Trends,KDD,2006
Pustejovsky 等:TimeML: Robust Specification of Event and Temporal Expressions in Text,New Directions in Question Answering,2003
Lewis 等:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks,NeurIPS,2020
END
