第22天:NLP实战(六)——基于PaddleHub的疫情期间网民情绪识别
首先给大家说一声抱歉,最近一直在准备秋招的事宜,没有很多时间学习NLP相关的知识。前面通过五次的实战,大致的将自然语言处理中经常用到的算法过了一遍,并且循序渐进,由浅入深。由刚开始的教我们如何从零开始获取语料,并且进行常用的数据分析到深度学习中最为简单的DNN网络带大家将环境搭建好,并且初步的介绍一个深度学习模型的流程,接下来就是到了最为核心的CNN模型、RNN中的GRU模型,并且分别用了典型的案例进行了实践。上一次我们通过NLP中典型的词向量对其进行了项目电影评论情感分析。前几次的项目都是利用百度飞桨的AI·Studio的paddle来进行的,因此本次就是利用paddle hub进行的疫情期间网民情绪识别。
背景介绍
新型冠状病毒(COVID-19)感染的肺炎疫情牵动着全国人民的心,全国同舟共济、众志成城,打响了一场没有硝烟的疫情阻击战。习主席指出:要鼓励运用大数据、人工智能、云计算等数字技术,在疫情监测分析、病毒溯源、防控救治、资源调配等方面更好发挥支撑作用。为助力疫情防控和疫情之后的经济社会恢复工作,推动北京市政府数据开放,吸纳大数据产业顶尖社会资源,充分释放专业人才智慧资源,北京市经济和信息化局、中国计算机学会大数据专家委员会联合主办科技战疫·大数据公益挑战赛。 2019新型冠状病毒(2019-nCoV)感染的肺炎疫情发生对人们生活生产的方方面面产生了重要影响,并引发国内舆论的广泛关注,众多网民参与疫情相关话题的讨论。为了帮助政府掌握真实社会舆论情况,科学高效地做好防控宣传和舆情引导工作,本师资培训项目针对疫情相关话题开展网民情绪识别的任务。
任务描述
数据给定了微博ID和微博内容,需要同学们设计算法对微博内容进行情绪识别,判断微博内容是积极的、消极的还是中性的。数据集依据与“新冠肺炎”相关的230个主题关键词进行数据采集,抓取了2020年1月1日—2020年2月20日期间共计100万条微博数据,并对其中10万条数据进行人工标注,标注分为三类,分别为:1(积极),0(中性)和-1(消极)。微博内容包括了文本、图片、视频等内容。本项目选用文本内容进行微博内容的情绪识别。采用Macro-F1值进行评分。
数据集介绍
我们本次项目使用的数据集是微博上在疫情期间的微博上的评论。本数据集可以在疫情期间网民情绪识别上自行下载。本人将数据集下载下来了,为了以防大家因为各种原因下载不下来,将数据集上传到了百度云,大家可以自行下载[验证码:7m38]。我们可以大致查看其数据集的相关内容: 大概的框架如图所示:
接下来是train_label的相关内容:
其中的微博ID和结果如下:
我们看下test_data的数据集内容如下:
实验环境搭建以及实验平台前几篇文章均介绍过,只要是跑了上一篇文章的这次实验环境不用动,要是没跑的可以去跑一遍上一个项目:NLP实战(五)——词向量Skip-gram实践,当然也可以按照前面介绍搭建好环境可以直接跑本项目。
项目实现
1、数据分析
首先我们先将挂载的数据集解压出来。具体代码实现如下:
!cd data/data22724 && unzip -o test_dataset.zip
!cd data/data22724 && unzip -o "train_ dataset.zip"
解压的结果如下:
由于数据采用GB2312编码,因此先将数据读出,转换为utf8编码再重新写入,方便后续pandas库的使用处理。
# 转换编码
def re_encode(path):
with open(path, 'r', encoding='GB2312', errors='ignore') as file:
lines = file.readlines()
with open(path, 'w', encoding='utf-8') as file:
file.write(''.join(lines))
re_encode('data/data22724/nCov_10k_test.csv') # 这里写你自己数据集的路径
re_encode('data/data22724/nCoV_100k_train.labled.csv') # 写自己文件的路径
2、数据预览
读取数据,查看数据大小、列名,可以看到:训练集包含十万条数据,测试集包含一万条数据。当然,数据包括了微博id, 微博发布时间, 发布人账号, 微博中文内容, 微博图片, 微博视频, 情感倾向。其中微博中文内容是我们将使用的训练和测试数据,情感倾向为问题标签。
# 读取数据
import pandas as pd
train_labled = pd.read_csv('data/data22724/nCoV_100k_train.labled.csv', engine ='python')
test = pd.read_csv('data/data22724/nCov_10k_test.csv', engine ='python')
print(train_labled.shape)
print(test.shape)
print(train_labled.columns)
打印的结果如下:
我们首先查看数据,实现结果如下:
train_labled.head(3)
test.head(3)
数据表打印如图所示:
3、标签分布
问题标签分为三类,分别为:1(积极),0(中性)和-1(消极),其分布如下。
%matplotlib inline
train_labled['情感倾向'].value_counts(normalize=True).plot(kind='bar');
其中的分布图如图所示:
从图中可以看出,中性数据数量占一半以上,积极次之,消极最少。此外,数据中还包含少量的未知标签,我们将其视为异常数据剔除。我可以清除异常标签数据。
# 清除异常标签数据
train_labled = train_labled[train_labled['情感倾向'].isin(['-1','0','1'])]
4、文本长度
训练集文本长度最大为 241,平均为 87。我们可以查看其中数据的分布。
train_labled['微博中文内容'].str.len().describe()
5、用PaddleHub快速实现Fine Tune
如果只考虑微博文本数据,需要解决的问题其实是文本分类问题。随着2018年ELMo、BERT等模型的发布,NLP领域进入了“大力出奇迹”的时代。采用大规模语料上进行无监督预训练的深层模型,在下游任务数据上微调一下,即可达到很好的效果。曾经需要反复调参、精心设计结构的任务,现在只需简单地使用更大的预训练数据、更深层的模型便可解决。因此NLP项目的入手门槛也随之变低。新手只要选择好预训练模型,在自己数据上微调,很多情况下就可以达到很好的效果。本项目将采用百度出品的PaddleHub预训练模型微调工具,快速构建比赛方案。模型方面则选择ERNIE模型。 ERNIE 1.0 通过建模海量数据中的词、实体及实体关系,学习真实世界的语义知识。相较于 BERT 学习原始语言信号,ERNIE 直接对先验语义知识单元进行建模,增强了模型语义表示能力。PaddlePaddle 出品的预训练模型管理和迁移学习工具,便捷地获取PaddlePaddle生态下的预训练模型,完成模型的管理和一键预测。配合使用Fine-tune API,可以基于大规模预训练模型快速完成迁移学习,让预训练模型能更好地服务于用户特定场景的应用。 其中的流程如下:
- 将数据整理成特定格式
- 定义Dataset数据类
- 加载模型
- 构建reader数据读取接口
- 确定finetune训练策略
- 配置finetune参数
- 确定任务,开始finetune(训练)
- 预测
以上流程每个步骤只需一两句代码即可完成,使得我们可以快速得到解决方案。首先,我们应该要更新并且安装PaddleHub.只需要pip安装即可。
!pip install paddlehub==1.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
6、数据整理
PaddleHub文本任务的输入数据格式为:
我们将数据划分为训练集和验证集,比例为8:2, 然后保存为文本文件,两列需用tab分隔符隔开。
# 划分验证集,保存格式 text[t]label
from sklearn.model_selection import train_test_split
train_labled = train_labled[['微博中文内容', '情感倾向']]
train, valid = train_test_split(train_labled, test_size=0.2, random_state=2020)
train.to_csv('/home/aistudio/data/data22724/train.txt', index=False, header=False, sep='t')
valid.to_csv('/home/aistudio/data/data22724/valid.txt', index=False, header=False, sep='t')
7、自定义数据加载
加载文本类自定义数据集,用户仅需要继承基类BaseNLPDatast,修改数据集存放地址以及类别即可。这里我们没有带标签的测试集,所以test_file直接用验证集代替 “valid.txt” 。
# 自定义数据集
import os
import codecs
import csv
from paddlehub.dataset.base_nlp_dataset import BaseNLPDataset
class MyDataset(BaseNLPDataset):
"""DemoDataset"""
def __init__(self):
# 数据集存放位置
self.dataset_dir = "/home/aistudio/data/data22724"
super(MyDataset, self).__init__(
base_path=self.dataset_dir,
train_file="train.txt",
dev_file="valid.txt",
test_file="valid.txt",
train_file_with_header=False,
dev_file_with_header=False,
test_file_with_header=False,
# 数据集类别集合
label_list=["-1", "0", "1"])
dataset = MyDataset()
for e in dataset.get_train_examples()[:3]:
print("{}t{}t{}".format(e.guid, e.text_a, e.label))
打印结果如下:
8、加载模型
这里我们选择ERNIE1.0的中文预训练模型。当然paddlehub也有其他的模型。我们只需修改name=‘xxx’ 就可以切换不同的模型。
# 加载模型
import paddlehub as hub
module = hub.Module(name="ernie")
加载的结果如下:
9、构建Reader
构建一个文本分类的reader,reader负责将dataset的数据进行预处理,首先对文本进行切词,接着以特定格式组织并输入给模型进行训练。通过max_seq_len可以修改最大序列长度,若序列长度不足,会通过padding方式补到max_seq_len, 若序列长度大于该值,则会以截断方式让序列长度为max_seq_len, 这里我们设置为128。
# 构建Reader
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
sp_model_path = module.get_spm_path(),
word_dict_path = module.get_word_dict_path(),
max_seq_len=128
)
10、finetune策略
选择迁移优化策略。此处我们设置最大学习率为 learning_rate=5e-5。权重衰减设置为 weight_decay=0.01,其避免模型overfitting。训练预热的比例设置warmup_proportion=0.1, 这样前10%的训练step中学习率会逐步提升到learning_rate。
# finetune策略1
strategy = hub.L2SPFinetuneStrategy(
learning_rate=1e-4,
optimizer_name="adam",
regularization_coeff=1e-3)
11、运行配置
设置训练时的epoch,batch_size,模型储存路径等参数。这里我们设置训练轮数 num_epoch = 1,模型保存路径 checkpoint_dir=“model”, 每100轮(eval_interval)对验证集验证一次评分,并保存最优模型。
# 运行配置
config = hub.RunConfig(
use_cuda=True,
num_epoch= 10,
batch_size= 128,
checkpoint_dir="hub_finetune",
strategy=strategy
)
12、组建Finetune Task
对于文本分类任务,我们需要获取模型的池化层输出,并在后面接上全连接层实现分类。因此,我们先获取module的上下文环境,包括输入和输出的变量,并从中获取池化层输出作为文本特征。再接入一个全连接层,生成Task。评价指标为F1,因此设置metrics_choices=[“f1”]。
# 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,
metrics_choices=["f1"])
13、开始finetune
我们使用finetune_and_eval接口就可以开始模型训练,finetune过程中,会周期性的进行模型效果的评估。
# finetune
run_states = cls_task.finetune_and_eval()
14、预测
finetune完成后,调用predict接口即可完成预测,预测数据格式为二维的list:[[‘第一条文本’], [‘第二条文本’], […], …]
# 预测
import numpy as np
inv_label_map = {val: key for key, val in reader.label_map.items()}
# Data to be prdicted
data = test[['微博中文内容']].fillna(' ').values.tolist()
run_states = cls_task.predict(data=data)
results = [run_state.run_results for run_state in run_states]
15、生成结果
# 生成预测结果
proba = np.vstack([r[0] for r in results])
prediction = list(np.argmax(proba, axis=1))
prediction = [inv_label_map[p] for p in prediction]
submission = pd.DataFrame()
submission['id'] = test['微博id'].values
submission['id'] = submission['id'].astype(str) + ' '
submission['y'] = prediction
np.save('proba.npy', proba)
submission.to_csv('result_ernie.csv', index=False)
submission.head()
让我们来看看具体预测结果:
submission['text'] = test[['微博中文内容']].fillna(' ').values
submission['label'] = submission['y'].values
display(submission[['text', 'label']][175:180])
总结
本项目使用了PaddleHub完成了疫情期间网民情绪识别比赛方案的快速构建, 并得到了不错的识别效果。PaddleHub包含了大量的预训练模型。不管是NLP还是CV,我们都可以使用PaddleHub来实现我们的baseline,不需要繁琐的代码,就可以快速切换不同模型来进行实验效果验证。疫情之下,掌握真实社会舆论情况,才能科学地做好防控宣传和舆情引导工作,AI技术可以帮助我们高效地完成识别。这些代码均可运行,希望大家在有时间的情况下,可以动手运行一遍,感受一下paddle hub是如何被应用的,另外就是感受一下一个网络的训练过程和预测过程。当然在文中也多次提到需要大家注意的几个问题,希望大家在练习的时候要特别关注,练习完之后感觉收获满满。加油,希望我们都学有所获,坚持练习,我们未来可期。
- LINQ to SQL(4):OR设计器
- 在不同浏览器都实用的各窗口大小获取方法
- JavaScript获得对象属性个数的方法
- 设计模式专题(十一)——抽象工厂模式
- Canvas 图形组合方式
- HTML5 FileReader接口学习笔记
- 如何用BBED使Offline的数据文件Online
- Entity Framework4.3 Code-First基于代码的数据迁移讲解1.建立一个最初的模型和数据库 2.启动Migration(数据迁移)3.第一个数据迁移4.订制的数据迁移4.动态
- 轻松初探Python(六)—函数
- css3实现圆角边框渐变
- 设计模式专题(十二)——状态模式
- HTML5新增属性学习笔记
- HTML5标签学习笔记
- 设计模式专题(十三) ——备忘录模式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法