[NLP实践01]simpletransformers安装和文本分类简单实现

2,192 阅读7分钟

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

快速安装 simpletransformers

simpletransformers 项目地址:hub.fastgit.org/ThilinaRaja…

simpletransformers 文档地址:

simpletransformers.ai/

快速安装方式:

  • 使用Conda安装

1)新建虚拟环境

conda create -n st python pandas tqdm
conda activate st

2)安装cuda环境

conda install pytorch>=1.6 cudatoolkit=11.0 -c pytorch

3)安装 simpletransformers

pip install simpletransformers
  1. 安装 wandb

wandb 用于在web浏览器中追踪和可视化Weights和Biases(wandb)

pip install wandb

目前支持的任务:

任务模型
二元和多类文本分类ClassificationModel
对话式人工智能(聊天机器人训练)ConvAIModel
语言生成LanguageGenerationModel
语言模型训练/微调LanguageModelingModel
多标签文本分类MultiLabelClassificationModel
多模态分类(文本和图像数据结合)MultiModalClassificationModel
命名实体识别NERModel
问答QuestionAnsweringModel
回归ClassificationModel
句子对分类ClassificationModel
文本表示生成RepresentationModel

预训练模型去哪里下载?

有关预训练模型,请参阅Hugging Face 文档

根据文档中给出的model_type,只要在args中正确设置model_name的字典值就是可以加载预训练模型

【实践01】文本分类

数据集

笔者选用CLUE的作为benchmark数据集

选取数据集:IFLYTEK' 长文本分类

中文语言理解测评基准(CLUE)

www.cluebenchmarks.com/dataSet_sea…

为更好的服务中文语言理解、任务和产业界,做为通用语言模型测评的补充,通过搜集整理发布中文任务及标准化测评等方式完善基础设施,最终促进中文NLP的发展。

Update: CLUE论文被计算语言学国际会议 COLING2020高分录用

  • IFLYTEK' 长文本分类

下载地址:github.com/CLUEbenchma…

该数据集共有1.7万多条关于app应用描述的长文本标注数据,包含和日常生活相关的各类应用主题,共119个类别:"打车":0,"地图导航":1,"免费WIFI":2,"租车":3,….,"女性":115,"经营":116,"收款":117,"其他":118(分别用0-118表示)。每一条数据有三个属性,从前往后分别是 类别ID,类别名称,文本内容。

数据量:训练集(12,133),验证集(2,599),测试集(2,600)

{"label": "110",
 "label_des": "社区超市",
 "sentence": "朴朴快送超市创立于2016年,专注于打造移动端30分钟即时配送一站式购物平台,商品品类包含水果、蔬菜、肉禽蛋奶、海鲜水产、粮油调味、酒水饮料、休闲食品、日用品、外卖等。朴朴公司希望能以全新的商业模式,更高效快捷的仓储配送模式,致力于成为更快、更好、更多、更省的在线零售平台,带给消费者更好的消费体验,同时推动中国食品安全进程,成为一家让社会尊敬的互联网公司。,朴朴一下,又好又快,1.配送时间提示更加清晰友好2.保障用户隐私的一些优化3.其他提高使用体验的调整4.修复了一些已知bug"}

数据处理

Simple Transformers要求数据必须包含在至少两列的Pandas DataFrames中。 只需为列的文本和标签命名,SimpleTransformers就会处理数据。
第一列包含文本,类型为str。
第二列包含标签,类型为int。
对于多类分类,标签应该是从0开始的整数。

import json
import pandas as pd

def load_clue_iflytek(path,mode=None):
    """适应simpletransformer的加载方式"""
    data = []
    with open(path, "r", encoding="utf-8") as fp:
        if mode == 'train' or mode =='dev':
            for idx, line in enumerate(fp):
                line = json.loads(line.strip())
                label = int(line["label"])
                text = line['sentence']
                data.append([text, label])
            data_df = pd.DataFrame(data, columns=["text", "labels"])
            return data_df
        elif mode == 'test':
            for idx, line in enumerate(fp):
                line = json.loads(line.strip())
                text = line['sentence']
                data.append([text])
            data_df = pd.DataFrame(data, columns=["text"])
            return data_df

模型搭建和训练

先进行参数配置,Simple Transformers具有dict args, 有关每个args的详细说明,可有参考:simpletransformers.ai/docs/tips-a…

1)参数配置

# 配置config
import argparse

def data_config(parser):
    parser.add_argument("--trainset_path", type=str, default="data/Chinese_Spam_Message/train.json",
                        help="训练集路径")
    parser.add_argument("--testset_path", type=str, default="data/Chinese_Spam_Message/test.txt",
                        help="测试集路径")
    parser.add_argument("--reprocess_input_data", type=bool, default=True,
                        help="如果为True,则即使cache_dir中存在输入数据的缓存文件,也将重新处理输入数据")
    parser.add_argument("--overwrite_output_dir", type=bool, default=True,
                        help="如果为True,则训练后的模型将保存到ouput_dir,并将覆盖同一目录中的现有已保存模型")
    parser.add_argument("--use_cached_eval_features", type=bool, default=True,
                        help="训练期间的评估使用缓存特征,将此设置为False将导致在每个评估步骤中重新计算特征")
    parser.add_argument("--output_dir", type=str, default="outputs/",
                        help="存储所有输出,包括模型checkpoints和评估结果")
    parser.add_argument("--best_model_dir", type=str, default="outputs/best_model/",
                        help="保存评估过程中的最好模型")
    return parser


def model_config(parser):
    parser.add_argument("--max_seq_length", type=int, default=64,
                        help="模型支持的最大序列长度")
    parser.add_argument("--model_type", type=str, default="bert",
                        help="模型类型bert/roberta")
    # 要加载以前保存的模型而不是默认模型的模型,可以将model_name更改为包含已保存模型的目录的路径。
    parser.add_argument("--model_name", type=str, default="./outputs/bert",
                        help="选择使用哪个预训练模型")
    parser.add_argument("--manual_seed", type=int, default=0,
                        help="为了产生可重现的结果,需要设置随机种子")
    parser.add_argument("--learning_rate", type=int, default=4e-5,
                        help="学习率")
    return parser


def train_config(parser):
    parser.add_argument("--num_train_epochs", type=int, default=3,
                        help="模型训练迭代数")
    parser.add_argument("--wandb_kwargs", type=dict, default={"name": "bert"},
                        help="")
    parser.add_argument("--n_gpu", type=int, default=1,
                        help="训练时使用的GPU个数")
    parser.add_argument("--train_batch_size", type=int, default=64)
    parser.add_argument("--eval_batch_size", type=int, default=32)
    return parser


def set_args():
    parser = argparse.ArgumentParser()
    parser = data_config(parser)
    parser = model_config(parser)
    parser = train_config(parser)

    args,_ = parser.parse_known_args()
    return args

2)模型搭建和训练

from simpletransformers.classification import ClassificationModel
from sklearn.metrics import f1_score, accuracy_score
import logging

def f1_multiclass(labels, preds):
      return f1_score(labels, preds, average='micro')

# 创建分类模型
model = ClassificationModel(args.model_type, args.model_name, num_labels=num_labels, args=vars(args))

# 训练模型,并在训练时评估
model.train_model(train,eval_df=dev)

# 模型预测
result, model_outputs, wrong_predictions = model.eval_model(dev, f1=f1_multiclass)


# 模型预测
predictions, raw_outputs = model.predict(test["text"][0])
print(predictions)
print(raw_outputs)

预测结果

笔记本电脑性能有限,为了保证能跑动,maxlen只用了64,也只训练了3轮,F1值的效果并不是很好 除了F1值也可以添加其他评价指标,如准确率、精确率、召回率等

{"eval_loss" = 1.8086365330510024,"f1" = 0.5917660638707195,"mcc" = 0.5727319886339782}

完整代码

import json
import pandas as pd
from simpletransformers.classification import ClassificationModel
from sklearn.metrics import f1_score, accuracy_score
import logging

def load_clue_iflytek(path,mode=None):
    """适应simpletransformer的加载方式"""
    data = []
    with open(path, "r", encoding="utf-8") as fp:
        if mode == 'train' or mode =='dev':
            for idx, line in enumerate(fp):
                line = json.loads(line.strip())
                label = int(line["label"])
                text = line['sentence']
                data.append([text, label])
            data_df = pd.DataFrame(data, columns=["text", "labels"])
            return data_df
        elif mode == 'test':
            for idx, line in enumerate(fp):
                line = json.loads(line.strip())
                text = line['sentence']
                data.append([text])
            data_df = pd.DataFrame(data, columns=["text"])
            return data_df

# 配置config
import argparse

def data_config(parser):
    parser.add_argument("--trainset_path", type=str, default="data/Chinese_Spam_Message/train.json",
                        help="训练集路径")
    parser.add_argument("--testset_path", type=str, default="data/Chinese_Spam_Message/test.txt",
                        help="测试集路径")
    parser.add_argument("--reprocess_input_data", type=bool, default=True,
                        help="如果为True,则即使cache_dir中存在输入数据的缓存文件,也将重新处理输入数据")
    parser.add_argument("--overwrite_output_dir", type=bool, default=True,
                        help="如果为True,则训练后的模型将保存到ouput_dir,并将覆盖同一目录中的现有已保存模型")
    parser.add_argument("--use_cached_eval_features", type=bool, default=True,
                        help="训练期间的评估使用缓存特征,将此设置为False将导致在每个评估步骤中重新计算特征")
    parser.add_argument("--output_dir", type=str, default="outputs/",
                        help="存储所有输出,包括模型checkpoints和评估结果")
    parser.add_argument("--best_model_dir", type=str, default="outputs/best_model/",
                        help="保存评估过程中的最好模型")
    return parser


def model_config(parser):
    parser.add_argument("--max_seq_length", type=int, default=64,
                        help="模型支持的最大序列长度")
    parser.add_argument("--model_type", type=str, default="bert",
                        help="模型类型bert/roberta")
    # 要加载以前保存的模型而不是默认模型的模型,可以将model_name更改为包含已保存模型的目录的路径。
    parser.add_argument("--model_name", type=str, default="./outputs/bert",
                        help="选择使用哪个预训练模型")
    parser.add_argument("--manual_seed", type=int, default=0,
                        help="为了产生可重现的结果,需要设置随机种子")
    parser.add_argument("--learning_rate", type=int, default=4e-5,
                        help="学习率")
    return parser


def train_config(parser):
    parser.add_argument("--num_train_epochs", type=int, default=3,
                        help="模型训练迭代数")
    parser.add_argument("--wandb_kwargs", type=dict, default={"name": "bert"},
                        help="")
    parser.add_argument("--n_gpu", type=int, default=1,
                        help="训练时使用的GPU个数")
    parser.add_argument("--train_batch_size", type=int, default=64)
    parser.add_argument("--eval_batch_size", type=int, default=32)
    return parser


def set_args():
    parser = argparse.ArgumentParser()
    parser = data_config(parser)
    parser = model_config(parser)
    parser = train_config(parser)

    args,_ = parser.parse_known_args()
    return args



def f1_multiclass(labels, preds):
      return f1_score(labels, preds, average='micro')


if __name__ == "__main__":
    args = set_args()

    logging.basicConfig(level=logging.INFO)
    transformers_logger = logging.getLogger("transformers")
    transformers_logger.setLevel(logging.WARNING)

    # 模型训练
    train = load_clue_iflytek("./data/iflytek/train.json", mode='train')
    dev = load_clue_iflytek("./data/iflytek/dev.json", mode='dev')
    test = load_clue_iflytek("./data/iflytek/test.json", mode='test')
    num_labels = len(train["labels"].unique())
    print(train.shape)
    print(dev.shape)

    # 创建分类模型
    model = ClassificationModel(args.model_type, args.model_name, num_labels=num_labels, args=vars(args))

    # 训练模型,并在训练时评估
    model.train_model(train,eval_df=dev)

    # 模型预测
    result, model_outputs, wrong_predictions = model.eval_model(dev, f1=f1_multiclass)

Simpletransformers上手快,但只偏向于快速应用或者写baseline,需要更改模型结构灵活组合方法还是需要掌握transformer等自由度高的python库

NLP萌新,才疏学浅,有错误或者不完善的地方,请批评指正!!