赞
踩
情感分析是通过计算技术对文本内容的主观客观性、情绪等挖掘分析,对文本的情感偏向做出判断。目的是识别出文本中的具体情感分类,之前做文本分类都是通过深度学习或者机器学习进行文本分类,但是需要进行数据标注,对于上述其中情感的分类,有相应的情感词典,想通过情感词典的方式进行七分类情感分析。
中文情感词汇本体库是大连理工大学信息检索研究室在林鸿飞教授的指导下经过全体教研室成员的努力整理和标注的一个中文本体资源。该资源从不同角度描述一个中文词汇或者短语,包括词语词性种类、情感类别、情感强度及极性等信息。
中文情感词汇本体的情感分类体系是在国外比较有影响的Ekman的6大类情感分类体系的基础上构建的。在Ekman的基础上,词汇本体加入情感类别“好”对褒义情感进行了更细致的划分。最终词汇本体中的情感共分为7大类21小类。如果需要的话可以自行去下载。http://ir.dlut.edu.cn/EmotionOntologyDownload,需要需要相关的信息才可以哦。也可以通过其他方式去下载。
由于这里的七大类和我要做的七类有点区别只是应用了其中的六类。
文本情感分类(一):传统模型中提供了一个情感极性词典的下载包,包中带了一个否定词的txt。
https://kexue.fm/usr/uploads/2017/09/1922797046.zip
不大 不丁点儿 不甚 不怎么 聊 没怎么 不可以 怎么不 几乎不 从来不 从不 不用 不曾 不该 不必 不会 不好 不能 很少 极少 没有 不是 难以 放下 扼杀 终止 停止 放弃 反对 缺乏 缺少 不 甭 勿 别 未 反 没 否 木有 非 无 请勿 无须 并非 毫无 决不 休想 永不 不要 未尝 未曾 毋 莫 从未 从未有过 尚未 一无 并未 尚无 从没 绝非 远非 切莫 绝不 毫不 禁止 忌 拒绝 杜绝 弗
程度副词可以使用从这里《知网》情感分析用词语集(beta版)下载,里面包含程度级别的词语,但是里面的格式是汇总的形式,需要转换为词典的形式,这里做了一个简单的程度副词标记,大于1,表示情感加强,小于1,表示情感弱化,下面主要按照极其1.8,超1.6,很1.5,较1,稍0.7,欠0.5进行了一个简单的标记,如下所示。也可以根据自己的需求及及进行修改。
- 百分之百,1.8
- 倍加,1.8
- 备至,1.8
- 不得了,1.8
- 不堪,1.8
- 不可开交,1.8
- 不亦乐乎,1.8
- 不折不扣,1.8
- 彻头彻尾,1.8
- 充分,1.8
- 到头,1.8
- 地地道道,1.8
- 非常,1.8
- 极,1.8
- 极度,1.8
- 极端,1.8
- 极其,1.8
- 极为,1.8
- 截然,1.8
- 尽,1.8
- 惊人地,1.8
- 绝,1.8
- 绝顶,1.8
- 绝对,1.8
- 绝对化,1.8
- 刻骨,1.8
- 酷,1.8
- 满,1.8
- 满贯,1.8
- 满心,1.8
- 莫大,1.8
- 奇,1.8
- 入骨,1.8
- 甚为,1.8
- 十二分,1.8
- 十分,1.8
- 十足,1.8
- 死,1.8
- 滔天,1.8
- 痛,1.8
- 透,1.8
- 完全,1.8
- 完完全全,1.8
- 万,1.8
- 万般,1.8
- 万分,1.8
- 万万,1.8
- 无比,1.8
- 无度,1.8
- 无可估量,1.8
- 无以复加,1.8
- 无以伦比,1.8
- 要命,1.8
- 要死,1.8
- 已极,1.8
- 已甚,1.8
- 异常,1.8
- 逾常,1.8
- 贼,1.8
- 之极,1.8
- 之至,1.8
- 至极,1.8
- 卓绝,1.8
- 最为,1.8
- 佼佼,1.8
- 郅,1.8
- 綦,1.8
- 齁,1.8
- 最,1.8
- 不过,1.5
- 不少,1.5
- 不胜,1.5
- 惨,1.5
- 沉,1.5
- 沉沉,1.5
- 出奇,1.5
- 大为,1.5
- 多,1.5
- 多多,1.5
- 多加,1.5
- 多么,1.5
- 分外,1.5
- 格外,1.5
- 够瞧的,1.5
- 够戗,1.5
- 好,1.5
- 好不,1.5
- 何等,1.5
- 很,1.5
- 很是,1.5
- 坏,1.5
- 可,1.5
- 老,1.5
- 老大,1.5
- 良,1.5
- 颇,1.5
- 颇为,1.5
- 甚,1.5
- 实在,1.5
- 太,1.5
- 太甚,1.5
- 特,1.5
- 特别,1.5
- 尤,1.5
- 尤其,1.5
- 尤为,1.5
- 尤以,1.5
- 远,1.5
- 着实,1.5
- 曷,1.5
- 碜,1.5
- 大不了,0.8
- 多,0.8
- 更,0.8
- 更加,0.8
- 更进一步,0.8
- 更为,0.8
- 还,0.8
- 还要,0.8
- 较,0.8
- 较比,0.8
- 较为,0.8
- 进一步,0.8
- 那般,0.8
- 那么,0.8
- 那样,0.8
- 强,0.8
- 如斯,0.8
- 益,0.8
- 益发,0.8
- 尤甚,0.8
- 逾,0.8
- 愈,0.8
- 愈 ... 愈,0.8
- 愈发,0.8
- 愈加,0.8
- 愈来愈,0.8
- 愈益,0.8
- 远远,0.8
- 越 ... 越,0.8
- 越发,0.8
- 越加,0.8
- 越来越,0.8
- 越是,0.8
- 这般,0.8
- 这样,0.8
- 足,0.8
- 足足,0.8
- 点点滴滴,0.7
- 多多少少,0.7
- 怪,0.7
- 好生,0.7
- 还,0.7
- 或多或少,0.7
- 略,0.7
- 略加,0.7
- 略略,0.7
- 略微,0.7
- 略为,0.7
- 蛮,0.7
- 稍,0.7
- 稍稍,0.7
- 稍微,0.7
- 稍为,0.7
- 稍许,0.7
- 挺,0.7
- 未免,0.7
- 相当,0.7
- 些,0.7
- 些微,0.7
- 些小,0.7
- 一点,0.7
- 一点儿,0.7
- 一些,0.7
- 有点,0.7
- 有点儿,0.7
- 有些,0.7
- 半点,0.5
- 不大,0.5
- 不丁点儿,0.5
- 不甚,0.5
- 不怎么,0.5
- 聊,0.5
- 没怎么,0.5
- 轻度,0.5
- 弱,0.5
- 丝毫,0.5
- 微,0.5
- 相对,0.5
- 不为过,1.6
- 超,1.6
- 超额,1.6
- 超外差,1.6
- 超微结构,1.6
- 超物质,1.6
- 出头,1.6
- 多,1.6
- 浮,1.6
- 过,1.6
- 过度,1.6
- 过分,1.6
- 过火,1.6
- 过劲,1.6
- 过了头,1.6
- 过猛,1.6
- 过热,1.6
- 过甚,1.6
- 过头,1.6
- 过于,1.6
- 过逾,1.6
- 何止,1.6
- 何啻,1.6
- 开外,1.6
- 苦,1.6
- 老,1.6
- 偏,1.6
- 强,1.6
- 溢,1.6
- 忒,1.6

停用词词典有很多,可以使用科院计算所中文自然语言处理开放平台发布了有1208个停用词的中文停用词表,也可以使用其他的停用词词表,但是要注意一下需要将否定词或者是程度副词的词典过滤掉,不然否定词在去除停用词的时候都过滤掉了,就缺少了一些程度副词或者否定词。如果需要过滤的话可以使用下面的方面过滤。
直接使用jieba分词工具进行分词
在jieba分词之后过滤掉停用词
- def seg_word(sentence):
- """使用jieba对文档分词"""
- seg_list = jieba.cut(sentence)
- seg_result = []
- for w in seg_list:
- seg_result.append(w)
- # 读取停用词文件
- stopwords = set()
- fr = codecs.open('data/stopwords.txt', 'r', 'utf-8')
- for word in fr:
- stopwords.add(word.strip())
- fr.close()
- # 去除停用词
- return list(filter(lambda x: x not in stopwords, seg_result))
词汇本体中的情绪共分为7种: “好、乐、哀、怒、惧、恶、惊”, 共含有情绪词27 466个, 情感强度分为: 1, 3, 5, 7, 9这5档, 9表示强度最大, 1为强度最小。该资源从不同角度描述一个中文词汇或者短语, 包括词语词性种类、情感类别、情感强度及极性等信息。每个词在每一类情感下都对应一个极性。其中, 0代表中性, 1代表褒义, 2代表贬义, 3代表兼有褒贬两性。为了根据词汇的情感强度值计算微博的情感强度, 本文将褒义极性值不变, 贬义极性值取-1。并且根据情感强度和情感极性计算了每个词的情感值。这里采用最简单的方式计算了每个词的情感值,W = 强度值 * 极性值,(这里没有考虑辅助情感类别),有兴趣的可以将辅助情感类别也加进来。
由于词汇本题中的词汇是21个类别,需要根据他们的小类别将其汇总到大类别中,并且这里只应用了6个类别,好这个类别的词汇并没有使用。需要的可以自行去添加。
- def get_label_data(path):
- """
- 根据情感词汇本体返回相应的标签
- """
- sem_data = pd.read_excel(path)
- for i in range(sem_data.shape[0]):
- score = 0
- if sem_data.iloc[i,6] == 2:
- sem_data.iat[i,6] = -1
- if sem_data.iloc[i,9] == 2:
- sem_data.iat[i,9] = -1
- # 是否需要辅助情感分类,目前先不要啦
- # if sem_data.iloc[i,8] >= 0:
- # score += sem_data.iloc[i, 8] * sem_data.iloc[i, 9]
- # print(score)
- # 增加每个词语的情感强度值
- score += sem_data.iloc[i,5] * sem_data.iloc[i,6]
- sem_data.iat[i, -1] = score
-
- # 定义情感字典
- match_dict = {
- "joy":["PA","PE"],
- "surprise":["PC"],
- "anger":["NA"],
- "sadness":["NB", "NJ", "NH", "PF"],
- "fear":["NI", "NC", "NG"],
- "disgust":["ND", "NE", "NN", "NK", "NL"],
- }
- # 获取情感类别
- label_dict = {}
- keys = match_dict.keys()
- for k in keys:
- word_dict = {}
- for i in range(sem_data.shape[0]):
- label = sem_data.iloc[i, 4]
- if label in match_dict[k]:
- word_dict[sem_data.iloc[i, 0]] = sem_data.iloc[i, -1]
- label_dict[k] = word_dict
- return label_dict
-
- path = "data/情感词汇本体.xlsx"
- label_dict_7_class = get_label_data(path)

3.1 找出文本中的情感词,否定词和程度副词
- def classify_words(word_dict):
- """词语分类,找出情感词、否定词、程度副词"""
- # 读取情感字典文件
- sen_file = open('data/BosonNLP_sentiment_score.txt', 'r+', encoding='utf-8')
- # 获取字典文件内容
- sen_list = sen_file.readlines()
- # 创建情感字典
- sen_dict = defaultdict()
- # 读取字典文件每一行内容,将其转换为字典对象,key为情感词,value为对应的分值
- for s in sen_list:
- # 每一行内容根据空格分割,索引0是情感词,索引01是情感分值
- sen_dict[s.split(' ')[0]] = s.split(' ')[1]
-
- # 读取否定词文件
- not_word_file = open('data/notDict.txt', 'r+', encoding='utf-8')
- # 由于否定词只有词,没有分值,使用list即可
- not_word_list = not_word_file.readlines()
-
- # 读取程度副词文件
- degree_file = open('data/degree.txt', 'r+', encoding='utf-8')
- degree_list = degree_file.readlines()
- degree_dic = defaultdict()
- # 程度副词与情感词处理方式一样,转为程度副词字典对象,key为程度副词,value为对应的程度值
- for d in degree_list:
- # print(d)
- degree_dic[d.split(',')[0]] = d.split(',')[1]
-
- # 分类结果,词语的index作为key,词语的分值作为value,否定词分值设为-1
- sen_word = dict()
- not_word = dict()
- degree_word = dict()
-
- # 分类
- for word in word_dict.keys():
- if word in sen_dict.keys() and word not in not_word_list and word not in degree_dic.keys():
- # 找出分词结果中在情感字典中的词
- sen_word[word_dict[word]] = sen_dict[word]
- elif word in not_word_list and word not in degree_dic.keys():
- # 分词结果中在否定词列表中的词
- not_word[word_dict[word]] = -1
- elif word in degree_dic.keys():
- # 分词结果中在程度副词中的词
- degree_word[word_dict[word]] = degree_dic[word]
- sen_file.close()
- degree_file.close()
- not_word_file.close()
- # 将分类结果返回
- return sen_word, not_word, degree_word

规则:分数采用情感词的分数和计算
遍历所有的情感词,查看当前情感词的前面是否有否定词和程度副词,如果没有否定词,就对当前情感词乘以1,如果有否定词或者有多个否定词,可以乘以(-1)^否定词的个数;如果有程度副词,就在当前情感词前面乘以程度副词的程度等级。
伪代码如下:
- finalSentiScore = (-1) ^ (num of notWords) * degreeNum * sentiScore
- finalScore = sum(finalSentiScore)
- def socre_sentiment(sen_word, not_word, degree_word, seg_result):
- """计算得分"""
- # 权重初始化为1
- W = 1
- score = 0
- # 情感词下标初始化
- sentiment_index = -1
- # 情感词的位置下标集合
- sentiment_index_list = list(sen_word.keys())
- # 遍历分词结果(遍历分词结果是为了定位两个情感词之间的程度副词和否定词)
- for i in range(0, len(seg_result)):
- # 如果是情感词(根据下标是否在情感词分类结果中判断)
- if i in sen_word.keys():
- # 权重*情感词得分
- score += W * float(sen_word[i])
- # 情感词下标加1,获取下一个情感词的位置
- sentiment_index += 1
- if sentiment_index < len(sentiment_index_list) - 1:
- # 判断当前的情感词与下一个情感词之间是否有程度副词或否定词
- for j in range(sentiment_index_list[sentiment_index], sentiment_index_list[sentiment_index + 1]):
- # 更新权重,如果有否定词,取反
- if j in not_word.keys():
- W *= -1
- elif j in degree_word.keys():
- # 更新权重,如果有程度副词,分值乘以程度副词的程度分值
- W *= float(degree_word[j])
- # 定位到下一个情感词
- if sentiment_index < len(sentiment_index_list) - 1:
- i = sentiment_index_list[sentiment_index + 1]
- return score

- def setiment_score(sententce):
- # 1.对文档分词
- seg_list = seg_word(sententce)
- # 2.将分词结果列表转为dic,然后找出情感词、否定词、程度副词
- sen_word, not_word, degree_word = classify_words(list_to_dict(seg_list))
- # 3.计算得分
- score = socre_sentiment(sen_word, not_word, degree_word, seg_list)
- return score
这里简单的说一下,预测值采用累加和的形式,最后哪一个类别的分值越大就预测为哪个类别。其中会有负数的情况,将其求绝对值之后再进行比较。
- def mul_classifier_7_class(content, label_dict):
- "根据新闻返回预测标签"
- scores = {}
- for k, v in label_dict.items():
- score = setiment_score_7_class(content, label_dict[k])
- scores[k] = score
- score_max = scores[max(scores, key=lambda x:abs(scores[x]))]
-
- if score_max == 0:
- return "unknow", 0
- else:
- return max(scores, key=lambda x:abs(scores[x])), scores[max(scores, key=lambda x:abs(scores[x]))]
下面附上完整代码:
- import pandas as pd
- from collections import defaultdict
- import os
- import re
- import jieba
- import codecs
- import pandas as pd
-
- def get_label_data(path):
- """
- 根据情感词汇本体返回相应的标签
- """
- sem_data = pd.read_excel(path)
- for i in range(sem_data.shape[0]):
- score = 0
- if sem_data.iloc[i,6] == 2:
- sem_data.iat[i,6] = -1
- if sem_data.iloc[i,9] == 2:
- sem_data.iat[i,9] = -1
- # 是否需要辅助情感分类,目前先不要啦
- # if sem_data.iloc[i,8] >= 0:
- # score += sem_data.iloc[i, 8] * sem_data.iloc[i, 9]
- # print(score)
- # 增加每个词语的情感强度值
- score += sem_data.iloc[i,5] * sem_data.iloc[i,6]
- sem_data.iat[i, -1] = score
-
- # 定义情感字典
- match_dict = {
- "joy":["PA","PE"],
- "surprise":["PC"],
- "anger":["NA"],
- "sadness":["NB", "NJ", "NH", "PF"],
- "fear":["NI", "NC", "NG"],
- "disgust":["ND", "NE", "NN", "NK", "NL"],
- }
- # 获取情感类别
- label_dict = {}
- keys = match_dict.keys()
- for k in keys:
- word_dict = {}
- for i in range(sem_data.shape[0]):
- label = sem_data.iloc[i, 4]
- if label in match_dict[k]:
- word_dict[sem_data.iloc[i, 0]] = sem_data.iloc[i, -1]
- label_dict[k] = word_dict
- return label_dict
-
- path = "data/情感词汇本体.xlsx"
- label_dict_7_class = get_label_data(path)
-
- def seg_word(sentence):
- """使用jieba对文档分词"""
- seg_list = jieba.cut(sentence)
- seg_result = []
- for w in seg_list:
- seg_result.append(w)
- # 读取停用词文件
- stopwords = set()
- fr = codecs.open('data/stopwords.txt', 'r', 'utf-8')
- for word in fr:
- stopwords.add(word.strip())
- fr.close()
- # 去除停用词
- return list(filter(lambda x: x not in stopwords, seg_result))
-
- def classify_words_7_class(word_dict, sen_dict):
- """词语分类,找出情感词、否定词、程度副词"""
-
- # 读取否定词文件
- not_word_file = open('data/notDict.txt', 'r+', encoding='utf-8')
- # 由于否定词只有词,没有分值,使用list即可
- not_word_list = not_word_file.readlines()
-
- # 读取程度副词文件
- degree_file = open('data/degree.txt', 'r+', encoding='utf-8')
- degree_list = degree_file.readlines()
- degree_dic = defaultdict()
- # 程度副词与情感词处理方式一样,转为程度副词字典对象,key为程度副词,value为对应的程度值
- for d in degree_list:
- # print(d)
- degree_dic[d.split(',')[0]] = d.split(',')[1]
-
- # 分类结果,词语的index作为key,词语的分值作为value,否定词分值设为-1
- sen_word = dict()
- not_word = dict()
- degree_word = dict()
-
- # 分类
- for word in word_dict.keys():
- if word in sen_dict.keys() and word not in not_word_list and word not in degree_dic.keys():
- # 找出分词结果中在情感字典中的词
- sen_word[word_dict[word]] = sen_dict[word]
- elif word in not_word_list and word not in degree_dic.keys():
- # 分词结果中在否定词列表中的词
- not_word[word_dict[word]] = -1
- elif word in degree_dic.keys():
- # 分词结果中在程度副词中的词
- degree_word[word_dict[word]] = degree_dic[word]
- degree_file.close()
- not_word_file.close()
- # 将分类结果返回
- return sen_word, not_word, degree_word
-
- def list_to_dict(word_list):
- """将分词后的列表转为字典,key为单词,value为单词在列表中的索引,索引相当于词语在文档中出现的位置"""
- data = {}
- for x in range(0, len(word_list)):
- data[word_list[x]] = x
- return data
-
- def get_init_weight(sen_word, not_word, degree_word):
- # 权重初始化为1
- W = 1
- # 将情感字典的key转为list
- sen_word_index_list = list(sen_word.keys())
- if len(sen_word_index_list) == 0:
- return W
- # 获取第一个情感词的下标,遍历从0到此位置之间的所有词,找出程度词和否定词
- for i in range(0, sen_word_index_list[0]):
- if i in not_word.keys():
- W *= -1
- elif i in degree_word.keys():
- # 更新权重,如果有程度副词,分值乘以程度副词的程度分值
- W *= float(degree_word[i])
- return W
-
- def socre_sentiment(sen_word, not_word, degree_word, seg_result):
- """计算得分"""
- # 权重初始化为1
- W = 1
- score = 0
- # 情感词下标初始化
- sentiment_index = -1
- # 情感词的位置下标集合
- sentiment_index_list = list(sen_word.keys())
- # 遍历分词结果(遍历分词结果是为了定位两个情感词之间的程度副词和否定词)
- for i in range(0, len(seg_result)):
- # 如果是情感词(根据下标是否在情感词分类结果中判断)
- if i in sen_word.keys():
- # 权重*情感词得分
- score += W * float(sen_word[i])
- # 情感词下标加1,获取下一个情感词的位置
- sentiment_index += 1
- if sentiment_index < len(sentiment_index_list) - 1:
- # 判断当前的情感词与下一个情感词之间是否有程度副词或否定词
- for j in range(sentiment_index_list[sentiment_index], sentiment_index_list[sentiment_index + 1]):
- # 更新权重,如果有否定词,取反
- if j in not_word.keys():
- W *= -1
- elif j in degree_word.keys():
- # 更新权重,如果有程度副词,分值乘以程度副词的程度分值
- W *= float(degree_word[j])
- # 定位到下一个情感词
- if sentiment_index < len(sentiment_index_list) - 1:
- i = sentiment_index_list[sentiment_index + 1]
- return score
-
- # 计算得分
- def setiment_score_7_class(sententce, sen_word):
- # 1.对文档分词
- seg_list = seg_word(sententce)
- # print(seg_list)
- # print(seg_list)
- # 2.将分词结果列表转为dic,然后找出情感词、否定词、程度副词
- sen_word, not_word, degree_word = classify_words_7_class(list_to_dict(seg_list), sen_word)
- # # 3.计算得分
- score = socre_sentiment(sen_word, not_word, degree_word, seg_list)
- return score
-
- def mul_classifier_7_class(content, label_dict):
- "根据新闻返回预测标签"
- scores = {}
- for k, v in label_dict.items():
- score = setiment_score_7_class(content, label_dict[k])
- scores[k] = score
- score_max = scores[max(scores, key=lambda x:abs(scores[x]))]
-
- if score_max == 0:
- return "unknow", 0
- else:
- return max(scores, key=lambda x:abs(scores[x])), scores[max(scores, key=lambda x:abs(scores[x]))]

先写这么多,后期优化的再补充。
https://blog.csdn.net/cjjwwss/article/details/79953397
https://blog.csdn.net/lom9357bye/article/details/79058946
参考论文:http://manu44.magtech.com.cn/Jwk_infotech_wk3/article/2017/2096-3467/2096-3467-1-7-61.shtml
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。