三万字带你一遍跑通uer
参考文档
今天给大家介绍个非常强大的项目uer,集成了许多可以做自然语言的东西,效果的话也非常好,很适合企业级的应用!
1. 先将项目uer从github拉取下来(zip或git都ok)
2. 用pycharm打开项目,按照requirements.txt配置环境,我这里有一个专门用与机器学习的conda环境,就直接在此基础上装所需的包了
3. 预处理
词典文件的格式是一行一个单词,我们使用谷歌提供的包含21128个中文字符的词典文件models/google_zh_vocab.txt。
我们首先对书评语料进行预处理。预处理阶段需要将语料处理成为指定预训练模型要求的数据格式(–data_processor):
python preprocess.py --corpus_path corpora/book_review_bert.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --data_processor bert
我的配置
运行图片
4. 生成预训练模型
注意我们需要安装 six>=1.12.0。
预处理非常耗时,使用多个进程可以大大加快预处理速度(–processes_num)。默认的分词器为 --tokenizer bert 。原始文本在预处理之后被转换为pretrain.py可以接收的输入,dataset.pt。然后下载Google中文预训练模型google_zh_model.bin(此文件为UER支持的格式,原始模型来自于这里),并将其放在 models 文件夹中。接着加载Google中文预训练模型,在书评语料上对其进行增量预训练。预训练模型通常由词向量层,编码层和目标任务层组成。因此要构建预训练模型,我们应该给出这些信息,比如编码层使用什么类型的Encoder模块。这里我们通过配置文件(–config_path)指定模型使用的模块类型和超参数等信息。google_zh_model.bin下载地址
具体可见models/bert/base_config.json。假设我们有一台带有8个GPU的机器(官方文档代码):
python3 pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt \
--pretrained_model_path models/google_zh_model.bin \
--config_path models/bert/base_config.json \
--output_model_path models/book_review_model.bin \
--world_size 8 --gpu_ranks 0 1 2 3 4 5 6 7 \
--total_steps 5000 --save_checkpoint_steps 1000 --batch_size 32
mv models/book_review_model.bin-5000 models/book_review_model.bin
当然以上代码要随个人做小小的改动。比如我是只有一个gpu,那么我的代码应该是
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --pretrained_model_path models/google_zh_model.bin --config_path models/bert/base_config.json --output_model_path models/book_review_model.bin --world_size 1 --gpu_ranks 0 --total_steps 5000 --save_checkpoint_steps 1000 --batch_size 32
mv models/book_review_model.bin-5000 models/book_review_model.bin
5.微调模型
我们在下游分类数据集上微调预训练模型,我们使用 pretrain.py 的输出book_review_model.bin(加载词向量层和编码层参数):
python finetune/run_classifier.py --pretrained_model_path models/book_review_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --epochs_num 3 --batch_size 32
微调后的模型的默认路径是models/finetuned_model.bin。注意到预训练的实际batch size大小是 --batch_size 乘以 --world_size;分类等下游任务微调的实际的batch size大小是 --batch_size 。然后我们利用微调后的分类器模型进行预测:
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --test_path datasets/book_review/test_nolabel.tsv --prediction_path datasets/book_review/prediction.tsv --labels_num 2
–test_path 指定需要预测的文件,文件需要包括text_a列; --prediction_path 指定预测结果的文件; 注意到我们需要指定分类任务标签的个数 --labels_num ,这里是二分类任务。
然后我们得到8000条预测结果
让我们来大致看一下预测效果(test_nolabel.tsv为预测样本,prediction.tsv为预测结果,标0是消极评论,标1为积极评论)
粗略一看差不多也有90多的正确率,我感觉还是ok的。
当然我们还可以使用 google_zh_model.bin 在下游分类数据集上微调:
python finetune/run_classifier.py --pretrained_model_path models/google_zh_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --epochs_num 3 --batch_size 32
可以看到,直接用 google_zh_model.bin的精度只比预训练生成的模型少一点点
一般来说,模型相关的关键信息一般放在配置文件中。这里我们进一步介绍前面使用的BERT-base模型的配置文件 models/bert/base_config.json :
{
"emb_size": 768,
"feedforward_size": 3072,
"hidden_size": 768,
"hidden_act": "gelu",
"heads_num": 12,
"layers_num": 12,
"max_seq_length": 512,
"dropout": 0.1,
"data_processor": "bert"
"embedding": ["word", "pos", "seg"],
"encoder": "transformer",
"mask": "fully_visible",
"target": ["mlm", "sp"],
"tie_weights": true
}
BERT模型的词向量层是word(token)、position、segment向量的求和,因此使用 “embedding”: [“word”, “pos”, “seg”] ;BERT使用Transformer编码器(“encoder”: “transformer”),并且句子中的任意一个词可以看到所有词的信息,因此我们使用 fully_visible 遮罩类型(“mask”: “fully_visible”);在预训练目标任务方面,BERT同时使用遮罩语言模型和句子预测,因此使用 “target”: [“mlm”, “sp”] 。“tie_weights”: true 指定embedding层和softmax前一层共享参数。预训练阶段还需要指定 dataset.pt 的格式(“data_processor”: “bert”)。这里和预处理阶段 --data_processor bert 保持一致。
我们通过对某些参数的微调同样也可以提高预测的精度
6.使用MLM目标任务预训练
预测是否是下一个句子(NSP)是BERT的目标任务之一,但是,NSP任务与句子级别的评论匹配度程度不是很高,因为我们需要将句子切分为多个部分去构造文档。 UER-py可以让用户自由选择不同的目标任务。这里我们选择使用遮罩语言模型(MLM)作为目标任务。去掉NSP目标任务,只使用MLM目标任务对书评语料可能是更合适的选择:
python preprocess.py --corpus_path corpora/book_review.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --dynamic_masking --data_processor mlm
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --pretrained_model_path models/google_zh_model.bin --config_path models/bert/base_config.json --output_model_path models/book_review_mlm_model.bin --world_size 1 --gpu_ranks 0 --total_steps 5000 --save_checkpoint_steps 1000 --batch_size 32 --data_processor mlm --target mlm
mv models/book_review_mlm_model.bin-5000 models/book_review_mlm_model.bin
python finetune/run_classifier.py --pretrained_model_path models/book_review_mlm_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --epochs_num 3 --batch_size 32
在预训练阶段,我们仍然使用BERT-base配置文件 models/bert/base_config.json ,但是由于只保留MLM目标任务,因此在命令行中进行指定 --data_processor mlm --target mlm 。我们可以通过多种方式去控制模型的类型,超参数等信息,它们的优先级如下:命令行、配置文件、默认设置。也就是说,我们在命令行中指定的信息会覆盖配置文件中的信息以及默认设置的信息。 这里使用了动态遮罩策略(–dynamic_masking)。动态遮罩策略能减小 dataset.pt 的大小,并且相对于静态遮罩通常会有更好的效果。因此推荐使用动态遮罩。 不同的预训练目标需要不同格式的语料。MLM目标对应的语料格式为一行一个文档:
doc1
doc2
doc3
注意到当预训练目标被改为MLM后,我们使用的预训练语料为 corpora/book_review.txt 而不是 corpora/book_review_bert.txt。
从以下这张图我们能很明显的看出文本样式的区别:
从训练精度来说使用动态遮罩后,效果确实得到了一定提升
7.使用BERT模型微调下游任务
除了分类外,UER-py还可以用于其他多种下游任务的微调。在下游任务数据集中,我们可以下载各种数据集。这里我们下载LCQMC、MSRA-NER、CMRC2018数据集,并将它们放到 datasets 文件夹中,如下
先来看看test.tsv(了解下这些数据集的大概样式)
其中0代表两句话句义不相似,1代表两句话句义相似
我们可以使用 run_classifier.py 进行文本对分类:
python finetune/run_classifier.py --pretrained_model_path models/google_zh_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/lcqmc/train.tsv --dev_path datasets/lcqmc/dev.tsv --test_path datasets/lcqmc/test.tsv --output_model_path models/classifier_model.bin --batch_size 32 --epochs_num 3 --seq_length 128
对于文本对分类,数据集需要包括text_a、text_b、label列。 然后我们使用微调后的文本对分类模型进行推理:
python inference/run_classifier_infer.py --load_model_path models/classifier_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --test_path datasets/lcqmc/test.tsv --prediction_path datasets/lcqmc/prediction.tsv --seq_length 128 --labels_num 2
被预测的文件(–test_path)需要包括text_a和text_b列。
从前21条预测结果来看,有4条打标有误,不过也不能以偏概全,只能说还需要再微调一下。
我们还可以使用 run_ner.py 进行命名实体识别:
python finetune/run_ner.py --pretrained_model_path models/google_zh_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/msra_ner/train.tsv --dev_path datasets/msra_ner/dev.tsv --test_path datasets/msra_ner/test.tsv --output_model_path models/ner_model.bin --label2id_path datasets/msra_ner/label2id.json --epochs_num 5 --batch_size 16
这张图里给出了精度、召回率和 F1 分数:
这些指标都在 0.94 以上,表明模型在处理该任务时性能非常好。
验证集上的分数(0.956, 0.955, 0.955)略高于测试集上的分数(0.943, 0.947, 0.945),这通常是正常的,因为模型在验证集上可能会更熟悉这些数据的模式。
–label2id_path 指定用于命名实体识别的label2id文件的路径。 然后我们使用微调后的命名实体模型模型进行推理:
python inference/run_ner_infer.py --load_model_path models/ner_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --test_path datasets/msra_ner/test_nolabel.tsv --prediction_path datasets/msra_ner/prediction.tsv --label2id_path datasets/msra_ner/label2id.json
在对比预测结果前,我们先来学点基础知识
这些标签是用于命名实体识别(NER,Named Entity Recognition)任务中的标记,表示不同类型的实体和它们在文本中的位置。以下是对这些标签的详细解释:
标签解释
-
O
(Outside):- 含义:该标记表示当前词不属于任何命名实体。
- 用途:适用于那些不需要特别标记的普通词语。
-
B-LOC
(Beginning of Location):- 含义:表示一个地理位置(如城市、国家等)实体的开始。
- 示例:在 “Paris is beautiful” 中,“Paris” 应该标记为
B-LOC
,因为它是地理位置的开始。
-
I-LOC
(Inside of Location):- 含义:表示地理位置实体的内部位置,通常用于多词组成的地理名称。
- 示例:在 “New York City” 中,“New” 应该标记为
B-LOC
,而 “York” 和 “City” 应该标记为I-LOC
,表示它们是一个连续的地理位置名称的一部分。
-
B-PER
(Beginning of Person):- 含义:表示一个人名实体的开始。
- 示例:在 “John Smith” 中,“John” 应该标记为
B-PER
,表示它是一个人名的开头。
-
I-PER
(Inside of Person):- 含义:表示人名实体的内部位置,通常用于多词组成的人名。
- 示例:在 “John Smith” 中,“Smith” 应该标记为
I-PER
,表示它是一个人名的后续部分。
-
B-ORG
(Beginning of Organization):- 含义:表示一个组织名称实体的开始,例如公司、机构或团体。
- 示例:在 “Google Inc.” 中,“Google” 应该标记为
B-ORG
,表示它是一个组织名称的开头。
-
I-ORG
(Inside of Organization):- 含义:表示组织名称实体的内部位置,通常用于多词组成的组织名称。
- 示例:在 “Google Inc.” 中,“Inc.” 应该标记为
I-ORG
,表示它是一个组织名称的后续部分。
标签格式
这些标签使用了 BIO 格式(有时也称为 IOB 格式),它是一种常用的序列标记方法,用于标记序列中的命名实体。BIO 是 Beginning
(开始), Inside
(内部) 和 Outside
(外部) 的缩写。
B-X
:表示某类型实体(X)的开头。I-X
:表示某类型实体(X)的后续部分。O
:表示不属于任何实体的词。
实际应用
在实体提取任务中,这些标签帮助模型识别并分类文本中的不同类型的实体。例如,模型可以从句子中提取并标记地名、人名和组织名称。下面是一个使用这些标签标记的句子的示例:
示例句子和标记
示例句子:
“张三在北京大学学习。”
我们需要标记以下几类实体:
- 人名 (
B-PER
,I-PER
) - 地名 (
B-LOC
,I-LOC
) - 组织名 (
B-ORG
,I-ORG
)
标记结果:
词 | 标签 |
---|---|
张 | B-PER |
三 | I-PER |
在 | O |
北京 | B-LOC |
大学 | I-LOC |
学习 | O |
BIO 标签解释
B-PER
: “张” 是人名的开始。I-PER
: “三” 是人名的后续部分。O
: “在” 和 “学习” 是普通词,不属于任何实体。B-LOC
: “北京” 是地名的开始。I-LOC
: “大学” 是地名的后续部分。
更复杂的中文例子
我们来看一个更复杂的例子,其中包含多种实体类型:
示例句子:
“李四在中国科学院工作,他的朋友王五在北京市政府工作。”
标记结果:
词 | 标签 |
---|---|
李 | B-PER |
四 | I-PER |
在 | O |
中国 | B-ORG |
科学院 | I-ORG |
工作 | O |
, | O |
他 | O |
的 | O |
朋友 | O |
王 | B-PER |
五 | I-PER |
在 | O |
北京 | B-LOC |
市政府 | I-LOC |
工作 | O |
。 | O |
BIO 标签解释
B-PER
: “李” 和 “王” 是人名的开始。I-PER
: “四” 和 “五” 是人名的后续部分。B-ORG
: “中国” 是组织名的开始。I-ORG
: “科学院” 是组织名的后续部分。B-LOC
: “北京” 是地名的开始。I-LOC
: “市政府” 是地名的后续部分。O
: 其他词都标记为O
,表示它们不属于任何命名实体。
可以说现在训练出来的对实体提取已经有不错的效果了
我们现在来看看使用 run_cmrc.py 进行机器阅读理解:
(这个其实就是训练本地知识库问答的一个东东)
python finetune/run_cmrc.py --pretrained_model_path models/google_zh_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/cmrc2018/train.json --dev_path datasets/cmrc2018/dev.json --output_model_path models/cmrc_model.bin --epochs_num 2 --batch_size 8 --seq_length 512
我们不指定 --test_path ,因为CMRC2018数据集不提供测试集的标签。 然后,我们使用微调后的机器阅读理解模型进行推理:
python inference/run_cmrc_infer.py --load_model_path models/cmrc_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --test_path datasets/cmrc2018/test.json --prediction_path datasets/cmrc2018/prediction.json --seq_length 512
我大概浏览了一下可以发现,这一遍跑出来的问答效果非常的好,赞
7.分类任务交叉验证
UER-py支持分类任务的交叉验证,在竞赛数据集SMP2020-EWECT上使用交叉验证的示例:
python finetune/run_classifier_cv.py --pretrained_model_path models/google_zh_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/base_config.json --train_path datasets/smp2020-ewect/virus/train.tsv --train_features_path datasets/smp2020-ewect/virus/train_features.npy --output_model_path models/classifier_model.bin --epochs_num 3 --batch_size 32 --folds_num 5
由上图可知,google_zh_model.bin 的结果为79.9/65.46(准确率/F1值); --folds_num 指定交叉验证的轮数; --output_path 指定微调模型的路径,共保存 --folds_num 个微调后的模型,并将 fold ID 后缀添加到模型名称中; --train_features_path 指定out-of-fold预测文件的路径;训练集被分成了 --folds_num 折。一折样本的预测概率是由其他折上的数据训练的模型预测得到的。train_features.npy 可用于stacking集成。竞赛解决方案章节给出了更多详细信息。
我们可以进一步尝试不同的预训练模型。例如,可以下载RoBERTa-wwm-ext-large from HIT并将其转换为UER格式(这里的话我建议打开这个网址,直接去下载pytorch_model.bin,然后放在models下):
python scripts/convert_bert_from_huggingface_to_uer.py --input_model_path models/pytorch_model.bin --output_model_path models/pytorch_uer_model.bin --layers_num 24
python finetune/run_classifier_cv.py --pretrained_model_path models/pytorch_uer_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/large_config.json --train_path datasets/smp2020-ewect/virus/train.tsv --train_features_path datasets/smp2020-ewect/virus/train_features.npy --output_model_path models/classifier_model.bin --folds_num 5 --epochs_num 3 --batch_size 32
RoBERTa-wwm-ext-large 的结果为80.3/66.8(准确率/F1值)。注意到这里使用BERT-large的配置文件(–config_path models/bert/large_config.json)。
使用下载好的预训练模型评论语料RoBERTa-large的示例如下:
python finetune/run_classifier_cv.py --pretrained_model_path models/review_roberta_large_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/bert/large_config.json --train_path datasets/smp2020-ewect/virus/train.tsv --train_features_path datasets/smp2020-ewect/virus/train_features.npy --output_model_path models/classifier_model.bin --folds_num 5 --learning_rate 1e-5 --epochs_num 3 --batch_size 32 --seed 11
结果为81.3/68.4(准确率/F1值),与其他开源预训练模型相比,这一结果很具有竞争力。这个预训练模型使用的语料和SMP2020-EWECT(微博评论数据集)高度相似。 有时大模型无法收敛,我们需要通过指定 --seed 尝试不同的随机种子
8.使用Transformer之外的编码器
UER-py支持Transformer之外的编码器。这里我们选择2层LSTM编码器来替代12层Transformer编码器。我们首先下载2层LSTM编码器的预训练模型cluecorpussmall_lstm_lm_model.bin。这个预训练模型在CLUECorpusSmall语料上训练了50万步(大家这里要去找开源人员申请,可以发邮箱过去,然后加联系方式),我这里还没申请到哈哈哈,大家就先看看代码吧:
python preprocess.py --corpus_path corpora/cluecorpussmall.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --seq_length 256 --data_processor lm
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --config_path models/rnn/lstm_config.json --output_model_path models/cluecorpussmall_lstm_lm_model.bin --world_size 8 --gpu_ranks 0 1 2 3 4 5 6 7 --total_steps 500000 --save_checkpoint_steps 100000 --learning_rate 1e-3 --batch_size 64
把预训练模型记录训练步数的后缀去掉,然后在下游分类数据集上对其进行微调:
python finetune/run_classifier.py --pretrained_model_path models/cluecorpussmall_lstm_lm_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/rnn/lstm_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --learning_rate 1e-3 --epochs_num 5 --batch_size 64 --pooling mean
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/rnn/lstm_config.json --test_path datasets/book_review/test_nolabel.tsv --prediction_path datasets/book_review/prediction.tsv --pooling mean --labels_num 2
UER-py还支持更多的预训练模型。 我们下载ELMo预训练模型cluecorpussmall_elmo_model.bin。这个预训练模型在CLUECorpusSmall语料上训练了50万步:
python preprocess.py --corpus_path corpora/cluecorpussmall.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --seq_length 256 --data_processor bilm
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --config_path models/rnn/bilstm_config.json --output_model_path models/cluecorpussmall_elmo_model.bin --world_size 1 --gpu_ranks 0 --total_steps 500000 --save_checkpoint_steps 100000 --learning_rate 5e-4 --batch_size 64
把预训练模型记录训练步数的后缀去掉,然后在书评情感分类数据集上对其进行增量预训练和微调:
python preprocess.py --corpus_path corpora/book_review.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --seq_length 192 --data_processor bilm
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --pretrained_model_path models/cluecorpussmall_elmo_model.bin --config_path models/rnn/bilstm_config.json --output_model_path models/book_review_elmo_model.bin --world_size 1 --gpu_ranks 0 --total_steps 5000 --save_checkpoint_steps 2500 --learning_rate 5e-4 --batch_size 64
mv models/book_review_elmo_model.bin-5000 models/book_review_elmo_model.bin
python finetune/run_classifier.py --pretrained_model_path models/book_review_elmo_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/rnn/bilstm_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --learning_rate 5e-4 --epochs_num 5 --batch_size 64 --seq_length 192 --pooling max
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/rnn/bilstm_config.json --test_path datasets/book_review/test_nolabel.tsv --prediction_path datasets/book_review/prediction.tsv --seq_length 192 --pooling max --labels_num 2
corpora/book_review.txt 是由书评情感分类数据集去掉标签得到的。
在分类数据集上微调GatedCNN模型的示例:
python finetune/run_classifier.py --pretrained_model_path models/cluecorpussmall_gatedcnn_lm_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/cnn/gatedcnn_9_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --learning_rate 5e-5 --epochs_num 5 --batch_size 64 --pooling mean
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --vocab_path models/google_zh_vocab.txt --config_path models/cnn/gatedcnn_9_config.json --test_path datasets/book_review/test_nolabel.tsv --prediction_path datasets/book_review/prediction.tsv --pooling mean --labels_num 2
大家可以下载wikizh_gatedcnn_lm_model.bin,这个预训练模型在CLUECorpusSmall语料上训练了50万步:
python preprocess.py --corpus_path corpora/cluecorpussmall.txt --vocab_path models/google_zh_vocab.txt --dataset_path dataset.pt --processes_num 8 --seq_length 256 --data_processor lm
python pretrain.py --dataset_path dataset.pt --vocab_path models/google_zh_vocab.txt --config_path models/cnn/gatedcnn_9_config.json --output_model_path models/cluecorpussmall_gatedcnn_lm_model.bin --world_size 1 --gpu_ranks 0 --total_steps 500000 --save_checkpoint_steps 100000 --report_steps 100 --learning_rate 1e-4 --batch_size 64
9.使用GPT2模型微调
使用GPT-2模型微调下游任务示例:
用户可以从这里下载 cluecorpussmall_gpt2_seq1024_model.bin
python finetune/run_classifier.py --pretrained_model_path models/cluecorpussmall_gpt2_seq1024_model.bin-250000 --vocab_path models/google_zh_vocab.txt --config_path models/gpt2/config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --epochs_num 3 --batch_size 32 --pooling mean
10.使用不同的分词器和词典
在大多数情况下,我们使用 --vocab_path models/google_zh_vocab.txt 和 --tokenizer bert 组合进行分词。在项目的大多数脚本中, --tokenizer bert 作为默认的分词器被使用。因此我们通常不会显式的指定 --tokenizer 。这里我们展示更多的分词器和词典的使用方法。
–tokenizer bert 在处理中文的时候是基于字的。如果我们想得到基于词的预训练模型并基于其进行微调和推理,那么首先我们要对语料进行分词,词与词之间用空格分隔。然后,我们基于分词后的语料(corpora/book_review_seg.txt)构建词典:(这里的book_review_seg文件夹包括语料集我没找到,可能也是要申请才能拿的到,大家就先看看代码)
python scripts/build_vocab.py --corpus_path corpora/book_review_seg.txt --output_path models/book_review_word_vocab.txt --delimiter space --workers_num 8 --min_count 5
由于词与词之间用空格分隔,因此在预处理和预训练阶段需要使用 --tokenizer space 。基于词的模型预处理和预训练示例:
python preprocess.py --corpus_path corpora/book_review_seg.txt --vocab_path models/book_review_word_vocab.txt --tokenizer space --dataset_path book_review_word_dataset.pt --processes_num 8 --seq_length 128 --dynamic_masking --data_processor mlm
python pretrain.py --dataset_path book_review_word_dataset.pt --vocab_path models/book_review_word_vocab.txt --tokenizer space --config_path models/bert/base_config.json --output_model_path models/book_review_word_model.bin --world_size 1 --gpu_ranks 0 --total_steps 5000 --save_checkpoint_steps 2500 --report_steps 500 --learning_rate 1e-4 --batch_size 64 --data_processor mlm --target mlm --tie_weights
在下游任务微调和推理的过程中,我们同样需要显式的指定 --vocab_path models/book_review_word_vocab.txt 和 --tokenizer space ,并且数据集的训练/验证/测试集文本列(text_a和text_b)需要使用相同的分词工具进行分词。我们对 datasets/book_review/ 中的文件进行分词,放在 datasets/book_review_seg/ 文件夹中:
mv models/book_review_word_model.bin-5000 models/book_review_word_model.bin
python finetune/run_classifier.py --pretrained_model_path models/book_review_word_model.bin --vocab_path models/book_review_word_vocab.txt --tokenizer space --config_path models/bert/base_config.json --train_path datasets/book_review_seg/train.tsv --dev_path datasets/book_review_seg/dev.tsv --test_path datasets/book_review_seg/test.tsv --epochs_num 3 --batch_size 32
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --vocab_path models/book_review_word_vocab.txt --tokenizer space --config_path models/bert/base_config.json --test_path datasets/book_review_seg/test_nolabel.tsv --prediction_path datasets/book_review_seg/prediction.tsv --labels_num 2
使用SentencePiece分词示例:
python preprocess.py --corpus_path corpora/book_review.txt --spm_model_path models/cluecorpussmall_spm.model --dataset_path book_review_word_sentencepiece_dataset.pt --processes_num 8 --seq_length 128 --dynamic_masking --data_processor mlm
python pretrain.py --dataset_path book_review_word_sentencepiece_dataset.pt --spm_model_path models/cluecorpussmall_spm.model --output_model_path models/book_review_word_sentencepiece_model.bin --world_size 1 --gpu_ranks 0 --total_steps 5000 --save_checkpoint_steps 2500 --report_steps 500 --learning_rate 1e-4 --batch_size 64 -data_processor mlm --target mlm --tie_weights
mv models/book_review_word_sentencepiece_model.bin-5000 models/book_review_word_sentencepiece_model.bin
python finetune/run_classifier.py --pretrained_model_path models/book_review_word_sentencepiece_model.bin --spm_model_path models/cluecorpussmall_spm.model --config_path models/bert/base_config.json --train_path datasets/book_review/train.tsv --dev_path datasets/book_review/dev.tsv --test_path datasets/book_review/test.tsv --epochs_num 3 --batch_size 32
python inference/run_classifier_infer.py --load_model_path models/finetuned_model.bin --spm_model_path models/cluecorpussmall_spm.model --config_path models/bert/base_config.json --test_path datasets/book_review/test_nolabel.tsv --prediction_path datasets/book_review/prediction.tsv --labels_num 2
通过 --spm_model_path models/cluecorpussmall_spm.model 指定使用在CLUECorpusSmall语料上训练的SentencePiece分词模型。
如果使用基于字的分词方式,可以使用 --vocab_path models/google_zh_vocab.txt 和 --tokenizer char 的组合代替上面的 --spm_model_path models/cluecorpussmall_spm.model,其余的选项不变。 由于谷歌提供的词典 models/google_zh_vocab.txt 是基于字的,因此可以直接使用。
本项目支持多种分词(tokenization)方式。最常用的,也是项目默认使用的是BertTokenizer。BertTokenizer有两种使用方式:第一种是通过 --vocab_path 指定词典路径。然后使用BERT原始的tokenization策略,根据词典对句子进行切分;第二种是通过 --spm_model_path 指定sentencepiece模型路径,然后导入sentencepiece模块,加载sentencepiece模型,对句子进行切分。如果用户指定了 --spm_model_path ,那么使用sentencepiece进行tokeniztion。否则,用户必须指定 --vocab_path ,使用BERT原始的策略进行tokenization。
此外,本项目支持CharTokenizer和SpaceTokenizer。CharTokenizer将文本按照字符分开。如果文本全都是中文,则CharTokenizer和BertTokenizer等价。CharTokenizer逻辑简单,速度大于BertTokenizer。SpaceTokenizer将文本按照空格分开。可以事先对文本进行预处理(比如进行分词),将文本按照空格分开,然后使用SpaceTokenizer。对于CharTokenizer和SpaceTokenizer,如果用户指定了 --spm_model_path ,那么使用sentencepiece模型中的词典。否则,用户必须通过 --vocab_path 指定使用的词典。
为了支持加载英文RoBERTa和GPT-2等模型,本项目支持使用BPETokenizer对句子进行切分,通过 --vocab_path 指定使用的词典,通过 --merges_path 指定使用的合并词典。
本项目还支持XLMRobertaTokenizer(和原始实现一致)。XLMRobertaTokenizer使用sentencepiece模型对句子进行切分,通过 --spm_model_path 指定sentencepiece模型路径。此外,XLMRoBERTaTokenizer会对词典进行修改,加上特殊字符。由于XLMRobertaTokenizer使用了和默认情况不一致的特殊字符,需要按照下一段中提到的方法修改特殊字符。
预处理、预训练、下游任务微调、推理阶段均需要通过 --vocab_path 或者 --smp_model_path 提供词典信息以及通过 --tokenizer 提供分词方式信息。如果用户使用自己的词典,默认情况下,填充字符、起始字符、分隔字符、遮罩字符分别为“[PAD]”、“[CLS]”、“[SEP]”、“[MASK]”(项目从默认的特殊字符映射表 models/special_tokens_map.json 中读取特殊字符)。如果用户词典中的特殊字符和默认的不一致,需要相应的提供特殊字符映射表,比如 models/xlmroberta_special_tokens_map.json ,然后修改 uer/utils/constants.py 中的特殊字符映射表路径。
刚刚我们对实体标注进行了一定的拓展,现在我们来拓展一下分词器与字典的知识
分词(Tokenization)方式详解
分词是自然语言处理(NLP)中的一个关键步骤,它将文本数据拆分成更小的单位,称为“token”。这些token可以是单词、子词或字符,具体取决于所选的分词策略和目标任务。以下是该项目支持的几种分词方式的详细说明,并通过中文例子来帮助理解这些概念。
1. BertTokenizer
BertTokenizer 是专为 BERT 模型设计的分词器,能够处理复杂的语言结构和词语组合。BERT 的分词策略主要包括基于词典的切分和基于子词的切分。
使用方式一:通过 --vocab_path
指定词典路径
- 解释:这种方式使用预定义的词典来切分句子。词典包含了常见词语及其子词。
- 例子:
- 输入句子:
"我喜欢学习自然语言处理"
- 词典:
["我", "喜欢", "学习", "自然", "语言", "处理"]
- 分词结果:
["我", "喜欢", "学习", "自然", "语言", "处理"]
- 输入句子:
- 应用场景:这种方式适用于标准化语言处理任务,尤其是在处理结构良好的文本时效果较好。
使用方式二:通过 --spm_model_path
指定 SentencePiece 模型路径
- 解释:这使用了 SentencePiece 模型,可以将句子切分为子词单位,适合处理未登录词(词典中不存在的词)。
- 例子:
- 输入句子:
"我喜欢学习自然语言处理"
- 可能的分词结果:
["我", "喜", "欢", "学", "习", "自然", "语", "言", "处", "理"]
- 或:
["我", "喜", "欢", "学习", "自然", "语", "言", "处理"]
- 输入句子:
- 应用场景:这种方式对处理多语言和包含大量未登录词的文本非常有效。
2. CharTokenizer
CharTokenizer 将文本按照单个字符进行分割,特别适合处理字符级别的信息。
- 解释:对于中文文本,这种方式与 BertTokenizer 的效果相同,因为中文词汇本身是字符单位。对于其他语言,如英文,它将每个字符单独分割。
- 例子:
- 中文输入:
"我喜欢学习"
- 分词结果:
["我", "喜", "欢", "学", "习"]
- 英文输入:
"Hello"
- 分词结果:
["H", "e", "l", "l", "o"]
- 中文输入:
- 应用场景:CharTokenizer 适合处理文本的细粒度信息,特别是在需要逐字符分析的任务中,如语言生成或字符级模型。
3. SpaceTokenizer
SpaceTokenizer 按照空格将文本进行分割,这对于预处理过的文本特别有用。
- 解释:假设输入的文本已经经过分词处理(每个词之间有空格)。
- 例子:
- 输入句子:
"我 喜欢 学习 自然 语言 处理"
- 分词结果:
["我", "喜欢", "学习", "自然", "语言", "处理"]
- 输入句子:
- 应用场景:这种方式适用于已经进行了初步分词的文本,或者语言本身词与词之间有明显分隔符(如英文)。
4. BPETokenizer
BPETokenizer 使用 Byte Pair Encoding (BPE) 算法,将常见的词汇和短语进行合并,逐步生成词典。
- 解释:BPE 是一种子词级别的分词策略,可以处理新词和组合词。
- 例子:
- 输入句子:
"我喜欢自然语言处理"
- 词典:
{"我", "喜欢", "自然", "语言", "处理", "自", "然", "语", "言"}
- 分词结果:
["我", "喜欢", "自然", "语言", "处理"]
- 对于新词:
"机器学习"
可能会分成["机", "器", "学习"]
或["机器", "学习"]
,取决于词典。
- 输入句子:
- 应用场景:BPETokenizer 适合处理拼写变体和复合词,广泛用于支持多语言的模型,如 RoBERTa 和 GPT-2。
5. XLMRobertaTokenizer
XLMRobertaTokenizer 是为 XLM-RoBERTa 模型设计的,使用 SentencePiece 模型进行分词,并支持特殊字符的处理。
- 解释:它在分词的同时,会对词典进行修改,加入特殊字符(如句子的起始和结束标志)。
- 例子:
- 输入句子:
"我喜欢学习自然语言处理"
- 分词结果:
["[CLS]", "我", "喜欢", "学习", "自然", "语言", "处理", "[SEP]"]
- 输入句子:
- 应用场景:这种分词器适用于需要处理多语言和跨语言任务的场景,特别是在使用 XLM-RoBERTa 模型时。
特殊字符和映射
在分词和文本处理过程中,特殊字符起到标记和控制的作用:
- [PAD]:填充字符,用于对齐不同长度的句子。
- [CLS]:分类标志,一般放在句子的开头,表示句子的开始。
- [SEP]:分隔标志,用于分隔句子或句子片段。
- [MASK]:掩蔽标志,用于模型在训练时预测被掩盖的词。
特殊字符的应用
- 默认设置:项目中的特殊字符映射表通常存储在
models/special_tokens_map.json
中。 - 自定义词典:如果使用了自定义词典,且特殊字符与默认字符不一致,需要提供对应的特殊字符映射表,例如
models/xlmroberta_special_tokens_map.json
,并修改uer/utils/constants.py
中的路径以匹配新的词典。
拓展2:base_config参数详解
这个参数配置描述了一个用于自然语言处理(NLP)任务的Transformer模型。让我们详细解释这些参数:
模型架构参数
-
emb_size(嵌入层大小): 768
- 这是模型的词嵌入维度,也就是每个词被表示成一个768维的向量。这些向量是模型学习的,能够捕捉词的语义特征。
-
feedforward_size(前馈神经网络层大小): 3072
- Transformer中每层的前馈神经网络(FFN)的隐藏层大小。在这个例子中是3072,通常是嵌入层大小的4倍。FFN帮助模型在每一层提取复杂的特征。
-
hidden_size(隐藏层大小): 768
- 定义了Transformer模型中每层的隐藏状态的维度,也就是模型内部的表示维度,与emb_size相同。
-
hidden_act(隐藏层激活函数): gelu
- 这是模型中使用的激活函数。在这里是gelu(高斯误差线性单元),它是一种平滑的非线性函数,通常在Transformer模型中使用,因为它有更好的训练效果。
-
heads_num(注意力头的数量): 12
- 这是多头自注意力机制的头数。在这个模型中有12个头,这意味着模型会在每个位置计算12个不同的注意力分布,从而捕捉到更多的上下文信息。
-
layers_num(层数): 12
- 这是Transformer模型的层数,表示模型由12层堆叠的Transformer编码器层组成。每层都包含一个多头自注意力机制和一个前馈神经网络。
-
max_seq_length(最大序列长度): 512
- 这是模型可以处理的输入序列的最大长度。输入文本被截断或填充到这个长度。在这个例子中,最长的输入序列是512个标记(tokens)。
-
dropout(随机失活率): 0.1
- 用于正则化的技术,在训练时随机丢弃10%的神经元连接,以防止模型过拟合。
数据处理和输入参数
-
data_processor(数据处理器): bert
- 这里的bert表示使用类似BERT模型的数据处理方法,包括分词、输入格式化等。
-
embedding(嵌入方式): [“word”, “pos”, “seg”]
- 这定义了输入的嵌入表示,包括词嵌入(word)、位置嵌入(pos)和分段嵌入(seg)。这些嵌入帮助模型理解输入文本的结构和内容:
- word: 每个词的词嵌入。
- pos: 每个词在句子中的位置(位置信息)。
- seg: 句子分段嵌入,用于区分输入中的不同句子(例如,问句和回答句)。
- 这定义了输入的嵌入表示,包括词嵌入(word)、位置嵌入(pos)和分段嵌入(seg)。这些嵌入帮助模型理解输入文本的结构和内容:
-
encoder(编码器): transformer
- 使用Transformer架构作为模型的编码器。Transformer通过自注意力机制和前馈神经网络层来处理输入数据。
-
mask(掩码机制): fully_visible
- 表示所有输入的位置在自注意力中都是可见的。对于BERT的掩码语言模型任务,所有标记的位置都可以关注到其他标记的位置。
目标和训练参数
-
target(目标): [“mlm”, “sp”]
- 这些是模型的训练目标,包括:
- mlm(Masked Language Modeling): 掩码语言模型任务,模型通过预测被掩盖的词来学习上下文。
- sp(Sentence Prediction): 句子预测任务,通常是BERT的下一句预测任务(NSP),用来预测两个句子是否是连续的。
- 这些是模型的训练目标,包括:
-
tie_weights(权重共享): true
- 这表示词嵌入层和输出层之间共享权重。共享权重可以减少模型的参数数量,提高训练效率,并且通常有助于模型的泛化能力。
例子
为了更好地理解这些参数,我们可以对照BERT模型:
- BERT Base的常见配置:
emb_size
: 768feedforward_size
: 3072hidden_size
: 768hidden_act
: geluheads_num
: 12layers_num
: 12max_seq_length
: 512dropout
: 0.1data_processor
: bertembedding
: [“word”, “pos”, “seg”]encoder
: transformermask
: fully_visibletarget
: [“mlm”, “sp”]tie_weights
: true
遇到的问题
我在做文本预训练的过程中碰到了一个问题。用pycharm打开预处理好的.pt文件出现乱码,我调了utf-8格式也解决不了。不知道有没有大佬赐教?
我检查过文件的“魔数”,是没问题的
于是我尝试过用torch加载它,可是不行
import torch
import os
file_path = 'dataset.pt'
# 检查文件是否存在
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
else:
# 查看文件大小
file_size = os.path.getsize(file_path)
print(f"File size: {file_size} bytes")
# 读取文件头部信息
with open(file_path, 'rb') as f:
header = f.read(4)
print(f"File header: {header}")
try:
# 尝试加载文件
data = torch.load(file_path, map_location=torch.device('cpu'))
print(data)
except Exception as e:
print(f"Failed to load the file: {e}")
Traceback (most recent call last):
File "D:\uer\UER-py-master\UER-py-master\check.py", line 4, in <module>
model = torch.load('dataset.pt')
^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\ancanda\envs\pytorch-gpu\Lib\site-packages\torch\serialization.py", line 1040, in load
return _legacy_load(opened_file, map_location, pickle_module, **pickle_load_args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\ancanda\envs\pytorch-gpu\Lib\site-packages\torch\serialization.py", line 1264, in _legacy_load
raise RuntimeError("Invalid magic number; corrupt file?")
RuntimeError: Invalid magic number; corrupt file?