首页 > 其他分享 >Bert【1】-基础

Bert【1】-基础

时间:2023-12-14 15:34:09浏览次数:25  
标签:Bert BERT 模型 基础 ids tokens tf input

2018年的10月11日,Google发布的论文《Pre-training of Deep Bidirectional Transformers for Language Understanding》,

成功在 11 项 NLP 任务中取得 state of the art 的结果,赢得自然语言处理学界的一片赞誉之声,BERT就出自该论文,

BERT模型的全称是 Bidirectional Encoder Representations from Transformers;

 

Bert 模型

Bert 只使用了 transformers 的 encode 模块,属于自编码语言模型, 

论文中,作者分别用 12层 和 24层 transformers encoder 组装了两套 bert模型,分别是

层的数量(Transformer Encoder 块的数量)为L ,隐藏层的维度为H ,自注意头的个数为A; 

在所有例子中,我们将前馈/过滤器(Transformer Encoder 端的 feed-forward 层)的维度设置为4H ,即当 H=768 时是3072 ;当 H=1024 是 4096;

网络结构如下

 

模型输入

 

Token emd:文本中各个字/词的初始向量,可以是随机初始,也可以使用 word2vec 进行初始化 【为方便描述且与 BERT 模型的当前中文版本保持一致,统一以「字向量」作为输入】

Segment emd:文本向量,不同于单个字,该 emd 学习了全局语义信息,通过训练得到,一般初始化为 111222

Position emd:由于出现在文本不同位置的字/词所携带的语义信息存在差异(比如:“我爱你”和“你爱我”),因此,BERT 模型对不同位置的字/词分别附加一个不同的向量以作区分,通过训练得到

3个 emd 相加 sum 作为 input;

模型输出 包含 输入各字对应的 融合全文语义信息 后的向量表示

 

下面代码截取自 Bert官方源码,大致是 如何把原始数据 转换成 符合模型输入的格式,从源码看出 Bert有两种(代码加粗)输入格式;

def convert_single_example(ex_index, example, label_list, max_seq_length,
                           tokenizer):
    """Converts a single `InputExample` into a single `InputFeatures`."""

    if isinstance(example, PaddingInputExample):
        return InputFeatures(
            input_ids=[0] * max_seq_length,
            input_mask=[0] * max_seq_length,
            segment_ids=[0] * max_seq_length,
            label_id=0,
            is_real_example=False)

    label_map = {}
    for (i, label) in enumerate(label_list):
        label_map[label] = i

    tokens_a = tokenizer.tokenize(example.text_a)
    tokens_b = None
    if example.text_b:
        tokens_b = tokenizer.tokenize(example.text_b)

    if tokens_b:
        # Modifies `tokens_a` and `tokens_b` in place so that the total
        # length is less than the specified length.
        # Account for [CLS], [SEP], [SEP] with "- 3"
        _truncate_seq_pair(tokens_a, tokens_b, max_seq_length - 3)
    else:
        # Account for [CLS] and [SEP] with "- 2"
        if len(tokens_a) > max_seq_length - 2:
            tokens_a = tokens_a[0:(max_seq_length - 2)]

    # The convention in BERT is:
    # (a) For sequence pairs:
    #  tokens:   [CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP]
    #  type_ids: 0     0  0    0    0     0       0 0     1  1  1  1   1 1
    # (b) For single sequences:
    #  tokens:   [CLS] the dog is hairy . [SEP]
    #  type_ids: 0     0   0   0  0     0 0
    #
    # Where "type_ids" are used to indicate whether this is the first
    # sequence or the second sequence. The embedding vectors for `type=0` and
    # `type=1` were learned during pre-training and are added to the wordpiece
    # embedding vector (and position vector). This is not *strictly* necessary
    # since the [SEP] token unambiguously separates the sequences, but it makes
    # it easier for the model to learn the concept of sequences.
    #
    # For classification tasks, the first vector (corresponding to [CLS]) is
    # used as the "sentence vector". Note that this only makes sense because
    # the entire model is fine-tuned.
    tokens = []
    segment_ids = []
    tokens.append("[CLS]")
    segment_ids.append(0)
    for token in tokens_a:
        tokens.append(token)
        segment_ids.append(0)
    tokens.append("[SEP]")
    segment_ids.append(0)

    if tokens_b:
        for token in tokens_b:
            tokens.append(token)
            segment_ids.append(1)
        tokens.append("[SEP]")
        segment_ids.append(1)

    input_ids = tokenizer.convert_tokens_to_ids(tokens)

    # The mask has 1 for real tokens and 0 for padding tokens. Only real
    # tokens are attended to.
    input_mask = [1] * len(input_ids)

    # Zero-pad up to the sequence length.
    while len(input_ids) < max_seq_length:
        input_ids.append(0)
        input_mask.append(0)
        segment_ids.append(0)

    label_id = label_map[example.label]

    feature = InputFeatures(
        input_ids=input_ids,
        input_mask=input_mask,
        segment_ids=segment_ids,
        label_id=label_id,
        is_real_example=True)
    return feature

下面代码截取自 Bert官方源码,大致是 在 Bert模型输出后 加上 全连接层 进行下游任务,该代码可以判断 Bert的输出是Emd;

def create_model(bert_config, is_training, input_ids, input_mask, segment_ids,
                 labels, num_labels, use_one_hot_embeddings):
    """Creates a classification model."""
    model = modeling.BertModel(
        config=bert_config,
        is_training=is_training,
        input_ids=input_ids,
        input_mask=input_mask,
        token_type_ids=segment_ids,
        use_one_hot_embeddings=use_one_hot_embeddings)

    # In the demo, we are doing a simple classification task on the entire
    # segment.
    #
    # If you want to use the token-level output, use model.get_sequence_output()
    # instead.
    output_layer = model.get_pooled_output()

    hidden_size = output_layer.shape[-1].value

    output_weights = tf.get_variable(
        "output_weights", [num_labels, hidden_size],
        initializer=tf.truncated_normal_initializer(stddev=0.02))

    output_bias = tf.get_variable(
        "output_bias", [num_labels], initializer=tf.zeros_initializer())

    with tf.variable_scope("loss"):
        if is_training:
            # I.e., 0.1 dropout
            output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)

        logits = tf.matmul(output_layer, output_weights, transpose_b=True)
        logits = tf.nn.bias_add(logits, output_bias)
        probabilities = tf.nn.softmax(logits, axis=-1)
        log_probs = tf.nn.log_softmax(logits, axis=-1)

        one_hot_labels = tf.one_hot(labels, depth=num_labels, dtype=tf.float32)

        per_example_loss = -tf.reduce_sum(one_hot_labels * log_probs, axis=-1)
        loss = tf.reduce_mean(per_example_loss)

        return (loss, per_example_loss, logits, probabilities)

 

Bert 预训练

作者设计了两个任务来预训练模型,

预训练的目标是构建语言模型,P(我爱吃饭)=P(我|爱吃饭)P(爱|吃饭)P(吃|饭)

BERT模型采用的是bidirectional Transformer,为什么需要双向呢?因为在预训练模型处理下游任务时,不仅需要该词左侧的语言信息,还需要右侧的语言信息

MLM

随机掩盖部分输入词,然后基于上下文对被掩盖的词进行预测;

在实际训练过程中,每次从序列中随机选出15% token用于masked,也就是每次只预测15%的词,而不是像word2vec中的cbow预测所有词;

在被选中的 token 中,80%用 MASK 替代,10%保持不变,10%随机选一个token替代原来的token;

这个任务 类似于 人类语言学习中的 《完形填空》任务 

 

NSP:Next Sentence Prediction

预测两个句子是否连续

样本如下

1. 从训练语料库中取出两个连续的句子作为正样本

2.从不同的文档中随机各取一个句子作为负样本

缺点:主题预测和连贯性预测 合并为一个单项任务

这个任务类似于 人类学习语言中的 《段落重排》任务

 

Bert 模型通过 Masked ML 任务和 NSP 任务联合训练,使模型输出的每个字/词的向量都尽可能全面、准确地刻画输入文本的整体信息,为后续的微调任务提供更好的模型参数初始值。

 

局限性

1. Bert在MLM训练任务中,把多个词MASK掉,并且认为这些词相互独立,然而有时候并不是独立的,比如 我爱吃饭 变成 我爱MASK MASK,吃和饭本身是有关系的

2. BERT 的在预训练时会出现特殊的[MASK],但是它在下游的 fine-tune 中不会出现,这就出现了预训练阶段和 fine-tune 阶段不一致的问题

但这两个问题在经过海量语料库训练后会得到缓解,对模型整体效果影响不大

  

下游任务 finetuning

目前将预训练的语言模型应用到NLP任务主要有两种策略,

一种是基于特征的语言模型,如ELMo模型;另一种是基于微调的语言模型,如OpenAI GPT

这两类语言模型各有其优缺点,BERT基本上融合了它们的优点,因此才可以在诸多后续特定任务上取得最优的效果。

多标签分类
如输入 一件 L 尺寸的棉服,输出两个标签——型号:L,类型:冬装
BERT 模型解决多标签分类问题时,其输入与普通单标签分类问题一致,得到其 embedding 表示之后(也就是 BERT 输出层的 embedding),
有几个 label 就连接到几个全连接层(也可以称为 projection layer),然后再分别接上 softmax 分类层,最后再将所有的 loss 相加起来即可。
这种做法就相当于将 n 个分类模型的特征提取层参数共享,得到一个共享的表示(其维度可以视任务而定,由于是多标签分类任务,因此其维度可以适当
增大一些),最后再做多标签分类任务。

 

一对句子的分类任务:例如自然语言推断 (MNLI),句子语义等价判断 (QQP) 等,

如上图 (a) 所示,需要将两个句子传入 BERT,然后使用 [CLS] 的输出值 C 进行句子对分类。

单个句子分类任务:例如句子情感分析 (SST-2),判断句子语法是否可以接受 (CoLA) 等,

如上图 (b) 所示,只需要输入一个句子,无需使用 [SEP] 标志,然后也是用 [CLS] 的输出值 C 进行分类。

问答任务:如 SQuAD v1.1 数据集,样本是语句对 (Question, Paragraph),Question 表示问题,Paragraph 是一段来自 Wikipedia 的文本,Paragraph

包含了问题的答案。而训练的目标是在 Paragraph 找出答案的起始位置 (Start,End)。

如上图 (c) 所示,将 Question 和 Paragraph 传入 BERT,然后 BERT 根据 Paragraph 所有单词的输出预测 Start 和 End 的位置。

单个句子标注任务:例如命名实体识别 (NER),输入单个句子,然后根据 BERT 对于每个单词的输出 T 预测这个单词的类别,是属于 Person,

Organization,Location,Miscellaneous 还是 Other (非命名实体)。

 

模型对比 

word2vec 作为 NLP 里程碑的模型,对NLP的发展起到了巨大的作用,但它本身是一种浅层结构,且学习到的语义信息收到窗口大小的影响;

lstm 部分解决了长距离依赖的问题,但 lstm 本身是单向学习,只能从前到后,或者从后往前学习,无法同时考虑上下文信息;

 

 

 

参考资料:

https://blog.csdn.net/weixin_44799217/article/details/115374101  BERT模型的详细介绍

https://www.jianshu.com/p/46cb208d45c3  彻底理解 Google BERT 模型

标签:Bert,BERT,模型,基础,ids,tokens,tf,input
From: https://www.cnblogs.com/yanshw/p/16965947.html

相关文章

  • 04_ARM硬件基础
    04_ARM硬件基础课程目标:了解我们常用硬件接口,并且编程控制重点难点:对各个接口原理的掌握考核目标:各个接口的作用,实现原理及特性1.硬件基础概述1.1.为什么我们要去研究硬件因为要去使用他,驱动它1.2.我们要研究什么硬件原理及构造地址寄存器协议时长/频率1.3.......
  • 05 基础入门——资产架构&端口&应用&WAF&站库分离&负载均衡
    一、资产架构1、网站配置(1)目录型网站安全bbs.xiaodi8.com  dz论坛      #该域名下有一套网站程序,dz论坛bbs.xiaodi8.com/blog wp程序  #该域名的某个目录下也配置了一套网站程序总结:一个网站,两个程序,其中任何一个程序出现漏洞,都可以进入安全测试(同一服务器......
  • 四、以太网交换基础
    一、以太网交换基础1.以太网协议介绍定义:以太网是当今现有局域网(LocalAreaNetwork,LAN)采用的最通用的通信协议标准,该标准定义了在局域网中采用的电缆类型和信号处理方法。以太网是建立在CSMA/CD(CarrierSenseMultipleAccess/CollisionDetection,载波监听多路访问/冲突......
  • gsap基础-JS动画库
    https://blog.csdn.net/m0_61662775/article/details/131430585https://blog.csdn.net/changbb/article/details/131675810学习文档简介GSAP的全名是GreenSockAnimationPlatform一直发展到今天已经是3.x版本,这是一个适用于现代浏览器的专业Javascript动画库核心语法......
  • 电路基础笔记1
    电路基础1电流正电荷在电路中的移动方向规定为电流的方向。电流通常用字母“I”表示,单位为安培(简称安),用“A”表示,比安培小的单位有毫安(mA)、微安(μA),它们之间的换算关系为1A=103mA=106μA直流电与交流电直流电是指方向始终固定不变的电压或电流。能产生直流电的电源称为直......
  • Java基础知识概览总结
    1.Java语法数据类型:Java中的基本数据类型包括整型、浮点型、字符型和布尔型,而引用类型包括类、接口和数组等。变量和常量:使用关键字var、final和static声明变量和常量。运算符:包括算术运算符、比较运算符、逻辑运算符和位运算符等。控制流程:使用条件语句(if-else、switch)、循环......
  • C++基础 -6- 二维数组,数组指针
    ———————二维数组,数组指针——————— ......
  • 零基础 从 yolo8 入门计算机视觉超简单:物体识别、图像分类、轨迹追踪、姿势识别
    目录安装Ultralytics训练模型验证预测&识别导出追踪图像分割提取分类姿势识别轨迹生成UltralyticsYOLOv8是备受好评的实时目标检测和图像分割模型,主要功能是物体识别、分割图片物体、分类、姿态识别和跟踪等。Ultralytics支持使用CPU、GPU进行训练,支持x64、arm64等CPU......
  • Netty-在NIO基础上的优化
    零拷贝零拷贝指的是,应用程序在需要把内核中的一块区域数据转移到另外一块内核区域去时,不需要经过先复制到用户空间,再转移到目标内核区域去了,而直接实现转移。在网络通信上,使用直接内存。Netty接收和发送ByteBuf采用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次......
  • Ajax-基础
     1.简介Ajax(AsynchronousJavaScriptandxml)就是异步的JS和XML;可在浏览器中向服务器发送异步请求,最大的优势是:无刷新获取数据。   广义: 同步:刷新页面 异步:刷新局部 一个请求仅有一个响应;2.优缺点Ajax的优点: 可以无需刷新页......