赞
踩
众所周知,人类自然语言中包含了丰富的情感色彩:表达人的情绪(如悲伤、快乐)、表达人的心情(如倦怠、忧郁)、表达人的喜好(如喜欢、讨厌)、表达人的个性特征和表达人的立场等等。情感分析在商品喜好、消费决策、舆情分析等场景中均有应用。利用机器自动分析这些情感倾向,不但有助于帮助企业了解消费者对其产品的感受,为产品改进提供依据;同时还有助于企业分析商业伙伴们的态度,以便更好地进行商业决策。
本实践将进行基于目标级的情感分析任务,与语句级别的情感分析不同,目标级的情感分析将对一句话中涉及的某个方面进行分析,如下面这句话所示。
这个薯片口味有点咸,太辣了,不过口感很脆。
对于这句话来讲,显然关于薯片的口味方面是一个负向评价(咸,太辣),然而对于口感方面却是一个正向评价(很脆)。
为方便目标级别的情感分析任务建模,同样可将情感极性分为正向、负向、中性三个类别,从而将情感分析任务转变为一个分类问题,如图1所示:
本实践将基于预训练SKEP模型,在seabsa16-phns
数据集上进行目标级别的情感分析任务。
学习资源
⭐ ⭐ ⭐ 欢迎点个小小的Star,开源不易,希望大家多多支持~⭐ ⭐ ⭐
本实践的设计思路如图2所示,SKEP模型的输入有两部分,一部分是要评价的对象(Aspect),另一个方面是相应的评论文本。将两者拼接之后便可以传入SKEP模型中,SKEP模型对该文本串进行语义编码后,本实践将CLS位置的token输出向量作为最终的语义编码向量。接下来将根据该语义编码向量进行情感分类。需要注意的是:seabsa16-phns数据集是个二分类数据集,其情感极性只包含正向和负向两个类别。
本实践将在数据集seabsa16-phns进行情感分析任务。该数据集是个目标级别的情感分析数据集,其中训练集包含1334条训练样本,529条测试样本。下面展示了该数据集中的一条样本:
{
"label":1
"text":"display#quality",
"text_pair":"今天有幸拿到了港版白色iPhone 5真机,试玩了一下,说说感受吧:1. 真机尺寸宽度与4/4s保持一致没有变化,长度多了大概一厘米,也就是之前所说的多了一排的图标。2. 真机重量比上一代轻了很多,个人感觉跟i9100的重量差不多。(用惯上一代的朋友可能需要一段时间适应了)3. 由于目前还没有版的SIM卡,无法插卡使用,有购买的朋友要注意了,并非简单的剪卡就可以用,而是需要去运营商更换新一代的SIM卡。4. 屏幕显示效果确实比上一代有进步,不论是从清晰度还是不同角度的视角,iPhone 5绝对要更上一层,我想这也许是相对上一代最有意义的升级了。5. 新的数据接口更小,比上一代更好用更方便,使用的过程会有这样的体会。6. 从简单的几个操作来讲速度比4s要快,这个不用测试软件也能感受出来,比如程序的调用以及照片的拍摄和浏览。不过,目前水货市场上坑爹的价格,最好大家可以再观望一下,不要急着出手。",
}
本节我们将训练、评估和测试数据集加载到内存中。 由于ChnSentiCorp数据集属于paddlenlp的内置数据集,所以本实践我们将直接从paddlenlp里面加载数据。相关代码如下。
import os import copy import argparse import numpy as np from functools import partial import paddle import paddle.nn.functional as F from paddlenlp.datasets import load_dataset from paddlenlp.data import Pad, Stack, Tuple from paddlenlp.metrics import AccuracyAndF1 from paddlenlp.transformers import SkepTokenizer, SkepModel, LinearDecayWithWarmup from utils.utils import set_seed from utils.data import convert_example_to_feature train_ds = load_dataset("seabsa16", "phns", splits=["train"])
100%|██████████| 381/381 [00:00<00:00, 22249.56it/s]
在将数据加载完成后,接下来,我们将训练和开发集数据转换成适合输入模型的特征形式,即将文本字符串数据转换成字典id的形式。这里我们要加载paddleNLP中的SkepTokenizer,其将帮助我们完成这个字符串到字典id的转换。
model_name = "skep_ernie_1.0_large_ch"
batch_size = 16
max_seq_len = 256
tokenizer = SkepTokenizer.from_pretrained(model_name)
trans_func = partial(convert_example_to_feature, tokenizer=tokenizer, max_seq_len=max_seq_len)
train_ds = train_ds.map(trans_func, lazy=False)
[2021-11-10 14:43:12,930] [ INFO] - Downloading skep_ernie_1.0_large_ch.vocab.txt from https://paddlenlp.bj.bcebos.com/models/transformers/skep/skep_ernie_1.0_large_ch.vocab.txt
100%|██████████| 55/55 [00:00<00:00, 2032.88it/s]
接下来,我们需要根据加载至内存的数据构造DataLoader,该DataLoader将支持以batch的形式将数据进行划分,从而以batch的形式训练相应模型。
batchify_fn = lambda samples, fn=Tuple(
Pad(axis=0, pad_val=tokenizer.pad_token_id),
Pad(axis=0, pad_val=tokenizer.pad_token_type_id),
Stack(dtype="int64")
): fn(samples)
train_batch_sampler = paddle.io.BatchSampler(train_ds, batch_size=batch_size, shuffle=True)
train_loader = paddle.io.DataLoader(train_ds, batch_sampler=train_batch_sampler, collate_fn=batchify_fn)
本案例中,我们将基于SKEP模型实现图2所展示的情感分析功能。具体来讲,我们将处理好的文本数据输入SLEP模型中,ERNIE将会对文本的每个token进行编码,产生对应向量序列,我们任务CLS位置对应的输出向量能够代表语句的完整语义,所以将利用该向量进行情感分类。相应代码如下。
class SkepForSquenceClassification(paddle.nn.Layer):
def __init__(self, skep, num_classes=2, dropout=None):
super(SkepForSquenceClassification, self).__init__()
self.num_classes = num_classes
self.skep = skep
self.dropout = paddle.nn.Dropout(p=dropout if dropout is not None else self.skep.config["hidden_dropout_prob"])
self.classifier = paddle.nn.Linear(self.skep.config["hidden_size"], self.num_classes)
def forward(self, input_ids, token_type_ids=None, position_ids=None, attention_mask=None):
_, pooled_output = self.skep(input_ids, token_type_ids=token_type_ids, position_ids=position_ids, attention_mask=attention_mask)
pooled_output = self.dropout(pooled_output)
logits = self.classifier(pooled_output)
return logits
接下来,定义情感分析模型训练时的环境,包括:配置训练参数、配置模型参数,定义模型的实例化对象,指定模型训练迭代的优化算法等,相关代码如下。
# model hyperparameter setting num_epoch = 3 learning_rate = 3e-5 weight_decay = 0.01 warmup_proportion = 0.1 max_grad_norm = 1.0 log_step = 20 eval_step = 100 seed = 1000 checkpoint = "./checkpoint/" set_seed(seed) use_gpu = True if paddle.get_device().startswith("gpu") else False if use_gpu: paddle.set_device("gpu:0") if not os.path.exists(checkpoint): os.mkdir(checkpoint) skep = SkepModel.from_pretrained(model_name) model = SkepForSquenceClassification(skep, num_classes=len(train_ds.label_list)) num_training_steps = len(train_loader) * num_epoch lr_scheduler = LinearDecayWithWarmup(learning_rate=learning_rate, total_steps=num_training_steps, warmup=warmup_proportion) decay_params = [p.name for n, p in model.named_parameters() if not any(nd in n for nd in ["bias", "norm"])] grad_clip = paddle.nn.ClipGradByGlobalNorm(max_grad_norm) optimizer = paddle.optimizer.AdamW(learning_rate=lr_scheduler, parameters=model.parameters(), weight_decay=weight_decay, apply_decay_param_fun=lambda x: x in decay_params, grad_clip=grad_clip) metric = AccuracyAndF1()
[2021-11-10 14:47:07,184] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/skep_ernie_1.0_large_ch/skep_ernie_1.0_large_ch.pdparams
本节我们将定义一个train函数,我们将在该函数中进行训练模型。在训练过程中,每隔log_steps步打印一次日志,以观测模型训练效果。相关代码如下:
def evaluate(model, data_loader, metric): model.eval() metric.reset() for idx, batch_data in enumerate(data_loader): input_ids, token_type_ids, labels = batch_data logits = model(input_ids, token_type_ids=token_type_ids) # count metric correct = metric.compute(logits, labels) metric.update(correct) accuracy, precision, recall, f1, _ = metric.accumulate() return accuracy, precision, recall, f1 def train(): global_step = 1 model.train() for epoch in range(1, num_epoch+1): for batch_data in train_loader(): input_ids, token_type_ids, labels = batch_data logits = model(input_ids, token_type_ids=token_type_ids) loss = F.cross_entropy(logits, labels) loss.backward() lr_scheduler.step() optimizer.step() optimizer.clear_grad() if global_step > 0 and global_step % log_step == 0: print(f"epoch: {epoch} - global_step: {global_step}/{num_training_steps} - loss:{loss.numpy().item():.6f}") global_step += 1 paddle.save(model.state_dict(), f"{checkpoint}/final.pdparams") train()
epoch: 1 - global_step: 20/252 - loss:0.664008
epoch: 1 - global_step: 40/252 - loss:0.815231
epoch: 1 - global_step: 60/252 - loss:0.662032
epoch: 1 - global_step: 80/252 - loss:0.609795
epoch: 2 - global_step: 100/252 - loss:0.606419
epoch: 2 - global_step: 120/252 - loss:0.653587
epoch: 2 - global_step: 140/252 - loss:0.592340
epoch: 2 - global_step: 160/252 - loss:0.961530
epoch: 3 - global_step: 180/252 - loss:0.514817
epoch: 3 - global_step: 200/252 - loss:0.630167
epoch: 3 - global_step: 220/252 - loss:0.496368
epoch: 3 - global_step: 240/252 - loss:0.686450
实现一个模型预测的函数,实现任意输入一串带有情感的文本串,如:“交通方便;环境很好;服务态度很好 房间较大”,期望能够输出这段文本描述中所蕴含的情感类别。首先我们先加载训练好的模型参数,然后进行推理。相关代码如下。
# process data related
model_name = "skep_ernie_1.0_large_ch"
model_path = os.path.join(checkpoint, "final.pdparams")
id2label = {0:"Negative", 1:"Positive"}
# load model
loaded_state_dict = paddle.load(model_path)
ernie = SkepModel.from_pretrained(model_name)
model = SkepForSquenceClassification(ernie, num_classes=len(train_ds.label_list))
model.load_dict(loaded_state_dict)
[2021-11-10 14:51:00,184] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/skep_ernie_1.0_large_ch/skep_ernie_1.0_large_ch.pdparams
def predict(text, text_pair, model, tokenizer, id2label, max_seq_len=256): model.eval() # processing input text encoded_inputs = tokenizer(text=text, text_pair=text_pair, max_seq_len=max_seq_len) input_ids = paddle.to_tensor([encoded_inputs["input_ids"]]) token_type_ids = paddle.to_tensor([encoded_inputs["token_type_ids"]]) # predict by model and decoding result logits = model(input_ids, token_type_ids=token_type_ids) label_id = paddle.argmax(logits, axis=1).numpy()[0] # print predict result print(f"text: {text} \ntext_pair:{text_pair} \nlabel: {id2label[label_id]}") text = "display#quality" text_pair = "mk16i用后的体验感觉不错,就是有点厚,屏幕分辨率高,运行流畅,就是不知道能不能刷4.0的系统啊" predict(text, text_pair, model, tokenizer, id2label, max_seq_len=max_seq_len)
text: display#quality
text_pair:mk16i用后的体验感觉不错,就是有点厚,屏幕分辨率高,运行流畅,就是不知道能不能刷4.0的系统啊
label: Positive
PaddleEdu使用过程中有任何问题欢迎在awesome-DeepLearning提issue,同时更多深度学习资料请参阅飞桨深度学习平台。
记得点个Star⭐收藏噢~~
目前QQ群已有2000+同学一起学习,欢迎扫码加入
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。