PaddleHub实战:基于OCEMOTION的中文微情感分析系统

一.项目成果展示

视频链接: https://www.bilibili.com/video/BV1944y1C7FQ/

二. 项目介绍

2.1 项目简介:

本项目主要基于PaddleHub通过预训练模型Erine-tiny在中文7情感分类数据集OCEMOTION上进行微调从而完成7分类情感分析模型的搭建,并基于PyQt5完成了最终中文微情感分析系统的开发,支持单条和批量文本细粒度情感分类预测,具有前沿性和广泛的应用价值。同时全流程教程讲解将带你玩转一个完整文本分类项目的开发!

2.2 项目亮点:

a.不同于传统的情感2分类(正向和负向),本项目使用了7分类数据集OCEMOTION可以达到更细粒度的情感分析,从而可以更好分析用户评论中表达情感,具有前沿性和广泛的应用价值。

b.基于PaddleHub通过预训练模型Erine-tiny的微调完成情感分析模型的搭建。基于大规模未标注语料库的预训练模型(Pretrained Models, PTM) 能够习得通用的语言表示,将预训练模型Fine-tune到下游任务,能够获得比传统分类模型Lstm等更出色的表现,也成为了目前竞赛及项目的主流选择。另外,预训练模型能够避免从零开始训练模型。

c.面向小白的全流程实战教程,全流程细致讲解带你拿下一个完整的文本分类实战项目!项目可扩展性高,感兴趣的也可以在其基础上做出更多的优化或迁移到类似的文本分类项目中去哦!喜欢人数多的话后面将考虑推出进阶教程哦!

2.3 情感分析研究意义:

在评论网站、论坛、博客和社交媒体中,可以获得大量表达意见的文本。而这些文本数据都是非结构化的,没有以预先定义的方式组织,数据量庞大通常难以分析、理解和分类,既费时又费钱。而在情感分析系统的帮助下,这种非结构化信息可以依靠自动化业务流程以有效且低成本的方式大规模转换为结构化数据,极大减少人工标注成本,提高效率。情感分析在舆情监控、话题监督、口碑分析等商业分析领域有着非常重要的应用价值。目前该技术也已有着较广泛的应用,例如新浪微博运用情感分析对全网数据进行挖掘构建舆情大数据平台。电商平台运用情感分析来进行商品评论挖掘,作为推荐系统的一部分提高营销效果。小度机器人通过识别用户在聊天中的情绪,帮助选择出更匹配用户情绪的文本进行回复。在不远的未来,情感分析也将成为现代公司不可或缺的工具。但目前情感分析仍然局限于有限的简单分类主要为2分类,而有限的情感分类并不能很好地挖掘文本中包含的微情感,不能很好地满足需求。故细粒度的情感分析研究具有前沿性和更广泛的应用价值。

2.4 运行环境要求:

注意模型的训练需要到GPU环境,一键fork后选择GPU环境即可运行!

可视化界面核心项目代码在work目录下"中文微情感分析系统"文件夹内,选择该文件夹并点击下载后将整个文件夹下载到本地,接下来根据文件夹内提供的‘环境配置指南及使用说明’进行操作即可,本地在CPU环境下也可以运行。

Github项目地址:https://github.com/hchhtc123/Emotion-analysis-system

2.5 项目总技术路线

a.对中文情感7分类数据集OCEMOTION进行数据清洗,并按照具体类别按8:1:1的比例划分训练、验证和测试数据集。

b.基于PaddleHub通过预训练模型的微调完成7分类中文微情感分析模型的训练与优化。

c.基于PyQt5完成可视化界面的开发,支持单条和批量文本情感分类预测。最后通过pyinstaller完成系统的打包便于演示。

三. OCEMOTION数据集

OCEMOTION是包含7个分类的细粒度情感性分析数据集,其中7个情感类别分别为sadness、happiness、disgust、anger、like、surprise、fear,适用于构建细粒度情感分析模型。 文件格式为:id 句子 标签,以’\t’分隔。

数据集引用说明:

Minglei Li, Yunfei Long, Qin Lu, and Wenjie Li. “Emotion Corpus Construction Based on Selection from Hashtags.” In Proceedings of International Conference on Language Resources and Evaluation (LREC). Portorož, Slovenia, 2016

论文链接: https://www.aclweb.org/anthology/L16-1291.pdf

  OCEMOTION数据集已上传AI Studio,在数据集那搜索“OCEMOTION-中文7分类细粒度情感分析数据集”后添加即可。

该OCEMOTION数据集具体下载主要来自于作者入门NLP时参加的全球人工智能技术创新大赛【热身赛二】提供的训练数据,比赛地址:https://tianchi.aliyun.com/competition/entrance/531865/information 感兴趣的也可以去了解下那个比赛,是道很经典的多任务赛题哦,适合学习!

扩展:分享下个人收藏的一些很不错的数据集查找网站:

Graviti Open Datasets:https://gas.graviti.cn/open-datasets

天池数据集:https://tianchi.aliyun.com/dataset

Papers With Code数据集:https://www.paperswithcode.com/datasets

AI Studio数据集:https://aistudio.baidu.com/aistudio/datasetoverview

3.1 解压并查看数据集

# 解压数据集
%cd /home/aistudio/data/data100731/
!unzip shuju.zip
/home/aistudio/data/data100731
Archive:  shuju.zip
  inflating: OCEMOTION.csv           
# 使用pandas读取数据集
import pandas as pd
data = pd.read_csv('OCEMOTION.csv', sep='\t',header=None)
# 由于该数据集没有列名,故需要为其添加上列名以便于更好处理
data.columns = ["id", "text_a", "label"]
# 查看数据前5条内容
data.head()
idtext_alabel
00'你知道多伦多附近有什么吗?哈哈有破布耶...真的书上写的你听哦...你家那块破布是世界上最...sadness
11平安夜,圣诞节,都过了,我很难过,和妈妈吵了两天,以死相逼才终止战争,现在还处于冷战中。sadness
22我只是自私了一点,做自己想做的事情!sadness
33让感动的不仅仅是雨过天晴,还有泪水流下来的迷人眼神。happiness
44好日子happiness
# 查看数据文件信息,可以看出总共有35315条数据
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 35315 entries, 0 to 35314
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   id      35315 non-null  int64 
 1   text_a  35315 non-null  object
 2   label   35315 non-null  object
dtypes: int64(1), object(2)
memory usage: 827.8+ KB
# 统计评论文本长度信息,从平均长度可以看出属于短文本
data['text_a'].map(len).describe()
count    35315.000000
mean        48.214328
std         84.391942
min          3.000000
25%         18.000000
50%         34.000000
75%         67.000000
max      12326.000000
Name: text_a, dtype: float64
# 统计数据集中7种情感类别标签的分布情况
data['label'].value_counts()
sadness      12475
happiness     8894
disgust       4347
anger         4068
like          4042
surprise       899
fear           590
Name: label, dtype: int64
# 可视化标签分布情况
%matplotlib inline
data['label'].value_counts(normalize=True).plot(kind='bar');

在这里插入图片描述

3.2 数据清洗

# 导入所需包
import re
import os
import shutil
from tqdm import tqdm
from collections import defaultdict

# 定义数据清洗函数:

# 清洗分隔字符
def clean_duplication(text):
    left_square_brackets_pat = re.compile(r'\[+')
    right_square_brackets_pat = re.compile(r'\]+')
    punct = [',', '\\.', '\\!', ',', '。', '!', '、', '\?', '?']

    def replace(string, char):
        pattern = char + '{2,}'
        if char.startswith('\\'):
            char = char[1:]
        string = re.sub(pattern, char, string)
        return string

    text = left_square_brackets_pat.sub('', text)
    text = right_square_brackets_pat.sub('', text)
    for p in punct:
        text = replace(text, p)
    return text

def emoji2zh(text, inverse_emoji_dict):
    for emoji, ch in inverse_emoji_dict.items():
        text = text.replace(emoji, ch)
    return text

# 清洗数据集中特殊表情,通过json文件的映射用中文替代表情
def clean_emotion(data_path, emoji2zh_data, save_dir, train=True):
    data = defaultdict(list)
    filename = os.path.basename(data_path)
    with open(data_path, 'r', encoding='utf8') as f:
        texts = f.readlines()
        for line in tqdm(texts, desc=data_path):
            if train:
                id_, text, label = line.strip().split('\t')
            else:
                id_, text = line.strip().split('\t')
            data['id'].append(id_)
            text = emoji2zh(text, emoji2zh_data)
            text = clean_duplication(text)
            data['text_a'].append(text)
            if train:
                data['label'].append(label)
    df = pd.DataFrame(data)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    df.to_csv(os.path.join(save_dir, filename), index=False,
              encoding='utf8', header=False, sep='\t')
    return df
# 读取表情映射json文件(放在work目录下,文件名为emoji2zh.json),用于替换表情为中文字符
import json
emoji2zh_data = json.load(open('/home/aistudio/work/emoji2zh.json', 'r', encoding='utf8'))
# 对数据进行数据清洗
data = clean_emotion('/home/aistudio/data/data100731/OCEMOTION.csv',emoji2zh_data,'./')
/home/aistudio/data/data100731/OCEMOTION.csv: 100%|██████████| 35694/35694 [00:04<00:00, 7890.77it/s]
# 去掉无用的id列,保存其格式为text_a,label
data = data[['text_a', 'label']]

3.3 转换情感类别标签

由于类别名为英文,此处主要将英文类别名转为中文类别名,从而更好应用于中文情感分析系统中去!

# 替换数据集中标签,{'sadness': '难过', 'happiness': '愉快', 'like': '喜欢', 'anger': '愤怒', 'fear': '害怕', 'surprise': '惊讶', 'disgust': '厌恶'}
data.loc[data['label']=='sadness', 'label'] = '难过'
data.loc[data['label']=='happiness', 'label'] = '愉快'
data.loc[data['label']=='like', 'label'] = '喜欢'
data.loc[data['label']=='anger', 'label'] = '愤怒'
data.loc[data['label']=='fear', 'label'] = '害怕'
data.loc[data['label']=='surprise', 'label'] = '惊讶'
data.loc[data['label']=='disgust', 'label'] = '厌恶'

3.4 手动划分训练、验证和测试集

划分训练、验证和测试集原因:

a)训练集直接参与了模型调参的过程,显然不能用来反映模型真实的能力(防止课本死记硬背的学生拥有最好的成绩,即防止过拟合)。

b)验证集参与了人工调参(超参数)的过程,也不能用来最终评判一个模型(刷题库的学生不能算是学习好的学生)。

c) 所以要通过最终的考试(测试集)来考察一个学(模)生(型)真正的能力(期末考试)。

下面提供了两种较常见的数据集划分方式,可以根据具体需要或效果进行选择:

# # # 划分方式1:根据比例直接划分训练、验证和测试集
# from sklearn.model_selection import train_test_split
# train_data, test_data = train_test_split(data, test_size=0.2)
# train_data,valid_data=train_test_split(train_data, test_size=0.2)

# # 对数据进行随机打乱
# from sklearn.utils import shuffle
# train_data = shuffle(train_data)
# valid_data = shuffle(valid_data)
# test_data = shuffle(test_data)

# # 保存划分好的数据集文件
# train_data.to_csv('./train.csv', index=False, sep="\t") # 训练集
# valid_data.to_csv('./valid.csv', index=False, sep="\t")  # 验证集
# test_data.to_csv('./test.csv', index=False, sep="\t")   # 测试集

# print('训练集长度:', len(train_dat), '验证集长度:', len(valid_data), '测试集长度', len(test_data))
# 划分方式2:根据具体类别按8:1:1去划分训练、验证和测试集,这样可以使得数据尽量同分布

from sklearn.utils import shuffle
train = pd.DataFrame()  # 训练集
valid = pd.DataFrame()  # 验证集
test = pd.DataFrame()  # 测试集

tags = data['label'].unique().tolist()  # 按照该标签进行等比例抽取

# 根据数据集的类别按8:1:1的比例划分训练、验证和测试集并随机打乱后保存
for tag in tags:
    # 随机选取0.2的数据作为训练和验证集
    target = data[(data['label'] == tag)]
    sample = target.sample(int(0.2 * len(target)))
    sample_index = sample.index
    # 将剩余0.8的数据作为训练集
    all_index = target.index
    residue_index = all_index.difference(sample_index)  # 去除sample之后剩余的数据
    residue = target.loc[residue_index]
    # 对划分出来的0.2的数据集按等比例进行测试集和验证集的划分
    test_sample = sample.sample(int(0.5 * len(sample)))
    test_sample_index = test_sample.index
    valid_sample_index = sample_index.difference(test_sample_index)
    valid_sample = sample.loc[valid_sample_index]
    # 拼接各个类别
    test = pd.concat([test, test_sample], ignore_index=True)
    valid = pd.concat([valid, valid_sample], ignore_index=True)
    train = pd.concat([train, residue], ignore_index=True)
    # 对数据进行随机打乱
    train = shuffle(train)
    valid = shuffle(valid)
    test = shuffle(test)

# 保存为tab分隔的文本
train.to_csv('train.csv', sep='\t', index=False)  # 训练集
valid.to_csv('valid.csv', sep='\t', index=False)  # 验证集
test.to_csv('test.csv', sep='\t', index=False)    # 测试集

print('训练集长度:', len(train), '验证集长度:', len(valid), '测试集长度', len(test))
训练集长度: 28558 验证集长度: 3570 测试集长度 3566

四. 基于PaddleHub构建微情感分析模型

PaddleHub简介:

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

更多模型详情请查看官网:https://www.paddlepaddle.org.cn/hub

PaddleHub 使用过程中,遇到问题可以提issue: https://github.com/PaddlePaddle/PaddleHub/issues

基于预训练模型,PaddleHub支持以下功能:

1.模型即软件,通过Python API或命令行实现快速预测,更方便地使用PaddlePaddle模型库。

2.迁移学习,用户通过Fine-tune API,只需要少量代码即可完成自然语言处理和计算机视觉场景的深度 迁移学习。

3.服务化部署,简单一行命令即可搭建属于自己的模型的API服务。

4.超参优化,自动搜索最优超参,得到更好的模型效果。

4.1 前置环境准备

# 下载最新版本的paddlehub
!pip install -U paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple
# 导入paddlehub和paddle包
import paddlehub as hub
import paddle

4.2 加载预训练模型-ERNIE Tiny

ERNIE Tiny 主要通过模型结构压缩和模型蒸馏的方法,将 ERNIE 2.0 Base 模型进行压缩。特点和优势如下:

a.采用 3 层 transformer 结构,线性提速 4 倍;

b.模型加宽隐层参数,从 ERNIE 2.0 的 768 扩展到 1024;

c.缩短输入文本的序列长度,降低计算复杂度,模型首次采用中文 subword 粒度输入,长度平均缩短 40%;

d.ERNIE Tiny 在训练中扮演学生角色,利用模型蒸馏的方式在 Transformer 层和 Prediction 层学习教师模型 ERNIE 2.0 模型对应层的分布和输出;

综合优化能带来4.3倍的预测提速,具有更高的工业落地能力。

# 设置要求进行分类的7个情感类别
label_list=list(data.label.unique())
print(label_list)

label_map = { 
    idx: label_text for idx, label_text in enumerate(label_list)
}
print(label_map)
['难过', '愉快', '喜欢', '愤怒', '害怕', '惊讶', '厌恶']
{0: '难过', 1: '愉快', 2: '喜欢', 3: '愤怒', 4: '害怕', 5: '惊讶', 6: '厌恶'}
# 只需指定想要使用的模型名称和文本分类的类别数即可完成Fine-tune网络定义,在预训练模型后拼接上一个全连接网络(Full Connected)进行分类
# 此处选择ernie_tiny预训练模型并设置微调任务为7分类任务
model = hub.Module(name="ernie_tiny", task='seq-cls', num_classes=7, label_map=label_map)
Download https://bj.bcebos.com/paddlehub/paddlehub_dev/ernie_tiny_2.0.2.tar.gz
[##################################################] 100.00%
Decompress /home/aistudio/.paddlehub/tmp/tmpyvupawg3/ernie_tiny_2.0.2.tar.gz
[##################################################] 100.00%


[2021-08-04 23:21:25,328] [    INFO] - Successfully installed ernie_tiny-2.0.2
[2021-08-04 23:21:25,332] [    INFO] - Downloading https://paddlenlp.bj.bcebos.com/models/transformers/ernie_tiny/ernie_tiny.pdparams and saved to /home/aistudio/.paddlenlp/models/ernie-tiny
[2021-08-04 23:21:25,335] [    INFO] - Downloading ernie_tiny.pdparams from https://paddlenlp.bj.bcebos.com/models/transformers/ernie_tiny/ernie_tiny.pdparams
100%|██████████| 354158/354158 [00:04<00:00, 71445.77it/s]
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1297: UserWarning: Skip loading for classifier.weight. classifier.weight is not found in the provided dict.
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1297: UserWarning: Skip loading for classifier.bias. classifier.bias is not found in the provided dict.
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))

hub.Module的参数用法如下:

  • name:模型名称,可以选择ernieernie_tinybert-base-casedbert-base-chinese, roberta-wwm-extroberta-wwm-ext-large等。
  • task:fine-tune任务。此处为seq-cls,表示文本分类任务。
  • num_classes:表示当前文本分类任务的类别数,根据具体使用的数据集确定,默认为2,需要根据具体分类任务进行选定。

PaddleHub模型搜索

PaddleHub还提供BERT等模型可供选择, 当前支持文本分类任务的模型对应的加载示例如下:

模型名PaddleHub Module
ERNIE, Chinesehub.Module(name='ernie')
ERNIE tiny, Chinesehub.Module(name='ernie_tiny')
ERNIE 2.0 Base, Englishhub.Module(name='ernie_v2_eng_base')
ERNIE 2.0 Large, Englishhub.Module(name='ernie_v2_eng_large')
BERT-Base, English Casedhub.Module(name='bert-base-cased')
BERT-Base, English Uncasedhub.Module(name='bert-base-uncased')
BERT-Large, English Casedhub.Module(name='bert-large-cased')
BERT-Large, English Uncasedhub.Module(name='bert-large-uncased')
BERT-Base, Multilingual Casedhub.Module(nane='bert-base-multilingual-cased')
BERT-Base, Multilingual Uncasedhub.Module(nane='bert-base-multilingual-uncased')
BERT-Base, Chinesehub.Module(name='bert-base-chinese')
BERT-wwm, Chinesehub.Module(name='chinese-bert-wwm')
BERT-wwm-ext, Chinesehub.Module(name='chinese-bert-wwm-ext')
RoBERTa-wwm-ext, Chinesehub.Module(name='roberta-wwm-ext')
RoBERTa-wwm-ext-large, Chinesehub.Module(name='roberta-wwm-ext-large')
RBT3, Chinesehub.Module(name='rbt3')
RBTL3, Chinesehub.Module(name='rbtl3')
ELECTRA-Small, Englishhub.Module(name='electra-small')
ELECTRA-Base, Englishhub.Module(name='electra-base')
ELECTRA-Large, Englishhub.Module(name='electra-large')
ELECTRA-Base, Chinesehub.Module(name='chinese-electra-base')
ELECTRA-Small, Chinesehub.Module(name='chinese-electra-small')

通过以上的一行代码,model初始化为一个适用于文本分类任务的模型,为ERNIE的预训练模型后拼接上一个全连接网络(Full Connected)。

4.3 加载并处理数据

# 导入依赖库
import os, io, csv
from paddlehub.datasets.base_nlp_dataset import InputExample, TextClassificationDataset
# 数据集存放位置
DATA_DIR="/home/aistudio/data/data100731/"
# 对数据进行处理,处理为模型可接受的格式
class OCEMOTION(TextClassificationDataset):
    def __init__(self, tokenizer, mode='train', max_seq_len=128):
        if mode == 'train':
            data_file = 'train.csv'  # 训练集
        elif mode == 'test':
            data_file = 'test.csv'   # 测试集
        else:
            data_file = 'valid.csv'  # 验证集
        
        super(OCEMOTION, self).__init__(
            base_path=DATA_DIR,
            data_file=data_file,
            tokenizer=tokenizer,
            max_seq_len=max_seq_len,
            mode=mode,
            is_file_with_header=True,
            label_list=label_list
            )

    # 解析文本文件里的样本
    def _read_file(self, input_file, is_file_with_header: bool = False):
        if not os.path.exists(input_file):
            raise RuntimeError("The file {} is not found.".format(input_file))
        else:
            with io.open(input_file, "r", encoding="UTF-8") as f:
                reader = csv.reader(f, delimiter="\t")
                examples = []
                seq_id = 0
                header = next(reader) if is_file_with_header else None
                for line in reader:
                    try:
                        example = InputExample(guid=seq_id, text_a=line[0], label=line[1])
                        seq_id += 1
                        examples.append(example)
                    except:
                        continue
                return examples
                
train_dataset = OCEMOTION(model.get_tokenizer(), mode='train', max_seq_len=128)  # max_seq_len根据具体文本长度进行确定,但需注意max_seq_len最长不超过512
dev_dataset = OCEMOTION(model.get_tokenizer(), mode='dev', max_seq_len=128)
test_dataset = OCEMOTION(model.get_tokenizer(), mode='test', max_seq_len=128)

# 查看训练集前3条
for e in train_dataset.examples[:3]:
    print(e)
# 查看验证集前3条
for e in dev_dataset.examples[:3]:
    print(e)
# 查看测试集前3条
for e in test_dataset.examples[:3]:
    print(e)
[2021-08-04 23:21:44,835] [    INFO] - Downloading vocab.txt from https://paddlenlp.bj.bcebos.com/models/transformers/ernie_tiny/vocab.txt
100%|██████████| 459/459 [00:00<00:00, 5903.16it/s]
[2021-08-04 23:21:45,047] [    INFO] - Downloading spm_cased_simp_sampled.model from https://paddlenlp.bj.bcebos.com/models/transformers/ernie_tiny/spm_cased_simp_sampled.model
100%|██████████| 1083/1083 [00:00<00:00, 11921.14it/s]
[2021-08-04 23:21:45,199] [    INFO] - Downloading dict.wordseg.pickle from https://paddlenlp.bj.bcebos.com/models/transformers/ernie_tiny/dict.wordseg.pickle
100%|██████████| 161822/161822 [00:02<00:00, 65316.99it/s]
[2021-08-04 23:22:00,079] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/vocab.txt
[2021-08-04 23:22:00,083] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/spm_cased_simp_sampled.model
[2021-08-04 23:22:00,087] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/dict.wordseg.pickle
[2021-08-04 23:22:05,911] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/vocab.txt
[2021-08-04 23:22:05,914] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/spm_cased_simp_sampled.model
[2021-08-04 23:22:05,916] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/dict.wordseg.pickle


text=是不是世界上有一种人不甘心屈居人下,是不是有一种人争一辈子竟不得,是不是有一种人宁肯死亡也不苟活。是不是有一种人被迫不能放弃。大半年了,我累了,槿汐,可是还没到安心睡觉的时候。	label=喜欢
text=我喜欢大关县的花开花谢,都无比美丽	label=喜欢
text=没有找到。	label=难过
text=徐洁奇和狗日的文学课ppt	label=愤怒
text=-我要午睡,安。	label=厌恶
text=开会,我没准备的情况下上去做presentation,某人替我捏着一大把汗。还好平时工作清楚,干脆利落三分钟把两个项目讲清楚,进展,问题,行动计划,时间死线一一讲明,满屋中外友人频频点头,某人和某人盯着我笑,齐齐偷偷冲我竖大拇指。呵呵,用英语讲工作是俺强项。哎呀,真爽。	label=愉快
text=还是需要给自己开个处方1熬,结合众人的经验这是必须的过程。2需要不断的肯定的心理暗示3需要从平时做的英语练习中工作中获得满足感和被肯定的感觉4这个是我上铺说的,必须有一件事情能给我强大的正面刺激5不间断的运动,以释放心理能量	label=难过
text=呜呜呜.贴吧密码忘了.登不了了.	label=难过
text=嘻嘻已经拒绝睡前讲唐诗有两个多月了吧,今天拿出唐诗准备讲讲,结果她自己叽咕叽咕的都背出来了,我说首诗名,她背诗,就这样一口气背了五六十首,真是让我惊喜不已!	label=愉快

4.4 选择优化策略和运行配置

# 优化器的选择,此处使用了AdamW优化器
optimizer = paddle.optimizer.AdamW(learning_rate=4e-5, parameters=model.parameters())
# 运行配置
trainer = hub.Trainer(model, optimizer, checkpoint_dir='./ckpt', use_gpu=True, use_vdl=True)      # fine-tune任务的执行者
[2021-08-04 23:22:30,859] [ WARNING] - PaddleHub model checkpoint not found, start from scratch...
运行配置

Trainer 主要控制Fine-tune任务的训练,是任务的发起者,包含以下可控制的参数:

  • model: 被优化模型;
  • optimizer: 优化器选择;
  • use_gpu: 是否使用gpu训练;
  • use_vdl: 是否使用vdl可视化训练过程;
  • checkpoint_dir: 保存模型参数的地址;
  • compare_metrics: 保存最优模型的衡量指标;

4.5 模型训练和验证

注意模型的训练需要GPU环境,在模型训练时可以通过下方的’性能监控’或者在终端输入’nvdia-smi’命令查看显存占用情况,若显存不足可以适当调小batch_size

trainer.train(train_dataset, epochs=5, batch_size=256, eval_dataset=dev_dataset, save_interval=1)   # 配置训练参数,启动训练,并指定验证集。

trainer.train 主要控制具体的训练过程,包含以下可控制的参数:

  • train_dataset: 训练时所用的数据集;
  • epochs: 训练轮数;
  • batch_size: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
  • num_workers: works的数量,默认为0;
  • eval_dataset: 验证集;
  • log_interval: 打印日志的间隔, 单位为执行批训练的次数。
  • save_interval: 保存模型的间隔频次,单位为执行训练的轮数。

4.6 在测试集上评估当前训练模型

# 在测试集上评估当前训练模型
result = trainer.evaluate(test_dataset, batch_size=128) 
[2021-08-04 23:30:35,095] [    INFO] - Evaluation on validation dataset: \
[2021-08-04 23:30:38,511] [    EVAL] - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - Evaluation on validation dataset: / - Evaluation on validation dataset: - - Evaluation on validation dataset: \ - Evaluation on validation dataset: | - [Evaluation result] avg_acc=0.5939
# 进阶扩展: 使用F1-score指标对测试集上效果进行更官方的评测
import numpy as np
# 读取测试集文件
df = pd.read_csv('./test.csv',sep = '\t')

news1 = pd.DataFrame(columns=['label'])
news1['label'] = df["label"]
news = pd.DataFrame(columns=['text_a'])
news['text_a'] = df["text_a"]

# 首先将pandas读取的数据转化为array
data_array = np.array(news)
# 然后转化为list形式
data_list =data_array.tolist()

# 对测试集进行预测得到预测的类别标签
y_pre = model.predict(data_list, max_seq_len=128, batch_size=128, use_gpu=True)

# 测试集的真实类别标签
data_array1 = np.array(news1)
y_val =data_array1.tolist()

# 计算预测结果的F1-score
from sklearn.metrics import precision_recall_fscore_support,f1_score,precision_score,recall_score
f1 = f1_score(y_val, y_pre, average='macro')
p = precision_score(y_val, y_pre, average='macro')
r = recall_score(y_val, y_pre, average='macro')
print(f1, p, r)
[2021-08-04 23:30:38,536] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/vocab.txt
[2021-08-04 23:30:38,539] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/spm_cased_simp_sampled.model
[2021-08-04 23:30:38,542] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/dict.wordseg.pickle
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/tensor/creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. 
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
  if data.dtype == np.object:


0.48899184212664987 0.5591275726811291 0.4574688432467382

ps:感兴趣的可以在基线模型的基础上通过调参、选用其他预训练模型、优化网络结构、重新预训练等方式进一步优化效果哦!

4.7 模型预测

# 要进行预测的数据
data = [
    # 难过
    ["你也不用说对不起,只是若相惜"],
    # 愉快
    ["幸福其实很简单"],
    # 害怕
    ["恐惧感啊。生病"],
    # 喜欢
    ["待你长发及腰,我们一起耕耘时光。我愿等待"]
]

# 定义要进行情感分类的7个类别
label_list=['难过', '愉快', '喜欢', '愤怒', '害怕', '惊讶', '厌恶']
label_map = {
    idx: label_text for idx, label_text in enumerate(label_list)
}

# 加载训练好的模型
model = hub.Module(
    name='ernie_tiny',
    task='seq-cls',
    num_classes=7,
    load_checkpoint='./ckpt/best_model/model.pdparams',
    label_map=label_map)

# 进行模型预测
results = model.predict(data, max_seq_len=128, batch_size=1, use_gpu=True)
for idx, text in enumerate(data):
    print('Data: {} \t Lable: {}'.format(text[0], results[idx]))
[2021-08-04 23:30:48,174] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-tiny/ernie_tiny.pdparams
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1297: UserWarning: Skip loading for classifier.weight. classifier.weight is not found in the provided dict.
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1297: UserWarning: Skip loading for classifier.bias. classifier.bias is not found in the provided dict.
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))
[2021-08-04 23:30:54,070] [    INFO] - Loaded parameters from /home/aistudio/data/data100731/ckpt/best_model/model.pdparams
[2021-08-04 23:30:54,077] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/vocab.txt
[2021-08-04 23:30:54,080] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/spm_cased_simp_sampled.model
[2021-08-04 23:30:54,083] [    INFO] - Found /home/aistudio/.paddlenlp/models/ernie-tiny/dict.wordseg.pickle
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/tensor/creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. 
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
  if data.dtype == np.object:


Data: 你也不用说对不起,只是若相惜 	 Lable: 难过
Data: 幸福其实很简单 	 Lable: 愉快
Data: 恐惧感啊。生病 	 Lable: 害怕
Data: 待你长发及腰,我们一起耕耘时光。我愿等待 	 Lable: 喜欢

五. 基于PyQt5完成可视化界面演示

ps:基于PyQt5的可视化界面核心代码已放至work/中文微情感分析系统下,将整个文件夹下载到本地后,根据提供的环境配置指南及使用说明进行操作即可运行!

# 将刚才训练好的最优模型参赛移动到work目录下,从而更好保存!
le: 难过
    Data: 幸福其实很简单 	 Lable: 愉快
    Data: 恐惧感啊。生病 	 Lable: 害怕
    Data: 待你长发及腰,我们一起耕耘时光。我愿等待 	 Lable: 喜欢


# 五. 基于PyQt5完成可视化界面演示
ps:基于PyQt5的可视化界面核心代码已放至work/中文微情感分析系统下,将整个文件夹下载到本地后,根据提供的环境配置指南及使用说明进行操作即可运行!


```python
# 将刚才训练好的最优模型参赛移动到work目录下,从而更好保存!
!cp -r /home/aistudio/data/data100731/ckpt/best_model/model.pdparams /home/aistudio/work/中文微情感分析系统/best_model/

5.1 PyQt5介绍:

PyQt5 是基于 Digia 公司强大的图形程式框架 Qt5 的 python 接口,其构建的程序可以运行于多个平台,包括:Unix、Windows 和Mac OS。QT给我们带来最方便的好处,就是它有一个QT Desiginer,这个设计器可以方便我们进行页面的布局,可以说在Tkinter里面需要一坨坨的代码完成的页面布局,在QT里面只要拖一拖控件就搞定了。

5.2 前置准备-PyQt5+Pycharm的安装和配置:

PyQt5+Pycharm安装和配置图文教程详解 网上教程较多,根据网上教程进行操作即可。

5.3 使用教程:

在完成PyQt5的安装和配置后,使用 Qt 提供的 Qt designer 软件通过拖拽即可完成页面设计UI文件然后将其导出为.py文件即页面设计程序。接着通过对界面button按钮及输入框等元素绑定相应功能函数即可完成整个系统界面功能的添加。(网上教程较多,这里不多赘述,直接上手体验更佳!)

5.4 界面美化:

Python图形界面美化的方法论,学会充分利用QSS及添加图片等美化界面效果。

本项目界面相关代码请查看‘中文微情感分析系统’文件夹下的interface.py(界面设计程序)和gui.py(主程序)。

可视化界面演示:

  1. 单条文本情感分析页面:

  1. 批量文本情感分析页面:

六. 系统打包

6.1 应用场景:

在完成系统的开发后,我们可以通过PyInstaller等完成整个python程序的打包,从而可在各平台上直接使用,免去复杂环境配置操作,更好交付给一些小白或未安装Python的小伙伴们使用也便于演示。

6.2 打包教程:

Pyinstaller打包网上教程较多,这里也不多赘述了,善用搜索引擎即可。【解决方案】Pyinstaller打包exe文件详细教程

除了比较常用的Pyinstaller,Windows用户也可以尝试下使用GT大佬的QPT进行系统程序的打包,体验更佳哦!使用上直接根据其提供的教程进行操作即可。

QPT - Quick packaging tool 快捷封装工具,QPT是一款可以“模拟”开发环境的多功能封装工具,最短只需一行命令即可将普通的Python脚本打包成EXE可执行程序,并选择性添加CUDA和NoAVX的支持,尽可能兼容更多用户环境。

七. 项目总结

该项目为本人目前在做的‘舆情分析系统’的一个衍生子项目‘微情感分析系统’,开源该项目教程更多希望能够帮助大家更好地上手一个完整的文本分类项目开发流程,全流程式的实战开发教程项目在AI Studio上目前感觉还是比较匮乏的,希望本项目能够对大家有所启发或帮助,从而更好拿下竞赛或毕设等项目!

到这儿,本项目就告一段落了。若大家喜欢,希望能够fork喜欢关注三连!❤

八. 个人介绍

华南师范大学 软件学院 软件工程专业 2019级 本科生 黄灿桦

主要方向:搞开发,目前主攻NLP和数据挖掘相关比赛或项目

Github地址:https://github.com/hchhtc123

昵称:炼丹师233

https://aistudio.baidu.com/aistudio/personalcenter/thirdview/330406 关注我,下次带来更多精彩项目分享!

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐