Skip to content

Commit a93a047

Browse files
1649759610ZeyuChenchenxiaozeng
authored
Sentiment Analysis Application (#1505)
* sentiment analysis initializing * modify README.md * sentiment analysis initializing * modify data_ext and data_cls link in README.md * sentiment analysis initializing * sentiment analysis initializing * sentiment analysis initializing * delete unuseful info. * sentiment analysis initializing * sentiment analysis initializing * sentiment analysis initializing * sentiment analysis initializing * delete sentiment_system.png * add sentiment_system.png * refine readme.md * sentiment analysis intializing * mv data and checkpoints in sub_dir to parent_dir * refine readme.md * refine readme * refine readme.md, modify requirements * refine readme.md * refine readme.md * refine readme.md * mv run_export.sh to run_export_model.sh * refine readme.md * remove some * remove some unnecessary packages * delete unuseful compute_md5 method * sentiment analysis initializing * sentiment analysis initializing * refine readme.md * set CUDA_VISIBLE_DEVICES=0 * refine style with pre-commit * use ppminilm in ernie instead of offline ppminilm Co-authored-by: Zeyu Chen <[email protected]> Co-authored-by: chenxiaozeng <[email protected]>
1 parent 843c69a commit a93a047

39 files changed

+2788
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# 情感分析
2+
3+
## 1. 场景概述
4+
5+
情感分析旨在对带有情感色彩的主观性文本进行分析、处理、归纳和推理,其广泛应用于消费决策、舆情分析、个性化推荐等领域,具有很高的商业价值。
6+
7+
依托百度领先的情感分析技术,食行生鲜自动生成菜品评论标签辅助用户购买,并指导运营采购部门调整选品和促销策略;房天下向购房者和开发商直观展示楼盘的用户口碑情况,并对好评楼盘置顶推荐;国美搭建服务智能化评分系统,客服运营成本减少40%,负面反馈处理率100%。
8+
9+
情感分析相关的任务有语句级情感分析、评论对象抽取、观点抽取等等。一般来讲,被人们所熟知的情感分析任务是语句级别的情感分析,该任务是在宏观上去分析整句话的感情色彩,其粒度可能相对比较粗。
10+
11+
因为在人们进行评论的时候,往往针对某一产品或服务进行多个属性的评论,对每个属性的评论可能也会褒贬不一,因此针对属性级别的情感分析在真实的场景中会更加实用,同时更能给到企业用户或商家更加具体的建议。例如这句关于薯片的评论。
12+
13+
> 这个薯片味道真的太好了,口感很脆,只是包装很一般。
14+
15+
可以看到,顾客在口感、包装和味道 三个属性上对薯片进行了评价,顾客在味道和口感两个方面给出了好评,但是在包装上给出了负面的评价。只有通过这种比较细粒度的分析,商家才能更有针对性的发现问题,进而改进自己的产品或服务。
16+
17+
基于这样的考虑,本项目提出了一种细粒度的情感分析能力,对于给定的文本,首先会抽取该文本中的评论观点,然后分析不同观点的情感极性。
18+
19+
20+
## 2. 产品功能介绍
21+
22+
### 2.1 系统特色
23+
为了降低技术门槛,方便开发者共享效果领先的情感分析技术,PaddleNLP本次开源的情感分析系统,具备三大亮点:
24+
25+
- 覆盖任务全
26+
- 集成评论观点抽取、属性级情感分类等情感分析能力,并开源模型,且打通模型训练、评估、预测部署全流程。
27+
- 效果领先
28+
- 集成百度研发的基于情感知识增强的预训练模型SKEP,为各类情感分析任务提供统一且强大的情感语义表示能力。
29+
- 预测性能强
30+
- 针对预训练模型预测效率低的问题,开源小模型PP-MiniLM,量化优化策略,预测性能大幅提升。
31+
32+
### 2.2 架构&功能
33+
34+
本项目提出的的情感分析解决方案如图1所示,整个情感分析的过程大致包含两个阶段,依次是评论观点抽取模型,属性级情感分类模型。对于给定的一段文本,首先基于前者抽取出文本语句中潜在的评论属性以及该属性相应的评论观点,然后将评论属性、观点以及原始文本进行拼接,传给属性级情感分类模型以识别出该评论属性的情感极性。
35+
36+
这里需要提到的是,由于目前市面上的大多数模型是基于通用语料训练出来的,这些模型可能并不会对情感信息那么敏感。基于这样的考量,本项目使用了百度自研的 SKEP 预训练模型,其在预训练阶段便设计了多种情感信息相关的预训练目标进行训练。作为一种情感专属的模型,其更适合用来做上边提到的评论观点抽取任务,以及属性级情感分类任务。
37+
38+
另外,本项目使用的是 Large 版的 SKEP 模型,考虑到企业用户在线上部署时会考虑到模型预测效率,所以本项目专门提供了一个通用版的小模型 [PP-MiniLM](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/examples/model_compression/pp-minilm) 以及一套量化策略,用户可以使用相应情感数据集对 PP-MiniLM 进行微调,然后进行量化,以达到更快的预测效率。
39+
40+
<div align="center">
41+
<img src="./imgs/sentiment_system.png" />
42+
<p>图1 情感分析系统图<p/>
43+
</div>
44+
45+
46+
## 3. 情感分析效果展示
47+
在图1中可以看到,本项目的核心模块为评论观点抽取和属性级情感分类模块,本项目中基于情感专属模型 SKEP 实现了两个模块,并且提供了两者训练和测试的脚本,分别放在 `extraction``classification` 目录下。
48+
49+
下表展示了我们训练的评论观点抽取模型在验证集 `dev` 和测试集 `test` 上的表现:
50+
|Model|数据集|precision|Recall|F1|
51+
| ------------ | ------------ | ------------ |-----------|------------ |
52+
|SKEP-Large|dev|0.87095|0.90056|0.88551|
53+
|SKEP-Large|test|0.87125|0.89944|0.88512|
54+
55+
下表展示了我们训练的属性级情感分类模型在验证集 `dev` 和测试集 `test` 上的表现:
56+
|Model|数据集|precision|Recall|F1|
57+
| ------------ | ------------ | ------------ |-----------|------------ |
58+
|SKEP-Large|dev|0.98758|0.99251|0.99004|
59+
|SKEP-Large|test|0.98497|0.99139|0.98817|
60+
61+
给定一段文本,使用我们提供的全流程预测脚本可以轻松获得情感分析结果,如下所示。
62+
```
63+
input_text: 蛋糕味道不错,很好吃,店家很耐心,服务也很好,很棒
64+
aspect: 蛋糕味道, opinions: ['不错', '好吃'], sentiment_polarity: 正向
65+
aspect: 店家, opinions: ['耐心'], sentiment_polarity: 正向
66+
aspect: 服务, opinions: ['好', '棒'], sentiment_polarity: 正向
67+
```
68+
如果你想了解更多评论观点抽取模型和属性级情感分类模型的实现细节,请分别点击 [extraction](extraction/README.md)[classification](classification/README.md)
69+
70+
71+
## 4. 情感分析实践
72+
以下是本项目运行的完整目录结构以及说明:
73+
74+
```shell
75+
.
76+
├── extraction # 评价观点抽取模型包
77+
├── classification # 细粒度情感分类模型包
78+
├── pp_minilm # PP-MiniLM特色小模型包
79+
├── deploy # 高性能预测部署包
80+
│ ├── predict.py # 高性能预测脚本
81+
│   ├── run_predict.py # 高性能预测命令
82+
├── imgs # 图片目录
83+
├── demo.py # demo脚本,方便体验预测效果
84+
├── predict.py # 全流程预测脚本
85+
├── export_model.py # 动转静模型导出脚本
86+
├── utils.py # 工具函数脚本
87+
├── run_demo.sh # 运行demo,快速体验情感分析效果
88+
├── run_predict.sh # 全流程预测命令
89+
├── run_export_model.sh # 动转静模型导出命令
90+
├── requirements.txt # 环境依赖
91+
└── README.md
92+
```
93+
94+
### 4.1 运行环境和依赖安装
95+
(1) 运行环境
96+
除非特殊说明,本实验默认是在以下配置环境研发运行的:
97+
```shell
98+
python version: 3.8
99+
CUDA Version: 10.2
100+
NVIDIA Driver Version: 440.64.00
101+
GPU: Tesla V100
102+
linux:CentOS Linux release 7.9.2009 (Core)
103+
```
104+
(2) 环境依赖
105+
```
106+
python >= 3.6
107+
paddlenlp >= 2.2.1
108+
paddlepaddle-gpu >= 2.2.1
109+
```
110+
111+
可以通过以下命令进行一键式软件环境安装:
112+
```shell
113+
pip install -r requirements.txt
114+
```
115+
116+
(3) 运行环境准备
117+
在运行之前,请在本目录下新建目录 `data``checkpoints`,分别用于存放数据和保存模型。
118+
119+
本项目需要训练两个阶段的模型:评论观点抽取模型,属性级情感分类模型。本次针对这抽取和分类模型,我们分别开源了 Demo 数据: [ext_data](https://bj.bcebos.com/v1/paddlenlp/data/ext_data.tar.gz)[cls_data](https://bj.bcebos.com/v1/paddlenlp/data/cls_data.tar.gz)
120+
121+
用户可分别点击下载,解压后将相应的数据文件依次放入 `./data/ext_data``./data/cls_data` 目录下即可。
122+
123+
### 4.2 使用说明
124+
本项目开源了训练后的评论观点模型 [ext_model](https://bj.bcebos.com/paddlenlp/models/best_ext.pdparams) 和 属性级情感分类模型 [cls_model](https://bj.bcebos.com/paddlenlp/models/best_cls.pdparams)。如有需要,可点击下载,下载后请将 `ext_model``cls_model` 重命名为 `best.pdparams`,分别放入 `./checkpoints/ext_checkpoints``./checkpoints/cls_checkpoints` 中。
125+
126+
另外,考虑到不同用户可能有不同的需求,本项目提供了如下的方式学习或使用本项目。
127+
128+
**(1)快速体验效果**
129+
如果你想快速体验本项目提供的情感分析能力,可使用本项目提供的 `demo.py` 脚本以交互式的方式进行体验。
130+
```shell
131+
sh run_demo.py
132+
```
133+
134+
**备注**:体验之前,请确保下载以上提到的 `ext_model``cls_model`,重命名后放入相应的目录中。
135+
136+
**(2) 文本批量预测**
137+
如果你有一批数据,不方便逐句输入,可使用本项目提供的正式预测脚本 `predict.py`, 以文件的形式进行输入,处理后该脚本会将结果文件保存到与输入文件相同的目录下,默认的结果文件名为 `sentiment_results.json`
138+
139+
本功能在预测时需要传入测试集文件路径,可将测试集文件命名为`test.txt`, 然后放入 `./data` 目录下。需要注意的是,测试集文件每行均为一个待预测的语句,如下所示。
140+
```
141+
蛋糕味道不错,很好吃,店家很耐心,服务也很好,很棒
142+
酒店干净整洁,性价比很高
143+
酒店环境不错,非常安静,性价比还可以
144+
房间很大,环境不错
145+
```
146+
147+
通过运行如下命令,便可进行批量文本情感分析预测:
148+
```python
149+
sh run_predict.sh
150+
```
151+
152+
**备注**:体验之前,请确保下载以上提到的 `ext_model``cls_model`,重命名后放入相应的目录中。
153+
154+
**(3)高性能预测**
155+
如果你想将本项目部署到线上环境去运行,那么建议你使用本项目基于 Paddle Inference 实现的高性能推理脚本 `deploy/predict.py`
156+
157+
在使用之前,首先需要将保存的动态图模型转为静态图,通过调用下面的命令,便可将评论观点抽取模型和属性级情感分类模型转为静态图模型:
158+
```shell
159+
sh run_export_model.sh extraction
160+
sh run_export_model.sh classification
161+
```
162+
163+
这里需要注意的是,要确保相应的动态图已经下载或者训练生成到 `model_path` 指定的目录中,静态图模型会自动生成到`save_path`指定的地址。
164+
165+
同上,高性能预测的默认输入和输出形式也为文件,可分别通过 `test_path``save_path` 进行指定,通过如下命令便可以基于Paddle Inference 进行高性能预测:
166+
```shell
167+
cd deploy
168+
sh run_predict.sh
169+
```
170+
171+
**(4)自定义模型训练**
172+
如果你希望自己尝试进行评论观点抽取模型训练,可使用4.1节中提供的 `ext_data` Demo 数据,或自己业务的标注数据重新训练模型,本项目已将评论观点抽取模型的相关训练和测试代码放入 `extraction` 目录下, 请到该目录下执行模型训练即可,更多的实现细节和和使用方式,请参考[这里](extraction/README.md)
173+
174+
如果你希望自己尝试进行属性级情感分类模型训练,可使用4.1节中提供的 `cls_data` Demo 数据,或自己业务的标注数据重新训练模型,本项目已将属性级情感分类模型的相关训练和测试代码放入 `classification` 目录下,请到该目录下执行模型训练即可,更多的实现细节和使用方式,请参考[这里](extraction/README.md)
175+
176+
在训练后,如果需要进行高性能预测,可参考(3)进行动转静,然后基于Paddle Inference 进行高性能预测。
177+
178+
179+
180+
## 5. 小模型优化策略
181+
以上实验中,无论是评论观点抽取模型,还是属性级情感分类模型,使用的均是 Large 版的 SKEP 模型,考虑到企业用户在线上部署时会考虑到模型预测效率,本项目提供了一套基于 [PP-MiniLM](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/examples/model_compression/pp-minilm) 中文特色小模型的解决方案。PP-MiniLM 提供了一套完整的小模型优化方案:首先使用 Task-agnostic 的方式进行模型蒸馏、然后依托于 [PaddleSlim](https://github.com/PaddlePaddle/PaddleSlim) 进行模型裁剪、模型量化等模型压缩技术,有效减小了模型的规模,加快了模型运行速度。
182+
183+
本项目基于 PP-MiniLM 中文特色小模型进行 fine-tune 属性级情感分类模型,然后使用 PaddleSlim 对训练好的模型进行量化操作。
184+
185+
在实验进行后,我们将 SKEP-Large、PP-MiniLM、量化PP-MiniLM 三个模型在性能和效果方面进行了对比,如下表所示。可以看到,三者在本任务数据集上的评估指标几乎相等,但是 PP-MiniLM 小模型运行速度较 SKEP-Large 提高了4倍,量化后的 PP-MiniLM 运行速度较 SKEP-Large 提高了近8倍。更多的详细信息请参考[这里](./speedup/README.md)
186+
187+
|Model|运行时间(s)|precision|Recall|F1|
188+
| ------------ | ------------ | ------------ |-----------|------------ |
189+
|SKEP-Large|1.00x|0.98497|0.99139|0.98817|
190+
|PP-MiniLM|4.95x|0.98379|0.98859|0.98618|
191+
|量化 PP-MiniLM|8.93x|0.98312|0.98953|0.98631|
192+
193+
## 4. 引用
194+
195+
[1] H. Tian et al., “SKEP: Sentiment Knowledge Enhanced Pre-training for Sentiment Analysis,” arXiv:2005.05635 [cs], May 2020, Accessed: Nov. 11, 2021.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# 细粒度情感分类模型
2+
3+
## 1. 方案设计
4+
5+
本项目将进行属性级别的情感分类,对于给定的一段文本,我们在基于评论观点抽取模型抽取出不同属性对应的观点后,便可以有针对性地对各个属性判别情感极性。具体来讲,本项目将抽取出的评论属性和观点进行拼接,然后和原始语句进行拼接作为一条独立的训练语句。
6+
7+
如图1所示,首先将评论属性和观点词进行拼接为"味道好",然后将"味道好"和原文进行拼接,然后传入SKEP模型,并使用 "CLS" 位置的向量进行细粒度情感倾向。
8+
9+
<div align="center">
10+
<img src="../imgs/design_cls_model.png" />
11+
<p>图1 细粒度情感分类模型<p/>
12+
</div>
13+
14+
## 2. 项目结构说明
15+
16+
以下是本项目运行的完整目录结构及说明:
17+
18+
```shell
19+
.
20+
├── data.py # 数据处理脚本
21+
├── model.py # 模型组网脚本
22+
├── train.py # 模型训练脚本
23+
├── evaluate.py # 模型评估脚本
24+
├── run_train.sh # 模型训练命令
25+
├── run_evaluate.sh # 模型评估命令
26+
└── README.md
27+
```
28+
29+
## 3. 数据说明
30+
31+
本实验中,相应的数据集需要包含3列数据:标签、评论观点和原文,下面给出了一条样本。
32+
33+
> 1 口味清淡 口味很清淡,价格也比较公道
34+
35+
可点击 [cls_data](https://bj.bcebos.com/v1/paddlenlp/data/cls_data.tar.gz) 进行 Demo 数据下载,将数据解压之后放入父目录的 `data/cls_data/` 文件夹下。
36+
37+
## 4. 模型效果展示
38+
39+
在分类模型训练过程中,总共训练了10轮,并选择了评估 F1 得分最高的 best 模型,下表展示了训练过程中使用的训练参数。我们同时开源了相应的模型,可点击下表的 `cls_model` 进行下载,下载后将模型重命名为 `best.pdparams`,然后放入父目录的 `checkpoints/cls_checkpoints` 中。
40+
|Model|训练参数配置|MD5|
41+
| ------------ | ------------ |-----------|
42+
|[cls_model](https://bj.bcebos.com/paddlenlp/models/best_cls.pdparams)|<div style="width: 150pt"> learning_rate: 3e-5, batch_size: 16, max_seq_len:256, epochs:10 </div>|3de6ddf581e665d9b1d035c29b49778a|
43+
44+
我们基于训练过程中的 best 模型在验证集 `dev` 和测试集 `test` 上进行了评估测试,模型效果如下表所示:
45+
|Model|数据集|precision|Recall|F1|
46+
| ------------ | ------------ | ------------ |-----------|------------ |
47+
|SKEP-Large|dev|0.98758|0.99251|0.99004|
48+
|SKEP-Large|test|0.98497|0.99139|0.98817|
49+
**备注**: 以上数据是基于全量数据训练和测试结果,并非 Demo 数据集。
50+
51+
## 5. 模型训练
52+
通过运行以下命令进行分类模型训练:
53+
```shell
54+
sh run_train.sh
55+
```
56+
57+
## 6. 模型测试
58+
通过运行以下命令进行分类模型测试:
59+
```shell
60+
sh run_evaluate.sh
61+
```
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import json
16+
from tqdm import tqdm
17+
18+
19+
def load_dict(dict_path):
20+
with open(dict_path, "r", encoding="utf-8") as f:
21+
words = [word.strip() for word in f.readlines()]
22+
word2id = dict(zip(words, range(len(words))))
23+
id2word = dict((v, k) for k, v in word2id.items())
24+
25+
return word2id, id2word
26+
27+
28+
def read(data_path):
29+
with open(data_path, "r", encoding="utf-8") as f:
30+
for line in f.readlines():
31+
items = line.strip().split("\t")
32+
assert len(items) == 3
33+
example = {
34+
"label": int(items[0]),
35+
"aspect_text": items[1],
36+
"text": items[2]
37+
}
38+
39+
yield example
40+
41+
42+
def convert_example_to_feature(example,
43+
tokenizer,
44+
label2id,
45+
max_seq_len=512,
46+
is_test=False):
47+
encoded_inputs = tokenizer(
48+
example["aspect_text"],
49+
text_pair=example["text"],
50+
max_seq_len=max_seq_len,
51+
return_length=True)
52+
53+
if not is_test:
54+
label = example["label"]
55+
return encoded_inputs["input_ids"], encoded_inputs[
56+
"token_type_ids"], encoded_inputs["seq_len"], label
57+
58+
return encoded_inputs["input_ids"], encoded_inputs[
59+
"token_type_ids"], encoded_inputs["seq_len"]

0 commit comments

Comments
 (0)