文章目录

  • 一. Mask背景
    • 1.2 例子1
    • 1.2 例子2
  • 二. 原理
  • 三. 方式
    • 3.1 配置keras.layers.Embedding 层
    • 3.2 添加keras.layers.Masking层
    • 3.3 自定义

一. Mask背景

在NLP中,mask使用最为常见。在NLP中,许多句子都有着不同的长度,我们往往需要按照一定的长度, 对句子进行填补和截取操作。 一般使用keras.preprocessing.sequence包中的pad_sequences方法, 在句子前面或者后面补0. 但是这些零是我们不需要的, 只是为了组成可以计算的结构才填补的. 因此计算过程中, 我们希望用mask的思想, 既然所有样本现在都具有了统一长度,那就必须告知模型,数据的某些部分实际上是填充,应该忽略。这种机制就是遮盖(Mask)

1.2 例子1


如图中所示,Hello world只有两个字符,而I am here有三个字符。因此在转化成list的时候,就需要在第一个list中加入0变成与第二个list相同长度。

1.2 例子2

这里用简单的向量来描述padding的原理。假设有一个长度为5的向量:
x=[1,0,3,4,5]

经过padding变成长度为8: x=[1,0,3,4,5,0,0,0]

当你将这个长度为8的向量输入到模型中时,模型并不知道你这个向量究竟是“长度为8的向量”还是“长度为5的向量,填充了3个无意义的0”。为了表示出哪些是有意义的,哪些是padding的,我们还需要一个mask向量(矩阵):m=[1,1,1,1,1,0,0,0]

这是一个0/1向量(矩阵),用1表示有意义的部分(True),用0表示无意义的padding部分(False)。
所谓mask,就是xm的运算,来排除padding带来的效应。比如我们要求x的均值,本来期望的结果是:
avg(x)=1+0+3+4+555=12.6avg(x)=\frac{1+0+3+4+55}{5}=12.6 avg(x)=51+0+3+4+55=12.6
但是由于向量已经经过padding,直接算的话就得到:
avg(x)=1+0+3+4+55+0+0+08=7.875avg(x)=\frac{1+0+3+4+55+0+0+0}{8}=7.875 avg(x)=81+0+3+4+55+0+0+0=7.875
会带来偏差。更严重的是,对于同一个输入,每次padding的零的数目可能是不固定的,因此同一个样本每次可能得到不同的均值,这是很不合理的。有了mask向量m之后,我们可以重写求均值的运算:
avg(x)=sum(x⊗m)sum(m)avg(x)=\frac{sum(x⊗m)}{sum(m)}avg(x)=sum(m)sum(xm)
这里的⊗是逐位对应相乘的意思。这样一来,分子只对非padding部分求和,分母则是对非padding部分计数,不管你padding多少个零,最终算出来的结果都是一样的。

二. 原理

在keras中, Tensor在各层之间传递, Layer对象接受的上层Layer得到的Tensor, 输出经过处理后的Tensor。Keras是用一个mask矩阵来参与到计算当中, 决定在计算中屏蔽哪些位置的值。 因此mask矩阵其中的值就是True/False, 其形状一般与对应的Tensor相同. 同样与Tensor相同的是, mask矩阵也会在每层Layer被处理, 得到传入到下一层的mask情况。

mask矩阵如上文例2中,m=[1,1,1,1,1,0,0,0]所示,其中1为True,0为False。

三. 方式

在 Keras 模型中引入输入掩码(Mask)有三种方式:

  • 使用 mask_zero=True 配置一个keras.layers.Embedding 层。
  • 添加一个 keras.layers.Masking 层
  • 在调用支持 mask 参数的层(如 RNN 层)时,手动传递此参数。

3.1 配置keras.layers.Embedding 层


首先,先了解一下Embedding层
假如现在有这么两句话

Hope to see you soon
Nice to see you again

在神经网络中,我们将这个作为输入,一般就会将每个单词用一个正整数代替,这样,上面的两句话在输入中是这样的

[0, 1, 2, 3, 4][5, 1, 2, 3, 6]

在神经网络中,第一层是

Embedding(input_dim=7, output_dim=2, input_length=5)

其中,第一个参数是input_dim,值为7,代表的是单词表的长度;第二个参数是output_dim,值为2,代表输出后向量长度为2;第三个参数是input_length,值为5,代表输入序列的长度。

一旦神经网络被训练了,Embedding层就会被赋予一个权重,计算出来的结果如下:

+------------+------------+
|   index    |  Embedding |
+------------+------------+
|     0      | [1.2, 3.1] |
|     1      | [0.1, 4.2] |
|     2      | [1.0, 3.1] |
|     3      | [0.3, 2.1] |
|     4      | [2.2, 1.4] |
|     5      | [0.7, 1.7] |
|     6      | [4.1, 2.0] |
+------------+------------+

根据这个权重,第二个输入计算出来的embedding vector就是下面这个,shape为(input_length,output_dim)=(5,2):

[[0.7, 1.7], [0.1, 4.2], [1.0, 3.1], [0.3, 2.1], [4.1, 2.0]]

来自Keras Embedding层详解的例子,解释其中迷惑的点。
若令mask_zero=True,则注意input_dim应该加一,例如原本ID类取值为0到5,为了实现padding,ID取值范围变成1到6,则此时input_dim=6+1=7。
下面给出一个例子,我们准备了四个ID列表型样本,经过了padding补零到长度4,为了方便展示,我们让所有非零ID相同。在Embedding层中设置mask_zero=True,然后调用自定义的MeanPooling层(见这里)。如果masking起作用,则输出的result应该是一个4x3矩阵,且所有行向量相同。
MyMeanPool()层就是把每个样本按行求平均值,具体例子看上述的例子2。如果应用Masking机制(mask_zero=True),对四个样本直接按行求平均值:Meanseq0=11=1Mean_{seq0}=\frac{1}{1}=1Meanseq0=11=1Meanseq1=1+12=1Mean_{seq1}=\frac{1+1}{2}=1Meanseq1=21+1=1Meanseq2=1+1+13=1Mean_{seq2}=\frac{1+1+1}{3}=1Meanseq2=31+1+1=1Meanseq3=1+1+1+14=1Mean_{seq3}=\frac{1+1+1+1}{4}=1Meanseq3=41+1+1+1=1 则结果相同,都为1。先经过Embedding生成一个(4,3)的Embedding矩阵,维度为(4,3)是因为(input_length,output_dim)=(4,3),再经过MyMeanPool(axis=1)后按行求平均值, 因此所有的行向量应该相同。

#四个ID列表型样本,经过padding
ID_seq0 = [1, 0, 0, 0]
ID_seq1 = [1, 1, 0, 0]
ID_seq2 = [1, 1, 1, 0]
ID_seq3 = [1, 1, 1, 1]
data = np.array([ID_seq0, ID_seq1, ID_seq2, ID_seq3])model =  tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(input_dim=5, output_dim=3, input_length=4, mask_zero=True))
model.add(MyMeanPool(axis=1))result = model.predict(data) #为输入样本生成输出预测
print(result)

输出结果如下,这些数值都是Embedding层随机生成的权值而已。

[[-0.00347356 -0.03284906 -0.03016986][-0.00347356 -0.03284906 -0.03016986][-0.00347356 -0.03284906 -0.03016986][-0.00347356 -0.03284906 -0.03016986]]

3.2 添加keras.layers.Masking层

from keras.layers import Input, Masking
from keras.models import Model
from MyMeanPooling import MyMeanPooldata = [[[10,10],[0, 0 ],[0, 0 ],[0, 0 ]],[[10,10],[20,20],[0, 0 ],[0, 0 ]],[[10,10],[20,20],[30,30],[0, 0 ]],[[10,10],[20,20],[30,30],[40,40]]]A = Input(shape=[4,2]) # None * 4 * 2
mA = Masking()(A)
out = MyMeanPool(axis=1)(mA)model = Model(inputs=[A], outputs=[out])print model.summary()
print model.predict(data)

结果如下,每一行对应一个样本的结果,例如第一个样本只有第一个时刻有值,输出结果是[10. 10. ],是正确的。

[[10. 10.][15. 15.][20. 20.][25. 25.]]

例2:

import tensorflow as tf
import numpy as npraw_inputs = [[83, 91, 1, 645, 1253, 927],[73, 8, 3215, 55, 927],[711, 632, 71]]
padded_input = tf.keras.preprocessing.sequence.pad_sequences(raw_inputs, maxlen=None,dtype='int32', padding='post',truncating='post', value=0.0)
print(padded_input)embedding_layer = tf.keras.layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)masked_output = embedding_layer(padded_input)
print('Shape of masked input passed through embedding: {}'.format(padded_input.shape))
print('Shape of Embeding output: {}'.format(masked_output.shape))
print('Shape of mask attached to this input: {}'.format(masked_output._keras_mask.shape))
print('Original input: \n{}'.format(padded_input))
print('attached Mask: \n{}'.format(masked_output._keras_mask))# Using Masking layer:
masking_layer = tf.keras.layers.Masking()
# simulate the embedding look up by expanding the 2D input to 3D
# with embedding dimension of 10
mask_value = 0.
x = np.random.rand(10, 6, 2)
x[0, 3:, :] = mask_valueprint('\nUsing masking layer...')
print("Unmasked embedding: \n {}".format(x.shape))masked_embedding = masking_layer(x)
print(masked_embedding._keras_mask.shape)
print(masked_embedding._keras_mask)

输出:

3.3 自定义

更多的,我们还是在自定义的层中,需要支持mask的操作,因此需要对应的逻辑。
如果我们希望自定义的这个层支持mask操作,就需要在__init__方法中指定

self.supports_masking = True

如果在本层计算中需要使用到mask,则call方法需要多传入一个mask参数,即

def call(self,inputs,mask=None):pass

然后,如果还要继续输出mask,供之后的层使用,如果不对mask矩阵进行变换,这不用进行任何操作,否则就需要实现compute_mask函数

def compute_mask(self,inputs,mask=None):pass

这里的inputs就是输入的Tensor,与call方法中接收到的一样,mask就是上层传入的mask矩阵。
如果希望mask到此为止,之后的层不再使用,则该函数直接返回None即可。

def compute_mask(self,inputs,mask=None):return  None

例子:在上文中的MyMeanpooling层,就是自定义的Masking:

from keras import backend as K
from keras.engine.topology import Layer
import tensorflow as tfclass MyMeanPool(Layer):def __init__(self, axis, **kwargs):self.supports_masking = Trueself.axis = axissuper(MyMeanPool, self).__init__(**kwargs)def compute_mask(self, input, input_mask=None):# need not to pass the mask to next layersreturn Nonedef call(self, x, mask=None):if mask is not None:mask = K.repeat(mask, x.shape[-1])mask = tf.transpose(mask, [0,2,1])mask = K.cast(mask, K.floatx())x = x * maskreturn K.sum(x, axis=self.axis) / K.sum(mask, axis=self.axis)else:return K.mean(x, axis=self.axis)def compute_output_shape(self, input_shape):output_shape = []for i in range(len(input_shape)):if i!=self.axis:output_shape.append(input_shape[i])return tuple(output_shape)

参考资料

  1. keras中的compute_mask函数讲解
  2. Keras自定义实现带masking的meanpooling层

详解keras中的Mask机制相关推荐

  1. 详解Java中的异常机制:运行期异常、编译器异常及如何自定义异常

    文章目录 前言 一.异常概述及分类 1.异常概述 2.异常的继承结构 3.异常的继承机构图 二.运行期异常-RuntimeException 1.JVM如何默认处理异常 2.try...catch的方 ...

  2. 根据实例详解Java中的反射机制

    概念: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java ...

  3. java 反射机制_详解Java中的反射机制的优缺点

    一.什么是反射? 对于程序员来说,应该很少需要直接使用反射工具:之所以在语言中提供它们,是为了支持其他Java特性,比如对象序列化.Java Beans以及RMI.还有就是在很多框架中,也是应用到了反 ...

  4. 详解JS中的事件机制(带实例)

    2019独角兽企业重金招聘Python工程师标准>>> 1. 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题 ...

  5. 【专题】详解Python中的反射机制

    Python面向对象的反射机制 一.反射的概念 二.熟悉面向对象的属性方法 三.面向对象的反射机制 四.实例应用 一.反射的概念 python的反射机制,核心就是利用字符串去已存在的模块中找到指定的属 ...

  6. 详解pandas中的groupy机制

    公众号:尤而小屋 作者:Peter 编辑:Peter 大家好,我是Peter~ 在自己的数据处理分析日常中,经常会遇到对数据的某个字段进行分组再求和或均值等其他操作的需求,比如电商中根据不同的支付用户 ...

  7. php中的session详解,PHP中的session机制详解

    Session是什么? 首先,我们大概知道session是浏览器与服务器之间的一次交互会话. 那么会话又是什么呢?顾名思义就是浏览器与服务器之间的对话,浏览器一关闭,会话就结束了. 说session不 ...

  8. 详解OpenCV中的Lucas Kanade稀疏光流单应追踪器

    详解OpenCV中的Lucas Kanade稀疏光流单应追踪器 1. 效果图 2. 源码 参考 这篇博客将详细介绍OpenCV中的Lucas Kanade稀疏光流单应追踪器. 光流是由物体或相机的运动 ...

  9. java同步异步调用_详解java 三种调用机制(同步、回调、异步)

    1:同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,jsPwwCe它是一种单向调用 2:回调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口: 3:异步调用:一种类似消 ...

最新文章

  1. 树梅派配置ad-hoc网络
  2. java 图像采集_JAVA B/S通过摄像头采集图片信息解决方案。
  3. html5中音乐播放器怎么写,打造属于自己的音乐播放器 HTML5之audio标签
  4. 软件架构引言之项目管理的问题
  5. php 发送表单数据,php - 将表单数据发送到会话变量 - SO中文参考 - www.soinside.com...
  6. 苹果系统安装python环境_Mac下安装Python虚拟环境Virtualenv
  7. 利用Matlab进行图像处理
  8. windows 匿名管道: 父进程与子进程通信 (进程间通信之CreatePipe)
  9. centos7随机生成密码
  10. ABYY FineReader PDF软件最新15个人​版安装下载步骤教程
  11. UML10种图例之包图
  12. 亚马逊云科技赋能全新基于云的安全SaaS平台——安智联365
  13. 4.1图像分割之区域生长法
  14. 宝宝树全自动引流脚本软件高质量活跃粉丝
  15. 馋猫美食记录本_隐私政策
  16. User Exit for MIGO after SAVE
  17. 美团点评java面试题_最新美团面经Java,美团点评一面(面试题)
  18. CDOJ-1057 秋实大哥与花(线段树区间更新)
  19. [渝粤教育] 南方医科大学 医学统计学 参考 资料
  20. Latex表格固定列宽并设置靠左、居中或靠右

热门文章

  1. R语言基于lm模型构建线性回归模型(蟋蟀的鸣叫声与温度的关系)、计算回归模型的RMSE指标、计算回归模型的R方指标(R-squared)
  2. 易诚互动在创业板更新招股书:上半年出现亏损,极其依赖阿里云
  3. 大数据图书分享-Python数据可视化实战课程
  4. Java中高级核心知识全面解析——Redis(集群【概述{主从复制、哨兵、集群化}、数据分区方案、节点通信机制、数据结构简析】)5
  5. 著名画家孙王平作品欣赏
  6. 如何用Python画一只肥肥的柯基狗狗——turtle库绘制椭圆与弧线实践
  7. 24、Java——银行存款取款系统(对象+集合)
  8. test\fmw\gtest\include\gtest/gtest-printers.h(714) : error C2977: “std::tuple”: 模板 参数太多
  9. 2.4G无线芯片NRF24L01 驱动源码及详解
  10. Axure交互模拟线框图等等