本教程基于 Huggingface 的 Transformers
(4.10.0),其中大部分来自于官方文档 ## 1.pipeline
首先是最简单的使用方法 pipeline
其可以直接利用制定的任务
在 pipeline
中指定了以下几种任务:情绪分析,文本生成,命名实体识别,回答问题,Mask预测,总结文本,翻译,文本特征向量提取
其使用也很简单
1 2 3 4 from transformers import pipelineclassifier = pipeline('sentiment-analysis' ) classifier('We are very happy' )
在这里由于模型没有指定使用什么预训练模型,因此默认调用 “
distilbert-base-uncased-finetuned-sst-2-english”
,可以注意到这是一个基于 DistilBERT 的架构,并在
sst-2
上针对情感预测进行了微调,当然你也可以指定想要使用的模型,可以在 model hub
中寻找你想要使用的模型。这里使用一下中文模型
“uer/roberta-base-finetuned-chinanews-chinese”,需要注意的是,这个模型是这是文本分类的任务,但也可以使用
'sentiment-analysis' 的 pipeline
1 2 3 4 from transformers import pipelineclasifier = pipeline("sentiment-analysis" , model="uer/roberta-base-finetuned-chinanews-chinese" ) print (clasifier("中国国足今天将和越南比赛" ))
除了网上的模型,我们还可以调用本地的模型,这里我们需要两个类
AutoTokenizer,AutoModelForMaskedLM ,这里笔者由于使用了自己训练的本地模型,因此使用的是
BertTokenizer 和
AutoModelForMaskedLM
1 2 3 4 5 6 from transformers import BertTokenizer, AutoModelForMaskedLM, pipelinemodel = AutoModelForMaskedLM.from_pretrained("Huggingface/output/checkpoint-884000/" ) tokenizer = BertTokenizer(vocab_file="/Huggingface/vocab.txt" ) unmask = pipeline("fill-mask" , model=model, tokenizer=tokenizer) print (unmask("实验室规[MASK]管理制度" ))
至于其它的 pipeline 的使用可以参考官方文档
2.tokenizer
tokenizer 是 transformers
的核心组件之一,负责将句子数字化和分词
对于使用 model hub
中的预训练模型来说,其必须要使用对应的 tokenizer
,可以通过 AutoTokenizer.from_pretrained() 获得
这里我们下载一下中文 BERT-BASE 的
tokenizer,一般会得到四个文件
1 2 3 from transformers import AutoTokenizer, AutoModelForMaskedLM, pipelinetokenizer = AutoTokenizer.from_pretrained("./save" ) tokenizer.save_pretrained("./save" )
image-20211116144002979
其中 special_token_map.json指定了特殊token的形式,如
[CLS],[UNK],[MASK] 等
tokener.json和vocab.txt,则分别是词对应的数字和的词表,其中json文件还多了一部分信息用于描述
tokenzier 中的特殊token
tokenizer_config.json 则同样描述了特殊token和下载的tokenizer信息
在得到 tokenizer
后就可以对文本进行预处理,这里以中文为例
1 2 3 4 from transformers import AutoTokenizertokenizer = AutoTokenizer.from_pretrained("hfl/chinese-bert-wwm-ext" ) print (tokenizer("实验室规章管理制度" ))
经过 tokenizer
编码后会输出三项,input_ids
是句子向量化以后的数字,token_type_ids
是描述句子上下关系的,如果两个句子有上下文关联则会使用0和1加以区分,attention_mask
则是描述句子的 padding 结构以指示哪些部分是
padding 的
1 2 3 4 5 6 7 from transformers import AutoTokenizer, AutoModelForMaskedLM, pipelinetokenizer = AutoTokenizer.from_pretrained("hfl/chinese-bert-wwm-ext" ) sentence_1 = ["我喜欢吃香菜" , "去实验室吗" ] sentence_2 = ["我也喜欢吃" , "去" ] print (tokenizer(sentence_1, sentence_2, padding=True , truncation=True ))
当然既然可以使用 tokenizer
对句子进行编码处理,那也可以使用 tokenizer
对已经编码的句子进行还原
1 2 3 encoded_inputs = tokenizer(sentence_1, sentence_2, padding=True , truncation=True ) print (tokenizer.decode(encoded_inputs["input_ids" ][1 ]))
当然我们也可以自己训练一个自己的
tokenizer ,这里就要用到 Tokenizers
库了,这里以训练 BPE 编码的tokenizer为例
在 Tokenizers 中提供了四种算法,包括
BPE,Unigram,WordLevel,WordPiece 这里仅以
BPE 为例
首先需要准备好训练的语料库,每个句子一行
image-20211117141652919
随后指定训练方法,预分词方法,预计词表大小,预先定义的特殊
token ,之后就可以开始训练模型并存储下来
1 2 3 4 5 tokenizer = Tokenizer(BPE()) tokenizer.pre_tokenizer = Whitespace() trainer = BpeTrainer(special_tokens=["[UNK]" , "[CLS]" , "[SEP]" , "[PAD]" , "[MASK]" ], vocab_size=9000 ) tokenizer.train(files=["News2016/valid.txt" ], trainer=trainer) tokenizer.save('tokenizer.json' )
最后就可以得到 tokenizer 文件
加载的时候需要注意的是,在 tokenizers 中的
tokenizer 和 Transformers 中的
tokenizer 不一样
在tokenizers中加载只需要之前保存的 tokenizer.json
但在 Transformers 中需要使用
PreTrainedTokenizerFast
来加载,且两者在后续的使用中虽然功能都一样但无法混用,即由
Tokenizer 初始化的 tokenizer
无法应用到 Transformers 中
1 2 3 4 tokenizer = Tokenizer.from_file("Huggingface/tokenizer.json" ) tokenizer = PreTrainedTokenizerFast(tokenizer_file="Huggingface/tokenizer.json" )
3.Models
Transformers
支持众多预训练模型,这里主要可以分为:
解码器或自回归模型(GPT)
编码器或自编码模型(BERT)
序列到序列模型(T5)
多模态模型
基于检索的模型
具体支持的模型可以在官方文档 下查看
具体使用的时候可以如同第一节所述直接加载训练好的模型,也可以自己重新训练一个模型,这里主要讲述如何训练一个模型
首先需要准备的是语料库,和 tokenizer ,注意这里的
tokenizer 不可以是之前第二节中说的从
tokenizers 中初始化的模型,必须从
Transformers 中初始化
接下来就是对所选模型的 config 进行改写,这里以
BERT 为例,训练一个掩码模型MLM
1 2 3 4 5 from transformers import BertModel, BertConfigconfiguration = BertConfig(vocab_size=9468 ) model = BertForMaskedLM(configuration)
在准备训练数据集部分还需要用到另一个 huggingface
的库 datasets 同样在 datasets
hub 上有许多开源的库,可以直接调用
1 2 from datasets import load_datasetdataset = load_dataset('glue' , 'mrpc' , split='train' )
但其中的纯中文语料库并不多,可能还是需要读者自行准备相应的训练语料库
1 2 3 from datasets import load_dataset, Datasetdataset = load_dataset('text' , data_files={'train' : 'News2016/train.txt' , 'validation' : 'News2016/valid.txt' })
接下来需要对dataset使用 tokenizer 进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def tokenize_function (examples ): examples["text" ] = [line for line in examples["text" ] if len (line) > 0 and not line.isspace()] return tokenizer( examples["text" ], padding="max_length" , truncation=True , max_length=100 , return_special_tokens_mask=True , ) tokenized_datasets = dataset.map ( tokenize_function, batched=True , num_proc=24 , remove_columns=['text' ], ) train_dataset = tokenized_datasets["train" ] eval_dataset = tokenized_datasets["validation" ]
之后就是设置 trainer 的相关参数
1 2 3 4 5 6 7 datacollator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True , mlm_probability=0.15 ) train_args = TrainingArguments(output_dir='Huggingface/output/' , overwrite_output_dir=True , num_train_epochs=10 , save_steps=2000 , learning_rate=5e-5 , per_device_train_batch_size=300 , save_total_limit=3 ) trainer = Trainer(model=model, args=train_args, data_collator=datacollator, train_dataset=train_dataset) trainer.train(resume_from_checkpoint=None ) trainer.save_model('Huggingface/output/' )
最后给出训练的完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 tokenizer = BertTokenizer(vocab_file='vocab.txt' ) dataset = load_dataset('text' , data_files={'train' : 'News2016/train.txt' , 'validation' : 'News2016/valid.txt' }) def tokenize_function (examples ): examples["text" ] = [line for line in examples["text" ] if len (line) > 0 and not line.isspace()] return tokenizer( examples["text" ], padding="max_length" , truncation=True , max_length=100 , return_special_tokens_mask=True , ) tokenized_datasets = dataset.map ( tokenize_function, batched=True , num_proc=24 , remove_columns=['text' ], ) train_dataset = tokenized_datasets["train" ] eval_dataset = tokenized_datasets["validation" ] config = BertConfig(vocab_size=9468 ) model = BertForMaskedLM(config) datacollator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True , mlm_probability=0.15 ) train_args = TrainingArguments(output_dir='Huggingface/output/' , overwrite_output_dir=True , num_train_epochs=10 , save_steps=2000 , learning_rate=5e-5 , per_device_train_batch_size=300 , save_total_limit=3 ) trainer = Trainer(model=model, args=train_args, data_collator=datacollator, train_dataset=train_dataset) trainer.train(resume_from_checkpoint=None ) trainer.save_model('Huggingface/output/' )
4.Fine-tuning
有了预训练模型后肯定还需要在具体任务上进行微调,这里以在IMDB上评价为正面还是负面为例(其实就是官方文档的例子)
这里主要介绍如何在pytorch上进行微调,其核心还是利用
Transformers
中已经构建好的微调任务模型或者自己重写如何利用预训练模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 from torch.utils.data import DataLoaderfrom transformers import AdamWfrom transformers import AutoModelForSequenceClassificationfrom transformers import get_schedulerfrom tqdm.auto import tqdmfrom transformers import AutoTokenizerfrom datasets import load_datasetraw_datasets = load_dataset("imdb" ) tokenizer = AutoTokenizer.from_pretrained("bert-base-cased" ) def tokenize_function (examples ): return tokenizer(examples["text" ], padding="max_length" , truncation=True ) tokenized_datasets = raw_datasets.map (tokenize_function, batched=True ) tokenized_datasets = tokenized_datasets.remove_columns(["text" ]) tokenized_datasets = tokenized_datasets.rename_column("label" , "labels" ) tokenized_datasets.set_format("torch" ) small_train_dataset = tokenized_datasets["train" ].shuffle(seed=42 ).select(range (1000 )) small_eval_dataset = tokenized_datasets["test" ].shuffle(seed=42 ).select(range (1000 )) train_dataloader = DataLoader(small_train_dataset, shuffle=True , batch_size=8 ) eval_dataloader = DataLoader(small_eval_dataset, batch_size=8 ) optimizer = AdamW(model.parameters(), lr=5e-5 ) model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased" , num_labels=2 ) num_epochs = 3 num_training_steps = num_epochs * len (train_dataloader) lr_scheduler = get_scheduler( "linear" , optimizer=optimizer, num_warmup_steps=0 , num_training_steps=num_training_steps ) device = torch.device("cuda" ) if torch.cuda.is_available() else torch.device("cpu" ) model.to(device) progress_bar = tqdm(range (num_training_steps)) model.train() for epoch in range (num_epochs): for batch in train_dataloader: batch = {k: v.to(device) for k, v in batch.items()} outputs = model(**batch) loss = outputs.loss loss.backward() optimizer.step() lr_scheduler.step() optimizer.zero_grad() progress_bar.update(1 )