使用DeBERTa V3进行基于方面的情感分析(ABSA)
基于方面的情感分析(Aspect Based Sentiment Analysis, ABSA)是一种细粒度的情感分析任务,即,对于给定的一段文本,识别出该文本针对文中指定的某一方面的情感极性。
本文将介绍如何在本机上部署并使用预训练的DeBERTa V3模型对文本中的某一特定方面进行情感分析,从而实现观点挖掘相关的工作。当然,由于本人没有涉足过NLP的具体研究,可能很多表述并不严谨,敬请批评指正。
前言
这两天接触了一下情感分析相关的工作,大致是需要确定一些文本对某些个体的情感倾向。经过搜索,这属于情感分析(Sentiment Analysis)的任务范畴,而更具体地,实际上是基于方面的情感分析(Aspect Based Sentiment Analysis, ABSA)。
所谓基于方面的情感分析,我们用一个例子来说明就可以。
The food here is delicious, but the serivce is terrible.
如果上面这句话是某个顾客对某家餐馆的评价,那么我们应该如何精确地描述这名顾客对餐馆的观点呢?
一个很显然的结论是,恐怕我们不能简单地将 delicious 或 terrible 作为顾客的态度代表,也不能简单地因为文本中出现了一个积极的词语和一个消极的词语,分别赋分1和-1,然后加起来得分为0,结论为情感中性。
但如果现在有一位饥肠辘辘的游人来到了这座城市,他更在乎食物的口味,但却对餐馆的服务态度毫不在意。现在,他正在手机上搜寻附近口味出众的餐馆,根据上述顾客的评论,我们能否将这家餐馆推荐给他呢?
通过上述这个案例,我们会发现,在现实中,我们面对的大量文本可能会同时描述多个实体(Entity),例如上面出现在同一句话中的 food 和 serivce ,并表示出出截然相反的态度。然而,传统的方法无法准确地判别出隐藏在文本中的上述情感差异。
而通过对文本中不同的实体进行情感分析,就有可能发现人们在观点/意见(Opinion)上的差异。在上述例子中,顾客的评价是对餐馆不同方面/实体的观点存在差异,现实中包括新闻、媒体、文章等,可能都会对不同的事物存在不同的态度。通过发掘,可能发现上述差异,从而辅助决策。
实现方法
搜索ABSA相关的应用方法,首先发现的是Azure提供的Sentiment analysis and opinion mining服务,刚好免费申请的Azure for Students权益包里似乎也能用这个API,只要把需要分析的文本上传,就可以得到分析的结果。
但思来想去,觉得光是调用API就没意思了。
继续搜索,发现HuggingFace上有很多训练好的模型,仔细一看,推理使用到的计算硬件资源也不大,在可以接受的范围内,于是决定试试在自己电脑上部署模型进行推理。
只是奈何自己也不会什么模型,最后还是得用其他人做好的模型来完成这项工作。不过倒也可以之后再改进吧。
这里我们使用的是DeBERTa V3模型,HuggingFace上已经有人使用ABSADatasets训练好了可用于ABSA任务的模型,请见yangheng/deberta-v3-large-absa-v1.1.
可以参考的其他模型
在情感分析方面,当然也有一些其他预训练好的模型可以使用,例如:
- bhadresh-savani/distilbert-base-uncased-emotion: 尽管这并不是一个ABSA任务的模型,但其可以输出6种类别的情感。
部署细节
下面大致介绍一下本地部署模型的相关步骤和可能踩坑的地方。
获取模型
确保Git已经启用LFS扩展,然后执行如下命令:
1 | git clone https://huggingface.co/yangheng/deberta-v3-large-absa-v1.1 |
如果不希望在克隆仓库的时候下载所有大文件(如你所见,仓库中的模型文件不止一个),请设置环境变量GIT_LFS_SKIP_SMUDGE=1
再运行上述git命令。
克隆了仓库的基本形态后,本地目录下的大文件将用一个较小的指针文件替代,此时,如果希望拉取远程仓库中的某一指定文件,请按以下步骤进行:
1 | git config lfs.fetchinclude "<xxx>" |
在这里,请将第一行代码中的<xxx>
替换成为希望下载的指定文件名称。例如,在我们使用的这一模型仓库里,我们需要拉取的是model.safetensors
和spm.model
两个文件,则需要执行:
1 | git config lfs.fetchinclude "model.safetensors" |
为什么拉取model.safetensors文件
答:这我也不懂,但似乎当目录中存在model.safetensors
文件时,则不会主动加载pytorch_model.bin
文件,而由于前者是一个没有有效数据内容的指针文件,因此加载会出错。
如果清楚safetensors格式的工作原理,请自行调整,反正能动起来就是万事大吉的。
本机环境
为了在本机上进行推理,需要创建一个Python环境,依赖如下:
这两个就是最简单的,使用CPU进行推理的配置了。稍后我们介绍不同模式时会再介绍其他模式下依赖的Python包。
运行推理
回到上面克隆的模型仓库目录的同一级目录下,创建一个.py
文件(或者.ipynb
,如果你喜欢),这里取名run.py
。大致文件结构如下:
1 | - MyABSA |
编辑run.py
文件,填入以下代码:
1 | from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline |
这里我们传入模型的输入分为两个部分,前半段即为本文开头的餐馆评价示例,而后面,用[SEP]
标志和主要文段内容分割开的,就是我们希望探知的情感的实体。
在上面的输入中,我们希望模型给出,前面语句对于service
这个实体的态度。
运行上述代码,或许你会得到类似这样的输出:
1 | [[{'label': 'Negative', 'score': 0.9997419714927673}, |
结果不难解读,模型给出的推理认为,前面的文本对service
一词表现出的消极态度占据最主导的地位。
WebAPI化
为了便于对外设计接口,还可以将推理任务设计成WebAPI,对外暴露服务。
测试了一下使用ONNX Runtime进行的部署,甚至还测试了使用DirectML加速的ONNX Runtime,但似乎没啥用,测试下来尽管GPU占用是有了,但速度甚至比用numpy在CPU上的推理还要慢了。很大可能是我代码写的不好,数据在CPU和GPU之间反复倒腾了,导致速度上不去。
总之这一部分的代码,首先需要导出模型为ONNX格式,然后再放到这里进行加载。其实这部分内容主要是我想亲自动手看看,在pipeline
中间到底发生了什么。实际使用其实并不用这样做的。
使用CPU进行推理的具体代码看下面吧,依赖项在引入部分已经写清楚了:
1 | from fastapi import FastAPI, Depends, Request |
这将创建一个WebAPI,监听POST请求,请求主体是一个JSON格式的输入,包含sentence
和aspect
两个字段,前者是文本内容,后者就是希望获取态度的Aspect/Entity。
TODO
- []试着在预训练模型上进行fine-tune.
- []试着将推理过程运行在GPU上进行加速