【学术小白的学习之路】基于情感词典的中文句子情感分析(代码词典获取在结尾)
【学术小白的学习之路】基于情感词典的情感分析
- 1.基础函数
- 1.1 判断情感词的否定词数量
- 1.2 导入情感词典
- 1.3 切分句子
- 1.3.1为什么划分
- 1.3.2 划分代码
- 1.4 完整代码
- 2.导入词典
- 3.中文情感分析算法思路
- 4.1情感词获取思路
- 4.2 计算情感分值
- 4.3 得分的归一化处理
- 4.实证
- 5.总结
本文的情感分析逻辑大致如下,没有进行专门的分词工作,进行了分句子工作,识别出需要的词,计算出情感倾向。
1.基础函数
1.1 判断情感词的否定词数量
在情感分析代码中,我们需要写一个函数来主要作用是判断情感词前面是否存在奇数个否定词。这对于情感分析的逻辑非常重要,因为否定词的数量会影响最终的情感得分。
def is_odd(self, num):
if num % 2 == 0:
return 'even'
else:
return 'odd'
具体用途在判断否定影响:
在分析情感词时,代码会检查该情感词前面出现的否定词数量。如果否定词的数量是奇数,则表示这个情感词的情感倾向应该反转。例如,“不是好” 表示负面情感,因为有一个否定词 “不”。
通过使用 is_odd 函数,代码能够更准确地反映文本中的情感。例如,在处理类似于 “我不喜欢这个产品” 的句子时,函数帮助正确地识别出该句子传达的是负面情感。
1.2 导入情感词典
我们需要写一个函数从指定的文件中加载词典数据,并将其存储为一个集合,以便后续的情感分析使用。
这个函数在情感分析中起着关键角色,负责加载和管理各种情感词典。通过这个函数,可以简化代码、提高效率,并确保情感分析的准确性和灵活性。因此,它是实现情感分析系统不可或缺的组成部分。
def load_dict(self, file):
"""
Load dictionary
"""
with open(file, encoding='utf-8', errors='ignore') as fp:
lines = fp.readlines()
lines = [l.strip() for l in lines]
print("Load data from file (%s) finished !" % file)
dictionary = [word.strip() for word in lines]
return set(dictionary)
作用与重要性就可以实现统一管理词典
1集中加载:通过 load_dict 函数,所有的情感词典(如正面词、负面词、否定词等)都可以通过简单的函数调用进行加载,避免了重复代码。
2便于维护:如果需要更新词典,只需修改文件内容即可,而不必更改代码逻辑。
1.3 切分句子
1.3.1为什么划分
在情感分析中,一个句子可能包含多个分句,而这些分句所传达的情感可能截然不同。一个切分函数在这种情况下显得尤其重要,需要将复杂句子拆分为独立的子句,从而确保对每个分句的情感进行准确分析。
例如:
- 虽然这个产品性能很好,但它的价格却很高,我觉得不太划算。"
分割之后:
- [‘虽然这个产品性能很好’, ‘但它的价格却很高’, ‘我觉得不太划算’]
情感分析的应用
- 逐句情感评估:
第一句:“虽然这个产品性能很好”表达了正面情感。
第二句:“但它的价格却很高”引入了负面情感,暗示价格是一个问题。
第三句:“我觉得不太划算”进一步强调了负面情感。 - 情感冲突识别:
通过分句,情感分析可以清楚地识别出文本中的情感冲突。第一句的正面情感和后两句的负面情感形成对比,表明用户对产品的综合评价并不完全积极。 - 综合情感得分:
通过分析每个分句的情感,分析系统可以计算出整体情感得分,可能得出一个中性或略偏负面的结论。这种方法比简单分析整个句子要准确得多,因为它考虑了句子内部的复杂情感变化。
1.3.2 划分代码
我们可以借助正则表达式。使用正则表达式将输入的长文本分割成多个短句,能够识别多种标点符号(如句号、问号、感叹号等)作为分隔符。
同时需要去除空字符串,这样确保返回的结果中不包含空字符串,返回有效的子句列表。
def sentence_split_regex(self, sentence):
"""
Segmentation of sentence
"""
if sentence is not None:
sentence = re.sub(r"–+|—+", "-", sentence)
sub_sentence = re.split(r"[。,,!!??;;\s…~~]+|\.{2,}|…+| +|_n|_t", sentence)
sub_sentence = [s for s in sub_sentence if s != '']
if sub_sentence != []:
return sub_sentence
else:
return [sentence]
return []
1.4 完整代码
import re
class ToolGeneral():
"""
Tool function
"""
def is_odd(self,num):
if num % 2 == 0:
return 'even'
else:
return 'odd'
def load_dict(self,file):
"""
Load dictionary
"""
with open(file,encoding='utf-8',errors='ignore') as fp:
lines = fp.readlines()
lines = [l.strip() for l in lines]
print("Load data from file (%s) finished !"%file)
dictionary = [word.strip() for word in lines]
return set(dictionary)
def sentence_split_regex(self,sentence):
"""
Segmentation of sentence
"""
if sentence is not None:
sentence = re.sub(r"–+|—+", "-", sentence)
sub_sentence = re.split(r"[。,,!!??;;\s…~~]+|\.{2,}|…+| +|_n|_t", sentence)
sub_sentence = [s for s in sub_sentence if s != '']
if sub_sentence != []:
return sub_sentence
else:
return [sentence]
return []
if __name__ == "__main__":
#
tool = ToolGeneral()
#
s = '我今天你现在在干嘛。昨天上午,还有现在'
ls = tool.sentence_split_regex(s)
print(ls)
结果:
2.导入词典
-
环境准备
首先,确保你已经导入必要的库,并且定义了 ToolGeneral 类。你还需要确保字典文件的路径正确。 -
Hyperparams 类的定义
接下来,我们定义一个名为 Hyperparams 的类,其中包含多个静态属性,这些属性用于加载不同的情感字典和副词字典。 -
加载情感字典
在 Hyperparams 类中,我们通过调用 load_dict 方法来加载情感字典。这些字典通常用于情感分析,分别表示否定词、正面词和负面词。
import os
tool = ToolGeneral()
pwd = os.getcwd()
class Hyperparams:
'''Hyper parameters'''
# Load sentiment dictionary
deny_word = tool.load_dict(os.path.join(pwd,'dict','not.txt'))
posdict = tool.load_dict(os.path.join(pwd,'dict','positive.txt'))
negdict = tool.load_dict(os.path.join(pwd,'dict', 'negative.txt'))
pos_neg_dict = posdict|negdict
# Load adverb dictionary
mostdict = tool.load_dict(os.path.join(pwd,'dict','most.txt'))
verydict = tool.load_dict(os.path.join(pwd,'dict','very.txt'))
moredict = tool.load_dict(os.path.join(pwd,'dict','more.txt'))
ishdict = tool.load_dict(os.path.join(pwd,'dict','ish.txt'))
insufficientlydict = tool.load_dict(os.path.join(pwd,'dict','insufficiently.txt'))
overdict = tool.load_dict(os.path.join(pwd,'dict','over.txt'))
inversedict = tool.load_dict(os.path.join(pwd,'dict','inverse.txt'))
导入成功就会现实finish
这些词典包含了否定词
积极情感词,和消极情感词
为了保障这个精准度,那还需要程度词
以及包含了其他各种可能影响的词
3.中文情感分析算法思路
设计了一个情感分析的类 SentimentAnalysis,其主要功能是分析文本的情感倾向,返回正面和负面情感的分值。以下是逐步解析其实现思路的详细说明。
代码如下:
import os
import sys
import jieba
import numpy as np
tool = ToolGeneral()
# jieba.load_userdict(os.path.join(os.path.dirname('./dict', 'dict','jieba_sentiment.txt'))
# dict_file = os.path.join(os.path.dirname(os.path.abspath('')), 'dict', 'jieba_sentiment.txt')
class SentimentAnalysis():
"""
Sentiment Analysis with some dictionarys
"""
def sentiment_score_list(self,dataset):
seg_sentence = tool.sentence_split_regex(dataset)
count1,count2 = [],[]
for sentence in seg_sentence:
words = jieba.lcut(sentence, cut_all=False)
i = 0
a = 0
for word in words:
"""
poscount 积极词的第一次分值;
poscount2 积极反转后的分值;
poscount3 积极词的最后分值(包括叹号的分值)
"""
poscount,negcount,poscount2,negcount2,poscount3,negcount3 = 0,0,0,0,0,0 #
if word in Hyperparams.posdict :
if word in ['好','真','实在'] and words[min(i+1,len(words)-1)] in Hyperparams.pos_neg_dict and words[min(i+1,len(words)-1)] != word:
continue
else:
poscount +=1
c = 0
for w in words[a:i]: # 扫描情感词前的程度词
if w in Hyperparams.mostdict:
poscount *= 4
elif w in Hyperparams.verydict:
poscount *= 3
elif w in Hyperparams.moredict:
poscount *= 2
elif w in Hyperparams.ishdict:
poscount *= 0.5
elif w in Hyperparams.insufficientlydict:
poscount *= -0.3
elif w in Hyperparams.overdict:
poscount *= -0.5
elif w in Hyperparams.inversedict:
c+= 1
else:
poscount *= 1
if tool.is_odd(c) == 'odd': # 扫描情感词前的否定词数
poscount *= -1.0
poscount2 += poscount
poscount = 0
poscount3 = poscount + poscount2 + poscount3
poscount2 = 0
else:
poscount3 = poscount + poscount2 + poscount3
poscount = 0
a = i+1
elif word in Hyperparams.negdict: # 消极情感的分析,与上面一致
if word in ['好','真','实在'] and words[min(i+1,len(words)-1)] in Hyperparams.pos_neg_dict and words[min(i+1,len(words)-1)] != word:
continue
else:
negcount += 1
d = 0
for w in words[a:i]:
if w in Hyperparams.mostdict:
negcount *= 4
elif w in Hyperparams.verydict:
negcount *= 3
elif w in Hyperparams.moredict:
negcount *= 2
elif w in Hyperparams.ishdict:
negcount *= 0.5
elif w in Hyperparams.insufficientlydict:
negcount *= -0.3
elif w in Hyperparams.overdict:
negcount *= -0.5
elif w in Hyperparams.inversedict:
d += 1
else:
negcount *= 1
if tool.is_odd(d) == 'odd':
negcount *= -1.0
negcount2 += negcount
negcount = 0
negcount3 = negcount + negcount2 + negcount3
negcount2 = 0
else:
negcount3 = negcount + negcount2 + negcount3
negcount = 0
a = i + 1
i += 1
pos_count = poscount3
neg_count = negcount3
count1.append([pos_count,neg_count])
if words[-1] in ['!','!']:# 扫描感叹号前的情感词,发现后权值*2
count1 = [[j*2 for j in c] for c in count1]
for w_im in ['但是','但']:
if w_im in words : # 扫描但是后面的情感词,发现后权值*5
ind = words.index(w_im)
count1_head = count1[:ind]
count1_tail = count1[ind:]
count1_tail_new = [[j*5 for j in c] for c in count1_tail]
count1 = []
count1.extend(count1_head)
count1.extend(count1_tail_new)
break
if words[-1] in ['?','?']:# 扫描是否有问好,发现后为负面
count1 = [[0,2]]
count2.append(count1)
count1=[]
return count2
def sentiment_score(self,s):
senti_score_list = self.sentiment_score_list(s)
if senti_score_list != []:
negatives=[]
positives=[]
for review in senti_score_list:
score_array = np.array(review)
AvgPos = np.sum(score_array[:,0])
AvgNeg = np.sum(score_array[:,1])
negatives.append(AvgNeg)
positives.append(AvgPos)
pos_score = np.mean(positives)
neg_score = np.mean(negatives)
if pos_score >=0 and neg_score<=0:
pos_score = pos_score
neg_score = abs(neg_score)
elif pos_score >=0 and neg_score>=0:
pos_score = pos_score
neg_score = neg_score
else:
pos_score,neg_score=0,0
return pos_score,neg_score
def normalization_score(self,sent):
score1,score0 = self.sentiment_score(sent)
if score1 > 4 and score0 > 4:
if score1 >= score0:
_score1 = 1
_score0 = score0/score1
elif score1 < score0:
_score0 = 1
_score1 = score1/score0
else :
if score1 >= 4 :
_score1 = 1
elif score1 < 4 :
_score1 = score1/4
if score0 >= 4 :
_score0 = 1
elif score0 < 4 :
_score0 = score0/4
return _score1,_score0
4.1情感词获取思路
- 情感分值列表计算
def sentiment_score_list(self, dataset):
该方法接收一个数据集(文本)并返回每个句子的情感分值列表。
2句子切分,用上面定义的方法进行切分句子
其中,seg_sentence = tool.sentence_split_regex(dataset)。使用 sentence_split_regex 方法将输入文本分割成独立句子。
count1, count2 = [], [] count1 用于存储每个句子的情感分值,count2 用于存储所有句子的情感分值。
3 使用下面的算法遍历每个句子
for sentence in seg_sentence:
words = jieba.lcut(sentence, cut_all=False)
对每个句子进行分词,得到词汇列表。
4 情感词分析
for word in words:
poscount, negcount, poscount2, negcount2, poscount3, negcount3 = 0, 0, 0, 0, 0, 0
```
初始化情感计数器:正面和负面情感的计数器以及它们的临时值。
5 正面情感词处理
```python
if word in Hyperparams.posdict:
检查当前词是否在正面词典中。
如果是,遍历其之前的词,分析程度词的影响并调整 poscount。
5.1 程度词影响
if w in Hyperparams.mostdict:
poscount *= 4 # 加强
根据程度词(如 “非常”、“极其” 等)调整正面情感的分值。
5.2 否定词处理
if tool.is_odd(c) == 'odd':
poscount *= -1.0
检查是否存在奇数个否定词,如果存在,反转正面情感的得分。
6 负面情感词处理
elif word in Hyperparams.negdict:
与正面情感处理类似,检查当前词是否在负面词典中,并进行相应分析。
7 特殊情况处理
检查句子末尾的感叹号和问号,影响情感得分。
if words[-1] in ['!','!']:
count1 = [[j*2 for j in c] for c in count1]
如果句子以感叹号结尾,将情感分值加倍。
4.2 计算情感分值
sentiment_score 这段代码通过计算输入文本中每个句子的情感得分,最终得出整体的正面和负面分值。它有效地整合了多句子的情感分析结果,考虑了分值的符号和特殊情况,确保了返回的情感得分具有合理性和准确性。这种方法适用于对评论或长文本进行情感倾向的评估。
def sentiment_score(self, s):
该方法计算输入文本的整体情感得分。
1 获取情感分值列表
senti_score_list = self.sentiment_score_list(s)
调用 sentiment_score_list 方法获取每个句子的情感分值。
2 计算平均分值
AvgPos = np.sum(score_array[:,0])
AvgNeg = np.sum(score_array[:,1])
计算所有句子正面和负面情感的平均分值。
4.3 得分的归一化处理
为了对情感分值进行合理的归一化处理,以便在不同情感强度的情况下保持分值的可比性和一致性。通过这种方式,分析结果可以更清晰地反映出文本的情感倾向,同时在高分和低分情况下采取不同的归一化策略,以适应不同的情感表达强度。这样的设计使得情感分析结果在实际应用中更具实用性和可解释性
def normalization_score(self, sent):
- 高分情况下的处理
if score1 >= score0:
_score1 = 1
_score0 = score0 / score1
elif score1 < score0:
_score0 = 1
_score1 = score1 / score0
该方法对情感得分进行归一化处理,返回标准化后的正面和负面分值。
根据正面和负面分值的大小关系进行归一化,使得分值在一定范围内更加平衡和可比较。
归一化逻辑:
如果正面得分大于或等于负面得分,设置正面得分 _score1 为 1,并将负面得分 _score0 归一化为其相对于正面得分的比例。
反之,如果负面得分更高,设置负面得分 _score0 为 1,并将正面得分 _score1 归一化为其相对于负面得分的比例。
else:
if score1 >= 4:
_score1 = 1
elif score1 < 4:
_score1 = score1 / 4
if score0 >= 4:
_score0 = 1
elif score0 < 4:
_score0 = score0 / 4
处理低分情况:
如果正面得分大于或等于 4,则将其归一化为 1。
如果正面得分小于 4,则将其归一化为其与 4 的比例。
对负面得分采用相同的逻辑。
这部分设计意图在于确保即使在情感表达较弱的情况下,分值也能在一个合理的范围内进行比较和理解。
4.实证
#计算完一条评论所有分句的情感值([正面分值, 负面分值]),用数组(list) 记录起来。
sa = SentimentAnalysis()
text = '虽然这个产品性能很好,但它的价格却很高,我觉得不太划算'
print(sa.normalization_score(text))
(0.25, 0.08333333333333333)
这样就可以得到正面和负面的情感得分,判断出来情感倾向
5.总结
本次代码通过分词、情感词典分析、程度词和否定词处理等步骤,构建了一个完整的情感分析流程。它能够从输入的文本中提取情感信息,并计算出正面和负面的情感得分,最终提供一个归一化的情感评分。通过这种方式,情感分析能够有效地捕捉文本中的情感表达,支持情感理解和决策。
代码词典获取链接,点赞关注,失效留言,或私信,提取码1111