第一学习在情绪分析比赛中使用Paddlehub

这篇博客主要是记录一下第一次学习使用Paddlehub来进行情感分析。(备注:这里的代码是参考一个比赛大佬的baseline,如果有侵权的话,可以联系我,我会删除引用的code的。)

1.数据处理

该比赛原本的数据是采用GB2312编码,如果用平常的读.csv文件的方法可能行不通,所以我们可以先通过将数据文件的编码格式转换为通常的utf-8格式。通过写一个函数,函数的功能无非是读文件,然后写文件,具体代码如下所示。这里要注意一下,数据文件经过一次数据转化就可以了,不然重复转化,因为文件本身已经是utf-8编码格式,如果再用其他编码格式读取内容在,重新写会,文件数据就会被破坏。

# 转换编码
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')

当数据文件的编码格式修改位utf-8之后,我们就可以用平常习惯的方式进行读取数据了。


# 读取数据
import pandas as pd
train_labled = pd.read_csv('data/data22724/nCoV_100k_train.labled.csv', encoding='utf-8')
test = pd.read_csv('data/data22724/nCov_10k_test.csv', encoding='utf-8')

得到数据之后,查看一下数据的shape,以及查看数据的columns。

print(train_labled.shape)
print(test.shape)
print(train_labled.columns)

然后观察训练集和测试集的部分数据情况。

train_labled.head(3)
test.head(3)

该比赛是对文本数据进行情感倾向分类的,共有三个类别:-1(消极),0(中性),1(积极)。所以我们要观察一下数据label的分布情况。

# 标签分布
%matplotlib inline
train_labled['情感倾向'].value_counts(normalize=True).plot(kind='bar');

经观察图形可知,训练集数据的label里面有一些异常异常label数据,然后就需要将异常数据进行剔除。

# 清除异常标签数据
train_labled = train_labled[train_labled['情感倾向'].isin(['-1','0','1'])]

然后对文本内容的长度进行信息分析。

train_labled['微博中文内容'].str.len().describe()

2.构建模型

2.1paddlehub简介

(引用内容来源:https://yunyaniu.blog.csdn.net/article/details/104404833)
此次不使用通常使用的机器学习模型或者是tensorflow等深度学习模型,而使用一种新接触的Paddlehub进行构建模型,下面先简单介绍一下什么是Paddlehub。

PaddleHub是飞桨预训练模型管理和迁移学习工具,通过PaddleHub开发者可以使用高质量的预训练模型结合Fine-tune API快速完成迁移学习到应用部署的全流程工作。其提供了飞桨生态下的高质量预训练模型,涵盖了图像分类、目标检测、词法分析、语义模型、情感分析、视频分类、图像生成、图像分割、文本审核、关键点检测等主流模型。更多模型详情请查看官网:https://www.paddlepaddle.org.cn/hub

基于预训练模型,PaddleHub支持以下功能:
1)模型即软件,通过Python API或命令行实现快速预测,更方便地使用PaddlePaddle模型库。
2)迁移学习,用户通过Fine-tune API,只需要少量代码即可完成自然语言处理和计算机视觉场景的深度迁移学习。
3)服务化部署,简单一行命令即可搭建属于自己的模型的API服务。
4)超参优化,自动搜索最优超参,得到更好的模型效果。
GitHub地址:PaddleHub发布最新版本1.5.0,https://github.com/PaddlePaddle/PaddleHub

PaddlePaddle 出品的预训练模型管理和迁移学习工具,便捷地获取PaddlePaddle生态下的预训练模型,完成模型的管理和一键预测。配合使用Fine-tune API,可以基于大规模预训练模型快速完成迁移学习,让预训练模型能更好地服务于用户特定场景的应用。

其使用流程如下:
1)将数据整理成特定格式
2)定义Dataset数据类
3)加载模型
4)构建reader数据读取接口
5)确定finetune训练策略
6)配置finetune参数
7)确定任务,开始finetune(训练)
8)预测

以上流程每个步骤只需一两句代码即可完成,使得我们可以快速得到解决方案。

2.2具体构建模型

首先,我们需要安装Paddlehub库,根据我之前的安装经验,我建议,按照如下安装顺序可能比较容易安装成功。

!pip install paddlepaddle
!pip install PaddlePaddle
!pip install paddlehub

然后测试一下paddlehub是否安装成功。

import paddlehub as hub

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')

加载文本类自定义数据集,用户仅需要继承基类BaseNLPDatast,修改数据集存放地址以及类别即可。这里我们没有带标签的测试集,所以test_file直接用验证集代替 “valid.txt” 。

# 自定义数据集
import os
import codecs #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))

这里我们选择加载ERNIE1.0的中文预训练模型。ERNIE 1.0 通过建模海量数据中的词、实体及实体关系,学习真实世界的语义知识。相较于 BERT 学习原始语言信号,ERNIE 直接对先验语义知识单元进行建模,增强了模型语义表示能力。

其他模型见:https://github.com/PaddlePaddle/PaddleHub
只需修改name=‘xxx’ 就可以切换不同的模型

# 加载模型
import paddlehub as hub
module = hub.Module(name="ernie")

构建一个文本分类的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)

选择迁移优化策略。详见 Strategy

此处我们设置最大学习率为 learning_rate=5e-5。

权重衰减设置为 weight_decay=0.01,其避免模型overfitting。

训练预热的比例设置为 warmup_proportion=0.1, 这样前10%的训练step中学习率会逐步提升到learning_rate

# finetune策略
strategy = hub.AdamWeightDecayStrategy(
    weight_decay=0.01,
    warmup_proportion=0.1,
    learning_rate=5e-5)

设置配置,设置训练时的epoch,batch_size,模型储存路径等参数。
这里我们设置训练轮数 num_epoch = 1,模型保存路径 checkpoint_dir=“model”, 每100轮(eval_interval)对验证集验证一次评分,并保存最优模型。需要注意的是,use_cuda参数表示是否用GPU进行运行,如果不想用GPU的话,可以将use_cuda改为False即可,

# 运行配置
config = hub.RunConfig(
    use_cuda=True,
    num_epoch=1,
    checkpoint_dir="model",
    batch_size=32,
    eval_interval=100,
    strategy=strategy)

组建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"])

开始finetune
我们使用finetune_and_eval接口就可以开始模型训练,finetune过程中,会周期性的进行模型效果的评估。

# finetune
run_states = cls_task.finetune_and_eval()

模型训练结束之后,进行测试集预测。
预测数据格式为二维的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]

生成预测结果

# 生成预测结果
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.csv', index=False)
submission.head()

在这里插入图片描述
查看预测的具体结果

submission['text'] = test[['微博中文内容']].fillna(' ').values
submission['label'] = submission['y'].map({-1: '消极', 0: '中性', 1: '积极'})
display(submission[['text', 'label']][176:181])

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章