blmoistawinde / HarvestText

文本挖掘和预处理工具(文本清洗、新词发现、情感分析、实体识别链接、关键词抽取、知识抽取、句法分析等),无监督或弱监督方法
MIT License
2.43k stars 330 forks source link

情感分析跑出来的结果超过[-1, 1] #4

Closed Sean16SYSU closed 5 years ago

Sean16SYSU commented 5 years ago

在情感分析当中,跑出来的结果超过了[-1,1]

docs = ["张市筹设兴华实业公司外区资本家踊跃投资晋察冀边区兴华实业公司,自筹备成立以来,解放区内外企业界人士及一般商民,均踊跃认股投资", 
        "该公司原定资本总额为二十五万万元,现已由各界分认达二十万万元,所属各厂、各公司亦募得股金一万万余元",
        "连日来解放区以外各工商人士,投函向该公司询问经营性质与范围以及股东权限等问题者甚多,络绎抵此的许多资本家,于参观该公司所属各厂经营状况后,对民主政府扶助与奖励私营企业发展的政策,均极表赞同,有些资本家因款项未能即刻汇来,多向筹备处预认投资的额数。由平津来张的林明棋先生,一次即以现款入股六十余万元"
       ]
ht = HarvestText()
sdict = ht.build_sent_dict(docs, min_times=1, pos_seeds=pos,neg_seeds=neg)
ht.analyse_sent(docs[0])

输出:2.571525361280356

blmoistawinde commented 5 years ago

你好,这里我的情感分析使用的是SO-PMI算法,这个算法可以得到词语级的情感值,并且没有上下界限制,而句子的情感值则是通过句子中所有(未被过滤的)词语的情感值取平均得到的,所以也没有上下界。

考虑到在应用中可能确实会有希望情感限制在一定区间内的情况,现在我更新了新版,新支持了两种伸缩方式,例子如下:

首先命令行里更新:

pip install harvesttext==0.5.4.1 --extra-index-url https://pypi.python.org/simple

然后是你的例子(我也放到了readme里)

print("\nsentiment dictionary using default seed words")
docs = ["张市筹设兴华实业公司外区资本家踊跃投资晋察冀边区兴华实业公司,自筹备成立以来,解放区内外企业界人士及一般商民,均踊跃认股投资",
        "打倒万恶的资本家",
    "该公司原定资本总额为二十五万万元,现已由各界分认达二十万万元,所属各厂、各公司亦募得股金一万万余元",
    "连日来解放区以外各工商人士,投函向该公司询问经营性质与范围以及股东权限等问题者甚多,络绎抵此的许多资本家,于参观该公司所属各厂经营状况后,对民主政府扶助与奖励私营企业发展的政策,均极表赞同,有些资本家因款项未能即刻汇来,多向筹备处预认投资的额数。由平津来张的林明棋先生,一次即以现款入股六十余万元"
   ]
# scale: 将所有词语的情感值范围调整到[-1,1]
# 省略pos_seeds, neg_seeds,将采用默认的情感词典 get_qh_sent_dict()
print("scale=\"0-1\", 按照最大为1,最小为0进行线性伸缩,0.5未必是中性")
sent_dict = ht.build_sent_dict(docs,min_times=1,scale="0-1")
print("%s:%f" % ("赞同",sent_dict["赞同"]))
print("%s:%f" % ("二十万",sent_dict["二十万"]))
print("%s:%f" % ("万恶",sent_dict["万恶"]))
print("%f:%s" % (ht.analyse_sent(docs[0]), docs[0]))
print("%f:%s" % (ht.analyse_sent(docs[1]), docs[1]))

print("scale=\"+-1\", 在正负区间内分别伸缩,保留0作为中性的语义")
sent_dict = ht.build_sent_dict(docs,min_times=1,scale="+-1")
print("%s:%f" % ("赞同",sent_dict["赞同"]))
print("%s:%f" % ("二十万",sent_dict["二十万"]))
print("%s:%f" % ("万恶",sent_dict["万恶"]))
print("%f:%s" % (ht.analyse_sent(docs[0]), docs[0]))
print("%f:%s" % (ht.analyse_sent(docs[1]), docs[1]))
sentiment dictionary using default seed words
scale="0-1", 按照最大为1,最小为0进行线性伸缩,0.5未必是中性
赞同:1.000000
二十万:0.153846
万恶:0.000000
0.449412:张市筹设兴华实业公司外区资本家踊跃投资晋察冀边区兴华实业公司,自筹备成立以来,解放区内外企业界人士及一般商民,均踊跃认股投资
0.364910:打倒万恶的资本家
scale="+-1", 在正负区间内分别伸缩,保留0作为中性的语义
赞同:1.000000
二十万:0.000000
万恶:-1.000000
0.349305:张市筹设兴华实业公司外区资本家踊跃投资晋察冀边区兴华实业公司,自筹备成立以来,解放区内外企业界人士及一般商民,均踊跃认股投资
-0.159652:打倒万恶的资本家

希望能解决你的需要,也谢谢你帮我发现了一个很好的改进点。

Sean16SYSU commented 5 years ago

感谢。哈哈哈本来打算抽空改了提交的,大佬既然这么快搞定了也ok。谢啦

Sean16SYSU commented 5 years ago

关于这个scale的过程,我比较好奇在+-1的设计。

直接使用 (x-mean)/ (max-min)的操作做嘛? 这样操作,我觉得不太合理。 因为正向的词汇应该还是正向的,负向的词汇应该还是负向的。 或许在对应的符号下做 (x-min)/(max-min)比较合理?

blmoistawinde commented 5 years ago

事实上在+-1的情况下我正是像你说的这么做的,具体实现可见这里

所以你在那个例子中会看到:

scale="+-1", 在正负区间内分别伸缩,保留0作为中性的语义
赞同:1.000000
二十万:0.000000
万恶:-1.000000

三个词分别代表积极、中性、消极的倾向,都得到了合理的取值。

当然这样操作也是有风险的,因为它会改变情感值的总体分布。例如,假设词典中正向最大为4,负向最大为-2,那么对于两个分别为+1,-1的词语,这样scale以后就会变成0.25,-0.5,原来的抵消关系就不存在了。

所以在另一种"0-1"scale模式中,我就采取了直接按照最大最小值scale的方法,可以保留总体分布,但是单个词语的中性可能被扭曲(0不会变成0.5,如例子中的 二十万:0.153846 )。

两种方法各有千秋,看个人需要选择吧。当然你要是有更多好建议,欢迎来提一个PR来改进哈。