1.为什么要使用mask
当我们处理一些序列任务时,一个batch中输入的序列可能是不等长的,这个时候我们一般会对序列进行padding处理,一般填充值为0,但是这个进而又会导致一些问题。
- 进行特征提取时的最大池化与平均值池化计算出现偏差
- 梯度回归时可能会因为计算了填充值,而导致算法准确度下降
- softmax计算概率时填充部分会分一部分概率,导致真正有意义的部分概率和不为1
因为这些存在的问题,所有我们采用mask矩阵来进行处理。
2.如何使用mask
比如一个batch的输入是
[ [1, 2, 3], [1, 2, 3, 4, 5], [-1, -2, -3, -4]]
如上这个batch的seq_length是5,所以要将其他长度不为5的序列进行填充,填充后为:
batch = [ [1, 2, 3, 0, 0], [1, 2, 3, 4, 5], [-1, -2, -3, -4, 0]]
mask数组是用来标记是否是有意义的数值,有意义就是1,填充值就是0,上面这个数组的mask矩阵是:
mask = [[1, 1, 1, 0, 0],[1, 1, 1, 1, 1],[1, 1, 1, 1, 0]]
- max-pooling计算:
未填充:value = [3, 5, -1]
填充后:value = [3, 5, 0] (此处第三个序列的最大池化的值已受填充值变化)
填充后+mask: value = max(batch - (1-mask)*10^10) = [3, 5, -1]
此处处理是将填充值处对应的值处理到极小,即-10^10,这样就几乎不可能对最大池化的结果造成影响 - mean-pooling计算:
未填充:value = [2, 3, -2.5]
填充后:value = [1.2, 3, -2] (进行填充的序列的平均池化值都变化了)
填充后+mask: value = sum(batch)/sum(mask)
此处处理在计算分母时,直接将mask矩阵的和作为分母,因为padding值对应的为0,所以就是有意义的值的数量 - softmax计算:
假设mask为:
[[1, 1, 1, 1, 0], [1, 1 , 1, 1, 1], [1, 1, 1 ,0, 0]]
全连接层的输出为:
output = [[ln2, ln3, ln4, ln5, ln6], [ln3, ln4, ln5, ln6, ln7], [ln4, ln5, ln6, ln7, ln8]]
填充后:value = [[2/20, 3/20, 4/20, 5/20, 6/20], [3/25, 4/25, 5/25, 6/25, 7/25], [4/30, 5/30, 6/30, 7/30, 8/30]]
从中可以看出第一个和第三个序列的有意义的值对应的概率之和不为1
因此使用mask处理: value = softmax(output - (1-mask)*10^10) = [[2/14, 3/14, 4/14, 5/14, 0], [3/25, 4/25, 5/25, 6/25, 7/25], [4/15, 5/15, 6/15, 0, 0]]
此处处理将填充值处对应的值变得很小,因此对应exp(x)的值接近于0,在经过softmax处理后,填充值处对应的概率值接近为0 - 梯度回归
此处有一些函数可以实现不对填充值对应的值进行更新,不是很了解其中实现