精华内容
下载资源
问答
  • 在本教程中,我们将紧跟HuggingFace Transformers示例中的BERT模型,对BERT模型应用动态量化。通过这一循序渐进的过程,我们将演示如何将BERT等众所周知的最新模型转换为动态量化模型。 BERT,或者说Transformers的...

     

    Introduction

    在本教程中,我们将紧跟HuggingFace Transformers示例中的BERT模型,对BERT模型应用动态量化。通过这一循序渐进的过程,我们将演示如何将BERT等众所周知的最新模型转换为动态量化模型。

    • BERT,或者说Transformers的双向嵌入表示法,是一种预训练语言表示法的新方法,可以在许多流行的自然语言处理(NLP)任务(例如问题解答,文本分类,和别的。原始文件可以在这里找到 。
    • PyTorch中的动态量化支持将浮点模型转换为具有静态int8或float16数据类型的权重和动态量化的量化模型。当权重量化为int8时,激活(每批)动态量化为int8。在PyTorch中,我们有torch.quantization.quantize_dynamic API,该API用仅动态权重的量化版本替换了指定的模块,并输出了量化的模型。
    • 我们 在通用语言理解评估基准(GLUE)中展示了Microsoft Research Paraphrase Corpus(MRPC)任务的准确性和推理性能结果 。MRPC(Dolan和Brockett,2005年)是从在线新闻源中自动提取的句子对的语料库,带有人工注释,说明句子中的句子在语义上是否等效。由于班级不平衡(68%积极,32%消极),我们遵循常规做法并报告 F1得分。MRPC是用于语言对分类的常见NLP任务,如下所示。

     

     

    1. Setup

     

    1.1 Install PyTorch and HuggingFace Transformers

    要启动本教程中,我们首先按照PyTorch安装说明这里和HuggingFace Github上回购这里。此外,我们还将安装scikit-learn软件包,因为我们将重用其内置的F1分数计算帮助器功能。

    pip install sklearn
    pip install transformers

    由于我们将使用PyTorch的Beta版部分,因此建议安装最新版本的Torch和Torchvision。可以在此处找到有关本地安装的最新说明。例如,要在Mac上安装:

    yes y | pip uninstall torch tochvision
    yes y |
     pip install --pre torch -f 
    https:
    /
    /download.pytorch.org/whl
    /nightly/cu
    101/torch_nightly.html

     

    1.2 Import the necessary modules

    在这一步中,我们将导入本教程所需的Python模块。

    from __future__ import absolute_import, division, print_function
    ​
    import logging
    import numpy as np
    import os
    import random
    import sys
    import time
    import torch
    ​
    from argparse import Namespace
    from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler,
                                  TensorDataset)
    from tqdm import tqdm
    from transformers import (BertConfig, BertForSequenceClassification, BertTokenizer,)
    from transformers import glue_compute_metrics as compute_metrics
    from transformers import glue_output_modes as output_modes
    from transformers import glue_processors as processors
    from transformers import glue_convert_examples_to_features as convert_examples_to_features
    ​
    # Setup logging
    logger = logging.getLogger(__name__)
    logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s -   %(message)s',
                        datefmt = '%m/%d/%Y %H:%M:%S',
                        level = logging.WARN)
    ​
    logging.getLogger("transformers.modeling_utils").setLevel(
       logging.WARN)  # Reduce logging
    ​
    print(torch.__version__)

    我们设置线程数以比较FP32和INT8性能之间的单线程性能。在本教程的最后,用户可以通过使用右侧并行后端构建PyTorch来设置其他线程数量。

    torch.set_num_threads(1)
    print(torch.__config__.parallel_info())

     

    1.3 Learn about helper functions

    帮助器功能内置在转换器库中。我们主要使用以下辅助函数:一个用于将文本示例转换为特征向量的函数;另一个用于测量预测结果的F1分数。

    glue_convert_examples_to_features函数将文本翻译成输入功能:

    • 标记输入序列;
    • 在开头插入[CLS];
    • 在第一句和第二句之间并在最后插入[SEP];
    • 生成令牌类型ID,以指示令牌是属于第一序列还是第二序列。

    glue_compute_metrics 函数具有与计算的度量F1得分,这可以被解释为加权平均的精确度和召回,其中一个F1得分为0到达1其最佳值和最差评分的精度和召回的相对贡献F1分数相等。

    F1分数的公式为:

    • F1=2∗(precision∗recall)/(precision+recall)

     

     

    1.4 Download the dataset

    在运行MRPC任务之前,我们通过运行此脚本下载GLUE数据 并将其解压缩到目录中。glue_data

    python download_glue_data.py --data_dir='glue_data' --tasks='MRPC'

     

    2. Fine-tune the BERT model

    BERT的意义是预训练语言表示形式,然后以最小的任务相关参数微调各种任务上的深层双向表示形式,并获得最新的结果。在本教程中,我们将专注于对预训练的BERT模型进行微调,以对MRPC任务上的语义等效句子对进行分类。

    要为bert-base-uncasedMRPC任务微调预训练的BERT模型(HuggingFace变压器中的模型),可以在示例中遵循以下命令:

    export GLUE_DIR=./glue_data
    export TASK_NAME=MRPC
    export OUT_DIR=./$TASK_NAME/
    python ./run_glue.py \
        --model_type bert \
        --model_name_or_path bert-base-uncased \
        --task_name $TASK_NAME \
        --do_train \
        --do_eval \
        --do_lower_case \
        --data_dir $GLUE_DIR/$TASK_NAME \
        --max_seq_length 128 \
        --per_gpu_eval_batch_size=8   \
        --per_gpu_train_batch_size=8   \
        --learning_rate 2e-5 \
        --num_train_epochs 3.0 \
        --save_steps 100000 \
        --output_dir $OUT_DIR

     

    我们在这里为MRPC任务提供了经过微调的BERT模型。为了节省时间,可以将模型文件(〜400 MB)直接下载到本地文件夹中$OUT_DIR

     

    2.1 Set global configurations

    在这里,我们设置了全局配置,用于评估动态量化前后的微调BERT模型。

     

    configs = Namespace()
    ​
    # The output directory for the fine-tuned model, $OUT_DIR.
    configs.output_dir = "./MRPC/"
    ​
    # The data directory for the MRPC task in the GLUE benchmark, $GLUE_DIR/$TASK_NAME.
    configs.data_dir = "./glue_data/MRPC"
    ​
    # The model name or path for the pre-trained model.
    configs.model_name_or_path = "bert-base-uncased"
    # The maximum length of an input sequence
    configs.max_seq_length = 128
    ​
    # Prepare GLUE task.
    configs.task_name = "MRPC".lower()
    configs.processor = processors[configs.task_name]()
    configs.output_mode = output_modes[configs.task_name]
    configs.label_list = configs.processor.get_labels()
    configs.model_type = "bert".lower()
    configs.do_lower_case = True
    ​
    # Set the device, batch size, topology, and caching flags.
    configs.device = "cpu"
    configs.per_gpu_eval_batch_size = 8
    configs.n_gpu = 0
    configs.local_rank = -1
    configs.overwrite_cache = False
    ​
    ​
    # Set random seed for reproducibility.
    def set_seed(seed):
        random.seed(seed)
        np.random.seed(seed)
        torch.manual_seed(seed)
    set_seed(42)

     

    2.2 Load the fine-tuned BERT model

    我们从中加载了分词器和经过微调的BERT序列分类器模型(FP32)configs.output_dir

    tokenizer = BertTokenizer.from_pretrained(
        configs.output_dir, do_lower_case=configs.do_lower_case)
    ​
    model = BertForSequenceClassification.from_pretrained(configs.output_dir)
    model.to(configs.device)

     

     

    2.3 Define the tokenize and evaluation function

    我们重用了Huggingface的标记化和评估功能。

    # coding=utf-8
    # Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
    # Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    ​
    def evaluate(args, model, tokenizer, prefix=""):
        # Loop to handle MNLI double evaluation (matched, mis-matched)
        eval_task_names = ("mnli", "mnli-mm") if args.task_name == "mnli" else (args.task_name,)
        eval_outputs_dirs = (args.output_dir, args.output_dir + '-MM') if args.task_name == "mnli" else (args.output_dir,)
    ​
        results = {}
        for eval_task, eval_output_dir in zip(eval_task_names, eval_outputs_dirs):
            eval_dataset = load_and_cache_examples(args, eval_task, tokenizer, evaluate=True)
    ​
            if not os.path.exists(eval_output_dir) and args.local_rank in [-1, 0]:
                os.makedirs(eval_output_dir)
    ​
            args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu)
            # Note that DistributedSampler samples randomly
            eval_sampler = SequentialSampler(eval_dataset) if args.local_rank == -1 else DistributedSampler(eval_dataset)
            eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.eval_batch_size)
    ​
            # multi-gpu eval
            if args.n_gpu > 1:
                model = torch.nn.DataParallel(model)
    ​
            # Eval!
            logger.info("***** Running evaluation {} *****".format(prefix))
            logger.info("  Num examples = %d", len(eval_dataset))
            logger.info("  Batch size = %d", args.eval_batch_size)
            eval_loss = 0.0
            nb_eval_steps = 0
            preds = None
            out_label_ids = None
            for batch in tqdm(eval_dataloader, desc="Evaluating"):
                model.eval()
                batch = tuple(t.to(args.device) for t in batch)
    ​
                with torch.no_grad():
                    inputs = {'input_ids':      batch[0],
                              'attention_mask': batch[1],
                              'labels':         batch[3]}
                    if args.model_type != 'distilbert':
                        inputs['token_type_ids'] = batch[2] if args.model_type in ['bert', 'xlnet'] else None  # XLM, DistilBERT and RoBERTa don't use segment_ids
                    outputs = model(**inputs)
                    tmp_eval_loss, logits = outputs[:2]
    ​
                    eval_loss += tmp_eval_loss.mean().item()
                nb_eval_steps += 1
                if preds is None:
                    preds = logits.detach().cpu().numpy()
                    out_label_ids = inputs['labels'].detach().cpu().numpy()
                else:
                    preds = np.append(preds, logits.detach().cpu().numpy(), axis=0)
                    out_label_ids = np.append(out_label_ids, inputs['labels'].detach().cpu().numpy(), axis=0)
    ​
            eval_loss = eval_loss / nb_eval_steps
            if args.output_mode == "classification":
                preds = np.argmax(preds, axis=1)
            elif args.output_mode == "regression":
                preds = np.squeeze(preds)
            result = compute_metrics(eval_task, preds, out_label_ids)
            results.update(result)
    ​
            output_eval_file = os.path.join(eval_output_dir, prefix, "eval_results.txt")
            with open(output_eval_file, "w") as writer:
                logger.info("***** Eval results {} *****".format(prefix))
                for key in sorted(result.keys()):
                    logger.info("  %s = %s", key, str(result[key]))
                    writer.write("%s = %s\n" % (key, str(result[key])))
    ​
        return results
    ​
    ​
    def load_and_cache_examples(args, task, tokenizer, evaluate=False):
        if args.local_rank not in [-1, 0] and not evaluate:
            torch.distributed.barrier()  # Make sure only the first process in distributed training process the dataset, and the others will use the cache
    ​
        processor = processors[task]()
        output_mode = output_modes[task]
        # Load data features from cache or dataset file
        cached_features_file = os.path.join(args.data_dir, 'cached_{}_{}_{}_{}'.format(
            'dev' if evaluate else 'train',
            list(filter(None, args.model_name_or_path.split('/'))).pop(),
            str(args.max_seq_length),
            str(task)))
        if os.path.exists(cached_features_file) and not args.overwrite_cache:
            logger.info("Loading features from cached file %s", cached_features_file)
            features = torch.load(cached_features_file)
        else:
            logger.info("Creating features from dataset file at %s", args.data_dir)
            label_list = processor.get_labels()
            if task in ['mnli', 'mnli-mm'] and args.model_type in ['roberta']:
                # HACK(label indices are swapped in RoBERTa pretrained model)
                label_list[1], label_list[2] = label_list[2], label_list[1]
            examples = processor.get_dev_examples(args.data_dir) if evaluate else processor.get_train_examples(args.data_dir)
            features = convert_examples_to_features(examples,
                                                    tokenizer,
                                                    label_list=label_list,
                                                    max_length=args.max_seq_length,
                                                    output_mode=output_mode,
                                                    pad_on_left=bool(args.model_type in ['xlnet']),                 # pad on the left for xlnet
                                                    pad_token=tokenizer.convert_tokens_to_ids([tokenizer.pad_token])[0],
                                                    pad_token_segment_id=4 if args.model_type in ['xlnet'] else 0,
            )
            if args.local_rank in [-1, 0]:
                logger.info("Saving features into cached file %s", cached_features_file)
                torch.save(features, cached_features_file)
    ​
        if args.local_rank == 0 and not evaluate:
            torch.distributed.barrier()  # Make sure only the first process in distributed training process the dataset, and the others will use the cache
    ​
        # Convert to Tensors and build dataset
        all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long)
        all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long)
        all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long)
        if output_mode == "classification":
            all_labels = torch.tensor([f.label for f in features], dtype=torch.long)
        elif output_mode == "regression":
            all_labels = torch.tensor([f.label for f in features], dtype=torch.float)
    ​
        dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels)
        return dataset

    3. Apply the dynamic quantization

    我们呼吁torch.quantization.quantize_dynamic该模型在HuggingFace BERT模型上应用动态量化。特别,

    • 我们指定要对模型中的torch.nn.Linear模块进行量化;
    • 我们指定希望将权重转换为量化的int8值。
    quantized_model = torch.quantization.quantize_dynamic(
        model, {torch.nn.Linear}, dtype=torch.qint8
    )
    print(quantized_model)

     

    3.1 Check the model size

    首先检查模型尺寸。我们可以观察到模型大小的显着减小(FP32总大小:438 MB; INT8总大小:181 MB):

    def print_size_of_model(model):
        torch.save(model.state_dict(), "temp.p")
        print('Size (MB):', os.path.getsize("temp.p")/1e6)
        os.remove('temp.p')
    ​
    print_size_of_model(model)
    print_size_of_model(quantized_model)

    本教程中使用的BERT模型(bert-base-uncased)的词汇量V为30522。在嵌入量为768的情况下,单词嵌入表的总大小为〜4(Bytes / FP32)* 30522 * 768 = 90 MB。因此,借助量化,非嵌入表部分的模型大小从350 MB(FP32模型)减少到90 MB(INT8模型)。

     

    3.2 Evaluate the inference accuracy and time

    接下来,让我们比较动态量化后原始FP32模型和INT8模型之间的推断时间以及评估精度。

    def time_model_evaluation(model, configs, tokenizer):
        eval_start_time = time.time()
        result = evaluate(configs, model, tokenizer, prefix="")
        eval_end_time = time.time()
        eval_duration_time = eval_end_time - eval_start_time
        print(result)
        print("Evaluate total time (seconds): {0:.1f}".format(eval_duration_time))
    ​
    # Evaluate the original FP32 BERT model
    time_model_evaluation(model, configs, tokenizer)
    ​
    # Evaluate the INT8 BERT model after the dynamic quantization
    time_model_evaluation(quantized_model, configs, tokenizer)

    在MacBook Pro上本地运行此程序,无需进行量化,推理(对于MRPC数据集中的所有408个示例)大约需要160秒,而进行量化则只需大约90秒。我们总结了在Macbook Pro上运行量化BERT模型推断的结果,如下所示:

    | Prec | F1 score | Model Size | 1 thread | 4 threads |
    | FP32 |  0.9019  |   438 MB   | 160 sec  | 85 sec    |
    | INT8 |  0.902   |   181 MB   |  90 sec  | 46 sec    |

     

    在MRPC任务的微调BERT模型上应用训练后动态量化后,我们的F1分数准确性为0.6%。作为比较,在最近的一篇论文中(表1),通过应用训练后动态量化,它达到0.8788,而通过应用量化感知训练,它达到0.8956。主要区别在于我们在PyTorch中支持非对称量化,而该论文仅支持对称量化。

    请注意,在本教程中,为了进行单线程比较,我们将线程数设置为1。对于这些量化的INT8运算符,我们还支持运算内并行化。用户现在可以通过设置多线程torch.set_num_threads(N)N是内部操作并行化线程的数量)。启用帧内并行化支持的一项初步要求是使用正确的后端( 例如OpenMP,Native或TBB)构建PyTorch 。您可以torch.__config__.parallel_info()用来检查并行化设置。在使用PyTorch和本机后端进行并行化的同一台MacBook Pro上,我们可以花大约46秒的时间来处理MRPC数据集的评估。

     

    3.3 Serialize the quantized model

    跟踪模型后,我们可以使用torch.jit.save序列化并保存量化模型,以备将来使用 。

    input_ids = ids_tensor([8, 128], 2)
    token_type_ids = ids_tensor([8, 128], 2)
    attention_mask = ids_tensor([8, 128], vocab_size=2)
    dummy_input = (input_ids, attention_mask, token_type_ids)
    traced_model = torch.jit.trace(quantized_model, dummy_input)
    torch.jit.save(traced_model, "bert_traced_eager_quant.pt")

    要加载量化模型,我们可以使用torch.jit.load

    loaded_quantized_model = torch.jit.load("bert_traced_eager_quant.pt")

     

    Conclusion

    在本教程中,我们演示了如何演示如何将BERT等著名的最新NLP模型转换为动态量化模型。动态量化可以减小模型的大小,而对准确性的影响有限。

     

    接下来,给大家介绍一下租用GPU做实验的方法,我们是在智星云租用的GPU,使用体验很好。具体大家可以参考:智星云官网: http://www.ai-galaxy.cn/,淘宝店:https://shop36573300.taobao.com/公众号: 智星AI

       

     

                    

     

    展开全文
  • BERT使用心得经验分享

    千次阅读 2019-06-10 09:33:45
    使用Bert有一段时间了,在项目中确实也能带来提升,这里总结下bert使用心得。 #1,资源的使用 Bert在训练的过程确实很消耗资源。 上面是Bert模型在12G显存的显卡上,每轮的样本数和句子长度。 开始还担心Bert模型...

    使用Bert有一段时间了,在项目中确实也能带来提升,这里总结下bert的使用心得。

    #1,资源的使用
    Bert在训练的过程确实很消耗资源。
    Bert资源图

    上面是Bert模型在12G显存的显卡上,每轮的样本数和句子长度。
    开始还担心Bert模型部署在客户的机器上能否正常运行。后面测试发现,如果没有太高的并发,其实在预测阶段对显存的消耗并不高,大概1-2G显存即可。

    #2,模型速度
    使用Bert模型一定要跑在GPU上,不论是训练还是预测阶段。我测试的跑在GPU上的速度是CPU的60多倍。

    #3,学习速率
    目前使用Bert做过分类,seq2seq,序列标注,发现很多任务上,Bert的学习速率不宜调的过高,否则训练的效果很差。而且在官方代码中学习速率是5e-5。而且这里的训练方式建议使用官方提供的优化器,官方提供的优化器学习速率是动态调整的。详细代码可见GIT里面的demo3

    #4,Bert模型不参与训练
    在使用Bert的时候,建议Bert先参与训练,然后固定住Bert的参数,在训练N轮。在我的项目中,这样的操作最后的效果比较好。
    Bert不参与训练需要调整两个地方,一个是在创建模型的时候is_train=False,还有一个是Bert的参数不参与反向传播。
    详细代码可见GIT里面的demo3

    展开全文
  • 日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) 应用于bert模型的动态量化技术 ... 掌握使用动态量化技术对训练后的bert模型进行压缩。 相关知识 模型压缩: ...

    日萌社

    人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)


    应用于bert模型的动态量化技术

    学习目标

    • 了解模型压缩技术中的动态量化与静态量化的相关知识。
    • 掌握使用huggingface中的预训练BERT模型进行微调。
    • 掌握使用动态量化技术对训练后的bert模型进行压缩。

    相关知识

    • 模型压缩:
      • 模型压缩是一种针对大型模型(参数量巨大)在使用过程中进行优化的一种常用措施。它往往能够使模型体积缩小,简化计算,增快推断速度,满足模型在特定场合(如: 移动端)的需求。目前,模型压缩可以从多方面考虑,如剪枝方法(简化模型架构),参数量化方法(简化模型参数),知识蒸馏等。本案例将着重讲解模型参数量化方法。
    • 模型参数量化:
      • 在机器学习(深度学习)领域,模型量化一般是指将模型参数由类型FP32转换为INT8/FP16的过程,如果转换为INT8,转换之后的模型大小被压缩为原来的¼,所需内存和带宽减小4倍,同时,计算量减小约为2-4倍。模型又可分为动态量化和静态量化。
    • 模型动态量化:
      • 操作最简单也是压缩效果最好的量化方式,量化过程发生在模型训练后,针对模型权重采取量化,之后会在模型预测过程中,再决定是否针对激活值采取量化,因此称作动态量化(在预测时可能发生量化)。这是我们本案例将会使用的量化方式。
    • 模型静态量化:
      • 考虑到动态量化这种“一刀切”的量化方式有时会带来模型预测效果的大幅度下降,因此引入静态量化,它同样发生在模型训练后,为了判断哪些权重或激活值应该被量化,哪些应该保留或小幅度量化,在预测过程开始前,在模型中节点插入“观测者”(衡量节点使用情况的一些计算方法),他们将在一些实验数据中评估节点使用情况,来决定是否将其权重或激活值进行量化,因为在预测过程中,这些节点是否被量化已经确定,因此称作静态量化。
    • (扩展知识)量化意识训练:
      • 这是一种操作相对复杂的模型量化方法,在训练过程中使用它,原理与静态量化类似,都需要像模型中插入“观测者”,同时它还需要插入量化计算操作,使得模型训练过程中除了进行原有的浮点型计算,还要进行量化计算,但模型参数的更新还是使用浮点型,而量化计算的作用就是让模型“意识”到这个过程,通过“观测者”评估每次量化结果与训练过程中参数更新程度,为之后模型如何进行量化还能保证准确率提供衡量指标。(类似于,人在接受训练时,意识到自己接下来可能除了训练内容外,还会接受其他“操作”(量化),因此也会准备一些如果进行量化仍能达成目标的措施)
    • BERT模型:
      • 这里使用bert-base-uncased,它的编码器具有12个隐层, 输出768维张量, 12个自注意力头, 共110M参数量, 在小写的英文文本上进行训练而得到。

    数据集说明

    • GLUE数据集合的介绍:

      • GLUE由纽约大学,华盛顿大学,Google联合推出,涵盖不同的NLP任务类型,持续至2020年1月,其中包括11个子任务数据集,成为NLP研究发展的标准。我们这里使用其实MRPC数据集。
    • 数据下载地址: 标准数据集一般使用下载脚本进行下载,会在之后的代码中演示。

    • MRPC数据集的任务类型:

      • 句子对二分类任务
        • 训练集上正样本占68%,负样本占32%
      • 评估指标这里使用:F1
      • 评估指标计算方式:F1=2∗(precision∗recall)/(precision+recall)
    • 数据集预览:

      • MRPC数据集文件样式
    - MRPC/
            - dev.tsv
            - test.tsv
            - train.tsv
        - dev_ids.tsv
        - msr_paraphrase_test.txt
        - msr_paraphrase_train.txt
    • 文件样式说明:
      • 在使用中常用到的文件是train.tsv,dev.tsv,test.tsv,分别代表训练集,验证集和测试集。其中train.tsv与dev.tsv数据样式相同,都是带有标签的数据,其中test.tsv是不带有标签的数据
    • train.tsv数据样式:
    Quality #1 ID   #2 ID   #1 String   #2 String
    1   702876  702977  Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence . Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .
    0   2108705 2108831 Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .   Yucaipa bought Dominick 's in 1995 for $ 693 million and sold it to Safeway for $ 1.8 billion in 1998 .
    1   1330381 1330521 They had published an advertisement on the Internet on June 10 , offering the cargo for sale , he added .   On June 10 , the ship 's owners had published an advertisement on the Internet , offering the explosives for sale .
    0   3344667 3344648 Around 0335 GMT , Tab shares were up 19 cents , or 4.4 % , at A $ 4.56 , having earlier set a record high of A $ 4.57 . Tab shares jumped 20 cents , or 4.6 % , to set a record closing high at A $ 4.57 .
    1   1236820 1236712 The stock rose $ 2.11 , or about 11 percent , to close Friday at $ 21.51 on the New York Stock Exchange .   PG & E Corp. shares jumped $ 1.63 or 8 percent to $ 21.03 on the New York Stock Exchange on Friday .
    1   738533  737951  Revenue in the first quarter of the year dropped 15 percent from the same period a year earlier .   With the scandal hanging over Stewart 's company , revenue the first quarter of the year dropped 15 percent from the same period a year earlier .
    0   264589  264502  The Nasdaq had a weekly gain of 17.27 , or 1.2 percent , closing at 1,520.15 on Friday .    The tech-laced Nasdaq Composite .IXIC rallied 30.46 points , or 2.04 percent , to 1,520.15 .
    1   579975  579810  The DVD-CCA then appealed to the state Supreme Court .  The DVD CCA appealed that decision to the U.S. Supreme Court .
    ...
    • train.tsv数据样式说明:
      • train.tsv中的数据内容共分为5列,第一列数据,0或1,代表每对句子是否具有相同的含义,0代表含义不相同,1代表含义相同。第二列和第三列分别代表每对句子的id,第四列和第五列分别具有相同/不同含义的句子对
    • test.tsv数据样式:
    index   #1 ID   #2 ID   #1 String   #2 String
    0   1089874 1089925 PCCW 's chief operating officer , Mike Butcher , and Alex Arena , the chief financial officer , will report directly to Mr So . Current Chief Operating Officer Mike Butcher and Group Chief Financial Officer Alex Arena will report to So .
    1   3019446 3019327 The world 's two largest automakers said their U.S. sales declined more than predicted last month as a late summer sales frenzy caused more of an industry backlash than expected . Domestic sales at both GM and No. 2 Ford Motor Co. declined more than predicted as a late summer sales frenzy prompted a larger-than-expected industry backlash .
    2   1945605 1945824 According to the federal Centers for Disease Control and Prevention ( news - web sites ) , there were 19 reported cases of measles in the United States in 2002 .   The Centers for Disease Control and Prevention said there were 19 reported cases of measles in the United States in 2002 .
    3   1430402 1430329 A tropical storm rapidly developed in the Gulf of Mexico Sunday and was expected to hit somewhere along the Texas or Louisiana coasts by Monday night . A tropical storm rapidly developed in the Gulf of Mexico on Sunday and could have hurricane-force winds when it hits land somewhere along the Louisiana coast Monday night .
    4   3354381 3354396 The company didn 't detail the costs of the replacement and repairs .   But company officials expect the costs of the replacement work to run into the millions of dollars .
    5   1390995 1391183 The settling companies would also assign their possible claims against the underwriters to the investor plaintiffs , he added . Under the agreement , the settling companies will also assign their potential claims against the underwriters to the investors , he added .
    6   2201401 2201285 Air Commodore Quaife said the Hornets remained on three-minute alert throughout the operation . Air Commodore John Quaife said the security operation was unprecedented .
    7   2453843 2453998 A Washington County man may have the countys first human case of West Nile virus , the health department said Friday .  The countys first and only human case of West Nile this year was confirmed by health officials on Sept . 8 .
    ...
    • test.tsv数据样式说明:
      • test.tsv中的数据内容共分为5列,第一列数据代表每条文本数据的索引;其余列的含义与train.tsv中相同

    使用huggingface中的预训练BERT模型进行微调

    • 第一步: 安装必要的工具包并导入
    • 第二步: 下载数据集并使用脚本进行微调
    • 第三步: 设定全局配置并加载微调模型
    • 第四步: 编写用于模型使用的评估函数

    第一步: 安装核心的工具包并导入

    • 安装核心工具包:
    # 这是由huggingface提供的预训练模型使用工具包
    pip install transformers==2.3.0
    • 工具包导入
    from __future__ import absolute_import, division, print_function
    
    import logging
    import numpy as np
    import os
    import random
    import sys
    import time
    import torch
    
    # 用于设定全局配置的命名空间
    from argparse import Namespace
    
    # 从torch.utils.data中导入常用的模型处理工具,会在代码使用中进行详细介绍
    from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler,
                                  TensorDataset)
    
    # 模型进度可视化工具,在评估过程中,帮助打印进度条
    from tqdm import tqdm
    
    # 从transformers中导入BERT模型的相关工具
    from transformers import (BertConfig, BertForSequenceClassification, BertTokenizer,)
    
    # 从transformers中导入GLUE数据集的评估指标计算方法
    from transformers import glue_compute_metrics as compute_metrics
    
    # 从transformers中导入GLUE数据集的输出模式(回归/分类)
    from transformers import glue_output_modes as outp
    ut_modes
    
    # 从transformers中导入GLUE数据集的预处理器processors
    # processors是将持久化文件加载到内存的过程,即输入一般为文件路径,输出是训练数据和对应标签的某种数据结构,如列表表示。
    from transformers import glue_processors as processors
    
    # 从transformers中导入GLUE数据集的特征处理器convert_examples_to_features
    # convert_examples_to_features是将processor的输出处理成模型需要的输入,NLP定中的一般流程为数值映射,指定长度的截断补齐等
    # 在BERT模型上处理句子对时,还需要在句子前插入[CLS]开始标记,在两个句子中间和第二个句子末端插入[SEP]分割/结束标记
    from transformers import glue_convert_examples_to_features as convert_examples_to_features
    
    
    # 设定与日志打印有关的配置
    logger = logging.getLogger(__name__)
    logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s -   %(message)s',
                        datefmt = '%m/%d/%Y %H:%M:%S',
                        level = logging.WARN)
    
    logging.getLogger("transformers.modeling_utils").setLevel(
       logging.WARN)  # Reduce logging
    
    
    print("torch version:", torch.__version__)
    
    # 设置torch允许启动的线程数, 因为之后会对比压缩模型的耗时,因此防止该变量产生影响
    torch.set_num_threads(1)
    print(torch.__config__.parallel_info())
    • 输出效果
    torch version: 1.3.1
    
    ATen/Parallel:
        at::get_num_threads() : 1
        at::get_num_interop_threads() : 8
    OpenMP 201511 (a.k.a. OpenMP 4.5)
        omp_get_max_threads() : 1
    Intel(R) Math Kernel Library Version 2019.0.4 Product Build 20190411 for Intel(R) 64 architecture applications
        mkl_get_max_threads() : 1
    Intel(R) MKL-DNN v0.20.5 (Git Hash 0125f28c61c1f822fd48570b4c1066f96fcb9b2e)
    std::thread::hardware_concurrency() : 16
    Environment variables:
        OMP_NUM_THREADS : [not set]
        MKL_NUM_THREADS : [not set]
    ATen parallel backend: OpenMP

    第二步: 下载数据集并使用脚本进行微调

    • 下载GLUE中的MRPC数据集:
    python download_glue_data.py --data_dir='./glue_data' --tasks='MRPC'
    • 使用run_glue.py进行模型微调:
    # 注意: 这是一段使用shell运行的脚本, 运行过程中需要请求AWS的S3进行预训练模型下载
    
    # 定义GLUE_DIR: 微调数据所在路径, 这里我们使用glue_data中的数据作为微调数据
    export GLUE_DIR=./glue_data
    # 定义OUT_DIR: 模型的保存路径, 我们将模型保存在当前目录的bert_finetuning_test文件中
    export OUT_DIR=./bert_finetuning_test/
    
    python ./run_glue.py \
        --model_type bert \
        --model_name_or_path bert-base-uncased \
        --task_name MRPC \
        --do_train \
        --do_eval \
        --do_lower_case \
        --data_dir $GLUE_DIR/MRPC \
        --max_seq_length 128 \
        --per_gpu_eval_batch_size=8   \
        --per_gpu_train_batch_size=8   \
        --learning_rate 2e-5 \
        --num_train_epochs 1.0 \
        --output_dir $OUT_DIR
    
    # 使用python运行微调脚本
    # --model_type: 选择需要微调的模型类型, 这里可以选择BERT, XLNET, XLM, roBERTa, distilBERT, ALBERT
    # --model_name_or_path: 选择具体的模型或者变体, 这里是在英文语料上微调, 因此选择bert-base-uncased
    # --task_name: 它将代表对应的任务类型, 如MRPC代表句子对二分类任务
    # --do_train: 使用微调脚本进行训练
    # --do_eval: 使用微调脚本进行验证
    # --data_dir: 训练集及其验证集所在路径, 将自动寻找该路径下的train.tsv和dev.tsv作为训练集和验证集
    # --max_seq_length: 输入句子的最大长度, 超过则截断, 不足则补齐
    # --learning_rate: 学习率
    # --num_train_epochs: 训练轮数
    # --output_dir $OUT_DIR: 训练后的模型保存路径
    • 输出效果
    ...
    03/18/2020 00:55:17 - INFO - __main__ -   Loading features from cached file ./glue_data/MRPC/cached_train_bert-base-uncased_128_mrpc
    03/18/2020 00:55:17 - INFO - __main__ -   ***** Running training *****
    03/18/2020 00:55:17 - INFO - __main__ -     Num examples = 3668
    03/18/2020 00:55:17 - INFO - __main__ -     Num Epochs = 1
    03/18/2020 00:55:17 - INFO - __main__ -     Instantaneous batch size per GPU = 8
    03/18/2020 00:55:17 - INFO - __main__ -     Total train batch size (w. parallel, distributed & accumulation) = 8
    03/18/2020 00:55:17 - INFO - __main__ -     Gradient Accumulation steps = 1
    03/18/2020 00:55:17 - INFO - __main__ -     Total optimization steps = 459
    Epoch:   0%|                 | 0/1 [00:00<?, ?it/s]
    Iteration:   2%|   | 8/459 [00:13<12:42,  1.69s/it]
    • 运行成功后会在当前目录下生成 ./bert_finetuning_test文件夹,内部文件如下:
    added_tokens.json  checkpoint-200  checkpoint-350  eval_results.txt         tokenizer_config.json
    checkpoint-100     checkpoint-250  checkpoint-50   pytorch_model.bin        training_args.bin
    checkpoint-150     checkpoint-300  config.json     special_tokens_map.json  vocab.txt

    第三步: 设定全局配置并加载微调模型

    • 设定全局配置:
    # 这些配置将在调用微调模型时进行使用
    
    # 实例化一个配置的命名空间
    configs = Namespace()
    
    # 模型的输出文件路径
    configs.output_dir = "./bert_finetuning_test/"
    
    # 验证数据集所在路径(与训练集相同)
    configs.data_dir = "./glue_data/MRPC"
    
    # 预训练模型的名字
    configs.model_name_or_path = "bert-base-uncased"
    
    # 文本的最大对齐长度
    configs.max_seq_length = 128
    
    # GLUE中的任务名(需要小写)
    configs.task_name = "MRPC".lower()
    
    # 根据任务名从GLUE数据集处理工具包中取出对应的预处理工具
    configs.processor = processors[configs.task_name]()
    
    # 得到对应模型输出模式(MRPC为分类)
    configs.output_mode = output_modes[configs.task_name]
    
    # 得到该任务的对应的标签种类列表
    configs.label_list = configs.processor.get_labels()
    
    # 定义模型类型
    configs.model_type = "bert".lower()
    
    # 是否全部使用小写文本
    configs.do_lower_case = True
    
    # 使用的设备
    configs.device = "cpu"
    # 每次验证的批次大小
    configs.per_eval_batch_size = 8
    
    # gpu的数量
    configs.n_gpu = 0
    
    # 是否需要重写数据缓存
    configs.overwrite_cache = False
    • 加载微调模型:
    # 因为在模型使用中,会使用一些随机方法,为了使每次运行的结果可以复现
    # 需要设定确定的随机种子,保证每次随机化的数字在范围内浮动
    def set_seed(seed):
        random.seed(seed)
        np.random.seed(seed)
        torch.manual_seed(seed)
    set_seed(42)
    
    
    ## 加载微调模型
    
    # 加载BERT预训练模型的数值映射器
    tokenizer = BertTokenizer.from_pretrained(
        configs.output_dir, do_lower_case=configs.do_lower_case)
    
    # 加载带有文本分类头的BERT模型
    model = BertForSequenceClassification.from_pretrained(configs.output_dir)
    
    # 将模型传到制定设备上
    model.to(configs.device)
    • 第四步: 编写用于模型使用的评估函数
    def evaluate(args, model, tokenizer):
        """
        模型评估函数
        :param args: 模型的全局配置对象,里面包含模型的各种配置信息
        :param model: 使用的模型
        :param tokenizer: 文本数据的数值映射器
        """
    
        # 因为之后会多次用到任务名和输出路径
        # 所以将其从参数中取出
        eval_task = args.task_name
        eval_output_dir = args.output_dir
        try:
            # 调用load_and_cache_examples加载原始或者已经缓存的数据 
            # 得到一个验证数据集的迭代器对象
            eval_dataset = load_and_cache_examples(args, eval_task, tokenizer)
    
            # 判断模型输出路径是否存在
            if not os.path.exists(eval_output_dir):
                # 不存在的话,创建该路径
                os.makedirs(eval_output_dir)
    
            # 使用SequentialSampler封装验证数据集的迭代器对象
            # SequentialSampler是采样器对象,一般在Dataloader数据加载器中使用,
            # 因为数据加载器是以迭代的方式产生数据,因此每个批次数据可以指定采样规则,
            # SequentialSampler是顺序采样器,不改变原有数据集的顺序,依次取出数据。
            eval_sampler = SequentialSampler(eval_dataset)
            # 使用Dataloader数据加载器,参数分别是数据集的迭代器对象,采集器对象,批次大小
            eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.eval_batch_size)
    
            # 开始评估
            logger.info("***** Running evaluation *****")
            logger.info("  Num examples = %d", len(eval_dataset))
            logger.info("  Batch size = %d", args.eval_batch_size)
            # 初始化验证损失
            eval_loss = 0.0
            # 初始化验证步数
            nb_eval_steps = 0
            # 初始化预测的概率分布
            preds = None
            # 初始化输出真实标签值
            out_label_ids = None
    
            # 循环数据批次,使用tqdm封装数据加载器,可以在评估时显示进度条
            # desc是进度条前面的描述信息
            for batch in tqdm(eval_dataloader, desc="Evaluating"):
                # 评估过程中模型开启评估模式,不进行反向传播
                model.eval()
                # 从batch中取出数据的所有相关信息存于元组中
                batch = tuple(t.to(args.device) for t in batch)
                # 不进行梯度计算
                with torch.no_grad():
                    # 将batch携带的数据信息表示称字典形式
                    # 这些数据信息和load_and_cache_examples函数返回的数据对象中信息相同
                    # 词汇的映射数值, 词汇的类型数值(0或1, 代表第一句和第二句话)
                    # 注意力掩码张量,以及对应的标签
                    inputs = {'input_ids':      batch[0],
                              'attention_mask': batch[1],
                              'token_type_ids': batch[2],
                              'labels':         batch[3]}
                    # 将该字典作为参数输入到模型中获得输出
                    outputs = model(**inputs)
                    # 获得损失和预测分布
                    tmp_eval_loss, logits = outputs
                    # 将损失累加求均值
                    eval_loss += tmp_eval_loss.mean().item()
                # 验证步数累加
                nb_eval_steps += 1
    
                # 如果是第一批次的数据
                if preds is None:
                    # 结果分布就是模型的输出分布
                    preds = logits.numpy()
                    # 输出真实标签值为输入对应的labels
                    out_label_ids = inputs['labels'].numpy()
                else:
                    # 结果分布就是每一次模型输出分布组成的数组
                    preds = np.append(preds, logits.numpy(), axis=0)
                    # 输出真实标签值为每一次输入对应的labels组成的数组
                    out_label_ids = np.append(out_label_ids, inputs['labels'].numpy(), axis=0)
    
            # 计算每一轮的平均损失
            eval_loss = eval_loss / nb_eval_steps
            # 取结果分布中最大的值对应的索引
            preds = np.argmax(preds, axis=1)
            # 使用compute_metrics计算对应的评估指标
            result = compute_metrics(eval_task, preds, out_label_ids)
            # 在日志中打印每一轮的评估结果
            logger.info("***** Eval results {} *****")
            logger.info(str(result))
        except Exception as e:
             print(e)
        return result
    
    
    def load_and_cache_examples(args, task, tokenizer):
        """
        加载或使用缓存数据
        :param args: 全局配置参数
        :param task: 任务名
        :param tokenizer: 数值映射器
        """
        # 根据任务名(MRPC)获得对应数据预处理器
        processor = processors[task]()
        # 获得输出模式
        output_mode = output_modes[task]
        # 定义缓存数据文件的名字
        cached_features_file = os.path.join(args.data_dir, 'cached_{}_{}_{}_{}'.format(
            'dev',
            list(filter(None, args.model_name_or_path.split('/'))).pop(),
            str(args.max_seq_length),
            str(task)))
        # 判断缓存文件是否存在,以及全局配置中是否需要重写数据
        if os.path.exists(cached_features_file) and not args.overwrite_cache:
            # 使用torch.load(解序列化,一般用于加载模型,在这里用于加载训练数据)加载缓存文件
            features = torch.load(cached_features_file)
        else:
            # 如果没有缓存文件,则需要使用processor从原始数据路径中加载数据
            examples = processor.get_dev_examples(args.data_dir) 
            # 获取对应的标签
            label_list = processor.get_labels()
            # 再使用convert_examples_to_features生成模型需要的输入形式
            features = convert_examples_to_features(examples,
                                                    tokenizer,
                                                    label_list=label_list,
                                                    max_length=args.max_seq_length,
                                                    output_mode=output_mode,
                                                    pad_token=tokenizer.convert_tokens_to_ids([tokenizer.pad_token])[0],
            )
            logger.info("Saving features into cached file %s", cached_features_file)
            # 将其保存至缓存文件路径中
            torch.save(features, cached_features_file)
    
    
        # 为了有效利用内存,之后将使用数据加载器,我们需要在这里将张量数据转换成数据迭代器对象TensorDatase
        # TensorDataset:用于自定义训练数据结构的迭代封装器,它可以封装任何与训练数据映射值相关的数据
        #(如:训练数据对应的标签,训练数据使用的掩码张量,token的类型id等),
        # 它们必须能转换成张量,将同训练数据映射值一起在训练过程中迭代使用。
    
        # 以下是分别把input_ids,attention_mask,token_type_ids,label封装在TensorDataset之中
        all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long)
        all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long)
        all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long)
        all_labels = torch.tensor([f.label for f in features], dtype=torch.long)
        dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels)
        # 返回数据迭代器对象
        return dataset
    • 我们将在下面的模型量化中调用该评估函数。

    使用动态量化技术对训练后的bert模型进行压缩

    • 第一步: 将模型应用动态量化技术
    • 第二步: 对比压缩后模型的大小
    • 第三步: 对比压缩后的模型的推理准确性和耗时
    • 第四步: 序列化模型以便之后使用

    第一步: 将模型应用动态量化技术

    • 应用动态量化技术:
    # 使用torch.quantization.quantize_dynamic获得动态量化的模型
    # 量化的网络层为所有的nn.Linear的权重,使其成为int8
    quantized_model = torch.quantization.quantize_dynamic(
        model, {torch.nn.Linear}, dtype=torch.qint8
    )
    
    # 打印动态量化后的BERT模型
    print(quantized_model)
    • 输出效果
    ## 模型中的所有Linear层变成了DynamicQuantizedLinear层
    
    BertForSequenceClassification(
      (bert): BertModel(
        (embeddings): BertEmbeddings(
          (word_embeddings): Embedding(30522, 768, padding_idx=0)
          (position_embeddings): Embedding(512, 768)
          (token_type_embeddings): Embedding(2, 768)
          (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
          (dropout): Dropout(p=0.1, inplace=False)
        )
        (encoder): BertEncoder(
          (layer): ModuleList(
            (0): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, scale=1.0, zero_point=0)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, scale=1.0, zero_point=0)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (1): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, scale=1.0, zero_point=0)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, scale=1.0, zero_point=0)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
    ...
    ...
    
            (11): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, scale=1.0, zero_point=0)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, scale=1.0, zero_point=0)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
          )
        )
        (pooler): BertPooler(
          (dense): DynamicQuantizedLinear(in_features=768, out_features=768, scale=1.0, zero_point=0)
          (activation): Tanh()
        )
      )
      (dropout): Dropout(p=0.1, inplace=False)
      (classifier): DynamicQuantizedLinear(in_features=768, out_features=2, scale=1.0, zero_point=0)
    )

    第二步: 对比压缩后模型的大小

    def print_size_of_model(model):
        """打印模型大小"""
        # 保存模型中的参数部分到持久化文件
        torch.save(model.state_dict(), "temp.p")
        # 打印持久化文件的大小
        print('Size (MB):', os.path.getsize("temp.p")/1e6)
        # 移除该文件
        os.remove('temp.p')
    
    # 分别打印model和quantized_model
    print_size_of_model(model)
    print_size_of_model(quantized_model)
    • 输出效果
    ## 模型参数文件大小缩减了257MB 
    
    Size (MB): 437.982584
    Size (MB): 181.430351

    第三步: 对比压缩后的模型的推理准确性和耗时

    def time_model_evaluation(model, configs, tokenizer):
        """获得模型评估结果和运行时间"""
        # 获得评估前时间
        eval_start_time = time.time()
        # 进行模型评估
        result = evaluate(configs, model, tokenizer)
        # 获得评估后时间
        eval_end_time = time.time()
        # 获得评估耗时
        eval_duration_time = eval_end_time - eval_start_time
        # 打印模型评估结果
        print("Evaluate result:", result)
        # 打印耗时
        print("Evaluate total time (seconds): {0:.1f}".format(eval_duration_time))
    • 输出效果
    Evaluating: 100%|██| 51/51 [01:36<00:00,  1.89s/it]
    {'acc': 0.7671568627450981, 'f1': 0.8475120385232745, 'acc_and_f1': 0.8073344506341863}
    Evaluate total time (seconds): 96.4
    Evaluating: 100%|██| 51/51 [00:59<00:00,  1.16s/it]
    {'acc': 0.7671568627450981, 'f1': 0.8475120385232745, 'acc_and_f1': 0.8073344506341863}
    Evaluate total time (seconds): 59.4
    • 结论:
      • 对模型进行动态量化后,参数文件大小明显减少。
      • 动态量化后的模型在验证集上评估指标几乎不变,但是耗时却只用了原来的一半左右。

    第四步: 序列化模型以便之后使用

    # 量化模型的保存路径
    quantized_output_dir = configs.output_dir + "quantized/"
    # 判断是否需要创建该路径
    if not os.path.exists(quantized_output_dir):
        os.makedirs(quantized_output_dir)
        # 使用save_pretrained保存模型
        quantized_model.save_pretrained(quantized_output_dir)
    • 输出效果
    # 在bert_finetuning_test/目录下
    
    - quantized/
        - config.json
        - pytorch_model.bin

    应用于bert模型的动态量化技术.py 

    # ------------------------------------------第一步: 安装核心的工具包并导入----------------------------------------#
    """
    第一步: 安装核心的工具包并导入
            1.安装核心工具包:
                # 这是由huggingface提供的预训练模型使用工具包
                pip install transformers
            2.github下载安装transformers
                # 必须在linux下克隆huggingface的transfomers文件,不建议在window下clone,
                # 因为从window下clone后又把transfomers文件传到linux中执行transformers-cli脚本时会报错如下:
                # /usr/bin/env: "python\r": 没有那个文件或目录
                git clone https://github.com/huggingface/transformers.git
                # 进入transformers文件夹
                cd transformers
                # 安装python的transformer工具包, 因为微调脚本是py文件.
                # 如需进入某个虚拟环境安装的话,请先进入该虚拟环境再安装
                # Window:activate 虚拟环境名。Linux:source activate 虚拟环境名。
                pip install .
    
    第二步: 下载数据集并使用脚本进行微调
            下载GLUE中的MRPC数据集:python download_glue_data.py --data_dir='./glue_data' --tasks='MRPC'
    """
    # 工具包导入
    from __future__ import absolute_import, division, print_function
    import logging
    import numpy as np
    import os
    import random
    import time
    import torch
    
    # 用于设定全局配置的命名空间
    from argparse import Namespace
    # 从torch.utils.data中导入常用的模型处理工具,会在代码使用中进行详细介绍
    from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler, TensorDataset)
    # 模型进度可视化工具,在评估过程中,帮助打印进度条
    from tqdm import tqdm
    # 从transformers中导入BERT模型的相关工具
    from transformers import (BertConfig, BertForSequenceClassification, BertTokenizer,)
    # 从transformers中导入GLUE数据集的评估指标计算方法
    from transformers import glue_compute_metrics as compute_metrics
    # 从transformers中导入GLUE数据集的输出模式(回归/分类)
    from transformers import glue_output_modes as output_modes
    # 从transformers中导入GLUE数据集的预处理器processors
    # processors是将持久化文件加载到内存的过程,即输入一般为文件路径,输出是训练数据和对应标签的某种数据结构,如列表表示。
    from transformers import glue_processors as processors
    # 从transformers中导入GLUE数据集的特征处理器convert_examples_to_features
    # convert_examples_to_features是将processor的输出处理成模型需要的输入,NLP定中的一般流程为数值映射,指定长度的截断补齐等
    # 在BERT模型上处理句子对时,还需要在句子前插入[CLS]开始标记,在两个句子中间和第二个句子末端插入[SEP]分割/结束标记
    from transformers import glue_convert_examples_to_features as convert_examples_to_features
    
    # 设定与日志打印有关的配置
    logger = logging.getLogger(__name__)
    logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s -   %(message)s',
                        datefmt = '%m/%d/%Y %H:%M:%S',
                        level = logging.WARN)
    
    logging.getLogger("transformers.modeling_utils").setLevel(logging.WARN)  # Reduce logging
    print("torch version:", torch.__version__)
    
    # 设置torch允许启动的线程数, 因为之后会对比压缩模型的耗时,因此防止该变量产生影响
    torch.set_num_threads(1)
    print(torch.__config__.parallel_info())
    
    # torch version: 1.5.0
    # ATen/Parallel:
    # 	at::get_num_threads() : 1
    # 	at::get_num_interop_threads() : 4
    # OpenMP 200203
    # 	omp_get_max_threads() : 1
    # Intel(R) Math Kernel Library Version 2020.0.0 Product Build 20191125 for Intel(R) 64 architecture applications
    # 	mkl_get_max_threads() : 1
    # Intel(R) MKL-DNN v0.21.1 (Git Hash 7d2fd500bc78936d1d648ca713b901012f470dbc)
    # std::thread::hardware_concurrency() : 8
    # Environment variables:
    # 	OMP_NUM_THREADS : [not set]
    # 	MKL_NUM_THREADS : [not set]
    # ATen parallel backend: OpenMP
    
    """
    模型压缩:
            模型压缩是一种针对大型模型(参数量巨大)在使用过程中进行优化的一种常用措施。它往往能够使模型体积缩小,
            简化计算,增快推断速度,满足模型在特定场合(如: 移动端)的需求。目前,模型压缩可以从多方面考虑,
            如剪枝方法(简化模型架构),参数量化方法(简化模型参数),知识蒸馏等。本案例将着重讲解模型参数量化方法。
    模型参数量化:
            在机器学习(深度学习)领域,模型量化一般是指将模型参数由类型FP32转换为INT8/FP16的过程,如果转换为INT8,
            转换之后的模型大小被压缩为原来的¼,所需内存和带宽减小4倍,同时,计算量减小约为2-4倍。模型又可分为动态量化和静态量化。
    模型动态量化:
            操作最简单也是压缩效果最好的量化方式,量化过程发生在模型训练后,针对模型权重采取量化,之后会在模型预测过程中,
            再决定是否针对激活值采取量化,因此称作动态量化(在预测时可能发生量化)。这是我们本案例将会使用的量化方式。
    模型静态量化:
            考虑到动态量化这种“一刀切”的量化方式有时会带来模型预测效果的大幅度下降,因此引入静态量化,它同样发生在模型训练后,
            为了判断哪些权重或激活值应该被量化,哪些应该保留或小幅度量化,在预测过程开始前,
            在模型中节点插入“观测者”(衡量节点使用情况的一些计算方法),他们将在一些实验数据中评估节点使用情况,
            来决定是否将其权重或激活值进行量化,因为在预测过程中,这些节点是否被量化已经确定,因此称作静态量化。
    (扩展知识)量化意识训练:
            这是一种操作相对复杂的模型量化方法,在训练过程中使用它,原理与静态量化类似,都需要像模型中插入“观测者”,
            同时它还需要插入量化计算操作,使得模型训练过程中除了进行原有的浮点型计算,还要进行量化计算,
            但模型参数的更新还是使用浮点型,而量化计算的作用就是让模型“意识”到这个过程,
            通过“观测者”评估每次量化结果与训练过程中参数更新程度,为之后模型如何进行量化还能保证准确率提供衡量指标。
            (类似于,人在接受训练时,意识到自己接下来可能除了训练内容外,还会接受其他“操作”(量化),
            因此也会准备一些如果进行量化仍能达成目标的措施)
    """
    
    """
    BERT模型:
            这里使用bert-base-uncased,它的编码器具有12个隐层, 输出768维张量, 12个自注意力头, 
            共110M参数量, 在小写的英文文本上进行训练而得到。
    
    MRPC数据集的任务类型:
            句子对二分类任务
            训练集上正样本占68%,负样本占32%
            评估指标这里使用:F1
            评估指标计算方式:F1=2∗(precision∗recall)/(precision+recall)
            文件样式说明:
                在使用中常用到的文件是train.tsv,dev.tsv,test.tsv,分别代表训练集,验证集和测试集。
                其中train.tsv与dev.tsv数据样式相同,都是带有标签的数据,其中test.tsv是不带有标签的数据。
            train.tsv数据样式说明:
                train.tsv中的数据内容共分为5列,第一列数据,0或1,代表每对句子是否具有相同的含义,
                0代表含义不相同,1代表含义相同。第二列和第三列分别代表每对句子的id,
                第四列和第五列分别具有相同/不同含义的句子对。
            test.tsv数据样式说明:
                test.tsv中的数据内容共分为5列,第一列数据代表每条文本数据的索引;
                其余列的含义与train.tsv中相同。
    """
    
    """
    使用huggingface中的预训练BERT模型进行微调
            第一步: 安装必要的工具包并导入
            第二步: 下载数据集并使用脚本进行微调
            第三步: 设定全局配置并加载微调模型
            第四步: 编写用于模型使用的评估函数
    """
    #------------------------------------------第二步: 下载数据集并使用脚本进行微调----------------------------------------#
    """
    第二步: 下载数据集并使用脚本进行微调
            下载GLUE中的MRPC数据集:python download_glue_data.py --data_dir='./glue_data' --tasks='MRPC'
    """
    
    """
    注意:
        1.解决每次执行torch.hub.load但是每次同时又重新下载预训练模型文件的问题:
            当第一次执行torch.hub.load加载预训练模型会自动下载对应的预训练模型文件,当第一次下载完成并成功运行之后,
            后面每次再执行相同的代码时如果出现重新下载之前已经下载过的并且目录已存在的预训练模型文件的话,
            那么解决方式是只需要到C:/Users/Administrator/.cache/torch/hub/huggingface_pytorch-transformers_master
            或 /root/.cache/torch/hub/huggingface_pytorch-transformers_master
            目录中执行一次“pip install .”命令重新再次安装一次transformers之后,即使安装过程中提示transformers已经安装存在了也没关系,
            当重新执行完“pip install .”之后那么再运行torch.hub.load即不会再重新下载之前已经下载过的并且目录已存在的预训练模型文件,
            即能解决每次执行torch.hub.load每次又重新下载预训练模型文件的问题。
            预训练模型文件自动下载在:C:/Users/Administrator/.cache/torch/transformers 或 /root/.cache/torch/transformers。
            
        2.解决每次python命令执行run_glue.py但是每次同时又重新下载预训练模型文件的问题:
            解决方式同上述第一点解决每次执行torch.hub.load又重新下载预训练模型文件的问题。
            当第一次python命令执行run_glue.py模型微调文件加载预训练模型会自动下载对应的预训练模型文件,当第一次下载完成并成功运行之后,
            后面每次再执行相同的python命令执行run_glue.py时如果出现重新下载之前已经下载过的并且目录已存在的预训练模型文件的话,
            那么解决方式是只需要到C:/Users/Administrator/.cache/torch/hub/huggingface_pytorch-transformers_master
            或 /root/.cache/torch/hub/huggingface_pytorch-transformers_master
            目录中执行一次“pip install .”命令重新再次安装一次transformers之后,即使安装过程中提示transformers已经安装存在了也没关系,
            当重新执行完“pip install .”之后那么再运行torch.hub.load即不会再重新下载之前已经下载过的并且目录已存在的预训练模型文件,
            即能解决每次python命令执行run_glue.py但是每次同时又重新下载预训练模型文件的问题。
            预训练模型文件自动下载在:C:/Users/Administrator/.cache/torch/transformers 或 /root/.cache/torch/transformers。
            
    创建run_glue.sh脚本中使用python ./run_glue.py进行模型微调:
            # 注意: 这是一段使用shell运行的脚本, 运行过程中需要请求AWS的S3进行预训练模型下载
            # 定义GLUE_DIR: 微调数据所在路径, 这里我们使用glue_data中的数据作为微调数据
            export GLUE_DIR=./glue_data
            # 定义OUT_DIR: 模型的保存路径, 我们将模型保存在当前目录的bert_finetuning_test文件中
            export OUT_DIR=./bert_finetuning_test/
            
            python ./run_glue.py \
                --model_type bert \
                --model_name_or_path bert-base-uncased \
                --task_name MRPC \
                --do_train \
                --do_eval \
                --do_lower_case \
                --data_dir $GLUE_DIR/MRPC \
                --max_seq_length 128 \
                --per_gpu_eval_batch_size=8   \
                --per_gpu_train_batch_size=8   \
                --learning_rate 2e-5 \
                --num_train_epochs 1.0 \
                --output_dir $OUT_DIR
            
            # 使用python运行微调脚本
            # --model_type: 选择需要微调的模型类型, 这里可以选择BERT, XLNET, XLM, roBERTa, distilBERT, ALBERT
            # --model_name_or_path: 选择具体的模型或者变体, 这里是在英文语料上微调, 因此选择bert-base-uncased
            # --task_name: 它将代表对应的任务类型, 如MRPC代表句子对二分类任务
            # --do_train: 使用微调脚本进行训练
            # --do_eval: 使用微调脚本进行验证
            # --data_dir: 训练集及其验证集所在路径, 将自动寻找该路径下的train.tsv和dev.tsv作为训练集和验证集
            # --max_seq_length: 输入句子的最大长度, 超过则截断, 不足则补齐
            # --learning_rate: 学习率
            # --num_train_epochs: 训练轮数
            # --output_dir $OUT_DIR: 训练后的模型保存路径
            # --overwrite_output_dir: 再次训练时将清空之前的保存路径内容重新写入
    
    模型微调脚本执行的输出结果:
            acc = 0.8137254901960784
            f1 = 0.8762214983713356
            acc_and_f1 = 0.844973494283707
            loss = 0.45698420732629064
    """
    
    #------------------------------------------第三步: 设定全局配置并加载微调模型----------------------------------------#
    #设定全局配置:
    # 这些配置将在调用微调模型时进行使用
    
    # 实例化一个配置的命名空间
    configs = Namespace()
    # 模型的输出文件路径
    configs.output_dir = "./bert_finetuning_test/"
    # 验证数据集所在路径(与训练集相同)
    configs.data_dir = "./glue_data/MRPC"
    # 预训练模型的名字
    configs.model_name_or_path = "bert-base-uncased"
    # 文本的最大对齐长度
    configs.max_seq_length = 128
    # GLUE中的任务名(需要小写)
    configs.task_name = "MRPC".lower()
    # 根据任务名从GLUE数据集处理工具包中取出对应的预处理工具
    configs.processor = processors[configs.task_name]()
    # 得到对应模型输出模式(MRPC为分类)
    configs.output_mode = output_modes[configs.task_name]
    # 得到该任务的对应的标签种类列表
    configs.label_list = configs.processor.get_labels()
    # 定义模型类型
    configs.model_type = "bert".lower()
    # 是否全部使用小写文本
    configs.do_lower_case = True
    
    """
    configs.device:
        设备字符串开头的cpu, cuda, mkldnn, opengl, opencl, ideep, hip, msnpu 的设备类型
    1.CPU
        # 使用的设备
        configs.device = "cpu"
        # gpu的数量
        configs.n_gpu = 0
    2.GPU
        # 使用的设备
        configs.device = "cuda"
        # # gpu的数量
        configs.n_gpu = 1
    """
    # 使用的设备
    configs.device = "cpu"
    # gpu的数量
    configs.n_gpu = 0
    # 使用的设备
    # configs.device = "cuda"
    # # gpu的数量
    # configs.n_gpu = 1
    # 每次验证的批次大小
    configs.per_eval_batch_size = 8
    # 是否需要重写数据缓存
    configs.overwrite_cache = False
    
    #加载微调模型:
    # 因为在模型使用中,会使用一些随机方法,为了使每次运行的结果可以复现
    # 需要设定确定的随机种子,保证每次随机化的数字在范围内浮动
    def set_seed(seed):
        random.seed(seed)
        np.random.seed(seed)
        torch.manual_seed(seed)
    set_seed(42)
    
    ## 加载微调模型
    # 加载BERT预训练模型的数值映射器
    tokenizer = BertTokenizer.from_pretrained(configs.output_dir, do_lower_case=configs.do_lower_case)
    # 加载带有文本分类头的BERT模型
    model = BertForSequenceClassification.from_pretrained(configs.output_dir)
    # 将模型传到制定设备上
    model.to(configs.device)
    
    
    #------------------------------------------第四步: 编写用于模型使用的评估函数----------------------------------------#
    
    def load_and_cache_examples(args, task, tokenizer):
        """
        加载或使用缓存数据
        :param args: 全局配置参数
        :param task: 任务名
        :param tokenizer: 数值映射器
        """
        # 根据任务名(MRPC)获得对应数据预处理器
        processor = processors[task]()
        # 获得输出模式
        output_mode = output_modes[task]
        """
        跑第一次成功之后才会自动生成 ./glue_data/MRPC/cached_dev_bert-base-uncased_128_mrpc
        """
        # 定义缓存数据文件的名字
        cached_features_file = os.path.join(args.data_dir, 'cached_{}_{}_{}_{}'.format(
            'dev',
            list(filter(None, args.model_name_or_path.split('/'))).pop(),
            str(args.max_seq_length),
            str(task)))
        #./glue_data/MRPC/cached_dev_bert-base-uncased_128_mrpc
        # print("cached_features_file",cached_features_file)
        # cached_features_file = "./glue_data/MRPC/cached_train_BertTokenizer_128_mrpc"
    
        # 判断缓存文件是否存在,以及全局配置中是否需要重写数据
        if os.path.exists(cached_features_file) and not args.overwrite_cache:
            # ./glue_data/MRPC/cached_dev_bert-base-uncased_128_mrpc
            print("cached_features_file", cached_features_file)
            # 使用torch.load(解序列化,一般用于加载模型,在这里用于加载训练数据)加载缓存文件
            features = torch.load(cached_features_file)
        else:
            # 如果没有缓存文件,则需要使用processor从原始数据路径中加载数据
            examples = processor.get_dev_examples(args.data_dir)
            # 获取对应的标签
            label_list = processor.get_labels()
            # 再使用convert_examples_to_features生成模型需要的输入形式
            features = convert_examples_to_features(examples,
                                                    tokenizer,
                                                    label_list=label_list,
                                                    max_length=args.max_seq_length,
                                                    output_mode=output_mode,
                                                    # pad_token=tokenizer.convert_tokens_to_ids([tokenizer.pad_token])[0]
            )
            logger.info("Saving features into cached file %s", cached_features_file)
            # 将其保存至缓存文件路径中
            torch.save(features, cached_features_file)
    
        # 为了有效利用内存,之后将使用数据加载器,我们需要在这里将张量数据转换成数据迭代器对象TensorDatase
        # TensorDataset:用于自定义训练数据结构的迭代封装器,它可以封装任何与训练数据映射值相关的数据
        #(如:训练数据对应的标签,训练数据使用的掩码张量,token的类型id等),
        # 它们必须能转换成张量,将同训练数据映射值一起在训练过程中迭代使用。
    
        # 以下是分别把input_ids,attention_mask,token_type_ids,label封装在TensorDataset之中
        all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long)
        all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long)
        all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long)
        all_labels = torch.tensor([f.label for f in features], dtype=torch.long)
        dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels)
        # 返回数据迭代器对象
        return dataset
    
    def evaluate(args, model, tokenizer):
        """
        模型评估函数
        :param args: 模型的全局配置对象,里面包含模型的各种配置信息
        :param model: 使用的模型
        :param tokenizer: 文本数据的数值映射器
        """
        result = None
        # 因为之后会多次用到任务名和输出路径
        # 所以将其从参数中取出
        eval_task = args.task_name
        eval_output_dir = args.output_dir
        try:
            # 调用load_and_cache_examples加载原始或者已经缓存的数据
            # 得到一个验证数据集的迭代器对象
            eval_dataset = load_and_cache_examples(args, eval_task, tokenizer)
    
            # 判断模型输出路径是否存在
            if not os.path.exists(eval_output_dir):
                # 不存在的话,创建该路径
                os.makedirs(eval_output_dir)
    
            # 使用SequentialSampler封装验证数据集的迭代器对象
            # SequentialSampler是采样器对象,一般在Dataloader数据加载器中使用,
            # 因为数据加载器是以迭代的方式产生数据,因此每个批次数据可以指定采样规则,
            # SequentialSampler是顺序采样器,不改变原有数据集的顺序,依次取出数据。
            eval_sampler = SequentialSampler(eval_dataset)
            # 使用Dataloader数据加载器,参数分别是数据集的迭代器对象,采集器对象,批次大小
            eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.per_eval_batch_size)
    
            # 开始评估
            logger.info("***** Running evaluation *****")
            logger.info("  Num examples = %d", len(eval_dataset))
            logger.info("  Batch size = %d", args.per_eval_batch_size)
            # 初始化验证损失
            eval_loss = 0.0
            # 初始化验证步数
            nb_eval_steps = 0
            # 初始化预测的概率分布
            preds = None
            # 初始化输出真实标签值
            out_label_ids = None
    
            # 循环数据批次,使用tqdm封装数据加载器,可以在评估时显示进度条
            # desc是进度条前面的描述信息
            for batch in tqdm(eval_dataloader, desc="Evaluating"):
                # 评估过程中模型开启评估模式,不进行反向传播
                # model.eval()
                # 从batch中取出数据的所有相关信息存于元组中
                batch = tuple(t.to(args.device) for t in batch)
                # 不进行梯度计算
                with torch.no_grad():
                    # 将batch携带的数据信息表示称字典形式
                    # 这些数据信息和load_and_cache_examples函数返回的数据对象中信息相同
                    # 词汇的映射数值, 词汇的类型数值(0或1, 代表第一句和第二句话)
                    # 注意力掩码张量,以及对应的标签
                    inputs = {'input_ids':      batch[0],
                              'attention_mask': batch[1],
                              'token_type_ids': batch[2],
                              'labels':         batch[3]}
                    # 将该字典作为参数输入到模型中获得输出
                    outputs = model(**inputs)
                    # 获得损失和预测分布
                    tmp_eval_loss, logits = outputs
                    # 将损失累加求均值
                    eval_loss += tmp_eval_loss.mean().item()
                # 验证步数累加
                nb_eval_steps += 1
    
                # 如果是第一批次的数据
                if preds is None:
                    # 结果分布就是模型的输出分布
                    preds = logits.numpy()
                    # 输出真实标签值为输入对应的labels
                    out_label_ids = inputs['labels'].numpy()
                else:
                    # 结果分布就是每一次模型输出分布组成的数组
                    preds = np.append(preds, logits.numpy(), axis=0)
                    # 输出真实标签值为每一次输入对应的labels组成的数组
                    out_label_ids = np.append(out_label_ids, inputs['labels'].numpy(), axis=0)
    
            # 计算每一轮的平均损失
            eval_loss = eval_loss / nb_eval_steps
            print("eval_loss",eval_loss)
            # 取结果分布中最大的值对应的索引
            preds = np.argmax(preds, axis=1)
            # 使用compute_metrics计算对应的评估指标
            result = compute_metrics(eval_task, preds, out_label_ids)
            # 在日志中打印每一轮的评估结果
            logger.info("***** Eval results {} *****")
            logger.info(str(result))
        except Exception as e:
             print(e)
        return result
    
    """
    将在下面的模型量化中调用该评估函数。
    使用动态量化技术对训练后的bert模型进行压缩
            第一步: 将模型应用动态量化技术
            第二步: 对比压缩后模型的大小
            第三步: 对比压缩后的模型的推理准确性和耗时
            第四步: 序列化模型以便之后使用
    """
    #------------------------------------------第一步: 将模型应用动态量化技术----------------------------------------#
    
    """
    报错:AttributeError: module 'torch' has no attribute 'quantization'
    解决:PyTorch/torch版本过低,更新到最新版
    """
    #应用动态量化技术:
    #   使用torch.quantization.quantize_dynamic获得动态量化的模型
    #   量化的网络层为所有的nn.Linear的权重,使其成为int8
    quantized_model = torch.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8)
    # 打印动态量化后的BERT模型
    print(quantized_model)
    
    #------------------------------------------第二步: 对比压缩后模型的大小----------------------------------------#
    def print_size_of_model(model, filename):
        """打印模型大小"""
        # 保存模型中的参数部分到持久化文件
        torch.save(model.state_dict(), filename)
        # 打印持久化文件的大小
        print('Size (MB):', os.path.getsize(filename)/1e6)
        # 移除该文件
        # os.remove('temp.p')
    
    # 分别打印model和quantized_model
    print_size_of_model(model, "./model.pt") #Size (MB): 437.982182
    print_size_of_model(quantized_model, "./quantized_model.pt") #Size (MB): 181.442959
    ## 模型参数文件大小缩减了257MB
    
    #------------------------------------------第三步: 对比压缩后的模型的推理准确性和耗时----------------------------------------#
    def time_model_evaluation(model, configs, tokenizer):
        """获得模型评估结果和运行时间"""
        # 获得评估前时间
        eval_start_time = time.time()
        # 进行模型评估
        result = evaluate(configs, model, tokenizer)
        # 获得评估后时间
        eval_end_time = time.time()
        # 获得评估耗时
        eval_duration_time = eval_end_time - eval_start_time
        # 打印模型评估结果
        print("Evaluate result:", result)
        # 打印耗时
        print("Evaluate total time (seconds): {0:.1f}".format(eval_duration_time))
    
    """
    结论:
        对模型进行动态量化后,参数文件大小明显减少。
        动态量化后的模型在验证集上评估指标几乎不变,但是耗时却只用了原来的一半左右。
    """
    
    #------------------------------------------第四步: 序列化模型以便之后使用----------------------------------------#
    # 量化模型的保存路径
    quantized_output_dir = configs.output_dir + "quantized/"
    # 判断是否需要创建该路径
    if not os.path.exists(quantized_output_dir):
        os.makedirs(quantized_output_dir)
    # 使用save_pretrained保存模型到本地文件,文件名默认为pytorch_model.bin
    quantized_model.save_pretrained(quantized_output_dir)
    
    """
    1.模型微调脚本执行的输出结果:
            acc = 0.8137254901960784
            f1 = 0.8762214983713356
            acc_and_f1 = 0.844973494283707
            loss = 0.45698420732629064
    
    2.压缩后的模型quantized_model:time_model_evaluation(quantized_model, configs, tokenizer) 
            Evaluate result: {'acc': 0.7990196078431373, 'f1': 0.8668831168831169, 'acc_and_f1': 0.8329513623631271}
            Evaluate total time (seconds): 92.8
            eval_loss 0.45583703646472856
    """
    
    #此处压缩后的模型可以选择加载本地的压缩后的模型状态文件
    state = torch.load("./quantized_model.pt")
    quantized_model.load_state_dict(state)
    
    #对比压缩后的模型的推理准确性和耗时
    #获得模型评估结果和运行时间
    #此处压缩后的模型可以选择加载本地的压缩后的模型状态文件
    time_model_evaluation(quantized_model, configs, tokenizer)
    
    
    
    # 实例化一个配置的命名空间
    configs_load_model = Namespace()
    # 模型的输出文件路径
    configs_load_model.output_dir = "./bert_finetuning_test/quantized/output"
    # 验证数据集所在路径(与训练集相同)
    configs_load_model.data_dir = "./glue_data/MRPC"
    # 预训练模型的名字
    configs_load_model.model_name_or_path = "bert-base-uncased"
    # 文本的最大对齐长度
    configs_load_model.max_seq_length = 128
    # GLUE中的任务名(需要小写)
    configs_load_model.task_name = "MRPC".lower()
    # 根据任务名从GLUE数据集处理工具包中取出对应的预处理工具
    configs_load_model.processor = processors[configs_load_model.task_name]()
    # 得到对应模型输出模式(MRPC为分类)
    configs_load_model.output_mode = output_modes[configs_load_model.task_name]
    # 得到该任务的对应的标签种类列表
    configs_load_model.label_list = configs_load_model.processor.get_labels()
    # 定义模型类型
    configs_load_model.model_type = "bert".lower()
    # 是否全部使用小写文本
    configs_load_model.do_lower_case = True
    # 使用的设备
    configs_load_model.device = "cpu"
    # gpu的数量
    configs_load_model.n_gpu = 0
    # 使用的设备
    # configs_load_model.device = "cuda"
    # # gpu的数量
    # configs_load_model.n_gpu = 1
    # 每次验证的批次大小
    configs_load_model.per_eval_batch_size = 8
    # 是否需要重写数据缓存
    configs_load_model.overwrite_cache = False
    
    """
    pytorch_model.bin模型状态(字典数据)文件、xx.pt、xx.pkl 的区别
    1.xx.pt、xx.pkl均是torch.save(model.state_dict(), "./xx.pt" 或 "./xx.pkl" ) 保存的 模型状态(字典数据)文件
    2.pytorch_model.bin实际也为模型状态(字典数据)文件,保存的权重数据(状态字典数据) 实际和 xx.pt/xx.pkl 这样的模型状态(字典数据)文件 的作用是一样的。
    3.通过save_pretrained("保存路径")所保存的默认文件pytorch_model.bin 的文件大小 实际和 
      使用 torch.save(model.state_dict(), "./xx.pt" 或 "./xx.pkl" )所保存的模型状态(字典数据)文件的文件大小 是一样的,
      也就是 pytorch_model.bin模型状态(字典数据)文件 和 xx.pt/xx.pkl 等不同类型的模型状态(字典数据)文件 都可以保存模型的状态字典数据,
      并且不管使用哪种类型的模型状态(字典数据)文件,他们的文件大小都是一致的。 
    
    1.BertTokenizer.from_pretrained:实际自动加载的是vocab.txt
      BertForSequenceClassification.from_pretrained:实际自动加载的是pytorch_model.bin模型状态文件
    
    2.保存没有压缩的/压缩后的模型的模型状态(字典数据)、加载没有压缩的/压缩后的模型状态(字典数据)
        1.保存没有压缩的原始模型的模型状态(字典数据)
            torch.save(model.state_dict(), "./xx.pt" 或 "./xx.pkl")
            
        2.加载没有压缩的原始模型的模型状态(字典数据)
            model = 模型类Model()
            model.load_state_dict(torch.load("./xx.pt" 或 "./xx.pkl"))
            
        3.对原始模型应用动态量化技术进行模型压缩,使用save_pretrained把压缩后的模型的模型状态(字典数据)存储到本地默认的pytorch_model.bin文件
            #应用动态量化技术:
            #   使用torch.quantization.quantize_dynamic获得动态量化的模型
            #   量化的网络层为所有的nn.Linear的权重,使其成为int8
            #quantize_dynamic输入的为没有压缩的原始模型,quantize_dynamic输出的为压缩后的模型
            quantized_model = torch.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8)
            # 使用save_pretrained保存压缩后的模型的模型状态到本地文件,文件名默认为pytorch_model.bin
            quantized_model.save_pretrained("./bert_finetuning_test/quantized")
            
        4.加载压缩后的模型的模型状态(字典数据)文件
                # 实际加载的是"./bert_finetuning_test/quantized"目录下的vocab.txt
                tokenizer_load_model = BertTokenizer.from_pretrained("./bert_finetuning_test/quantized", do_lower_case=configs_load_model.do_lower_case)
                # 加载带有文本分类头的BERT模型:实际加载的是"./bert_finetuning_test/quantized"目录下的pytorch_model.bin模型状态(字典数据)文件
                quantized_load_model = BertForSequenceClassification.from_pretrained("./bert_finetuning_test/quantized")
                # 因为加载的为压缩后的模型的模型状态(字典数据)文件,因此此处需要使用动态量化技术
                quantized_load_model = torch.quantization.quantize_dynamic(quantized_load_model, {torch.nn.Linear}, dtype=torch.qint8)
                # 可以选择加载pytorch_model.bin模型状态(字典数据)文件,或者选择加载pt/pkl文件也可以
                state = torch.load("./pytorch_model.bin" 或 "./xx.pt或./xx.pkl")
                # 把模型状态(字典数据) 加载到模型中
                quantized_load_model.load_state_dict(state)
    """
    
    
    ##############
    # 加载量化模型
    ##############
    # 加载BERT预训练模型的数值映射器
    # 实际加载的是"./bert_finetuning_test/quantized"目录下的vocab.txt
    tokenizer_load_model = BertTokenizer.from_pretrained("./bert_finetuning_test/quantized", do_lower_case=configs.do_lower_case)
    # 加载带有文本分类头的BERT模型
    # 加载带有文本分类头的BERT模型:实际加载的是"./bert_finetuning_test/quantized"目录下的pytorch_model.bin模型状态(字典数据)文件
    quantized_load_model = BertForSequenceClassification.from_pretrained("./bert_finetuning_test/quantized")
    # 应用动态量化技术:
    #   使用torch.quantization.quantize_dynamic获得动态量化的模型
    #   量化的网络层为所有的nn.Linear的权重,使其成为int8
    quantized_load_model = torch.quantization.quantize_dynamic(quantized_load_model, {torch.nn.Linear}, dtype=torch.qint8)
    #实际加载的pytorch_model.bin该文件仍然是模型的状态字典的文件,并不是包含模型结构的文件
    quantized_load_model.load_state_dict(torch.load("./bert_finetuning_test/quantized/pytorch_model.bin"))
    # quantized_load_model.load_state_dict(torch.load("./quantized_model.pt"))
    # print(quantized_load_model)
    # 将模型传到制定设备上
    quantized_load_model.to(configs_load_model.device)
    #对比压缩后的模型的推理准确性和耗时
    #获得模型评估结果和运行时间
    time_model_evaluation(quantized_load_model, configs_load_model, tokenizer_load_model)
    
    """
    打印动态量化后的BERT模型
    
    BertForSequenceClassification(
      (bert): BertModel(
        (embeddings): BertEmbeddings(
          (word_embeddings): Embedding(30522, 768, padding_idx=0)
          (position_embeddings): Embedding(512, 768)
          (token_type_embeddings): Embedding(2, 768)
          (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
          (dropout): Dropout(p=0.1, inplace=False)
        )
        (encoder): BertEncoder(
          (layer): ModuleList(
            (0): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (1): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (2): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (3): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (4): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (5): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (6): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (7): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (8): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (9): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (10): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
            (11): BertLayer(
              (attention): BertAttention(
                (self): BertSelfAttention(
                  (query): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (key): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (value): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
                (output): BertSelfOutput(
                  (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
                  (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                  (dropout): Dropout(p=0.1, inplace=False)
                )
              )
              (intermediate): BertIntermediate(
                (dense): DynamicQuantizedLinear(in_features=768, out_features=3072, qscheme=torch.per_tensor_affine)
              )
              (output): BertOutput(
                (dense): DynamicQuantizedLinear(in_features=3072, out_features=768, qscheme=torch.per_tensor_affine)
                (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
                (dropout): Dropout(p=0.1, inplace=False)
              )
            )
          )
        )
        (pooler): BertPooler(
          (dense): DynamicQuantizedLinear(in_features=768, out_features=768, qscheme=torch.per_tensor_affine)
          (activation): Tanh()
        )
      )
      (dropout): Dropout(p=0.1, inplace=False)
      (classifier): DynamicQuantizedLinear(in_features=768, out_features=2, qscheme=torch.per_tensor_affine)
    )
    """
    

    展开全文
  • Bert原理与使用

    2019-07-23 16:42:27
    ELMO的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,但是在使用的时候可以根据单词的上下文意思对它的词向量进行动态调整 ELMO采用了典型的两阶段过程,第一个阶段是利用语言...

    一.简介
    1.BERT模型的核心思想有两点,对推动NLP的发展有着重要的作用:(1)Transformer结构;(2)无监督的预训练。Transformer是一个只基于注意力(Attention)机制的序列模型。BERT模型同时需要预训练,从两个无监督任务中获取权重:语言建模(给定左右上下文,预测丢失的单词)以及下一个句子预测(预测一个句子是否跟在另一个句子后面)。

    二.NLP预训练过程的发展
    1.最开始NLP里面做预训练用的是语言模型,像NNLM,就是输入前面单词的one-hot编码乘以矩阵向量,经过softmax分类器,要求被预测的单词的概率最大化,从而得到被预测的单词和wordembedding矩阵
    2.然后出现了Word2Vec,原理和NNLM基本类似
    3.词向量虽然可以作为预训练的输出输入到下游任务,但是问题是对多义词没办法区分,出现了ELMO,在此之前的Word Embedding本质上是个静态的方式,所谓静态指的是训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的Word Embedding不会跟着上下文场景的变化而改变。ELMO的本质思想是:我事先用语言模型学好一个单词的Word Embedding,此时多义词无法区分,但是在使用的时候可以根据单词的上下文意思对它的词向量进行动态调整
    ELMO采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练(双层双向LSTM);第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的Word Embedding作为新特征补充到下游任务中
    预训练训练完后得到单词的三个embedding(单词的,句法的,语义的)。将这个三个加权求

    展开全文
  • 在本文中,我们提出了一种新的动态BERT模型(简称DynaBERT),它可以在自适应的宽度和深度上运行。DynaBERT的训练过程包括首先训练一个宽度自适应的BERT,然后通过从全尺寸的模型中提取知识到小的子网络中,允许自适应...
  • 今天给大家介绍一篇BERT用于推荐系统的文章,题目是《BERT4Rec: Sequential Recommendation with Bidirectional Encoder Representations from Transformer》,文章作者都是出自阿里。 引用:Sun F, Liu J, Wu J,...
  • 论文标题:ConvBERT: Improving BERT with Span-based Dynamic Convolution 论文链接:https://arxiv.org/abs/2008.02496 一、概述 简介 本文提出了一种新的预训练语言模型ConvBERT,这个模型是在BERT的基础上进行...
  • BERTBERTBERT!

    千次阅读 2020-12-20 21:46:25
    BERT的基本原理、模型架构、迁移特性、细节说明。BERT详解!
  • 一文读懂BERT(原理篇)

    万次阅读 多人点赞 2019-04-19 08:37:17
    一文读懂BERT(从原理到实践) 2018年的10月11日,Google发布的论文《Pre-training of Deep Bidirectional Transformers for Language Understanding》,成功在 11 项 NLP 任务中取得 state of the art 的结果,赢得...
  • BERT之提取特征向量 及 bert-as-server的使用

    万次阅读 多人点赞 2019-07-05 18:54:33
    前一篇文章 BERT介绍及中文文本相似度任务实践 简单介绍了使用BERT进行中文文本相似度计算的方法,这篇文章着重对特征提取方法进行讲述。 提取句向量 1、句向量简介 1-1传统句向量 更多采用word embedding的方式取...
  • 目录0. 本文概览1. BERT4Rec简介2....今天给大家介绍一篇BERT用于推荐系统的文章,题目是《BERT4Rec: Sequential Recommendation with Bidirectional Encoder Representations from Transformer》,文章作者都
  • bert-as-service的使用

    千次阅读 2019-05-23 18:08:01
    谷歌训练的bert使用的是tensorflow,若想使用GPU加速,需要安装相应的驱动和软件,并使用tensorflow-gpu代替常规tensorflow python包,需要的软件及安装步骤见附件; text file 服务器端创建虚拟环境并在该环境中...
  • 来自:Microstrong今天给大家介绍一篇BERT用于推荐系统的文章,题目是《BERT4Rec: Sequential Recommendation with Bidirection...
  • BERT家族:族长BERT

    2020-05-25 16:32:32
    自18年底谷歌BERT问世以后,NLP便逐渐步入bert时代,bert家族儿孙满堂,如RoBERTa、ALBert、ERNIE等等,这些bert们正在给并持续给nlp领域输入无限生机,让人工智能皇冠上的明珠更加光彩夺目,在其光芒的照耀下,人类...
  • BERT模型的简单使用(Python)

    千次阅读 2020-06-05 09:47:58
    不涉及原理 更详细的请参考官方文档 https://bert-as-service.readthedocs.io/en/latest/index.html 下载预训练模型 ...pip install bert-serving-server pip install bert-serving-client 启动服务 在命
  • ①AR AE方式结合,通过随机取一句话排列的一种,然后将末尾一定量的词给“遮掩”(和 BERT 里的直接替换 “[MASK]” 有些不同)掉,最后用 AR 的方式来按照这种排列方式依此预测被“遮掩”掉的词。 ②在 BERT 这样...
  • BERT:直接实施BERT-源码

    2021-04-19 13:29:18
    利用动态고정된 Masking蒙版(我将只使用고정된 Masking ) Pretrain-MLM + NSP(将使用Namuwiki数据对数据进行预训练+ NSMC将进行微调) 使用namuwikiextrator提取Namuwiki数据 仅使用50,000个文档(大约500万个...
  • 使用Bert得到句向量简单总结

    千次阅读 2020-04-08 19:19:07
    bert获得句向量 tensorflow版本查看 tensorflow2.1.0版本运行结果 tensorflow1.14.0版本安装 ...
  • BERT简述

    2020-08-09 19:28:57
    BERT 2020年8月9日 17:10 目录 - - 概述 - ELMO - BERT - 如何训练 - MARK - CLS - How to use? - Extraction-based Question Answering - GPT - ...
  • 本文记录使用BERT预训练模型,修改最顶层softmax层,微调几个epoch,进行文本分类任务。 BERT源码 首先BERT源码来自谷歌官方tensorflow版:https://github.com/google-research/bert 注意,这是tensorflow 1.x ...
  • 详解BERT

    2020-08-07 16:55:31
    理解BERT 本文是看了大神的博客,深有感悟,因此在此过一遍加深印象,在本文的最后有原文链接,希望对大家有所帮助. 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本文...
  • 图解BERT模型:从零开始构建BERT

    千次阅读 多人点赞 2020-06-05 21:07:25
    本文首先介绍BERT模型要做什么,即:模型的输入、输出分别是什么,以及模型的预训练任务是什么;然后,分析模型的内部结构,图解如何将模型的输入一步步地转化为模型输出;最后,我们在多个中/英文、不同规模的数据...
  • word2vec、glove、fasttext是静态词向量,无法解决一词多义问题,elmo、GPT、Bert词向量是基于语言模型的动态词向量。 elmo、GPT、bert三者之间有什么区别? 特征提取器: elmo采用LSTM进行提取,GPT和bert则采用...
  • Bert-实战

    2019-09-18 17:08:41
    参考BERT fine-tune 终极实践教程 Bert 实战 bert在主要分为两个任务:一、训练语言模型和预训练部分(run_pretraining.py),二、训练具体任务的fine-turning部分(run_classifier.py适用于分类情况/run_squad.py...
  • 解决这个问题的最自然的方法,如用滑动窗口对文本进行切片,或者简化transformer,使用不充分的长范围attention,或者需要定制的CUDA内核。。BERT的最大长度限制提醒我们人类工作记忆的容量是有限的(5个∼9个区块),...
  • 本文由新加坡国立大学等联合提出,提出...因此,作者提出了一种基于跨度的动态卷积,以取代这些自注意力头来直接对局部模型进行建模 依赖性。卷积头,以及其余的自注意力头形成了一个混合注意力块,在全局和局部上.
  • bert(一)介绍和使用

    千次阅读 2020-10-02 00:37:07
    0 是 padding 的位置,1是没有padding的字 3.3 bert的下游任务 加一个线性层就可以分类了 3.4 使用bert提取Embedding向量 可以看到每个Encoder 的隐层输出我们都可以当成是Ebedding, 但哪一层的Embedding更能代表上...
  • PTMs-BERT

    2021-04-19 17:11:07
    前沿: 相比于GPT的单向自回归模型,BERT考虑到了利用上下文的双向信息,但是如果同时考虑双向信息,自然自回归的方式就不适宜了,BERT论文作者引入两项新的预训练任务。 论文: BERT: Pre-training of Deep ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,369
精华内容 1,747
关键字:

bert的动态使用