赞
踩
最近在做微博的评论情感分析,本来想用Tensorflow的RNN来实现,但网上一直找不到好的训练集,在CSDN上买的几万条的微博情感标注集效果也不好,模型对训练集的准确率很高,但放到实际预测中效果很差。
在暂时没有时间自己做标注的情况下,只能先考虑一些现有的工具了,之前用过百度AI的自然语言处理,凑合能用,个人感觉他们的训练集应该量是比较大的,能适应的场景比较多。实际使用后还没有发现很离谱的打标打错的情况,所以记录一下供大家参考,使用的是Python3.6+Requests库。
首先要先在百度AI控制台申请一下应用。
创建应用后保存自己的API key和Secret key,要用到这两个字段去获取AccessToken,然后再用AccessToken访问API。
虽然百度API的技术文档里有介绍,但用的是python2.7版本的,而且是urllib库。转换成Requests库更方便,代码如下:
- # 获取AccessToken类
- class GetAccessToken:
- def __init__(self):
- # 【修改点1】输入自己在百度AI控制台申请应用后获得的AK和SK
- self.AK = ''
- self.SK = ''
- self.token_url = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+self.AK+'&client_secret='+self.SK
- self.headers = {'Content-Type':'application/json; charset=UTF-8'}
-
- def get_access_token(self):
- r = requests.get(self.token_url,headers = self.headers)
- if r.text:
- tokenkey = json.loads(r.text)['access_token']
- print('get token success')
- return tokenkey
- else:
- print('get token fail')
- return ''
将之前申请的API key和Secret key填入字段即可获得AccessToken。
由于是穷人,只能使用普通权限,QPS限制为5,也就是最多这个接口每秒能处理5次请求,超过就会返回错误。但实际的请求速度还是跟网速和电脑配置有关,比如设置了每个0.1秒请求一次,但实际发送请求时有时候是0.1S发送,有时候是0.3S发送,但如果连续多次0.1S发送请求就会报错,因此在调用时考虑每次出现错误就增加一点等待时间,而一段时间没有报错就减少一点等待时间,保持自己能最大化利用免费接口的请求量(这就是穷人的心机吗...)。代码解释见注释,直接使用需修改两个地方1.更改待分析文本所在文件夹地址;2.根据自己文本的格式,修改读取文件的方式
- # 调用API类
- class SentimentBaidu:
- def __init__(self,tp):
- # 调用自己需要使用的API,这里使用情感分析API作为示例,其他接口查询百度AI说明文档后替换 + 前的URL
- self.HOST = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify' + '?charset=UTF-8&access_token='
- self.headers = {'Content-Type': 'application/json','Connection': 'close'}
- self.textpath = tp
- self.commentcomment = []
- self.count = 0
- # 速度设置,对应百度AI的QPS限制,由于request发送请求的速度跟网速、设备性能有关,所以虽然限制是5QPS,但可以把请求的间隔写短点,这里采用的是最快0.06s
- self.speedlimit = 0.06
- # 初始速度,后续实际请求速度程序自动根据错误调整
- self.sleepdt = 0.08
- self.errorwaittime = 0.5
- self.qpserror = 0
- self.qpserrorindex = 0
- self.errorallcount = 0
-
- # 调用百度API获得情感分析结果方法
- def get_content_sentiments(self,text,at):
- raw = {'text': text}
- data = json.dumps(raw).encode('gbk')
- url = self.HOST+at
- try:
- if self.count - self.qpserrorindex > 500:
- if self.sleepdt > self.speedlimit:
- self.sleepdt -= 0.001
- print('speed up, current speed:',
- self.sleepdt)
- self.qpserrorindex = self.count
- time.sleep(self.sleepdt)
- r = requests.post(url=url, data=data, headers=self.headers)
- if 'error_code' in r.text:
- error = r.json()['error_code']
- print('error_code',error)
- if error == 18:
- self.errorallcount += 1
- self.qpserror += 1
- self.qpserrorindex = self.count
- self.sleepdt += 0.001
- print('current qps error count = ', self.qpserror, 'speed down, current speed:', self.sleepdt, self.errorallcount)
- time.sleep(self.errorwaittime)
- content = r.json()
- except Exception as e:
- self.errorallcount += 1
- time.sleep(self.errorwaittime)
- return
- try:
- if content['items']:
- contentposprob = content['items'][0]['positive_prob']
- contentnegprob = content['items'][0]['negative_prob']
- contentconfi = content['items'][0]['confidence']
- contentsenti = content['items'][0]['sentiment']
- temp = [contentposprob,contentnegprob,contentconfi,contentsenti]
- return temp
- except KeyError as e:
- self.errorallcount += 1
- print('error reason:',content)
- time.sleep(self.errorwaittime)
- return
-
- # 使用pandas读取所有待分析文本
- def get_comment_ori(self,fp):
- fpath = fp
- fpl = os.listdir(fpath)
- contentall = []
- for file in fpl:
- fd = fpath + '/' + file
- print('reading',fd)
- temp = pd.read_csv(fd)
- contentall.append(temp)
- contentalldf = pd.concat(contentall, ignore_index=True, sort=False)
- print('comment get:',contentalldf.shape[0])
- return contentalldf
-
- # 主程序
- def run(self):
- requests.adapters.DEFAULT_RETRIES = 5
- ATclass = GetAccessToken()
- AT = ATclass.get_access_token()
- print('progress start current speed = ', self.sleepdt)
- # 【修改点3】以下4行为获取文本,可选择自己常用的方式,将所有文本待分析文本放到一个iterator中,这里使用的pandas读取文本
- contentalldf = self.get_comment_ori(self.textpath)
- commentcontent = contentalldf['commentContent']
- commentcontent = pd.DataFrame(commentcontent)
- commentcontent.columns = ['comment']
- # 如果在调用接口前想先对文本符号、表情等信息进行清理,可调用clean_comment函数,通过pandas的apply快速处理所有文本
- # commentcontent['comment'] = commentcontent['comment'].apply(self.clean_comment)
- for comment in commentcontent['comment']:
- if comment:
- self.count += 1
- if self.count % 100 == 0:
- print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())),'正在处理第{0}条评论'.format(self.count))
- commentsenti = self.get_content_sentiments(comment,AT)
- if commentsenti is not None:
- commentbatch = [comment]+commentsenti
- self.commentcomment.append(commentbatch)
- if self.count % 100 == 0:
- commentsentidf = pd.DataFrame(self.commentcomment,
- columns=['comment', 'contentposprob', 'contentnegprob',
- 'contentconfi', 'contentsenti'])
- fpath = self.textpath + '/cpbaidu.csv'
- if os.path.exists(fpath):
- commentsentidf.to_csv(fpath, mode='a', encoding='utf-8-sig', index=False, header=False)
- else:
- commentsentidf.to_csv(fpath, encoding='utf-8-sig', index=False)
- # print('write to path',fpath,'write num:',commentsentidf.shape[0])
- self.commentcomment = []
- print('finished progress')
-
- # 文本清理方法,可选是否剔除文本内的符号、空格、emoji的剔除
- def clean_comment(self,text):
- emoji = re.compile(u'['
- u'\U0001F300-\U0001F64F'
- u'\U0001F680-\U0001F6FF'
- u'\u2600-\u2B55]+',
- re.UNICODE)
- text = re.sub("[\s+\.\!\/_,$%^*(+\"\']+|[+——!,。?、~@#¥%……&*()::]+", "", text)
- text = re.sub(emoji,'',text)
- return text
-
-
- if __name__=='__main__':
- tp = '' # 【修改点2】修改为自己的文本存储的文件夹位置
- runner = SentimentBaidu(tp)
- runner.run()
实际效果打标结果只能说勉强能用,而且由于之前没有做停用词去除,所以很多微博名字也被放到文本中进行分析了,所以最好在将文本提交API之前先对文本进行清理,去掉停用词和符号等内容,另外如果纯表情的微博回复调用接口会直接报空,也可以提前清除。打标结果如下,后面字段分别为,积极概率,消极概率,判断信心,情感分类(0消极 1中性 2积极)
完整项目见:https://github.com/pandasgb/SentimentAnalysisWithBaiduAI
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。