为了分析热点话题背后演化的逻辑,本项目选取了掀起大范围讨论的OpenAI发布的语言训练模型“ChatGPT”作为研究对象。通过对微博、豆瓣、知乎等社交平台进行考察分析,微博以活跃用户多、讨论热度高、公众关注度广等特点成为了本小组的第一首选。因此我们决定选用微博评论来作为数据分析主要数据来源。通过爬取3月1日到3月31日“ChatGPT”每个小时的相关热点话题下的博文,包括博主名称、微博内容、博文是否含有表情、主题、图片、视频、博主的性别、粉丝数、发文数、是否获得认证、以及时间、转发量、评论量、点赞量等数据,同时,利用百度指数平台爬取了3月1日到3月31日有关“ChatGPT”的搜索指数,借助 LDA 模型完成主题挖掘,详尽分析各主题的产生及其内在原因。针对微博评论,我们通过词频分析探究chatgpt在全国整体的舆论态度;借助 SnowNLP、pandas 进行情感分析,探究人们对于chatgpt的情感倾向。
一、数据获取
本项目使用了 Python requests 爬虫库以及 Beautiful Soup 解析库。爬虫循环获取网页内容,使用 Beautiful Soup 库解析 HTML 文本获取所需内容存入excel 文件用于初步分析。爬取了包括博主、内容、是否含有表情、是否有@、是否有主题、时间、转发量、评论量、点赞数、是否包含图片、是否包含视频、博主地区、博主性别、博主粉丝数、博主发文数、博主是否认证、地区标签在内的数据。部分代码如下所示(全部代码太长啦不放了):
二、数据预处理
1.数据清洗
在进行多爬取到的微博内容进行数据分析之前,需要先进行分词操作。我选用的是基于统计的分词方法的中文分词工具包——jieba 分词。
首先,要通过观察jieba分词结果,识别出分得不好的词语,通过jieba.add_word(word, freq=None, tag=None)这个函数,加入自定义词典,确保一些词语能被准确识别:
其次,加入停用词,将一些不重要的标点符号和语气词过滤掉。
为了保证分词的质量,不仅要过滤掉停用词,只保留词性为“n(名词)”“nz(专有名词)”“vn(动名词)”的词语,且长度需要大于等于2。
这里会出现一个问题,通过add_word函数添加的自定义词典词性为x,即未知词性,在筛选的过程中会将其过滤掉,导致分词结果中没有自定义词典。所以,我定义了一个名为 custom_words的列表,将自定义词典放入列表里,通过词性过滤时判断该词是否在custom_words中,在则留下。最后输出一个excel文件,存放分词结果。
这里用到了pandas、jieba、re等库:
import re
import jieba
import jieba.posseg as psg
import pandas as pd
from openpyxl import Workbook
def chinese_word_cut(mytext):try:stopword_list = open(stop_file, encoding='utf-8')except:stopword_list = []print("error in stop_file")stop_list = []flag_list = ['n', 'nz', 'vn']for line in stopword_list:line = re.sub(u'\n|\\r', '', line) # 去除换行符stop_list.append(line)word_list = []custom_words = ['文心一言', 'openAI',’ chatgpt’]jieba.add_word('文心一言')jieba.add_word('openAI')jieba.add_word('chatgpt')# 使用jieba分词seg_list = psg.cut(mytext)for seg_word in seg_list:word = seg_word.wordfind = 0for stop_word in stop_list:if stop_word == word or len(word) < 2:find = 1breakif find == 0 and (seg_word.flag in flag_list or seg_word.word in custom_words):word_list.append(word)return " ".join(word_list)data["content_cutted"] = data.content.apply(chinese_word_cut)
output_excel_path = r'D:\桌面\python练习\lda\result\output.xlsx'
data.to_excel(output_excel_path, index=False)
2.数据集成
在此项目中,由于数据源包括微博和百度指数两部分。因此进行数据集成。最终通过处理,数据包括了博主、内容、是否含有表情、是否有@、是否有主题、时间、转发量、评论量、点赞数、总热度、是否包含图片、是否包含视频、博主地区、博主性别、博主粉丝数、博主发文数、博主是否认证、搜索指数、咨询指数、情感倾向、地区标签。集成结果如下:
三、数据分析
1.基于 LDA 的话题聚类
(1)确定主题聚类个数
经过数据预处理后,本文选用 gensim 中的类实例化 LDA 主题模型,对预处理后的文本进行分类训练,并拟定在区间[1,15]内的整数作为候选主题数,通过调用 LDA 主题模型类下的 Perplexity 方法,分别对数据进行 LDA 聚类分析,得出不同模型的对数化困惑度数值:
首先,求解困惑度,选出最合适的主题数:
plexs = []
n_max_topics = 16
for i in range(1, n_max_topics):print(i)lda = LatentDirichletAllocation(n_components=i, max_iter=50,learning_method='batch',learning_offset=50, random_state=0)lda.fit(tf)plexs.append(lda.perplexity(tf))n_t = 15 # 区间最右侧的值。注意:不能大于 n_max_topics
x = list(range(1, n_t))
plt.plot(x, plexs[1:n_t])
plt.xlabel("number of topics")
plt.ylabel("perplexity")
plt.show()
困惑度折线图如下:
该折线图显示,随着主题数的增加,总体上困惑度呈现下降的态势;困惑度的局部极小值点,出现在主题数为 10的模型选择上。主题数越多,则后续的主题分析也越为复杂。根据奥卡姆剃刀准则,本文选择 perplexity 相对小且主题数量相对较少的主题数值 T=10 作为 LDA模型训练的最优模型参数。在采用 LDA 模型进行主题求解过程中,参数选择分别为:T=10,α=0.1,β=0.01,Gibbs 抽样的迭代次数为 50 次。
(2)主题聚类
创建了一个 CountVectorizer
对象 tf_vectorizer
,用于将文本转换为特征向量。使用 tf_vectorizer
对象将文本数据 data.content_cutted
转换为特征向量矩阵 tf
。再定义主题模型的数量 n_topics
,并创建了一个 LatentDirichletAllocation
对象 lda
,用于执行主题建模。
def print_top_words(model, feature_names, n_top_words):tword = []for topic_idx, topic in enumerate(model.components_):print("Topic #%d:" % topic_idx)topic_w = " ".join([feature_names[i] for i in topic.argsort()[:-n_top_words - 1:-1]])tword.append(topic_w)print(topic_w)return twordn_features = 1000 #提取 1000 个特征词语
tf_vectorizer = CountVectorizer(strip_accents='unicode',max_features=n_features,stop_words='english',max_df=0.5,min_df=10)
tf = tf_vectorizer.fit_transform(data.content_cutted)
n_topics = 10
lda = LatentDirichletAllocation(n_components=n_topics, max_iter=50,learning_method='batch',learning_offset=50,
# doc_topic_prior=0.1,
# topic_word_prior=0.01,random_state=0)
lda.fit(tf)
n_top_words = 25
tf_feature_names = tf_vectorizer.get_feature_names_out()
topic_word = print_top_words(lda, tf_feature_names, n_top_words)topics=lda.transform(tf)
topic = []
for t in topics:topic.append(list(t).index(np.max(t)))
data['topic']=topic
data.to_excel("data_topic.xlsx",index=False)
print(topics[0])#0 1 2
结果如下所示:
筛选出无意义、重复的词语,通过整理后可得10个主题及其相关的词语如下:
主题1 | 公司 | 产品 | 服务 | 百度 | 文心一言 | 大厂 |
主题2 | 网页 | 链接 | 开源 | 对话 | 视频 | 代码 |
主题3 | 插件 | 功能 | 小说 | 工具 | 文学 | 新闻 |
主题4 | 技术 | 人工智能 | 时代 | 价值 | 解决问题 | 行业 |
主题5 | 科技 | 影响 | 生活 | 革命 | 人类 | 历史 |
主题6 | 模型 | 数据 | 算力 | 训练 | 语言 | 软件 |
主题7 | 社会 | 电商 | 数字 | 信息 | 效率 | 发展 |
主题8 | 作品 | 文章 | 内容 | 绘画 | 风格 | 创作 |
主题9 | 代码 | 论文 | 学生 | 老师 | 程序员 | 大学 |
主题10 | 机器人 | 聊天 | 监管 | 裁员 | 数据保护 | 工作岗位 |
(3)LDA 聚类结果分析
根据各个主题及其关键词,我们可以分析出各个主题涉及到的主要讨论内容如下表所示:
主题1 | 该主题可能讨论ChatGPT所属的公司以及相关的产品和服务,包括百度的文心一言情况 |
主题2 | 该主题可能讨论与ChatGPT相关的技术内容 |
主题3 | 该主题可能讨论与ChatGPT相关的插件功能,在小说、工具、文学作品和新闻中的应用等内容 |
主题4 | 该主题可能讨论技术领域中的人工智能、时代变革以及ChatGPT在解决问题和行业中的价值和应用。 |
主题5 | 该主题可能讨论ChatGPT对科技发展、生活方式、人类社会的影响 |
主题6 | 该主题可能讨论ChatGPT的模型、数据处理、算力需求、训练方法和语言处理软件等技术方面的内容。 |
主题7 | 该主题可能讨论ChatGPT在社会领域、电商、数字化、信息交流等方面的应用和发展。 |
主题8 | 该主题可能讨论与ChatGPT相关的作品、文章、内容创作、绘画风格等艺术创作方面的内容。 |
主题9 | 该主题可能讨论与ChatGPT相关的代码编程、论文书写以及ChatGPT在大学教育中的应用。 |
主题10 | 该主题可能讨论了与ChatGPT有关的负面影响如裁员与数据安全问题 |
2.SnowNLP情感分析
对于基于机器学习的情感倾向性分析,目前 Python 有许多已经封装好的优秀类库,直接调用类库即可,十分便捷,常用的中文情感类库主要有 Snownlp、bosonnlp、jiagu 等。其中 bosonnlp 库可做通用、汽车、厨具、餐饮、新闻、微博等几大类文本内容的情感分析,但其模型都已训练完成,对于某类特定的文本内容效果不佳;jiagu 与 Bosonnlp 类似,是根据已训练好贝叶斯模型进行分类,对文本情感进行分类判断,输出结果为正面或负面。鉴于不同中文情感分析的原理、适用、效果的差异,本文最终选择 Snownlp实现对微博评论文本的分析。Snownlp 同样利用贝叶斯模型进行情感倾向分析,输出结果[0,1]的一个情感值,情感值约接近 1,则表达的情绪越积极,反之则情绪越消极。利用 Snownlp 进行情感分析能够实现主观文本的客观量化以便直观展现用户对chatgpt的情感态度。
首先,导入用到的库,这里用到了numpy、pandas、re、snownlp:
import numpy as np
import pandas as pd
import re
from snownlp import SnowNLP
在导入相关的库后,遍历文本内容,使用snownlp进行情感分析,这里用的是snownlp自带的sentiments模型:
sen_data = data.iloc[:, 0] # 取出第一列即未经过分词的评论内容
sentiments = []
for text in sen_data:s = SnowNLP(text)sentiment = s.sentiments #调用snownlp自带的sentiments模型sentiments.append(sentiment)
data['sentiment'] = sentiments
data.to_excel('sentiment_analysis_result.xlsx', index=False) # 将结果保存到Excel文件中
分析完成后,可视化观察情感倾向:
y = data['sentiment']
plt.figure(figsize=(10, 4), dpi=100)
plt.plot(y)
plt.show()plt.hist(sentiments, bins=np.arange(0, 1, 0.01), facecolor='g')
plt.xlabel(' Sentiments Probability')
plt.ylabel('Quantity')
plt.title('Analysis of Sentiments')
plt.show()
可视化结果如下:
从可视化结果可以看出,积极情感(情感值接近1)与消极情感(情感值接近0)占比都较大,可以得出微博上人们对于chatgpt的消极评论与积极评论呈两极分化的状况。
但是我这里使用的是snownlp自带sentiments模型,主要用于对商品评价进行情感分析,用于微博文本分析不是很合适。
可以点开输出的excel文档来看一下刚刚输出的情感评分,能发现许多地方评分不合理。这里随便挑出几条:
其实snowNLP包里自带了训练函数,可以使用自己的数据集进行学习。我下载了github上针对微博文本的情感分析标记语料共12万条,训练出的模型替换snownlp自带的模型,再进行了一次情感分析。下载地址:https://github.com/SophonPlus/ChineseNlpCorpus
训练很简单,将消极和积极情感的文本输入,几行代码搞定:
from snownlp import sentiment
sentiment.train('neg.txt', 'pos.txt')
sentiment.save("sentiment.marshal")
然后找到snownlp的下载地址,将sentiment.marshal.3替换成刚训练出的sentiment.marshal.3就可以了。
重新进行情感分析后的输出结果,虽然还是有些小错误,看起来比上一次的结果好了不少:
将情感分析的结果可视化后:
这次的情感分析结果相较于第一次来说是相对更加正确的。从可视化结果来看,在一共近1600条微博文本中,消极的结果(情感值接近0)占了一定比例,并且极端消极的情感占了不少比重。看起来微博上的人们对于chatgpt态度不是很积极啊。
对chatgpt持消极态度的人们在讨论什么呢?我对情感分析的结果用excel筛选了一下情感值小于0.3的微博文本,使用与第一步相同的方法再进行一次话题聚类,可以总结出以下4个主题及部分关键词:
主题1 | 意大利 禁止 数据 信息 监管 |
主题2 | 工作 取代 影响 失业 技术 大学生 |
主题3 | 国内 研究 一点 百度 内容 美国 文心一言 |
主题4 | 人类 控制 工作 可怕 意识 暂停 |
将结果总结一下,可以得出人们主要关心的chatgpt引发的问题有:信息监管问题;失业问题;国内外差距;人类发展。
3.词云图
“词云”是对文本数据中出现频率较高的“关键词”予以视觉上的突出,形成“关键词云层”或“关键词渲染”,使浏览者只要扫一眼就能够明白文章主旨。对微博评论频次较高的词语通过词云表现出来,可以方便读者抓住评论的主要关键词。利用 Python 的 wordcloud 包制作出chatgpt评论词云图,自定义了词云形状以及字体,利用分词处理后的数据作为输入。
首先导入相关包,用到了numpy、pandas、jieba、wordcloud、matplotlib等:
import numpy as np
import pandas as pd
from jieba import analyse
from PIL import Image
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt
import os
接着取出现频率最高的75个词制作词云,自定义了词云形状以及字体,并保存词云结果图:
content_data = data["content_cutted"]
lyric = ' '.join(content_data)
result = analyse.textrank(lyric, topK=75, withWeight=True)
keywords = dict()
for i in result:keywords[i[0]] = i[1]
print(keywords)
image = Image.open(r'D:\桌面\python练习\chatgpt.png')
graph = np.array(image)
wc = WordCloud(font_path='./fonts/STXIHEI.TTF', background_color='white', mask=np.array(image), max_words=75)
wc.generate_from_frequencies(keywords)
image_color = ImageColorGenerator(graph)
plt.imshow(wc)
plt.imshow(wc.recolor(color_func=image_color))
plt.axis("off")
plt.show()
wc.to_file('dream.png')
结果如下:
4.基于多元线性回归的特征选择
本项目使用微博数据进行热度因素分析,客观数据包括是博文是否含有表情、主题、图片、视频、和博主的性别、粉丝数、发文数、是否获得认证以及当天的搜索指数与资讯指数等。通过对这些数据分别进行统计分析和多元线性回归,获得博文热度的影响因素体系。
在多元线性回归中,并不是所用特征越多越好;选择少量、合适的特征既可以避免过拟合,也可以增加模型解释度。在这里我们选择向后剔除法进行多元线性回归特征选择。
向后剔除法:从全变量回归方程开始,逐步删去某个变量,使指标到达最优为止。向后选择法(backwardelimination)也称向后剔除法、向后消元法,是一种回归模型的自变量选择方法,其过程与向前选择法相反:首先将全部自变量都选入模型,然后对各个自变量进行偏F检验,将最小的F值记为FL,与预先规定的显著性水平F0进行比较,若FL<F0,就剔除该变量,将余下的变量重新拟合回归模型,重复上述步骤,直到模型中所有自变量都不能剔除为止。经过处理,可以剔除对回归方程影响不显著的变量。
(1)变量选取
本探究分析实验自变量选取如下:
(2)数据处理
首先要在excel中对输入数据进行处理,因为使用python进行多元线性回归,输入的自变量与因变量都必须是数字。
(3)回归分析
在读取到数据后,要使用sm.add_constant(data1)函数,创建一个新的数据集,其中包含了原始的data1数据集以及额外的一列,该列的值始终为1。这个新的数据集用于拟合线性回归模型,并将截距项纳入考虑。
进入一个循环,遍历特征列表cols中的每个特征列:
- 在每次循环中,从data中选择对应的特征列,存储在data1变量中。
- 使用sm.add_constant函数在data1中添加一列常数项,得到扩展后的特征矩阵,存储在x变量中。
- 从data中选择因变量列,存储在y变量中。
- 使用sm.OLS创建一个最小二乘回归模型对象,将y作为因变量,x作为自变量。
- 调用.fit()方法拟合回归模型,将拟合结果存储在result变量中。
- 使用result.pvalues获取模型中各个特征的p值,并存储在pvalues变量中。
- 使用pvalues.drop('const', inplace=True)删除p值中的常数项。
- 使用max(pvalues)找到最大的p值,存储在pmax变量中。
- 如果最大的p值超过设定的显著性水平阈值limit,则进入条件语句:
- 使用pvalues.idxmax()找到最大p值对应的特征索引,存储在ind变量中。
- 使用cols.remove(ind)从特征列表cols中移除该特征。
- 如果最大的p值不超过显著性水平阈值limit,则继续下一次循环。
完成特征选择后,最终的特征列表存储在cols中,包含了经过向后剔除法筛选后的显著特征。
data = pd.read_excel(r'D:\桌面\python练习\multiple_linear_regression\lineardata.xlsx')
cols = ['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11']
limit = 0.05
for i in range(len(cols)):data1 = data[cols]x = sm.add_constant(data1)y = data['y']model = sm.OLS(y, x)result = model.fit()pvaules = result.pvaluespvaules.drop('const', inplace=True)pmax = max(pvaules)if pmax > limit:ind = pvaules.idxmax()cols.remove(ind)else:continue
print(result.summary())
(4)结果分析
打印线性回归结果如下
可以看出,R-squared(决定系数)为 0.004,表示模型对因变量的变异解释程度较低。这意味着模型中的自变量仅能解释因变量变异的很小一部分。仅有两个自变量(x7 和 x10)在统计上对因变量 y 有显著影响。其他自变量的系数估计值不显著,并且模型整体上是显著的。
综上所述,根据回归结果,我们可以得出结论,chatgpt话题下某一条微博的似乎只与博主粉丝数与百度搜索指数有关。博主粉丝数代表了博主在社交媒体上的影响力和受众数量。当博主粉丝数增加时,更多的人会看到博主的内容,可能会进行转发、评论和点赞,从而增加了总热度;百度搜索指数可以代表chatgpt在百度搜索引擎中的热度和受关注程度,百度作为国内最大的搜索引擎,百度搜索指数越高,可以反映出公众的关注度。公众的关注度越高,也引发了人们在微博上的讨论越激烈。