ReLU神经网络:分段线性

Contents

  1. 1. ReLU神经网络是分段线性函数
  2. 2. 一阶导为“常数”
  3. 3. 高阶导为零
  4. 4. Grad-CAM++中的高阶导处理
  5. 5. 总结

ReLU神经网络是分段线性函数(Piecewise Linear)。

ReLU神经网络是分段线性函数

如果一个神经网络的非线性层(激活函数)都是一个分段线性函数(如ReLU)的话,那么这个网络在某个输入子集内,是一个线性函数。按复合函数的角度理解,每个复合的函数都是线性的,整个函数当然也是线性的。

但是由于激活层分段线性,且网络的特征表示维度很高,所以神经网络这个函数还是很复杂的,可以“假装”其是一个非线性的函数来用。

看似无法想象一个处处线性的函数具有如此强的拟合能力。其实其只是分段线性,就好像它不是一个球,但是它是一个n面体(n很大)一样。

一阶导为“常数”

ReLU激活的神经网络二阶导为零。

设神经网络为映射:

设有一输入和输出,在网络中如下传播:

则其对元素一阶导:

其中每项都是常数,但是对于不同的输入,其中激活层的导不同(虽然都是一堆0和1,ReLU的话)。

高阶导为零

由上,继续求二阶导:

其中,由于和线性部分的二阶导做了积,使其为零:

更高阶的导,于是也为零。

Grad-CAM++中的高阶导处理

Grad-CAM++中涉及到二阶三阶导。

由于ReLU神经网络(不包括最后的softmax、sigmoid激活)高阶导为零,而Grad-CAM++假设了函数是光滑函数,于是作者做了如下处理,定义了一个新的作为类别的得分:

由于是非线性的(处处),且其无穷阶可导,同时(在工程上)可以认为是“等价的”。

如果看Grad-CAM++的代码,可以发现,实现直接用了power函数代替了高阶求导操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def _get_weights(self, class_idx: int, scores: Tensor) -> Tensor:  # type: ignore[override]
"""Computes the weight coefficients of the hooked activation maps"""
"""https://github.com/frgfm/torch-cam/blob/master/torchcam/cams/gradcam.py"""
self.hook_g: Tensor
# Backpropagate
self._backprop(scores, class_idx)
# Alpha coefficient for each pixel
grad_2 = self.hook_g.pow(2)
grad_3 = grad_2 * self.hook_g
# Watch out for NaNs produced by underflow
denom = 2 * grad_2 + (grad_3 * self.hook_a).sum(dim=(2, 3), keepdim=True)
nan_mask = grad_2 > 0
alpha = grad_2
alpha[nan_mask].div_(denom[nan_mask])

# Apply pixel coefficient in each weight
return alpha.squeeze_(0).mul_(torch.relu(self.hook_g.squeeze(0))).sum(dim=(1, 2))

Grad-CAM++论文(最新版本)指出的,这是因为(是第层特征图):

一阶导:

二阶导:

注意:

同理:

总结

  • 用生物上神经元的视角看神经网络,还是有一些道理的。