返回介绍

数学基础

统计学习

深度学习

工具

Scala

二、微调 Masked Language Model

发布于 2023-07-17 23:38:23 字数 31076 浏览 0 评论 0 收藏 0

  1. token classification 任务可以为文本中的单词或字符分配标签,如:

    • 实体命名识别 (NER):找出句子中的实体(如人物、地点或组织)。

    • 词性标注 (POS):将句子中的每个单词标记为对应的词性(如名词、动词、形容词等)。

    • 分块(chunking):找到属于同一实体的 Token (如,找出名词短语、动词短语等)。

1.1 加载数据集

  1. 查看数据集:

    
    
    xxxxxxxxxx
    from datasets import load_dataset raw_datasets = load_dataset("conll2003") print(raw_datasets) # 有三个数据集:train, validation, test # DatasetDict({ # train: Dataset({ # features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], # num_rows: 14041 # }) # validation: Dataset({ # features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], # num_rows: 3250 # }) # test: Dataset({ # features: ['chunk_tags', 'id', 'ner_tags', 'pos_tags', 'tokens'], # num_rows: 3453 # }) # }) print(raw_datasets["train"][0]["tokens"]) # 训练集第零个样本的单词序列 # ['EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'lamb', '.'] print(raw_datasets["train"][0]["ner_tags"]) # 训练集第零个样本的 label 序列 # [3, 0, 7, 0, 0, 0, 7, 0, 0] ner_feature = raw_datasets["train"].features["ner_tags"] print(ner_feature) # NER label 的名称 # Sequence(feature=ClassLabel(names=['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC'],>

1.2 数据处理

  1. 我们的文本需要转换为 Token ID,然后模型才能理解它们。

    
    
    xxxxxxxxxx
    from transformers import AutoTokenizer model_checkpoint = "bert-base-cased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) print(tokenizer.is_fast) # True ## 注意,样本,如 raw_datasets["train"][0]["tokens"] 已经是完成了分词,因此这里无需分词步骤 (is_split_into_words 设为 True ) inputs = tokenizer(raw_datasets["train"][0]["tokens"], is_split_into_words=True) # 执行 tokenization print(inputs) # { # 'input_ids': [101, 7270, 22961, 1528, 1840, 1106, 21423, 1418, 2495, 12913, 119, 102], # 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # } print(inputs.tokens()) # ['[CLS]', 'EU', 'rejects', 'German', 'call', 'to', 'boycott', 'British', 'la', '##mb', '.', '[SEP]']

    注意:单词 "lamb" 被分为两个子词 "la""##mb"。这导致了 token 序列和标签序列之间的不匹配:标签序列只有 9 个元素,而 token 序列有 12token 。我们需要扩展标签序列以匹配 token 序列 。这里有两条规则:

    • special token (如,[CLS], [SEP])的标签为 -100 。这是因为默认情况下 -100 是一个在我们将使用的损失函数(交叉熵)中被忽略的 index

    • 每个 token 都会获得与其所在单词相同的标签,因为它们是同一实体的一部分。对于单词内部但不在开头的 token ,我们将 B- 替换为 I- ,因为单词内部的 token 不是实体的开头。

    
    
    xxxxxxxxxx
    def align_labels_with_tokens(labels, word_ids): # labels : word_id -> label 的映射 # word_ids: 每个 token 对应的 word_id new_labels = [] current_word = None for word_id in word_ids: if word_id != current_word: # 遇到一个新的单词 current_word = word_id label = -100 if word_id is None else labels[word_id] new_labels.append(label) elif word_id is None: # special token new_labels.append(-100) else: # 同一个单词内部 label = labels[word_id] if label % 2 == 1: # 如果 label 是 B-XXX,那么修改为 I-XXX label += 1 new_labels.append(label) return new_labels
  2. 为了预处理整个数据集,我们需要 tokenize 所有输入并在所有 label 上应用 align_labels_with_tokens() 。为了利用我们的快速分词器的速度优势,最好同时对大量文本进行 tokenize ,因此我们将编写一个处理样本列表的函数并使用带 batched = TrueDataset.map()方法。

    
    
    xxxxxxxxxx
    def tokenize_and_align_labels(examples): # examples 是样本的列表 tokenized_inputs = tokenizer( examples["tokens"], truncation=True, is_split_into_words=True ) all_labels = examples["ner_tags"] new_labels = [] for i, labels in enumerate(all_labels): word_ids = tokenized_inputs.word_ids(i) # 获取第 i 个样本的 word_ids new_labels.append(align_labels_with_tokens(labels, word_ids)) tokenized_inputs["labels"] = new_labels # 调整标签 return tokenized_inputs tokenized_datasets = raw_datasets.map( tokenize_and_align_labels, batched=True, # 一次性处理 batch 的样本 remove_columns=raw_datasets["train"].column_names, ) print(tokenized_datasets) # DatasetDict({ # train: Dataset({ # features: ['input_ids', 'token_type_ids', 'attention_mask', 'labels'], # num_rows: 14041 # }) # validation: Dataset({ # features: ['input_ids', 'token_type_ids', 'attention_mask', 'labels'], # num_rows: 3250 # }) # test: Dataset({ # features: ['input_ids', 'token_type_ids', 'attention_mask', 'labels'], # num_rows: 3453 # }) # }) print(tokenized_datasets["train"][0]["input_ids"]) # 训练集第零个样本的 token id 序列 # [101, 7270, 22961, 1528, 1840, 1106, 21423, 1418, 2495, 12913, 119, 102] print(tokenized_datasets["train"][0]["labels"]) # 训练集第零个样本的 label id 序列 # [-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100] print(tokenized_datasets["train"].features["labels"]) # Sequence(feature=Value(dtype='int64',>

1.3 使用 Trainer API 微调模型

a. 构建 mini-batch

  1. 前面的数据处理可以批量地将输入文本转换为 input_idstoken id 序列),但是我们还需要将样本拼接成 mini-batch 然而馈入模型。DataCollatorForTokenClassification 支持以相同的方式来填充输入文本和 label ,使得 input_idslabels 的长度保持相同。labelpadding 值为 -100,这样在损失计算中就可以忽略相应的预测。

    注意:input_idspadding 值为 0labelspadding 值为 -100

    
    
    xxxxxxxxxx
    from transformers import DataCollatorForTokenClassification data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer) sample_list = [tokenized_datasets["train"][i] for i in range(3)] # 3 个样本,每个样本的序列长度不同 print([len(i['labels']) for i in sample_list]) # [12, 4, 11] batch = data_collator(sample_list) print(batch) # {'input_ids': # tensor([[ 101, 7270, 22961, 1528, 1840, 1106, 21423, 1418, 2495, 12913, # 119, 102], # [ 101, 1943, 14428, 102, 0, 0, 0, 0, 0, 0, # 0, 0], # [ 101, 26660, 13329, 12649, 15928, 1820, 118, 4775, 118, 1659, # 102, 0]]), # 'token_type_ids': # tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), # 'attention_mask': # tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], # [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]]), # 'labels': # tensor([[-100, 3, 0, 7, 0, 0, 0, 7, 0, 0, 0, -100], # [-100, 1, 2, -100, -100, -100, -100, -100, -100, -100, -100, -100], # [-100, 5, 6, 6, 6, 0, 0, 0, 0, 0, -100, -100]])}

b. evaluation 指标

  1. 用于评估 Token 分类效果的传统框架是 seqeval 。要使用此指标,我们首先需要安装 seqeval 库:

    
    
    xxxxxxxxxx
    pip install seqeval

    然后我们可以通过 load_metric() 函数加载它:

    
    
    xxxxxxxxxx
    from datasets import load_metric metric = load_metric("seqeval")

    这个评估框架以字符串而不是整数的格式传入 label ,因此在将 predictionlabel 传递给它之前需要对 prediction/label 进行解码。让我们看看它是如何工作的:

    首先,我们获得第一个训练样本的标签:

    
    
    xxxxxxxxxx
    labels = raw_datasets["train"][0]["ner_tags"] print(labels) # [3, 0, 7, 0, 0, 0, 7, 0, 0] label_names = raw_datasets["train"].features["ner_tags"].feature.names labels = [label_names[i] for i in labels] print(labels) # ['B-ORG', 'O', 'B-MISC', 'O', 'O', 'O', 'B-MISC', 'O', 'O']

    然后我们可以通过更改 labels 来创建 ”虚拟” 的 prediction

    
    
    xxxxxxxxxx
    predictions = labels.copy() # 注意,需要执行深层拷贝使得 predictions 和 labels 不共享底层数据 predictions[2] = "O" metric.compute(predictions=[predictions], references=[labels])

    metric.compute 的输入是一组样本的 prediction (不仅仅是单个样本的 prediction )、以及一组样本的 label 。输出为:

    
    
    xxxxxxxxxx
    {'MISC': {'precision': 1.0, 'recall': 0.5, 'f1': 0.6666666666666666, 'number': 2}, 'ORG': {'precision': 1.0, 'recall': 1.0, 'f1': 1.0, 'number': 1}, 'overall_precision': 1.0, 'overall_recall': 0.6666666666666666, 'overall_f1': 0.8, 'overall_accuracy': 0.8888888888888888}

    可以看到它返回了每个单独实体(如 "MISC"、"ORG")、以及整体的精度、召回率、以及 F1 分数。对于单独实体还返回出现的次数("number" ),对于整体还返回准确率。

  2. 为了让 Trainer 在每个 epoch 计算一个指标,我们需要定义一个 compute_metrics() 函数,该函数接受 predictionlabel ,并返回一个包含指标名称和值的字典。

    这个compute_metrics() 首先对 logits 执行 argmax 从而得到 prediction (这里无需采用 softmax,因为我们只需要找到概率最大的那个 token id 即可,也就是 logit 最大的那个 token id)。然后我们将 predictionlabel 从整数转换为字符串(注意删除 label = -100label 及其对应位置的 prediction)。然后我们将 prediction 字符串和 label 字符串传递给 metric.compute() 方法:

    
    
    xxxxxxxxxx
    import numpy as np def compute_metrics(eval_preds): logits, labels = eval_preds predictions = np.argmax(logits, axis=-1) # 移除 label = -100 位置的数据 (包括 token 和 label) true_labels = [[label_names[l] for l in label if l != -100] for label in labels] true_predictions = [ [label_names[p] for (p, l) in zip(prediction, label) if l != -100] for prediction, label in zip(predictions, labels) ] all_metrics = metric.compute(predictions=true_predictions, references=true_labels) return { "precision": all_metrics["overall_precision"], "recall": all_metrics["overall_recall"], "f1": all_metrics["overall_f1"], "accuracy": all_metrics["overall_accuracy"], }

c. 定义模型

  1. 由于我们正在研究 Token 分类问题,因此我们将使用 AutoModelForTokenClassification 类。

    首先我们定义两个字典 id2labellabel2id ,其中包含从 label idlabel name的映射:

    
    
    xxxxxxxxxx
    id2label = {i: label for i, label in enumerate(label_names)} print(id2label) # {0: 'O', 1: 'B-PER', 2: 'I-PER', 3: 'B-ORG', 4: 'I-ORG', 5: 'B-LOC', 6: 'I-LOC', 7: 'B-MISC', 8: 'I-MISC'} label2id = {v: k for k, v in id2label.items()} print(label2id) # {'O': 0, 'B-PER': 1, 'I-PER': 2, 'B-ORG': 3, 'I-ORG': 4, 'B-LOC': 5, 'I-LOC': 6, 'B-MISC': 7, 'I-MISC': 8}

    然后我们加载预训练好的模型:

    
    
    xxxxxxxxxx
    from transformers import AutoModelForTokenClassification, TrainingArguments, Trainer model = AutoModelForTokenClassification.from_pretrained( "bert-base-cased", id2label=id2label, label2id=label2id ) print(model) # BertForTokenClassification( # (bert): BertModel( # (embeddings): BertEmbeddings( # ... # ) # (encoder): BertEncoder( # ... # ) # ) # (dropout): Dropout(p=0.1, inplace=False) # (classifier): Linear(in_features=768, out_features=9, bias=True) #)

    注意,预训练好的模型必须和 AutoTokenizer 相匹配,这里都是用的 "bert-base-cased"

    创建模型会发出警告,提示某些权重未被使用(来自 pretrained-head 的权重),另外提示某些权重被随机初始化(来新分类头的权重),我们将要训练这个模型因此可以忽略这两种警告。

    我们可以查看模型配置:

    
    
    xxxxxxxxxx
    print(model.config) # BertConfig { # "_name_or_path": "bert-base-cased", # "architectures": [ # "BertForMaskedLM" # ], # "attention_probs_dropout_prob": 0.1, # "classifier_dropout": null, # "gradient_checkpointing": false, # "hidden_act": "gelu", # "hidden_dropout_prob": 0.1, # "hidden_size": 768, # "id2label": { # ... # }, # "initializer_range": 0.02, # "intermediate_size": 3072, # "label2id": { # ... # }, # "layer_norm_eps": 1e-12, # "max_position_embeddings": 512, # "model_type": "bert", # "num_attention_heads": 12, # "num_hidden_layers": 12, # "pad_token_id": 0, # "position_embedding_type": "absolute", # "transformers_version": "4.25.1", # "type_vocab_size": 2, # "use_cache": true, # "vocab_size": 28996 # }
  2. 然后我们需要定义训练参数,以及登录 Hugging Face (如果不需要把训练结果推送到 HuggingFace,则无需登录并且设置 push_to_hub=False )。

    • 登录:

      
      
      xxxxxxxxxx
      huggingface-cli login
    • 定义训练参数:

      
      
      xxxxxxxxxx
      from transformers import TrainingArguments args = TrainingArguments( "bert-finetuned-ner", # 输出模型的名称 evaluation_strategy="epoch", save_strategy="epoch", learning_rate=2e-5, num_train_epochs=3, weight_decay=0.01, push_to_hub=True, # 注意,如果你不想 push 到 HuggingFace,设置它为 False ) print(args) # 参数含义可以参考前面对应章节的内容 # TrainingArguments( # _n_gpu=0, # 如果机器上有 GPU,那么这里表示 GPU 数量 # adafactor=False, # adam_beta1=0.9, # adam_beta2=0.999, # adam_epsilon=1e-08, # auto_find_batch_size=False, # bf16=False, # bf16_full_eval=False, # data_seed=None, # dataloader_drop_last=False, # dataloader_num_workers=0, # dataloader_pin_memory=True, # ddp_bucket_cap_mb=None, # ddp_find_unused_parameters=None, # ddp_timeout=1800, # debug=[], # deepspeed=None, # disable_tqdm=False, # do_eval=True, # do_predict=False, # do_train=False, # eval_accumulation_steps=None, # eval_delay=0, # eval_steps=None, # evaluation_strategy=epoch, # fp16=False, # fp16_backend=auto, # fp16_full_eval=False, # fp16_opt_level=O1, # fsdp=[], # fsdp_min_num_params=0, # fsdp_transformer_layer_cls_to_wrap=None, # full_determinism=False, # gradient_accumulation_steps=1, # gradient_checkpointing=False, # greater_is_better=None, # group_by_length=False, # half_precision_backend=auto, # hub_model_id=None, # hub_private_repo=False, # hub_strategy=every_save, # hub_token=<HUB_TOKEN>, # ignore_data_skip=False, # include_inputs_for_metrics=False, # jit_mode_eval=False, # label_names=None, # label_smoothing_factor=0.0, # learning_rate=2e-05, # length_column_name=length, # load_best_model_at_end=False, # local_rank=-1, # log_level=passive, # log_level_replica=passive, # log_on_each_node=True, # logging_dir=bert-finetuned-ner/runs/Apr15_06-45-26_SHAUNHUA-MB0, # logging_first_step=False, # logging_nan_inf_filter=True, # logging_steps=500, # logging_strategy=steps, # lr_scheduler_type=linear, # max_grad_norm=1.0, # max_steps=-1, # metric_for_best_model=None, # mp_parameters=, # no_cuda=False, # num_train_epochs=3, # optim=adamw_hf, # optim_args=None, # output_dir=bert-finetuned-ner, # overwrite_output_dir=False, # past_index=-1, # per_device_eval_batch_size=8, # per_device_train_batch_size=8, # prediction_loss_only=False, # push_to_hub=True, # push_to_hub_model_id=None, # push_to_hub_organization=None, # push_to_hub_token=<PUSH_TO_HUB_TOKEN>, # ray_scope=last, # remove_unused_columns=True, # report_to=['tensorboard'], # resume_from_checkpoint=None, # run_name=bert-finetuned-ner, # save_on_each_node=False, # save_steps=500, # save_strategy=epoch, # save_total_limit=None, # seed=42, # sharded_ddp=[], # skip_memory_metrics=True, # tf32=None, # torchdynamo=None, # tpu_metrics_debug=False, # tpu_num_cores=None, # use_ipex=False, # use_legacy_prediction_loop=False, # use_mps_device=False, # warmup_ratio=0.0, # warmup_steps=0, # weight_decay=0.01, # xpu_backend=None, # )
  3. 接下来我们构建 Trainer 并启动训练:

    
    
    xxxxxxxxxx
    from transformers import Trainer trainer = Trainer( model=model, args=args, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["validation"], data_collator=data_collator, compute_metrics=compute_metrics, tokenizer=tokenizer, ) trainer.train() # TrainOutput(global_step=5268, training_loss=0.06639557918455896, # metrics={'train_runtime': 198.3512, 'train_samples_per_second': 212.366, 'train_steps_per_second': 26.559, 'total_flos': 923954298531210.0, 'train_loss': 0.06639557918455896, 'epoch': 3.0})

    建议使用 GPU 训练,CPU 速度太慢。在 MacBook Pro 2018 (型号 A1990)上训练速度:0.38 iteration/s,在 RTX 4090 上训练速度 26.48 iteration/s

    如果 push_to_hub=True ,那么训练期间每次保存模型时(这里是每个 epooch 保存一次),trainer 都会在后台将 checkpoint 上传到 HuggingFace Model Hub 。这样,你就能够在另一台机器上继续训练。并且训练完成之后,可以上传模型的最终版本:

    
    
    xxxxxxxxxx
    trainer.push_to_hub(commit_message="Training finish!")

    Trainer 还创建了一张包含所有评估结果的 Model Card 并上传。在此阶段,你可以使用 Hugging Face Model Hub 上的 inference widget 来测试该模型,并分享给其它人。

1.4 使用微调后的模型

  1. 通过指定模型名称来使用微调后的模型:

    
    
    xxxxxxxxxx
    from transformers import pipeline token_classifier = pipeline( # model 参数指定模型名称(对应于 HuggingFace Model Hub、或者模型权重的位置(对应于本地) "token-classification", model="./bert-finetuned-ner/checkpoint-5268"", aggregation_strategy="simple" ) token_classifier("Wuhan is a beautiful city in China.") # [{'entity_group': 'LOC', 'score': 0.99749047, 'word': 'Wuhan', 'start': 0, 'end': 5}, # {'entity_group': 'LOC', 'score': 0.9990609, 'word': 'China', 'start': 29, 'end': 34}]

1.5 自定义训练过程

  1. 我们也可以自定义训练过程,从而代替 Trainer API ,这样可以对训练过程进行更精细的控制。

    
    
    xxxxxxxxxx
    from torch.utils.data import DataLoader from datasets import load_dataset from transformers import AutoTokenizer from transformers import DataCollatorForTokenClassification from transformers import AutoModelForTokenClassification from datasets import load_metric from tqdm.auto import tqdm import torch ##****************** 创建数据集 ********************** model_checkpoint = "bert-base-cased" tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) data_collator = DataCollatorForTokenClassification(tokenizer=tokenizer) def align_labels_with_tokens(labels, word_ids): # labels : word_id -> label 的映射 # word_ids: 每个 token 对应的 word_id new_labels = [] current_word = None for word_id in word_ids: if word_id != current_word: # 遇到一个新的单词 current_word = word_id label = -100 if word_id is None else labels[word_id] new_labels.append(label) elif word_id is None: # special token new_labels.append(-100) else: # 同一个单词内部 label = labels[word_id] if label % 2 == 1: # 如果 label 是 B-XXX,那么修改为 I-XXX label += 1 new_labels.append(label) return new_labels def tokenize_and_align_labels(examples): # examples 是样本的列表 tokenized_inputs = tokenizer( examples["tokens"], truncation=True, is_split_into_words=True ) all_labels = examples["ner_tags"] new_labels = [] for i, labels in enumerate(all_labels): word_ids = tokenized_inputs.word_ids(i) # 获取第 i 个样本的 word_ids new_labels.append(align_labels_with_tokens(labels, word_ids)) tokenized_inputs["labels"] = new_labels # 调整标签 return tokenized_inputs raw_datasets = load_dataset("conll2003") tokenized_datasets = raw_datasets.map( tokenize_and_align_labels, batched=True, # 一次性处理 batch 的样本 remove_columns=raw_datasets["train"].column_names, ) train_dataloader = DataLoader( tokenized_datasets["train"], shuffle=True, collate_fn=data_collator, batch_size=8, ) eval_dataloader = DataLoader( tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8 ) ##****************** 创建模型 ********************** label_names = raw_datasets["train"].features["ner_tags"].feature.names id2label = {i: label for i, label in enumerate(label_names)} label2id = {v: k for k, v in id2label.items()} model = AutoModelForTokenClassification.from_pretrained( model_checkpoint, id2label=id2label, label2id=label2id, ) ##****************** 创建优化器 ********************** from torch.optim import AdamW optimizer = AdamW(model.parameters(), lr=2e-5) ##****************** 调用 accelerator ********************** from accelerate import Accelerator accelerator = Accelerator() model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare( model, optimizer, train_dataloader, eval_dataloader ) ##****************** 创建调度器 ********************** from transformers import get_scheduler num_train_epochs = 3 num_update_steps_per_epoch = len(train_dataloader) # 注意,必须在 accelerator.prepare() 之后在调用数据集的 len(...),因为 accelerator.prepare() 可能改变数据集的长度 num_training_steps = num_train_epochs * num_update_steps_per_epoch lr_scheduler = get_scheduler( "linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps, ) ##****************** 创建 Repo ********************** from huggingface_hub import Repository, get_full_repo_name # model_name = "bert-finetuned-ner-accelerate" # repo_name = get_full_repo_name(model_name) # print(repo_name) output_dir = "bert-finetuned-ner-accelerate" # repo = Repository(output_dir, clone_from=repo_name) # 也可以忽略这一步 ##****************** 定义 metric 、以及后处理函数 ********************** metric = load_metric("seqeval") def postprocess(predictions, labels): # 将模型输出和标签转换为 metric 可接受的格式 predictions = predictions.detach().cpu().clone().numpy() labels = labels.detach().cpu().clone().numpy() # 移除 label = -100 位置的数据 (包括 token 和 label) true_labels = [[label_names[l] for l in label if l != -100] for label in labels] true_predictions = [ [label_names[p] for (p, l) in zip(prediction, label) if l != -100] for prediction, label in zip(predictions, labels) ] return true_labels, true_predictions ##****************** 训练流程 ********************** progress_bar = tqdm(range(num_training_steps)) for epoch in range(num_train_epochs): # Training model.train() for batch in train_dataloader: outputs = model(**batch) loss = outputs.loss accelerator.backward(loss) optimizer.step() # 参数更新 lr_scheduler.step() # 学习率更新 optimizer.zero_grad() # 梯度清零 progress_bar.update(1) # Evaluation model.eval() for batch in eval_dataloader: with torch.no_grad(): outputs = model(**batch) predictions = outputs.logits.argmax(dim=-1) labels = batch["labels"] # 在多进程场景下,可能两个进程将 predictions/labels 在进程内部对齐,但是在进程间不一致,这里需要跨进程对齐 predictions = accelerator.pad_across_processes(predictions, dim=1, pad_index=-100) labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100) predictions_gathered = accelerator.gather(predictions) # 跨进程收集 labels_gathered = accelerator.gather(labels) true_predictions, true_labels = postprocess(predictions_gathered, labels_gathered) metric.add_batch(predictions=true_predictions, references=true_labels) results = metric.compute() # 计算验证集的指标 print(f"epoch {epoch}:", { key: results[f"overall_{key}"] for key in ["precision", "recall", "f1", "accuracy"]}, ) # epoch 0: {'precision': 0.930, 'recall': 0.904, 'f1': 0.917, 'accuracy': 0.982} # epoch 1: {'precision': 0.944, 'recall': 0.912, 'f1': 0.928, 'accuracy': 0.985} # epoch 2: {'precision': 0.948, 'recall': 0.928, 'f1': 0.938, 'accuracy': 0.987} ##****************** 保存和上传模型 ********************** accelerator.wait_for_everyone() # 所有进程都到达这个阶段然后继续执行,如果任何一个进程未到达,则所有其他进程都要等待 unwrapped_model = accelerator.unwrap_model(model) # 获取底层的模型,因为 accelerator 可能在分布式环境中工作 unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save) # 用 accelerator.save 来保存 if accelerator.is_main_process: # 只有主进程才执行下面的步骤 tokenizer.save_pretrained(output_dir) # 保存 tokenizer # repo.push_to_hub(commit_message=f"Training in progress epoch {epoch}", blocking=False) # blocking = False 表示异步推送

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文