159
社区成员




通过课程设计的练习,加深学生对所学自然语言处理的理论知识与操作技能的理解和掌握,使得学生能综合运用所学理论知识和操作技能进行实际工程项目的设计开发,让学生真正体会到自然语言处理算法在实际工程项目中的具体应用方法,为今后能够独立或协助工程师进行人工智能产品的开发设计工作奠定基础。通过综合应用项目的实施,培养学生团队协作沟通能力,培养学生运用现代工具分析和解决复杂工程问题的能力;引导学生深刻理解并自觉实践职业精神和职业规范;培养学生遵纪守法、爱岗敬业、诚实守信、开拓创新的职业品格和行为习惯。
2.1 实验仪器及设备
2.2 设计要求
课程设计的主要环节包括课程设计作品和课程设计报告的撰写。课程设计作品的完成主要包含方案设计、计算机编程实现、作品测试几个方面。课程设计报告主要是将课程设计的理论设计内容、实现的过程及测试结果进行全面的总结,把实践内容上升到理论高度。
3.设计内容
根据选择的实验方向,我将实验设计定为中文情感分析,实验的数据集选择使用京东商城里爬取的热水器评价,评论会相对反馈发送者的情感与状态,对于不同的人对商品会有不同的情感,为此对于一个商品的情感变化进行分析与讨论。采用jieba,requests,re,string等库进行中文情感分析。同时还采用word cold时刻体现每一时间段的关键词与词云表达消费者当时的观点与看法。
4.设计过程
对于总的设计内容已经有了一个大概的思路与想法,以下流程图方便后面对于代码的编写和理解。
导入所需库。
定义一个crawling函数,确定要获取评论的京东商品页面链接,将链接存储在两个列表中,分别对应着好评和差评。
使用pandas库中的DataFrame来存储和处理数据。因为爬取得到的数据比较杂乱,所以先将其转换成json格式,并抽取一些有用的信息,如商品名称、用户昵称、评论时间以及评论内容。最后将这些有用的信息添加到DataFrame中去。
使用time库来使程序等待一段时间,以免对服务器造成过大压力,也可以避免被封IP的影响。
将好评和差评的DataFrame组合成一个DataFrame,最后以CSV格式存储在本地硬盘上。
这是一个简单的Tkinter GUI界面,包含了一个“开始爬取”和一个“关闭”按钮。当点击“开始爬取”时,会执行crawling函数。当点击“关闭”时,会弹出一个提示框,确定要退出整个进程,如果点击“是”,则关闭窗口,否则什么也不做。
通过pd.read_csv()读取之前保存的CSV文件来获得所有评论数据。
使用duplicated()方法来查找重复的评论,根据’content’和’content_type’两列来判定。
通过drop_duplicates()方法将所有重复的评论去掉,得到不含有重复评论的DataFrame。
调用reset_index()方法来重新设置DataFrame中评论索引,得到最终的数据可用于后续分析、可视化等处理
这段代码是在评论文本中剔除掉不需要的内容,如字母、数字、品牌名称和网店名称
定义一个lambda函数worker,这个函数将一条评论字符串作为输入,返回该评论字符串的分词结果。其中,worker函数调用了jieba的精确模式,即psg.cut()方法来进行分词,将分词的结果组成一个由词语和词性对(word, flag)构成的列表。
使用apply()方法对每条评论的文本进行分词,在得到单个评论文本结果后,将其结果插入到一个名为seg_word的list中。
这段代码是将每个分完词的单元格拆分成多个行,同时将每行的字词、词性和内容类型合并到一个DataFrame中,用于进一步分析或保存。
这段代码是将中文文本中一些无用的常用单词(如’的’,‘是’等)或出现频率过高的单词(如’电热水器’)剔除,不考虑这些词汇对文本情感的影响。
生成一个词云,用于直观的展示文本中出现频率较高的单词。
这段代码用于对分词后的数据进行情感分析,得到每个词汇的情感极性,并结合之前的分词结果,生成一个新的DataFrame,用于情感分析的可视化和进一步分析。
这段代码是对之前的情感分析结果进行修正,包括对含有‘不’字的单词进行权重减小的处理,并生成修正后的结果,也就是经过处理后的only_inclination数据集。
通过对only_inclination数据集进行聚合,计算每个语料对象的情感得分,并根据得分向量得出情感结果(正向/负向/中性)。最终生成的result数据集是包含数量和情感识别标签的数据集。
计算情感分析模型的评价指标,包括精度、准确率、召回率、F1值等,并打印输出结果。
绘制情感分析结果中正向评论和负向评论的词云图
使用gensim模块对正向评论(posdata)和负向评论(negdata)数据集进行预处理,生成词典和语料库(corpus)对象。
这段代码定义了两个函数,cos()和lda_k(),分别用于计算两个向量的余弦相似度和确定LDA主题模型中的主题个数。
这段代码利用tkinter和matplotlib模块,为情感评论数据集中的正向评论和负向评论绘制LDA模型主题数寻优图像,并在GUI界面中展示出来。
使用gensim模块的LdaModel()函数对正向评论(posdata)和负向评论(negdata)数据集分别进行主题建模,输出每个主题中权重最高的前10个单词。
设计总结
上述代码主要是为了在情感分析领域应用主题模型LDA。LDA模型是一种常用的话题挖掘方法,可以通过对文本进行主题建模和分类,帮助我们更好地理解文本数据。在情感分析中,使用LDA模型可以有效地提取主题词,并分析它们在文本中的使用情况,从而更好地了解情感倾向。
具体而言,我们爬取了正面评论和负面评论,绘制了正面评论和负面评论LDA模型主题数寻优图像,并使用tkinter模块展示出来。
总之,LDA模型是一种可靠的主题建模技术,尤其对于情感分析这类文本数据来说,它更加具有解释能力和可行性。可以通过主题模型,挖掘文本中的热门话题、明确情感倾向,进而深入理解文本数据的内在意义。此外,该代码还具有可视化功能,可以将结果以图形的形式展现出来,便于我们更直观地理解分析结果。
代码:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import re
import jieba.posseg as psg
import urllib.request
import json
import time
import random
import tkinter as tk
from PIL import ImageTk, Image
from matplotlib.backends.backend_tkagg import FigureCanvasTkAggdef
import warnings
warnings.filterwarnings("ignore")
def crawling():
n = 100
pos_url = []
neg_url = []
for i in range(0, n): pos_url.append('https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv80998&productId=1106432&score=3&sortType=5&page=' + str(i) + '&pageSize=10&isShadowSku=0&rid=0&fold=1') neg_url.append('https://club.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv80998&productId=1106432&score=1&sortType=5&page=' + str(i) + '&pageSize=10&isShadowSku=0&rid=0&fold=1')
pos_content = pd.DataFrame()
neg_content = pd.DataFrame()
for i in range(0, n):
print(" 正在获取好评第 {} 页评论数据 !".format(i+1))
pos_html = urllib.request.urlopen(pos_url[i]).read().decode('gbk')
pos_jsondata = pos_html[27:-2]
pos_data = json.loads(pos_jsondata)
referenceName = [x['referenceName'] for x in pos_data['comments']]
nickname = [x['nickname'] for x in pos_data['comments']]
creationTime = [x['creationTime'] for x in pos_data['comments']]
content = [x['content'] for x in pos_data['comments']]
pos_content = pos_content.append(pd.DataFrame({'referenceName':referenceName,'nickname':nickname,'creationTime':creationTime,'content':content}))
time.sleep(random.randint(2,3))
print(" 正在获取差评第 {} 页评论数据 !".format(i+1))
neg_html = urllib.request.urlopen(neg_url[i]).read().decode('gbk')
neg_jsondata = neg_html[27:-2]
neg_data = json.loads(neg_jsondata)
referenceName = [x['referenceName'] for x in neg_data['comments']]
nickname = [x['nickname'] for x in neg_data['comments']]
creationTime = [x['creationTime'] for x in neg_data['comments']]
content = [x['content'] for x in neg_data['comments']]
neg_content = neg_content.append(pd.DataFrame({'referenceName':referenceName,'nickname':nickname,'creationTime':creationTime,'content':content}))
time.sleep(random.randint(2,3))
pos_content['content_type'] = 'pos'
neg_content['content_type'] = 'neg'
reviews = pos_content.append(neg_content)
reviews.to_csv("data/reviews.csv", index = False, encoding = 'utf-8')
window = tk.Tk()
window.title("网络爬取")
Label = tk.Label(window, text="欢迎使用该功能", font=("微软雅黑", 12))
Label.pack(pady=10)
b1 = tk.Button(window, text="开始爬取",command=crawling)
b1.pack(padx=70,pady=10)
import sys
from tkinter import messagebox
def callbackClose():
messagebox.showwarning(title='警告', message='你确定要结束整个进程吗?')
window.destroy()
b2 = tk.Button(window, text="关闭", command=callbackClose)
b2.pack(before=b1,side='right',padx=70,pady=10)
window.protocol("WM_DELETE_WINDOW", callbackClose)
window.mainloop()
reviews = pd.read_csv('data/reviews.csv')
reviews[['content', 'content_type']].duplicated().sum()
reviews = reviews[['content', 'content_type']].drop_duplicates()
reviews.reset_index(drop=True,inplace=True)
content = reviews['content']
pattern = re.compile('[a-zA-Z0-9]|京东|美的|电热水器|热水器|京东商城|')
content = content.apply(lambda x : pattern.sub('',x))
worker = lambda s : [[x.word,x.flag] for x in psg.cut(s)]
seg_word = content.apply(worker)
seg_word
n_word = seg_word.apply(lambda x: len(x))
n_content = [[x+1]*y for x,y in zip(list(seg_word.index), list(n_word))]
index_content = sum(n_content, [])
seg_word = sum(seg_word,[])
word = [x[0] for x in seg_word]
nature = [x[1] for x in seg_word]
content_type = [[x]*y for x,y in zip(list(reviews['content_type']),list(n_word))]
content_type = sum(content_type,[])
result = pd.DataFrame({'index_content': index_content,
'word' : word,
'nature': nature,
'content_type' : content_type})
result = result[result['nature'] != 'x']
stop_path = open('data/stoplist.txt','r',encoding='utf-8')
stop = [x.replace('\n','') for x in stop_path.readlines()]
word = list(set(word) - set(stop))
result = result[result['word'].isin(word)]
n_word = list(result.groupby(by=['index_content'])['index_content'].count())
index_word = [list(np.arange(0,x)) for x in n_word]
index_word = sum(index_word,[])
result['index_word'] = index_word
result.reset_index(drop=True,inplace=True)
ind = result[[x == 'n' for x in result['nature']]]['index_content'].unique()
result = result[result['index_content'].isin(ind)]
result.reset_index(drop=True,inplace=True)
from wordcloud import WordCloud
import matplotlib.pyplot as plt
frequencies = result.groupby(by = ['word'])['word'].count()
frequencies = frequencies.sort_values(ascending = False)
backgroud_Image=plt.imread('data/pl.jpg')
wordcloud = WordCloud(font_path="C:\Windows\Fonts\STZHONGS.ttf",
max_words=200,
background_color='white',
mask=backgroud_Image)
my_wordcloud = wordcloud.fit_words(frequencies)
plt.imshow(my_wordcloud)
plt.axis('off')
plt.show()
result.to_csv("word.csv", index = False, encoding = 'utf-8')
word = pd.read_csv('word.csv',header=0)
pos_comment = pd.read_csv("data/正面评价词语(中文).txt", header=None,sep="/n",
encoding = 'utf-8', engine='python')
neg_comment = pd.read_csv("data/负面评价词语(中文).txt", header=None,sep="/n",
encoding = 'utf-8', engine='python')
pos_emotion = pd.read_csv("data/正面情感词语(中文).txt", header=None,sep="/n",
encoding = 'utf-8', engine='python')
neg_emotion = pd.read_csv("data/负面情感词语(中文).txt", header=None,sep="/n",
encoding = 'utf-8', engine='python')
positive = set(pos_comment.iloc[:,0])|set(pos_emotion.iloc[:,0])
negative = set(neg_comment.iloc[:,0])|set(neg_emotion.iloc[:,0])
intersection = positive & negative
positive = list(positive - intersection)
negative = list(negative - intersection)
positive = pd.DataFrame({"word":positive,
"weight":[1]*len(positive)})
negative = pd.DataFrame({"word":negative,
"weight":[-1]*len(negative)})
posneg = positive.append(negative)
data_posneg = pd.merge(left=word,right=posneg,on='word',how='left')
data_posneg = data_posneg.sort_values(by = ['index_content','index_word'])
notdict = pd.read_csv("data/not.csv")
data_posneg['amend_weight'] = data_posneg['weight']
data_posneg['id'] = np.arange(0, len(data_posneg))
only_inclination = data_posneg.dropna()
only_inclination.index = np.arange(0, len(only_inclination))
only_inclination
index = only_inclination['id']
for i in np.arange(0, len(only_inclination)):
review = data_posneg[data_posneg['index_content'] == only_inclination['index_content'][i]]
review.index = np.arange(0, len(review))
affective = only_inclination['index_word'][i]
if affective == 1:
ne = sum([i in notdict['term'] for i in review['word'][affective - 1]])
if ne == 1:
data_posneg['amend_weight'][index[i]] = -data_posneg['weight'][index[i]]
elif affective > 1:
ne = sum([i in notdict['term'] for i in review['word'][[affective - 1, affective - 2]]])
if ne == 1:
data_posneg['amend_weight'][index[i]] = -data_posneg['weight'][index[i]]
only_inclination.isnull().sum()
emotional_value = only_inclination.groupby(['index_content'],as_index=False)['amend_weight'].sum()
emotional_value = emotional_value[emotional_value['amend_weight'] != 0]
emotional_value.reset_index(drop=True,inplace=True)
emotional_value
emotional_value['a_type'] = ''
emotional_value['a_type'][emotional_value['amend_weight'] > 0] = 'pos'
emotional_value['a_type'][emotional_value['amend_weight'] < 0] = 'neg'
result = pd.merge(left=word,right=emotional_value,on='index_content',how='right')
result
result = result[['index_content','content_type', 'a_type']].drop_duplicates()
confusion_matrix = pd.crosstab(result['content_type'],result['a_type'],margins=True)
Accuracy=(confusion_matrix.iloc[0,0] + confusion_matrix.iloc[1,1])/confusion_matrix.iloc[2,2]
print(Accuracy)
Precision=confusion_matrix.iloc[0,0] /confusion_matrix.iloc[2,0]
print(Precision)
Recall=confusion_matrix.iloc[0,0] /confusion_matrix.iloc[0,2]
print(Recall)
F1 = 2*(Precision*Recall)/(Precision+Recall)
print(F1)
ind_pos = list(emotional_value[emotional_value['a_type'] == 'pos']['index_content'])
ind_neg = list(emotional_value[emotional_value['a_type'] == 'neg']['index_content'])
posdata = word[[i in ind_pos for i in word['index_content']]]
negdata = word[[i in ind_neg for i in word['index_content']]]
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
window = tk.Tk()
window.title("词云图")
freq_pos = posdata.groupby(by=['word'])['word'].count()
freq_pos = freq_pos.sort_values(ascending=False)
background_image = plt.imread('data/plg.jpg')
wordcloud = WordCloud(
font_path="C:/Windows/Fonts/STZHONGS.ttf",
max_words=100,
background_color='white',
mask=background_image)
pos_wordcloud = wordcloud.fit_words(freq_pos)
freq_neg = negdata.groupby(by=['word'])['word'].count()
freq_neg = freq_neg.sort_values(ascending=False)
neg_wordcloud = wordcloud.fit_words(freq_neg)
img_label = tk.Label(window)
img_label.pack()
fig, axs = plt.subplots(1, 2, figsize=(10, 5))
axs[0].imshow(pos_wordcloud)
axs[0].axis('off')
axs[0].set_title('正面评论词云图')
axs[1].imshow(neg_wordcloud)
axs[1].axis('off')
axs[1].set_title('负面评论词云图')
plt.subplots_adjust(wspace=0.2)
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=5)
window.mainloop()
posdata.to_csv("posdata.csv", index = False, encoding = 'utf-8')
negdata.to_csv("negdata.csv", index = False, encoding = 'utf-8')
import pandas as pd
import numpy as np
import re
import itertools
import matplotlib.pyplot as plt
posdata = pd.read_csv("posdata.csv", encoding = 'utf-8')
negdata = pd.read_csv("negdata.csv", encoding = 'utf-8')
from gensim import corpora, models
pos_dict = corpora.Dictionary([[i] for i in posdata['word']])
neg_dict = corpora.Dictionary([[i] for i in negdata['word']])
pos_corpus = [pos_dict.doc2bow(j) for j in [[i] for i in posdata['word']]]
neg_corpus = [neg_dict.doc2bow(j) for j in [[i] for i in negdata['word']]]
def cos(vector1, vector2):
dot_product = 0.0
normA = 0.0
normB = 0.0
for a, b in zip(vector1, vector2):
dot_product += a * b
normA += a ** 2
normB += b ** 2
if normA == 0.0 or normB == 0.0:
return (None)
else:
return (dot_product / ((normA * normB) ** 0.5))
def lda_k(x_corpus, x_dict):
mean_similarity = []
mean_similarity.append(1)
for i in np.arange(2, 11):
lda = models.LdaModel(x_corpus, num_topics=i, id2word=x_dict)
for j in np.arange(i):
term = lda.show_topics(num_words=50)
top_word = []
for k in np.arange(i):
top_word.append([''.join(re.findall('"(.*)"', i)) for i in term[k][1].split('+')])
word = sum(top_word, [])
unique_word = set(word)
mat = []
for j in np.arange(i):
top_w = top_word[j]
mat.append(tuple([top_w.count(k) for k in unique_word]))
p = list(itertools.permutations(list(np.arange(i)), 2))
l = len(p)
top_similarity = [0]
for w in np.arange(l):
vector1 = mat[p[w][0]]
vector2 = mat[p[w][1]]
top_similarity.append(cos(vector1, vector2))
mean_similarity.append(sum(top_similarity) / l)
return (mean_similarity)
pos_k = lda_k(pos_corpus, pos_dict)
neg_k = lda_k(neg_corpus, neg_dict)
print('正面评论主题的平均相似度',pos_k)
print('负面评论主题的平均相似度',neg_k)
import tkinter as tk
import matplotlib.pyplot as plt
window = tk.Tk()
window.title("LDA模型图像")
pos_k = lda_k(pos_corpus, pos_dict)
neg_k = lda_k(neg_corpus, neg_dict)
fig = plt.figure(figsize=(10,8))
ax1 = fig.add_subplot(211)
ax1.plot(pos_k)
ax1.set_xlabel('正面评论LDA主题数寻优',fontsize=14)
ax2 = fig.add_subplot(212)
ax2.plot(neg_k)
ax2.set_xlabel('负面评论LDA主题数寻优', fontsize=14)
img_label = tk.Label(text='正面评论与负面评论LDA模型图像')
img_label.pack()
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
window.mainloop()
pos_lda = models.LdaModel(pos_corpus, num_topics = 3, id2word = pos_dict)
neg_lda = models.LdaModel(neg_corpus, num_topics = 4, id2word = neg_dict)
pos_lda.print_topics(num_words = 10)
neg_lda.print_topics(num_words = 10)