1. 简介
现在情感分析有很广泛的应用,例如:
评论分析与决策,通过对产品多维度评论观点进行倾向性分析,给用户提供该产品全方位的评价,方便用户进行决策
评论分类,通过对评论进行情感倾向性分析,将不同用户对同一事件或对象的评论内容按情感极性予以分类展示
舆情监控,通过对需要舆情监控的实时文字数据流进行情感倾向性分析,把握用户对热点信息的情感倾向性变化
等等
百度针对情感分析也提供了多种解决方案,包括:
百度情感倾向分析服务:对包含主观信息的文本进行情感倾向性判断,可支持在线训练模型调优效果,为口碑分析、话题监控、舆情分析等应用提供帮助。
ERINE模型:今年百度发布了知识增强的语义表示模型 ERNIE(Enhanced Representation from kNowledge IntEgration),并发布了基于 PaddlePaddle 的开源代码与模型,在语言推断、语义相似度、命名实体识别、情感分析、问答匹配等自然语言处理(NLP)各类中文任务上的验证显示,模型效果全面超越 BERT。
情感倾向分析(Sentiment Classification,简称Senta)针对带有主观描述的中文文本,可自动判断该文本的情感极性类别并给出相应的置信度,能够帮助企业理解用户消费习惯、分析热点话题和危机舆情监控,为企业提供有利的决策支持。PaddleHub现在这方面的模型有:senta_lstm,senta_gru,senta_cnn,senta_bow,senta_bilstm,emotion_detection_textcnn等。这些PaddleHub Module预训练数据集为百度自建数据集,支持预测和Fine-tune。
本文对使用百度情感倾向分析服务进行在线情感分析及使用百度ERINE模型、PaddleHub情感分析模型进行离线情感分析进行评测分析,并提供了详细的使用攻略及代码,方便大家使用。
2 情感倾向分析API调用攻略(Python3)
2.1 认证授权:
在开始调用任何API之前需要先进行认证授权,具体的说明请参考:
http://ai.baidu.com/docs#/Auth/top
具体Python3代码如下:
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import urllib
import json
#client_id 为官网获取的AK, client_secret 为官网获取的SK
client_id =【百度云应用的AK】
client_secret =【百度云应用的SK】
#获取token
def get_token():
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + client_id + '&client_secret=' + client_secret
request = urllib.request.Request(host)
request.add_header('Content-Type', 'application/json; charset=UTF-8')
response = urllib.request.urlopen(request)
token_content = response.read()
if token_content:
token_info = json.loads(token_content)
token_key = token_info['access_token']
return token_key
2.2 情感倾向分析接口调用:
详细说明请参考:http://ai.baidu.com/docs#/NLP-API/57b9b630
接口描述
情感倾向分析接口(通用版):自动对包含主观信息的文本进行情感倾向性判断(积极、消极、中性),并给出相应的置信度。为口碑分析、话题监控、舆情分析等应用提供基础技术支持,同时支持用户自行定制模型效果调优。
请求说明
HTTP方法: POST
请求URL: https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify
URL参数:
参数 值
access_token 通过API Key和Secret Key获取的access_token,参考“Access Token获取”
Header如下:
参数 值
Content-Type application/json
Body请求示例:
{
"text": "苹果是一家伟大的公司"
}
请求参数
参数 类型 描述 是否必填
text string 文本内容,最大2048字节
返回说明
参数 说明 描述
log_id uint64 请求唯一标识码
sentiment int 表示情感极性分类结果,0:负向,1:中性,2:正向
confidence float 表示分类的置信度,取值范围[0,1]
positive_prob float 表示属于积极类别的概率 ,取值范围[0,1]
negative_prob float 表示属于消极类别的概率,取值范围[0,1]
Python3调用代码如下:
#调用情感分类接口
def get_classify(content):
print (content)
token=get_token()
url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify'
params = dict()
params['text'] = content
params = json.dumps(params).encode('utf-8')
access_token = token
url = url + "?access_token=" + access_token
request = urllib.request.Request(url=url, data=params)
request.add_header('Content-Type', 'application/json')
response = urllib.request.urlopen(request)
content = response.read()
if content:
content=content.decode('gb2312')
#print (content)
data = json.loads(content)
data=data['items'][0]
sentiment=data['sentiment']
if sentiment==0:
sentiment='负向'
elif sentiment==1:
sentiment='中性'
else:
sentiment='正向'
print ('情感分类结果:',sentiment)
print ('分类的置信度:',data['confidence'])
print ('积极类别概率:',data['positive_prob'])
print ('消极类别概率:',data['negative_prob'])
test_list = [
"这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般",
"交通方便;环境很好;服务态度很好 房间较小",
"稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。" ,
"服务很不错,下次还会来。" ,
"前台接待太差,下次不会再选择入住此店啦",
"菜做的很好,味道很不错。" ,
"19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀",
"现在是高峰期,人太多了,我们晚点来吧"
]
import datetime
print (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
for text in test_list:
get_classify(text)
print (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
3、ERINE模型
ERNIE 通过建模海量数据中的词、实体及实体关系,学习真实世界的语义知识。相较于 BERT 学习原始语言信号,ERNIE 直接对先验语义知识单元进行建模,增强了模型语义表示能力,以 Transformer 为网络基本组件,以Masked Bi-Language Model和 Next Sentence Prediction 为训练目标,通过预训练得到通用语义表示,再结合简单的输出层,应用到下游的 NLP 任务。我们可以使用ERINE进行情感分类,具体步骤如下:
1,首先我们要在PaddleHub中选择ernie作为预训练模型,进行Fine-tune。ChnSentiCorp数据集是一个中文情感分类数据集。PaddleHub已支持加载该数据集。关于该数据集,详情请查看ChnSentiCorp数据集使用。
https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Dataset
2,生成Reader:接着生成一个文本分类的reader,reader负责将dataset的数据进行预处理,首先对文本进行切词,接着以特定格式组织并输入给模型进行训练。
ClassifyReader的参数有以下三个:
dataset: 传入PaddleHub Dataset;
vocab_path: 传入ERNIE/BERT模型对应的词表文件路径;
max_seq_len: ERNIE模型的最大序列长度,若序列长度不足,会通过padding方式补到max_seq_len, 若序列长度大于该值,则会以截断方式让序列长度为max_seq_len;
3,选择Fine-Tune优化策略,适用于ERNIE/BERT这类Transformer模型的迁移优化策略为AdamWeightDecayStrategy。详情请查看Strategy。
AdamWeightDecayStrategy的参数有以下三个:
learning_rate: 最大学习率
lr_scheduler: 有linear_decay和noam_decay两种衰减策略可选
warmup_proprotion: 训练预热的比例,若设置为0.1, 则会在前10%的训练step中学习率逐步提升到learning_rate
weight_decay: 权重衰减,类似模型正则项策略,避免模型overfitting
optimizer_name: 优化器名称,使用Adam
4,选择运行时配置
在进行Finetune前,我们可以设置一些运行时的配置,例如如下代码中的配置,表示:
use_cuda:设置为False表示使用CPU进行训练。如果您本机支持GPU,且安装的是GPU版本的PaddlePaddle,我们建议您将这个选项设置为True;
epoch:要求Finetune的任务只遍历1次训练集;
batch_size:每次训练的时候,给模型输入的每批数据大小为32,模型训练时能够并行处理批数据,因此batch_size越大,训练的效率越高,但是同时带来了内存的负荷,过大的batch_size可能导致内存不足而无法训练,因此选择一个合适的batch_size是很重要的一步;
log_interval:每隔10 step打印一次训练日志;
eval_interval:每隔50 step在验证集上进行一次性能评估;
checkpoint_dir:将训练的参数和数据保存到ernie_txt_cls_turtorial_demo目录中;
strategy:使用DefaultFinetuneStrategy策略进行finetune;
更多运行配置,请查看RunConfig
5,组建Finetune Task
有了合适的预训练模型和准备要迁移的数据集后,我们开始组建一个Task。
获取module的上下文环境,包括输入和输出的变量,以及Paddle Program;
从输出变量中找到用于情感分类的文本特征pooled_output;
在pooled_output后面接入一个全连接层,生成Task;
6,开始Finetune
选择finetune_and_eval接口来进行模型训练,这个接口在finetune的过程中,会周期性的进行模型效果的评估,以便我们了解整个训练过程的性能变化。
7,使用模型进行预测
当Finetune完成后,使用模型来进行预测
具体代码如下:
from paddle.fluid.framework import switch_main_program
import paddlehub as hub
import paddle.fluid as fluid
module = hub.Module(name="ernie", version="1.0.1")
dataset = hub.dataset.ChnSentiCorp()
#生成Reader
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128)
#选择Fine-Tune优化策略
strategy = hub.AdamWeightDecayStrategy(
weight_decay=0.01,
warmup_proportion=0.1,
learning_rate=5e-5,
lr_scheduler="linear_decay",
optimizer_name="adam")
#选择运行时配置
config = hub.RunConfig(
use_cuda=True,
num_epoch=1,
checkpoint_dir="ernie_txt_cls_turtorial_demo",
batch_size=32,
log_interval=10,
eval_interval=50,
strategy=strategy)
#组建Finetune Task
inputs, outputs, program = module.context(
trainable=True, max_seq_len=128)
# Use "pooled_output" for classification tasks on an entire sentence.
pooled_output = outputs["pooled_output"]
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
cls_task = hub.TextClassifierTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
#开始Finetune
cls_task.finetune_and_eval()
#使用模型进行预测
# coding: utf-8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
import os
import paddle
import paddle.fluid as fluid
import paddlehub as hub
# loading Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
inputs, outputs, program = module.context(max_seq_len=128)
# Sentence classification dataset reader
dataset = hub.dataset.ChnSentiCorp()
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
pooled_output = outputs["pooled_output"]
# Setup feed list for data feeder
# Must feed all the tensor of ERNIE's module need
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
strategy = hub.AdamWeightDecayStrategy(
weight_decay=0.01,
warmup_proportion=0.1,
learning_rate=5e-5,
lr_scheduler="linear_decay",
optimizer_name="adam")
# Setup runing config for PaddleHub Finetune API
config = hub.RunConfig(
use_data_parallel=False,
use_pyreader=False,
use_cuda=True,
batch_size=32,
enable_memory_optim=False,
checkpoint_dir="ernie_txt_cls_turtorial_demo",
strategy=strategy)
# Define a classfication finetune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
# Data to be prdicted
data = [
["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"],
["交通方便;环境很好;服务态度很好 房间较小"],
["稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。" ],
["服务很不错,下次还会来。" ],
["前台接待太差,下次不会再选择入住此店啦"],
["菜做的很好,味道很不错。" ],
["19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀"],
["现在是高峰期,人太多了,我们晚点来吧"]
]
index = 0
run_states = cls_task.predict(data=data)
results = [run_state.run_results for run_state in run_states]
for batch_result in results:
# get predict index
batch_result = np.argmax(batch_result, axis=2)[0]
for result in batch_result:
if result==1:
result='正向'
else:
result='负向'
print(data[index][0], result)
index += 1
4、PaddleHub情感分析模型(以senta_bilstm为例)
模型概述
情感倾向分析(Sentiment Classification,简称Senta)针对带有主观描述的中文文本,可自动判断该文本的情感极性类别并给出相应的置信度,能够帮助企业理解用户消费习惯、分析热点话题和危机舆情监控,为企业提供有利的决策支持。该模型基于一个双向LSTM结构,情感类型分为积极、消极。该PaddleHub Module支持预测和Fine-tune。
参数:
data:dict类型,key为text,str类型;value为待预测文本,list类型。
返回:
results:list类型,每个元素为对应输入文本的预测结果。预测结果为dict类型,有以下字段:
text字段存放原输入待预测文本;
sentiment_label为情感分类结果对应的label;
sentiment_key表示分类结果为positive或是negative;
positive_probs为原输入文本对应positive的概率值;
negative_probs为原输入文本对应negative的概率值;
调用代码:
import paddlehub as hub
senta = hub.Module(name="senta_bilstm")
test_text = [
"这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般",
"交通方便;环境很好;服务态度很好 房间较小",
"稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。" ,
"服务很不错,下次还会来。" ,
"前台接待太差,下次不会再选择入住此店啦",
"菜做的很好,味道很不错。" ,
"19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀",
"现在是高峰期,人太多了,我们晚点来吧"
]
input_dict = {"text": test_text}
results = senta.sentiment_classify(data=input_dict)
for result in results:
print(result['text'])
print(result['sentiment_label'])
print(result['sentiment_key'])
print(result['positive_probs'])
print(result['negative_probs'])
5、功能评测:
选用不同的数据对情感分类的准确性进行测试。具体案例如下:
"这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般",
"交通方便;环境很好;服务态度很好 房间较小",
"稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。" ,
"服务很不错,下次还会来。" ,
"前台接待太差,下次不会再选择入住此店啦",
"菜做的很好,味道很不错。" ,
"19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀",
"现在是高峰期,人太多了,我们晚点来吧"
5.1 情感分析API结果:
这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般
情感分类结果: 负向
分类的置信度: 0.943242
积极类别概率: 0.025541
消极类别概率: 0.974459
交通方便;环境很好;服务态度很好 房间较小
情感分类结果: 正向
分类的置信度: 0.890898
积极类别概率: 0.950904
消极类别概率: 0.049096
稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。
情感分类结果: 负向
分类的置信度: 0.0922925
积极类别概率: 0.408468
消极类别概率: 0.591532
服务很不错,下次还会来。
情感分类结果: 正向
分类的置信度: 0.990288
积极类别概率: 0.99563
消极类别概率: 0.00437049
前台接待太差,下次不会再选择入住此店啦
情感分类结果: 负向
分类的置信度: 0.980575
积极类别概率: 0.00874128
消极类别概率: 0.991259
菜做的很好,味道很不错。
情感分类结果: 正向
分类的置信度: 0.959412
积极类别概率: 0.981735
消极类别概率: 0.0182646
19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀
情感分类结果: 负向
分类的置信度: 0.366781
积极类别概率: 0.284949
消极类别概率: 0.715051
现在是高峰期,人太多了,我们晚点来吧
情感分类结果: 正向
分类的置信度: 0.400084
积极类别概率: 0.730038
消极类别概率: 0.269962
5.2 ERINE的结果:
这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般 负向
交通方便;环境很好;服务态度很好 房间较小 正向
稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。 负向
服务很不错,下次还会来。 正向
前台接待太差,下次不会再选择入住此店啦 负向
菜做的很好,味道很不错。 正向
19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀 负向
现在是高峰期,人太多了,我们晚点来吧 负向
5.3、PaddleHub情感分析模型(以senta_bilstm为例)
这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般
0
negative
0.0103
0.9897
交通方便;环境很好;服务态度很好 房间较小
1
positive
0.9374
0.0626
稍微重了点,可能是硬盘大的原故,还要再轻半斤就好了。
0
negative
0.2237
0.7763
服务很不错,下次还会来。
1
positive
0.9891
0.0109
前台接待太差,下次不会再选择入住此店啦
0
negative
0.0057
0.9943
菜做的很好,味道很不错。
1
positive
0.9714
0.0286
19天硬盘就罢工了,算上运来的一周都没用上15天,你说这算什么事呀
0
negative
0.0266
0.9734
现在是高峰期,人太多了,我们晚点来吧
1
positive
0.7662
0.2338
6、结论及建议
通过测试发现不论直接调用百度情感分析服务还是使用百度ERINE模型,对情感分析都有很好、很准确的结果。百度情感分析服务调用起来更加的方便,不需要使用者掌握太多的深度学习方面的技术,适用于绝大多数的应用场景。
PaddleHub情感分析模型库,是一个针对情感分析的模型库,并且针对情感分析场景进行了预训练,支持FineTune,不过即使没有FineTune也可以直接用于情感分析,调用起来比较方便。不过需要使用者掌握一定的深度学习方面的技术。
ERINE模型是一个通用模型给与用户更大的灵活性,可以根据自己的需要定制化的开发适合自己的引用,不过需要对深度学习及Paddle Paddle有比较深入的了解。大家可以根据自己的实际情况选择适合的技术。
是啊,这些课程都很好
估计以后会有更多样的分析
主要应该是在销售上
确实是,而且百度还有很多免费课程教大家
这个可以运用于网评分析上
分析的非常准确
这个情感分析只能是进行正向和负向的分析吗
大家可以一起来测试一下
ERINE的确强大
测试后发现百度NLP功能很强大