参考网上博客阅读了bert的代码,记个笔记。代码是 bert_modeling.py

参考的博客地址:

https://blog.csdn.net/weixin_39470744/article/details/84401339

https://www.jianshu.com/p/2a3872148766

主要分为三部分:

1、输入数据处理,将词(中文的字)转换为对应的embeddging,增加positional embeddding 和token type embedding.

positional embedding 是词的位置信息,词在句子中的位置。token type embedding表示是哪个句子中的词。

输出的数据格式是[batch_size,seq_length;width], width是词向量的长度。

2、encoder部分主要是使用transformer对句子进行编码,transformer的主要结构是来自 attention is all you need,但是和论文中的结构有些小区别。

3、decoder部分主要是解码部分。

先介绍数据处理部分:

1、bert模型输入的文本处理之后封装为InputExample类,这个类包扩 guid,text_a,text_b,label

  1. 这些内容会被转换成一下的格式。##表示被mark的词,[CLS]起始第一个,在分类任务中表示句子的 sentence vector
  1. [seq]表示句子的分隔符,如果只有一个句子text_b可以为空
    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

这里的输入句子会限定一个最大输入长度,不足的补0,这个0是指词对应的token_id。处理完成之后,将词的ID序列

转化为词向量的序列。

词ID序列到词向量序列的代码如下:

  1. # Perform embedding lookup on the word ids.
  2. (self.embedding_output, self.embedding_table) = embedding_lookup(
  3. input_ids=input_ids,
  4. vocab_size=config.vocab_size,
  5. embedding_size=config.hidden_size,
  6. initializer_range=config.initializer_range,
  7. word_embedding_name="word_embeddings",
  8. use_one_hot_embeddings=use_one_hot_embeddings)

下面代码在词向量序列上增加了 positional embeddings 和  token type embeddings。embedding_postprocessor 它包括token_type_embedding和position_embedding。也就是图中的Segement Embeddings和Position Embeddings。

  1. ##配置项 这部分代码注释写的非常详细,embedding_postprocessor的具体实现可以看源码的注释,Bert的position Embedding是作为参数学习得到的,
    transformer的论文里是计算得到的。
  1. self.embedding_output = embedding_postprocessor(
  2. input_tensor=self.embedding_output,
  3. use_token_type=True,
  4. token_type_ids=token_type_ids,
  5. token_type_vocab_size=config.type_vocab_size,
  6. token_type_embedding_name="token_type_embeddings",
  7. use_position_embeddings=True,
  8. position_embedding_name="position_embeddings",
  9. initializer_range=config.initializer_range,
  10. max_position_embeddings=config.max_position_embeddings,
  11. dropout_prob=config.hidden_dropout_prob)
  1. 特别说明一下,最后的输出增加了 normdropout output = layer_norm_and_dropout(output, dropout_prob)

2、Encoder部分代码

首先是对输入做了个attention_mask的处理

attention_mask = create_attention_mask_from_input_mask(input_ids, input_mask)

这个主要是减少对于mask的词和填充部分的词的关注。mask部分和填充部分在计算attention的时候分数自然应该很低才对。

然后是transformer_model,这部分主要是transformer,关于transformer可以参考 attention is all you need,这篇博客写的也不错,https://blog.csdn.net/yujianmin1990/article/details/85221271,这是翻译的一篇。

  1. self.all_encoder_layers = transformer_model(
  2. input_tensor=self.embedding_output,
  3. attention_mask=attention_mask,
  4. hidden_size=config.hidden_size,
  5. num_hidden_layers=config.num_hidden_layers,
  6. num_attention_heads=config.num_attention_heads,
  7. intermediate_size=config.intermediate_size,
  8. intermediate_act_fn=get_activation(config.hidden_act),
  9. hidden_dropout_prob=config.hidden_dropout_prob,
  10. attention_probs_dropout_prob=config.attention_probs_dropout_prob,
  11. initializer_range=config.initializer_range,
  12. do_return_all_layers=True)
  1. 接下来详细写写transformer_model的代码
    函数定义如下:
  1. def transformer_model(input_tensor,
  2. attention_mask=None,
  3. hidden_size=768,
  4. num_hidden_layers=12,
  5. num_attention_heads=12,
  6. intermediate_size=3072,
  7. intermediate_act_fn=gelu,
  8. hidden_dropout_prob=0.1,
  9. attention_probs_dropout_prob=0.1,
  10. initializer_range=0.02,
  11. do_return_all_layers=False):
  1. input_tensor是[batch_size, seq_length, hidden_size]
    attention_mask就是之前提过的用于处理padding部分和mask部分attention值的 形状[batch_size, seq_length,seq_length]
  1. hidden_size这个是transformer的隐层的大小
  1. num_hidden_layerstransformer有多少层,也就是blocks的数目。一个block的结构如下:

  1. num_attention_heads transformerattention heads的个数,比如bert设置的是12,多头机制中head数。
  1. intermediate_sizefeed forward中间层的大小
    接下来开始介绍代码,开始判断了一下hidden_size是否是num_attention_size的整数倍
  1. 对输入由三维改为二维,避免处理过程中多次tensor的变相,提高效率。
    这一步将[batch_size,seq_len,width]改为[batch_size*seq_len,width]
    prev_output = reshape_to_matrix(input_tensor)
  2.  
  3. 接下来是 attention layer,这个是计算self-attention,当然如果 querykey一样的话,就是self-attention
    首先第一步是计算query_layerkey_layervalue_layer
    这里把attention的计算抽象为 query,keyvalue三部分,通常keyvalue是一样的,然后根据query来计算不同的key value贡献的大小。
    比如如果RNN这种seq2seq的话(encoderdecoder都是RNN),querydecoder前一时刻的输出,keyvalueencoder RNN各个时刻的状态。
    在计算时query_layer=W*query ,其他key value类似
  1. # `query_layer` = [B*F, N*H]
  2. query_layer = tf.layers.dense(
  3. from_tensor_2d,
  4. num_attention_heads * size_per_head,
  5. activation=query_act,
  6. name="query",
  7. kernel_initializer=create_initializer(initializer_range))
  8.  
  9. # `key_layer` = [B*T, N*H]
  10. key_layer = tf.layers.dense(
  11. to_tensor_2d,
  12. num_attention_heads * size_per_head,
  13. activation=key_act,
  14. name="key",
  15. kernel_initializer=create_initializer(initializer_range))
  16. # `value_layer` = [B*T, N*H]
  17. value_layer = tf.layers.dense(
  18. to_tensor_2d,
  19. num_attention_heads * size_per_head,
  20. activation=value_act,
  21. name="value",
  22. kernel_initializer=create_initializer(initializer_range))
  1. 然后是计算attention的分数,这个和transformer论文中的计算方式一致,
  1. attention_scores = tf.matmul(query_layer, key_layer, transpose_b=True)
  2. attention_scores = tf.multiply(attention_scores,
  3. 1.0 / math.sqrt(float(size_per_head)))
  1.  
  2. 这部分代码中tensor的形状变化,和矩阵乘法的应用比较巧妙,可以推一下看看,代码写的很简洁。
  1. 这个部分是对attention mask的使用,如果是之前被maskpadding的部分,对应的分数设置为-10000,然后使用softmax计算分数
  1. if attention_mask is not None:
  2. # `attention_mask` = [B, 1, F, T]
  3. attention_mask = tf.expand_dims(attention_mask, axis=[1])
  4.  
  5. # Since attention_mask is 1.0 for positions we want to attend and 0.0 for
  6. # masked positions, this operation will create a tensor which is 0.0 for
  7. # positions we want to attend and -10000.0 for masked positions.
  8. adder = (1.0 - tf.cast(attention_mask, tf.float32)) * -10000.0
  9.  
  10. # Since we are adding it to the raw scores before the softmax, this is
  11. # effectively the same as removing these entirely.
  12. attention_scores += adder
  13. # Normalize the attention scores to probabilities.
  14. # `attention_probs` = [B, N, F, T]
  15. attention_probs = tf.nn.softmax(attention_scores)
  1.  
  2. attention的分数这部分也有dropout
  3. # This is actually dropping out entire tokens to attend to, which might
  1. # seem a bit unusual, but is taken from the original Transformer paper.
    attention_probs = dropout(attention_probs, attention_probs_dropout_prob)
  1. 接下来就是value_layer乘以attention_probs
    attention_layer最后的输出是
  1. [B*F, N*V]或者[B, F, N*V]
  1. # Scalar dimensions referenced here:
    # B = batch size (number of sequences)
    # F = `from_tensor` sequence length
    # T = `to_tensor` sequence length
    # N = `num_attention_heads`
    # H = `size_per_head`
  2.  
  3. 对于多头机制,每个head都计算完attention_layer之后,将这些结果全都拼接起来。
  1. attention_output = tf.concat(attention_heads, axis=-1)
  1. 注意这里attention_output最后一维的维度和layer_input一样的
  1. attention_output = dropout(attention_output, hidden_dropout_prob)
    attention_output = layer_norm(attention_output + layer_input)
    这个是加上残差链接。
    两个全连接层,最后加上dropout layer_norm
  1. # The activation is only applied to the "intermediate" hidden layer.
  2. with tf.variable_scope("intermediate"):
  3. intermediate_output = tf.layers.dense(
  4. attention_output,
  5. intermediate_size,
  6. activation=intermediate_act_fn,
  7. kernel_initializer=create_initializer(initializer_range))
  8.  
  9. # Down-project back to `hidden_size` then add the residual.
  10. with tf.variable_scope("output"):
  11. layer_output = tf.layers.dense(
  12. intermediate_output,
  13. hidden_size,
  14. kernel_initializer=create_initializer(initializer_range))
  15. layer_output = dropout(layer_output, hidden_dropout_prob)
  16. layer_output = layer_norm(layer_output + attention_output)
  17. prev_output = layer_output
  18. all_layer_outputs.append(layer_output)

google tensorflow bert代码分析的更多相关文章

  1. tensorflow笔记:多层LSTM代码分析

    tensorflow笔记:多层LSTM代码分析 标签(空格分隔): tensorflow笔记 tensorflow笔记系列: (一) tensorflow笔记:流程,概念和简单代码注释 (二) ten ...

  2. tensorflow笔记:多层CNN代码分析

    tensorflow笔记系列: (一) tensorflow笔记:流程,概念和简单代码注释 (二) tensorflow笔记:多层CNN代码分析 (三) tensorflow笔记:多层LSTM代码分析 ...

  3. AI繁荣下的隐忧——Google Tensorflow安全风险剖析

    本文由云+社区发表 作者:[ Tencent Blade Team ] Cradmin 我们身处一个巨变的时代,各种新技术层出不穷,人工智能作为一个诞生于上世纪50年代的概念,近两年出现井喷式发展,得 ...

  4. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  5. wifi display代码 分析

    转自:http://blog.csdn.net/lilian0118/article/details/23168531 这一章中我们来看Wifi Display连接过程的建立,包含P2P的部分和RTS ...

  6. AngularJS PhoneCat代码分析

    转载自:http://www.tuicool.com/articles/ym6Jfen AngularJS 官方网站提供了一个用于学习的示例项目:PhoneCat.这是一个Web应用,用户可以浏览一些 ...

  7. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  8. MVP模式, 开源库mosby的使用及代码分析

    Android中的构架模式一直是一个很hot的topic, 近年来Architecture components推出之后, MVVM异军突起, 风头正在逐渐盖过之前的MVP. 其实我觉得MVP还是有好 ...

  9. 【转载】word2vec原理推导与代码分析

    本文的理论部分大量参考<word2vec中的数学原理详解>,按照我这种初学者方便理解的顺序重新编排.重新叙述.题图来自siegfang的博客.我提出的Java方案基于kojisekig,我 ...

随机推荐

  1. Mac iTerm2登陆CentOS提示warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

    [报错原因]:没有utf-8这个语系(没添加语言_国名前缀),LC_ALL又没设定值. 服务端解决方法: 在远程系统上, /etc/environment 加入以下两行,重新登陆即可. LANG=en ...

  2. Cesium简介 [转]

    http://www.cnblogs.com/laixiangran/p/4984522.html 一.Cesium介绍 Cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎. ...

  3. STM32的时钟配置随笔

    以前使用STM32都是使用库函数开发,最近心血来潮想要使用寄存器来试试手感,于是乎便在工作之余研究了一下STM32F4的时钟配置,在此将经历过程写下来作为锻炼,同时也供和我一样的新手参考,如有错误或者 ...

  4. datagridview 代码添加列

    int column_key = 0; private DataGridViewTextBoxColumn column_add(string name) { DataGridViewTextBoxC ...

  5. .Net Core + DDD基础分层 + 项目基本框架 + 个人总结

    为什么要写这篇文章 1,在大半年前,公司开发任务比较轻松,自己不知道干什么,但是又想要学习技术,比较迷茫,后面我接触到了博客园,看了一个帖子,深有感触,我当时不知道学习什么,于是我信息给他,他居然回复 ...

  6. python web开发——c3 数据库交互和flask-SQLALchemy

    ORM(对象关系映射) 定义:将数据库中表与表之间的关系和代码中类(class)与类之间的关系联系起来,这就是ORM

  7. C++的访问关系

    1.C++的访问关系

  8. Ubuntu16.04 - 安装RabbitVCS,linux下的TortoiseSVN!!!

    RabbitVCS 官网:http://rabbitvcs.org/ 1,添加PPA源.在shell里面执行下面命令: sudo add-apt-repository ppa:rabbitvcs/pp ...

  9. webpack快速入门——CSS进阶:SASS文件的打包和分离

    1.安裝:因为sass-loader依赖于node-sass,所以需要先安装node-sass cnpm install node-sass --save-dev cnpm install sass- ...

  10. java保留小数点两位的4种方法

    import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; public c ...