700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 天池零基础入门NLP竞赛实战:Task4-基于深度学习的文本分类3-基于Bert预训练和微调进

天池零基础入门NLP竞赛实战:Task4-基于深度学习的文本分类3-基于Bert预训练和微调进

时间:2022-04-03 15:17:08

相关推荐

天池零基础入门NLP竞赛实战:Task4-基于深度学习的文本分类3-基于Bert预训练和微调进

Task4-基于深度学习的文本分类3-基于Bert预训练和微调进行文本分类

因为天池这个比赛的数据集是脱敏的,无法利用其它已经预训练好的模型,所以需要针对这个数据集自己从头预训练一个模型。

我们利用Huggingface的transformer包,按照自己的需求从头开始预训练一个模型,然后将该模型应用于下游任务。

完整代码见:NLP-hands-on/天池-零基础入门NLP at main · ifwind/NLP-hands-on ()

注意:利用Huggingface做预训练需要安装wandb包,如果报错可参考:[wandb.errors.UsageError: api_key not configured (no-tty). call wandb.login(key=[your_api_key\])_](/hhhhhhhhhhwwwwwwwwww/article/details/116124285)

预训练模型

利用Huggingface的transformer包进行预训练主要包括以下几个步骤:

用数据集训练Tokenizer;加载数据及数据预处理;设定预训练模型参数,初始化预训练模型;设定训练参数,加载训练器;训练并保存模型。

用数据集训练Tokenizer

Tokenizer是分词器,分词方式有很多种,可以按照空格直接切分、也可以在按词组划分等,可以查看HuggingFace关于tokenizers的官方文档。

Huggingface中,Tokenizer的训练方式为:

根据tokenizers.models实例化一个Tokenizer对象tokenizer,从tokenizers.trainers中选模型相应的训练器实例化,得到trainer;从tokenizers.pre_tokenizers选定一个预训练分词器,对tokenizer的预训练分词器实例化;利用tokenizer.train()结合trainer对语料(注意,语料为一行一句)进行训练;利用tokenizer.save()保存tokenizer

因为天池这个比赛的数据集是脱敏的,词都是用数字进行表示,没有办法训练wordpiece等复杂形式的分词器,只能用空格分隔,在wordlevel进行分词。

因此,我们利用tokenizers.models中的WordLevel模型,对应tokenizers.trainers中的WordLevelTrainer,选择预训练分词器为Whitespace训练分词器。

另外,在训练Tokenizer时,可以利用上全部的语料(包括训练集和最终的测试集)。

完整代码如下:

import joblibfrom config import *import pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.model_selection import StratifiedKFoldimport osdef data_preprocess():rawdata = pd.read_csv(data_file, sep='\t', encoding='UTF-8')#用正则表达式按标点替换文本import rerawdata['words']=rawdata['text'].apply(lambda x: re.sub('3750|900|648',"",x))del rawdata['text']#预测final_test_data = pd.read_csv(final_test_data_file, sep='\t', encoding='UTF-8')final_test_data['words'] = final_test_data['text'].apply(lambda x: re.sub('3750|900|648',"",x))del final_test_data['text']all_value= rawdata['words'].append(final_test_data['words'])all_value.columns=['text']all_value.to_csv('../alldata.csv',index=False)data_preprocess()from tokenizers import Tokenizerfrom tokenizers.models import BPE,WordLeveltokenizer= Tokenizer(WordLevel(unk_token="[UNK]"))from tokenizers.trainers import BpeTrainer,WordLevelTrainer#加入一些特殊字符trainer = WordLevelTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])#空格分词器from tokenizers.pre_tokenizers import Whitespacetokenizer.pre_tokenizer = Whitespace()#保存语料库文件tokenizer.train(['../alldata.csv'], trainer)tokenizer.mask_token='[MASK]'tokenizer.save("../tokenizer-my-Whitespace.json")

加载数据及数据预处理

在预训练模型时,利用的是普通的、不带标签的句子,因此,在预训练模型时,同样采用全部的语料(包括训练集和最终的测试集,也就是前面的alldata.csv)。

使用Huggingface的datasets包的load_dataset函数加载数据,这里用的是csv的数据格式,关于其他格式的数据可以参考之前的一篇博客:加载数据。

输入模型之前,还需要对句子进行分词,转换成word id,此外,还需要padding长度不足的句子、获取对padding部分掩码的矩阵等等操作,这些由tokenizer进行处理,如果还需要其他预处理操作,都可以通过定义一个函数,将预处理操作封装起来,然后利用dataset.map()进行处理。

另外我们还在这里实例化了一个数据收集器data_collator,将经预处理的输入分batch再次处理后喂给模型。这是为了告诉Trainer如何从预处理的输入数据中构造batch。

from transformers import PreTrainedTokenizerFast#注意这里用了另外一种方式加载Tokenizertokenizer = PreTrainedTokenizerFast(tokenizer_file="tokenizer-my-Whitespace.json")tokenizer.mask_token='[MASK]'tokenizer.pad_token='[PAD]'#加载数据from datasets import load_datasetdataset = load_dataset('csv', data_files={'train':'alldata.csv'},cache_dir='Bert_pre_train\\') #这里的cache_dir考虑用绝对路径,不然可能会卡住#预处理函数def preprocess_function(examples):return tokenizer(examples['text'], truncation=True,max_length=512)encoded_dataset = dataset.map(preprocess_function, batched=True)#数据收集器from transformers import DataCollatorForLanguageModelingdata_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15 #mlm表示是否使用masked language model;mlm_probability表示mask的几率)

设定预训练模型参数,初始化预训练模型

这里选择了XLNet模型作为预训练模型的基础架构,然后根据调整模型的参数,也可以选 Roberta、ALBert等模型,参考官方文档进行配置。

from transformers import RobertaConfig,AlbertConfig,XLNetConfig# 将模型的配置参数载入config_kwargs = {"d_model": 512,"n_head": 4,"vocab_size": tokenizer.get_vocab_size(), # 自己设置词汇大小"embedding_size":64,"bi_data":True,"n_layer":8}config = XLNetConfig(**config_kwargs)# 载入预训练模型,这里其实是根据某个模型结构调整config然后创建模型from transformers import RobertaForMaskedLM,AlbertForMaskedLM,XLNetLMHeadModelmodel = XLNetLMHeadModel(config=config)

设定训练参数,加载训练器

from transformers import Trainer, TrainingArgumentstraining_args = TrainingArguments(output_dir="./BERT",overwrite_output_dir=True,num_train_epochs=1, #训练epoch次数per_gpu_train_batch_size=12, #训练时的batchsizesave_steps=10_000, #每10000步保存一次模型save_total_limit=2,#最多保存两次模型prediction_loss_only=True,)trainer = Trainer(model=model,args=training_args,data_collator=data_collator, #数据收集器在这里train_dataset=encoded_dataset["train"] #注意这里选择的是预处理后的数据集)

训练并保存模型

#开始训练trainer.train()#保存模型trainer.save_model("./BERT")

微调模型进行分类

主要包括以下几个步骤:

训练集划分;数据预处理;加载预训练模型、设置微调参数;微调训练下游任务模型并保存。

训练集划分

去掉可能的标点符号,并把当前竞赛给的训练集划分为三个部分:训练集、验证集、测试集。其中,训练集用于训练,验证集用于调参,测试集用于评估线下和线上的模型效果。

这里首先用train_test_split(注意使用分层抽样)把训练集划分为训练集和测试集(9:1),然后再将训练集进一步划分为训练集和开发集(9:1)。

import joblibfrom config import *import pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.model_selection import StratifiedKFoldimport osdef data_preprocess():rawdata = pd.read_csv(data_file, sep='\t', encoding='UTF-8')#用正则表达式按标点替换文本import rerawdata['words']=rawdata['text'].apply(lambda x: re.sub('3750|900|648',"",x))del rawdata['text']#数据划分#如果之前已经做了就直接加载if os.path.exists(test_index_file) and os.path.exists(train_index_file):test_index=joblib.load(test_index_file)train_index=joblib.load(train_index_file)else:rawdata.reset_index(inplace=True, drop=True)X = list(rawdata.index)y = rawdata['label']X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1,stratify=y) # stratify=y表示分层抽样,根据不同类别的样本占比进行抽样test_index = {'X_test': X_test, 'y_test': y_test}joblib.dump(test_index, 'test_index.pkl')train_index = {'X_train': X_train, 'y_train': y_train}joblib.dump(train_index, 'train_index.pkl')train_x=rawdata.loc[train_index['X_train']]train_y=rawdata.loc[train_index['X_train']]['label'].valuesX_train, X_test, y_train, y_test = train_test_split(train_x, train_y, test_size=0.1,stratify=train_y)#训练集X_train.columns=['label', 'text']X_train.to_csv('train_data.csv',index=False)#开发集X_test.columns=['label', 'text']X_test.to_csv('dev_data.csv',index=False)#测试集test_x=rawdata.loc[test_index['X_test']]test_x.columns=['label', 'text']test_x.to_csv('test_data.csv',index=False)

数据预处理

和预训练模型时一致,加载数据集后还需要对数据进行预处理,注意上一步中加入了label这个字段,作为句子的分类标签。

Huggingface封装了模型的有效字段,如果字段名称对不上会在trainerself.get_train_dataloader()去掉无效字段,有效字段可以在debug模式下,在transformertrainer.pyself.args.remove_unused_columns中查看。

from datasets import load_datasetdataset = load_dataset('csv', data_files={'train':'train_data.csv','dev':'dev_data.csv', 'test':'test_data.csv'},cache_dir='fine-tune\\') #这里的cache_dir考虑用绝对路径,不然可能会卡住from transformers import PreTrainedTokenizerFast#注意这里用了另外一种方式加载Tokenizertokenizer = PreTrainedTokenizerFast(tokenizer_file="tokenizer-my-Whitespace.json")tokenizer.mask_token='[MASK]'tokenizer.pad_token='[PAD]'def preprocess_function(examples):return tokenizer(examples['text'], truncation=True,max_length=512)encoded_dataset = dataset.map(preprocess_function, batched=True)

加载预训练模型

model_checkpoint = "BERT" #所选择的预训练模型num_labels = 14from transformers import XLNetForSequenceClassification, TrainingArguments, Trainermodel = XLNetForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)

设置微调参数

metric_name = "acc"args = TrainingArguments( "test-glue", evaluation_strategy = "epoch", #每个epcoh会做一次验证评估; save_strategy = "epoch", learning_rate=2e-5, per_device_train_batch_size=batch_size, per_device_eval_batch_size=batch_size*4, num_train_epochs=5, weight_decay=0.01, load_best_model_at_end=True, metric_for_best_model=metric_name, #根据哪个评价指标选最优模型 save_steps=10_000, save_total_limit=2,)from datasets import load_metricimport numpy as npdef compute_metrics(eval_pred): metric = load_metric('f1') predictions, labels = eval_pred predictions = np.argmax(predictions, axis=1) return pute(predictions=predictions, references=labels,average='macro')trainer = Trainer( model, args, train_dataset=encoded_dataset["train"], eval_dataset=encoded_dataset["dev"], tokenizer=tokenizer, compute_metrics=compute_metrics)

微调训练下游任务模型并保存

trainer.train()trainer.save_model("./test-glue")

评估模型

这里先用之前训练过程中保存的test-glue/checkpoint-13500模型进行评估,看一下训练效果,F1可以达到91.39左右。

model_checkpoint = "test-glue/checkpoint-13500"#"BERT" #所选择的预训练模型num_labels = 14from transformers import XLNetForSequenceClassification, TrainingArguments, Trainermodel = XLNetForSequenceClassification.from_pretrained(model_checkpoint, num_labels=num_labels)batch_size = 12metric_name = "acc"args = TrainingArguments( "test-glue", evaluation_strategy = "epoch", #每个epcoh会做一次验证评估; save_strategy = "epoch", learning_rate=2e-5, per_device_train_batch_size=batch_size, per_device_eval_batch_size=batch_size*4, num_train_epochs=5, weight_decay=0.01, load_best_model_at_end=True, metric_for_best_model=metric_name, #根据哪个评价指标选最优模型 save_steps=10_000, save_total_limit=2,)from datasets import load_metricimport numpy as npdef compute_metrics(eval_pred): metric = load_metric('f1') predictions, labels = eval_pred predictions = np.argmax(predictions, axis=1) return pute(predictions=predictions, references=labels,average='macro')trainer = Trainer( model, args, train_dataset=encoded_dataset["train"], eval_dataset=encoded_dataset["dev"], tokenizer=tokenizer, compute_metrics=compute_metrics)trainer.evaluate()

参考资料

ALBERT — transformers 4.11.3 documentation (huggingface.co)

[BERT相关——(5)Pre-train Model | 冬于的博客 (ifwind.github.io)](https://ifwind.github.io//08/22/BERT相关——(5)Pre-train Model/#重新配置模型)

BERT实战——(1)文本分类 | 冬于的博客 (ifwind.github.io)

阅读源码-理解pytorch_pretrained_bert中BertTokenizer工作方式_枪枪枪的博客-CSDN博客

NLP学习1 - 使用Huggingface Transformers框架从头训练语言模型 - 简书 ()

天池零基础入门NLP竞赛实战:Task4-基于深度学习的文本分类3-基于Bert预训练和微调进行文本分类

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。