BERT是自然语言处理领域最新的词向量技术,而BERTopic 是基于BERT词向量进行主题建模技术,它利用 Transformer 和 c-TF-IDF 来创建密集的集群,允许轻松解释主题,同时在主题描述中保留重要词。
BERTopic亮点
- 支持引导式Guided
- 支持(半)监督式
- 支持动态主题。
- 支持可视化
安装
!pip3 install bertopic==0.10.0
!pip3 install cntext==1.6.5
准备数据
这里使用的新闻数据集, 共2000条。 新闻类别涵 '娱乐', '教育', '游戏', '财经', '时政', '时尚', '科技', '体育', '家居', '房产'
这里假设大家不知道有10类新闻题材, 构建模型的时候不会用到label字段的数据。
import pandas as pd
df = pd.read_csv('cnews.csv')
df.head()
# 新闻题材
print(df.label.unique())
#记录数
print(len(df))
Run
['娱乐' '教育' '游戏' '财经' '时政' '时尚' '科技' '体育' '家居' '房产']
2000
# 各类题材的新闻记录数
df.label.value_counts()
Run
时政 120
科技 106
时尚 106
财经 105
家居 103
教育 97
娱乐 96
体育 95
房产 87
游戏 85
这里定义了一个清洗数据函数clean_text,需要注意BERTopic需要先将中文分词改造成类似英文文本格式(用空格间隔词语)
import re
import jieba
import cntext as ct
stopwords = ct.load_pkl_dict('STOPWORDS.pkl')['STOPWORDS']['chinese']
def clean_text(text):
words = jieba.lcut(text)
words = [w for w in words if w not in stopwords]
return ' '.join(words)
test = "云南永善县级地震已致人伤间民房受损中新网月日电据云南昭通市防震减灾局官方网站消息截至日时云南昭通永善县级地震已造成人受伤其中重伤人轻伤人已全部送医院救治民房受损户间倒塌户间个乡镇所学校不同程度受损目前被损毁电力交通通讯设施已全部抢通修复当地已调拨帐篷顶紧急转移万人月日时分云南昭通永善县发生里氏级地震震源深度公里当地震感强烈此外成都等四川多地也有明显震感"
clean_text(test)
Run
'云南 永善县 级 地震 已致 伤间 民房 受损 中新网 日电 云南 昭通市 防震 减灾 局 官方网站 消息 日时 云南 昭通 永善县 级 地震 造成 受伤 重伤 轻伤 送 医院 救治 民房 受损 户间 倒塌 户间 乡镇 学校 不同 程度 受损 目前 损毁 电力 交通 通讯 设施 抢通 修复 调拨 帐篷 顶 紧急 转移 万人 时分 云南 昭通 永善县 发生 里氏 级 地震 震源 深度 公里 震感 强烈 成都 四川 多地 明显 震感'
对2000条数据进行clean_text,得到的结果存储到content字段中。
我的macbook内存16G, 运行时间10s
df['content'] = df['text'].apply(clean_text)
df.head()
训练Topic模型
文本分析步骤包括构建特征工程和训练,在本文中,直接使用开源的预训练中文词向量,省去了特征模型的学习时间。
选取的与训练模型均为word2vec格式,这样方便我们使用gensim将其导入。
模型名 | 数据 | 预训练模型资源地址 |
---|---|---|
sgns.zhihu.words.bz2 | 知乎 | 链接: https://pan.baidu.com/s/1BDxP28KL_23Odj9NWZGe-Q 提取码: n1qq |
sgns.wiki.words.bz2 | 中文维基百科 | 链接: https://pan.baidu.com/s/1B1sxHmPeIPJYiCuP1zrmMw 提取码: hofj |
sgns.financial.words.bz2 | 金融 | 链接: https://pan.baidu.com/s/1L_hmGjZMY2ExBn9Vfc_eRg 提取码: hhn6 |
sgns.renmin.words.bz2 | 人民日报 | 链接: https://pan.baidu.com/s/1VQIDrwZH3Y3Lpy4-smPutw 提取码: 3b53 |
sgns.sougou.words.bz2 | 搜狗新闻 | 链接: https://pan.baidu.com/s/15nCaeB41mwK0ZVLrukXpFQ 提取码: 04en |
Note:
除了表格外的资源,还可以使用spacy现有的预训练模型。
本文案例cnews.csv是新闻类数据,这里最好选择使用同样为新闻题材的文本训练出的模型,这样BERTopic效果会更精准一些。sgns.sougou.words.bz2是使用搜狗新闻数据训练的语言模型。
from gensim.models import KeyedVectors
chinese_sougou_news_models = KeyedVectors.load_word2vec_format('sgns.sogou.word.bz2', unicode_errors='ignore')
chinese_sougou_news_models
Run
<gensim.models.keyedvectors.KeyedVectors at 0x7f93e5b8cc10>
from bertopic import BERTopic
topic_model = BERTopic(language="chinese (simplified)",
embedding_model=chinese_sougou_news_models,
calculate_probabilities=True,
verbose=True)
docs = df['content'].tolist()
#2000条进行fit_transform需要1min
topics, probs = topic_model.fit_transform(docs)
100%|██████████| 2000/2000 [01:31<00:00, 21.91it/s]
2021-10-28 12:11:25,583 - BERTopic - Transformed documents to Embeddings
2021-10-28 12:11:34,582 - BERTopic - Reduced dimensionality with UMAP
2021-10-28 12:11:34,718 - BERTopic - Clustered UMAP embeddings with HDBSCAN
CPU times: user 1min 50s, sys: 7.7 s, total: 1min 57s
Wall time: 1min 43s
主题模型方法
- topic_model.get_topic_info 查看各主题信息
- topic_model.find_topics(term, top_n=5) 查找term最有可能所属话题
- topic_model.get_topic(0) 查看Topic 0的特征词
- topic_model.visualize_topics() 话题间距离的可视化
- topic_model.visualize_distribution(probs[0]) 查看某条文本的主题分布
- topic_model.visualize_hierarchy(top_n_topics=20) 主题层次聚类可视化
- topic_model.visualize_barchart(topics=[1]) 显示主题1的词条形图
- topic_model.visualize_heatmap(n_clusters=10) 主题相似度热力图
- topic_model.visualize_term_rank() 可视化词语
- topic_model.save() 保存主题模型
- topic_model.reduce_topics() 压缩主题个数(合并相近的主题)
.get_topic_info()
查看BERTopic基于cnews.csv数据, 跑出的各主题
topic_model.get_topic_info()
.find_topics(term)
查看与词语【投资】最相关的主题,返回候选的最相思的5个主题id
#
similar_topics, similarity = topic_model.find_topics("投资", top_n=5)
similar_topics
Run
[3, 9, 8, 10, 4]
.get_topic()
查看id为3的主题信息(主题词及权重)
topic_model.get_topic(3)
Run
[('基金', 0.15109221307919193),
('投资', 0.042856192509064),
('公司', 0.039785278320496976),
('市场', 0.037072163603417835),
('股票', 0.03230913401086524),
('型基金', 0.02721898070238429),
('收益', 0.025435672141638468),
('投资者', 0.024633503649868493),
('经理', 0.02458550023931051),
('发行', 0.022672639068067168)]
.visualize_topics()
可视化主题间距离
visualize_topics1 = topic_model.visualize_topics()
#可视化结果保存至html中,可以动态显示信息
visualize_topics1.write_html('visualize_topics.html')
visualize_topics1
.visualize_distribution()
显示第一条新闻的主题概率分布
first_new_topic_probs = topic_model.visualize_distribution(probs[0])
first_new_topic_probs.write_html('first_new_topic_probs.html')
first_new_topic_probs
点击查看first_new_topic_probs.html
为了理解主题的潜在层次结构,我们可以使用 scipy.cluster.hierarchy 创建聚类并可视化它们之间的关系。 这有助于合并相似主题,达到降低主题模型主题数量nr_topics。
.visualize_hierarchy(top_n_topics)
话题层次聚类可视化,模型跑出12个主题,这里就按12进行分层聚类
topic_model.visualize_hierarchy(top_n_topics=12)
.visualize_barchart(topics)
显示topics的词条形图
topic_model.visualize_barchart(topics=[1])
.visualize_heatmap(n_clusters)
话题相似热力图。BERTopic可将主题以embeddings形式(向量)表示, 因此我们可以应用余弦相似度来创建相似度矩阵。 每两两主题可进行余弦计算,最终结果将是一个矩阵,显示主题间的相似程度。
topic_similar_heatmap = topic_model.visualize_heatmap(n_clusters=11)
topic_similar_heatmap.write_html('topic_similar_heatmap.html')
topic_similar_heatmap
点击查看topic_similar_heatmap.html
通过根据每个主题表示的 c-TF-IDF 分数创建条形图来可视化主题的选定词语。 从主题之间和主题内的相对 c-TF-IDF 分数中获得见解。 此外,可以轻松地将主题表示相互比较。
.visualize_term_rank()
通过根据每个主题表示的 c-TF-IDF 分数创建条形图来可视化主题的选定词语。
从主题之间和主题内的相对 c-TF-IDF 分数中获得见解。
此外,可以轻松地将主题表示相互比较。
term_score_decline = topic_model.visualize_term_rank()
term_score_decline.write_html('term_score_decline.html')
term_score_decline
.update_topics()
更新主题模型。当您训练了一个模型并查看了代表它们的主题和单词时,您可能对表示不满意。 也许您忘记删除停用词,或者您想尝试不同的 n_gram_range。 我们可以使用函数 update_topics 使用 c-TF-IDF 的新参数更新主题表示。
使用.update_topics()更新,
topic_model.update_topics(df.content.tolist(), topics, n_gram_range=(1, 3))
topic_model得到了更新,
similar_topics, similarity = topic_model.find_topics("手机", top_n=5)
similar_topics
Run
[2, 7, 4, 1, 5]
查看话题2的信息
topic_model.get_topic(2)
Run
[('功能', 0.022132351014298786),
('采用', 0.02136925357979149),
('像素', 0.020797285140907094),
('拍摄', 0.017850841110848677),
('机身', 0.015056931248982912),
('英寸', 0.014624438184138326),
('佳能', 0.012857768505732597),
('支持', 0.012600856600766349),
('光学', 0.012462085658291079),
('相机', 0.011832978982454568)]
模型保存
# Save model
#model.save("my_model")
# Load model
#my_model = BERTopic.load("my_model")
.reduce_topics()
压缩主题数
new_topics, new_probs = topic_model.reduce_topics(docs, topics, probs, nr_topics=10)
Run
2021-10-28 12:28:01,976 - BERTopic - Reduced number of topics from 20 to 11
代码数据
总结
本文使用中文文本数据展示BERTopic部分功能,如果对英文数据感兴趣,可以前往 https://github.com/MaartenGr/BERTopic 深入学习。