Keras 中的 Masking 与 Padding

机器学习中的 Mask 与 Padding 是两个经常会使用到的概念,而之前我们也接触到一部分,那就是在我们进行文本分类的时候曾经接触过 Padding。而这节课我们就来详细了解一下 Masking 与 Padding 的概念以及基本的用法。

1. 基本概念

虽然 Mask(遮掩)和 Padding(填充)这两个概念看上去似乎很难理解,但是他他们的作用却很简单。简单来说,Mask与Padding的作用分别是:

  • Padding:将本来不相同的样本填充到相同的长度,以便于后面的处理,我们一般使用0做填充;
  • Mask:告诉网络层那些是真正的数据,哪些是填充的“0”,从而帮助网络层更好地计算。

简单举例,下面是我们的三个文本:

[
    ['Hello', 'Hi'],
    ['Who', 'are', 'you'],
    ['what', 'time', 'is', 'it', 'now']
]

假设我们将其编码之后得到的向量为:

[
    [1, 2],
    [3, 4, 5],
    [6, 7, 8, 9, 10]
]

于是我们将这三个张量填充到共同长度——5:

[
    [1, 2, 0, 0, 0],
    [3, 4, 5, 0, 0],
    [6, 7, 8, 9, 10]
]

经过填充之后我们得到了三个长度相同的向量,于是我们可以计算出mask来告诉网络哪些数据是填充的,哪些数据是真实的数据。于是我们得到了该向量的Mask:

[
    [True, True, False, False, False],
    [True, True, True,  False, False],
    [True, True, True,  True,  True ]
]

通过上面的例子相信大家可以清楚的了解到机器学习中Mask与Padding的概念。下面我们可以看一下在TesnorFlow之中如何使用这两者吧。

2. TensorFlow 中的 Padding

在TensorFlow之中,我们最常使用的方法是采用预处理的方式进行序列的Padding,就像我们在文本分类中所做的那样。

下面是一个简单的示例:

import tensorflow as tf

inputs = [
    [1, 2],
    [3, 4, 5],
    [6, 7, 8, 9, 10]
]

inputs = tf.keras.preprocessing.sequence.pad_sequences(
    raw_inputs, padding="post", value=0
)
print(inputs)

于是我们可以得到输出:

[[ 1  2  0  0  0]
 [ 3  4  5  0  0]
 [ 6  7  8  9 10]]

以上就是TensorFlow中进行填充最常用的方法。

3. TensorFlow 中的 Mask

在TensorFlow之中使用Mask也是比较简单的,主要有两种方法:

  • 添加一个tf.keras.layers.Embedding层并设置参数mask_zero=True;
  • 添加一个tf.keras.layers.Masking层。

这里我们使用之前的文本分类的模型作为例子来进行演示。值得注意的是,在很多情况之下,如果我们不使用Mask,那么模型依然会继续运行,但是采用Mask会让模型更加精确。

我们原来的网络为:

model = tf.keras.Sequential([
    tf.keras.layers.Embedding(10000, 32),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
model.summary()

我们可以查看到模型的结构为:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 32)          320000    
_________________________________________________________________
global_average_pooling1d (Gl (None, 32)                0         
_________________________________________________________________
dense (Dense)                (None, 64)                2112      
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 65        
=================================================================
Total params: 322,177
Trainable params: 322,177
Non-trainable params: 0
_________________________________________________________________

1. 使用tf.keras.layers.Embedding层并设置mask_zero=True

因为我们已经使用了Embedding,因此我们只需要直接设置参数mask_zero=True即可:

model2 = tf.keras.Sequential([
    tf.keras.layers.Embedding(10000, 32, mask_zero=True),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
model2.summary()

于是我们可以看到网络的结构:

Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_2 (Embedding)      (None, None, 32)          320000    
_________________________________________________________________
global_average_pooling1d_2 ( (None, 32)                0         
_________________________________________________________________
dense_4 (Dense)              (None, 64)                2112      
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 65        
=================================================================
Total params: 322,177
Trainable params: 322,177
Non-trainable params: 0
_________________________________________________________________

我们可以发现网络的结构没有发生变化,为了验证该Mask已经生效,于是我们可以进行测试:

print(model(inputs))
print(model2(inputs))

我们可以通过输出看到,我们设置的遮掩(Mask)已经生效:

tf.Tensor(
[[0.5014145 ]
 [0.50220466]
 [0.5058755 ]], shape=(3, 1), dtype=float32)

tf.Tensor(
[[0.4913409 ]
 [0.49880138]
 [0.49782944]], shape=(3, 1), dtype=float32)

2. 使用tf.keras.layers.Masking层

我们依然以我们的文本分类模型为例,为了添加Masking层,我们将模型进行如下修改:

model3 = tf.keras.Sequential([
    tf.keras.layers.Masking(input_shape=(256,)),
    tf.keras.layers.Embedding(10000, 32),
    tf.keras.layers.GlobalAveragePooling1D(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
model3.summary()

在该模型之中,因为Masking层是位于第一层,因此我们要设置参数input_shape,这里的参数(256,)表示的是我们的每条数据都是padding长度为256的序列。

于是我们可以得到输出:

Model: "sequential_10"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
masking_5 (Masking)          (None, 256)               0         
_________________________________________________________________
embedding_10 (Embedding)     (None, 256, 32)           320000    
_________________________________________________________________
global_average_pooling1d_10  (None, 32)                0         
_________________________________________________________________
dense_20 (Dense)             (None, 64)                2112      
_________________________________________________________________
dense_21 (Dense)             (None, 1)                 65        
=================================================================
Total params: 322,177
Trainable params: 322,177
Non-trainable params: 0
_________________________________________________________________

由此可以看到,虽然masking层没有参数,但是它确实作为一个网络层包含进了网络。

3. 小结

在这节课之中,我们学习了如何进行Padding,也学习到了如何设置Mask。这两种操作互相结合可以使得我们的处理序列的模型的精确度和准确率有很大的提升。

图片描述