如何计算测试评估期间 BatchNormalization 层中发生的乘法次数?

How to calculate the number of multiplications happening in BatchNormalization layer during test evaluation?

或者,为什么我的 CNN 的测试评估在使用 BatchNormalization 时比没有使用时要长得多?

我需要估算在测试集上评估训练有素的 CNN(使用带有 TF 后端的 Keras)的理论运行时间。因此,我试图计算评估期间发生的乘法运算的次数,以将其用作度量标准。

但出于某种原因,批归一化 (BN) 似乎对评估时间有重大影响,尽管在我的理解中在理论上并不相关。

我可以计算密集层和卷积层的乘法次数,我想我可以忽略激活函数和批量归一化的计算,因为两者都只为每个输入添加一个乘法,这明显少于卷积层做。

然而,当我在每个 ConvLayer 之后使用和不使用批归一化测试同一个网络时,我注意到我不能忽略它: 在下面给出的简单示例中,只有一个过滤器大小为 (3x3) 的 ConvLayer,在我进行分类时紧接着是一个 softmax 激活的密集层。 在转换层之后使用 BN,我需要大约 4.6 秒来完成测试集。 使用没有 BN 的其他完全相同的网络架构,相同的测试集处理时间减半。

使用 BN 的测试配置摘要(在 ~4.6 秒内完成测试集评估):

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 32, 32, 32)        896       
_________________________________________________________________
batch_normalization_1 (Batch (None, 32, 32, 32)        128       
_________________________________________________________________
flatten_1 (Flatten)          (None, 32768)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 43)                1409067   
=================================================================
Total params: 1,410,091
Trainable params: 1,410,027
Non-trainable params: 64                

没有 BN(在 ~2.3 秒内完成测试集评估):

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_2 (Conv2D)            (None, 32, 32, 32)        896       
_________________________________________________________________
flatten_2 (Flatten)          (None, 32768)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 43)                1409067   
=================================================================
Total params: 1,409,963
Trainable params: 1,409,963
Non-trainable params: 0     

我不知道这是如何扩展的,因为我一开始就不明白原因,但我可以说我已经用 3 到 6 个相同的 conv 层测试了其他网络(使用 padding = same 来保持维度常数),并且在大多数情况下,测试评估的差异似乎在 ~25% 到 ~50% 之间变化(下面给出的单层转换示例甚至有 ~100%)。

为什么 BN 有这么大的影响,换句话说,我错过了什么计算? 我想:BN 只是对每个输入加一次乘法。因此,例如在上面给出的 BN 网络中: 例如,我预计 batch_normalization_1 会增加 32*32*32 次乘法,而 conv2d_1 会增加 32*32*32*3*3 次乘法。

但是,即使 ConvLayers 添加了更多的乘法运算,这又如何对整体运行时间产生如此大的影响?

用于构建模型的代码:

model = Sequential()
model.add(Conv2D(32, (3, 3), activation="relu", input_shape=x_train.shape[1:], padding="same"))
model.add(BatchNormalization())
model.add(Flatten())
model.add(Dense(43, activation='softmax'))

其中 x_train.shape[1:](32, 32, 3),表示具有 RGB 颜色的 32x32 图像。

在这里回答我自己的问题,以防有人遇到同样的问题。

通过嵌入 Fritz AI 基准库 https://docs.fritz.ai/python-library/benchmark.html,我实际上可以检查每层的 flops 数量,事实证明,归一化只增加了可忽略的计算量。

----------------------------------------------------------------------------------------------------------------------
Layer (type)                                   Output Shape           MFLOPS     Weights       Core ML Compatible     
======================================================================================================================
conv2d_1 (Conv2D)                              [None, 32, 32, 32]     0.92       896           True          
----------------------------------------------------------------------------------------------------------------------
batch_normalization_1 (BatchNormalization)     [None, 32, 32, 32]     0.07       128           True          
----------------------------------------------------------------------------------------------------------------------
flatten_1 (Flatten)                            [None, 32768]          0.00       0             True          
----------------------------------------------------------------------------------------------------------------------
dense_1 (Dense)                                [None, 43]             2.82       1,409,067     True          
----------------------------------------------------------------------------------------------------------------------

也就是说,这个问题一定是由某些低效的例程或什至是 keras 中的错误引起的,无法使用批量归一化评估模型。很奇怪,但这是唯一可能的解释。