一、任务目标
众所周知,微博热搜几乎是许多网友的主要新闻来源,上面实时更新着当前最新的社会消息,其时效性甚至比每天晚上播出的新闻联播还要强。这篇文章,我们使用来自Kaggle的《MicroBlog-Hot-Search-Labeled》数据集,对其中的热搜词条文本进行探索性分析,不考虑源数据集中每条文本所带有的标签,通过无监督的方式挖掘一下热搜文本中所包含的有效信息。这一任务需求类似于我们工作中所碰到的各种除了文本之外没有任何其他的辅助信息的数据集,而NLP技术则给了我们能力在这样的数据中去挖掘出有价值的东西。例如在这个微博热搜词条数据中,我们可以进行热点挖掘的工作,在如此多条热搜数据中挖掘一些热点主题。
二、建模流程
1、数据集读取
首先,我们读取数据集看看数据长什么样。
python">import pandas as pddf = pd.read_csv('weibo-hot-searchlabeled/weibo-hot-search-labeled.csv')
print('数据量:', len(df))
print(df.info())
df.head()
可以看到,数据整体上没有空值,总共是6000多条词条。下面的实验我们只需要使用“热搜词条”列的所有文本数据。
2、数据可视化分析
对于一份完全未知的文本数据集,要想通过数据分析方法来深度了解它,我们可以展开以下思路的探索:(1)统计文本长度信息,如最大/最小/平均长度以及长度分布图;(2)对文本进行分词并制作词云图;(3)如果是跨语种数据集,还可以进行语种分布的可视化分析。
(1)文本长度信息
这里,我们对文本长度信息进行可视化。透过可视化结果可以看到,大部分热搜词条的长度都在8-12个汉字之间。可以预见的是,这种长度的文本还是能够挖掘出不少语义信息的,如果大量的文本长度都在2-3个字,那么这样的文本数据集几乎很难得出什么有用的结论。
python">import matplotlib.pyplot as plt
import collectionslength = [len(li) for li in text]
print('Max length:', max(length))
print('Min length:', min(length))
print('Mean length:', sum(length)/len(length))
# 对不同的长度值进行计数统计
counter = collections.Counter(length)
counter = list(counter.items())
counter.sort(key=lambda x: x[0])
# 每一个长度值
counter_length = [li[0] for li in counter]
# 每一个长度值对应的计数
counter_length_cnt = [li[1] for li in counter]
# 可视化为条形图
plt.bar(counter_length, counter_length_cnt)
plt.xlabel('text length')
plt.ylabel('text length counts')
plt.show()
(2)词云分析
虽然现在我们建模几乎都不怎么用词云图了(用得太频繁了,以至于不少人看到就觉得很low的程度。。),但是词云图对于PPT汇报、论文撰写还是有一定的参考价值的。这里我们使用jieba库对文本进行分词并使用wordcloud库构建一个词云图。
值得注意的是,热搜文本有诸多较为新颖的网络信息,例如明星姓名、网络热梗,这些词组在传统的分词词典中是不会有的。如果直接进行分词,有可能错分、漏分,例如把“杨超越”分词成“杨”和“超越”。这里有三个思路可以缓解问题:一个是通过新词发现的途径来挖掘可能的网络新词,添加到分词词典中进行分词;一个是借助大语言模型强大的文本理解能力,让它来帮我们提炼一句话中的新词;还有一个就是通过使用开源的命名实体识别模型,识别出句子中的人名、地名等信息。
然而,大语言模型分析的途径耗时较长且需要支付API费用,如果数据量很大的话,成本也会随之增大;命名实体识别模型的途径需要找到有效的、高精度的开源模型,还要一系列的环境搭建才能使用,人工成本也不低。因此,综合考虑下来,本文采用成本最低的新词发现方法(关于新词发现的概念和python实现可以参考这里)。通过新词发现,结合人工筛选(不论什么方法的新词发现,人工筛选都能确保更高的精度),最终选择出了如下词组添加到jieba的分词词典中。
python">import jiebanew_words = ['阴阳师', '携号转网', '见义勇为', '殷世航', '勤深深', '螺蛳粉', '井柏然', '字母哥', '吐鲁番', '崂山啤酒', '陶崇园', '蓬佩奥', '艾福杰尼', '可可西里', '全开麦','产业链', '哈利波特', '房地产税', '哈妮克孜', '瞿泽林', '金大喜', '股权质押', '扫黄打非', '车银优', '富兰克林', '符龙飞', '沙县小吃', '呼伦贝尔', '娱乐圈', '窦靖童', '燕云台', '海澜之家']
for word in new_words:jieba.add_word(word)
接着,我们进行词云图绘制。
python">from wordcloud import WordCloud# 使用jieba进行分词
combined_text = ' '.join(text)
words = jieba.cut(combined_text)
# 将分词结果用空格连接成字符串,需先去掉停用词
# 直接百度中文停用词,找一个看起来比较完整的来源复制到本地txt文件即可
stopwords = pd.read_csv('./data/stopwords_cn.txt', header=None)[0].tolist()
# 即便去掉了停用词,为避免出现太多单个字,还是要限定一下长度
text_after_jieba = ' '.join([li for li in words if li not in stopwords and len(li)>=2])# 创建词云对象
wordcloud = WordCloud(font_path='simhei.ttf', width=800, height=400, background_color='white').generate(text_after_jieba)# 绘制词云图
plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off') # 关闭坐标轴
plt.show()
当然,我们也可以直接统计词语出现次数然后排序打印出来。
python">combined_text = ' '.join(text)
words = jieba.cut(combined_text)
words = [li for li in words if len(li.strip())>=2]
cnt = collections.Counter(words)
cnt = list(cnt.items())
cnt.sort(key=lambda x: -x[1])
# 打印前10高频词
for word, count in cnt[:10]:print(f'词语:{word} 出现频次:{count}')
3、聚类分析
对于没有任何辅助信息的文本数据,最常用的数据探查分析方法就是聚类了。聚类是无监督的机器学习算法,通过对文本进行向量化之后采用合适的聚类算法,我们可以将数据划分到不同的类簇中,从而分别对同一类簇内的数据进行进一步的分析和观点凝练。通过聚类算法,我们可以对当前的热搜数据进行一定程度的热点挖掘。
这里,我们先对文本进行向量化。需要注意,这里的单条文本指的是完整的一条热搜词条,而非上面可视化分析过程中的词语单元。
python">from transformers import BertTokenizer, BertModel
import torch# 这是一个基础版本的bert中文模型,如果需要更强大的、在特定任务中微调过的模型,可以去HuggingFace查找
# 预训练模型同样依赖自身的词典进行分词,而在bert-base-chinese这个模型中,对中文的分词粒度是字,也就是按照字的粒度划分中文文本,有一些模型是按照词粒度进行中文分词的,这个同样可以到HF上去查找
model_name = 'bert-base-chinese'
# 初始化分词器
tokenizer = BertTokenizer.from_pretrained(model_name)
# 加载模型
model = BertModel.from_pretrained(model_name)texts = df['热搜词条'].tolist()
batch_size = 8sentence_embeddings = []
# 禁用梯度,打开预测模式
with torch.no_grad():for i in range(0, len(texts), batch_size):if i%100==0:print(f'Embedding No.{i} sample.')# 分词器生成Bert模型必要的输入格式inputs = tokenizer(texts[i:i+batch_size], padding=True, truncation=True, return_tensors="pt")# 预测输出outputs = model(**inputs)sentence_embeddings += outputs.pooler_output# 取最后一层的token平均向量
print(len(sentence_embeddings))
对于当前的文本向量数据集,本文考虑使用HDBSCAN算法进行聚类分析,这种算法不需要指定聚类簇的数目,且相比于DBSCAN算法性能更强大。
python">import numpy as np
import matplotlib.pyplot as plt
import hdbscan
from sklearn.decomposition import PCA
import numpy as npsentence_embeddings = [np.array(li) for li in sentence_embeddings]# 创建 HDBSCAN 聚类对象
clusterer = hdbscan.HDBSCAN(min_cluster_size=20, min_samples=5) # 进行聚类分析
cluster_labels = clusterer.fit_predict(sentence_embeddings)pca = PCA(n_components=2)
pca_embeddings = pca.fit_transform(sentence_embeddings)# 绘制聚类结果
plt.figure(figsize=(10, 6))
plt.scatter(pca_embeddings[:, 0], pca_embeddings[:, 1], c=cluster_labels, cmap='viridis', s=50)
plt.colorbar()
plt.title('HDBSCAN Clustering')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()
我们可以看到,HDBSCAN把绝大部分的数据样本都列为了噪声(标签为-1)。但不要慌,我们现在做的是类似于热点挖掘的工作,而这些热点往往只是由少部分的数据所反映出来的,大量的噪声是在所难免的。我们先看一下算法划分出来的有效类簇内部是什么情况。首先,把聚类标签和文本都整理一下。
python"># 将每一条热搜都归入对应的聚类类别中
text_groups = {}
for text, label in zip(texts, cluster_labels):if label not in text_groups:text_groups[label] = []text_groups[label].append(text)
打印结果我们可以看到,第0类大都是某个城市新增新冠病例的热搜。
而第1类大都是进出口物品或者食品检测出新冠病毒。
第2类则是社会事件新闻。
第3类是网络热梗。
第4类主要是游戏圈的新闻。
第5类以人名等短文本为主,语义信息较为稀少。
第6类则主要是英文字符,当中也是各种人名、典礼名称等娱乐信息。
可见,我们采用的Bert+HDBSCAN方法一定程度上挖掘出了当前热搜数据集中的一些热点主题。同时,通过采用更强大的文本表示模型,调整HDBSCAN模型参数来控制聚类效果等途径,还可以进一步优化热点主题挖掘模型的性能。
三、总结
本文使用微博热搜数据展示了对于文本数据的探索性分析以及无监督热点主题挖掘的过程。在舆论监督、评论挖掘等多个NLP任务中,通过上述的技巧和方式,都能够帮助我们有效地了解手头上的数据,并从中挖掘出对业务决策有价值的语义信息。