在batchsize128的情况下,变化ramp-up,看最佳的ramp-upepoch是多少。
ramp-up | 20 | 50 | 80 |
---|---|---|---|
FRN | 91.74% | 92.30% | 92.28% |
感觉最优的rampup应在50~80个epoch之间,暂用50.
0:后续实验计划
-
cifar100上FRN效果太差,复现的结果有问题。需要更正。
-
测试3 的方法究竟有什么好处?更快的收敛究竟意味着什么?
接下来的实验要从三方面入手:
1:该方法是否稳定?
2:该方法究竟有什么好处?
3:该方法能够进一步优化?
针对第一点,比较好的方法就是多个数据集检验。鉴于目前算力,只在svhn和cifar100上跑一下就可以,应该足够说明问题。
针对第二点,因为FRN复现有问题(cifar100上结果不对,同时也找不到更好的调参方式),同时,如果该方法对BN,LN,WN等方式都有好处,将会有更大的意义。所以先不再以FRN为基础,而是转向BN,LN,WN。从BN开始。目前FRN面临的问题就是训练困难。需要采用warmup和cosine形式的lr下降方式才能work。这种新的训练方式能否降低对这些技巧的依赖,使得该方法更加普适?
针对第三点,这种训练方式也许说明了方差不那么重要。能否用其他的东西来代替方差?另外,这种方法对BN有没有效果?
1:修正
1.1 Max_Min_FRN
在ramp-up 50的情况下,变化batchsize,看各种算法的accuracy。
Image per GPU | 128 | 32 |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
No_sigma_FRN_V1 | 91.51% | 91.50% |
No_sigma_FRN_V2 | 91.71% | 91.71% |
三种layer的公式如下:
-
-
-
-
-
从上面表格可以看出, -
FRN在两种batchsize中表现一致。这不仅依赖于FRNlayer的设计(与batch无关,且无需计算mean),也与FRN的lr设置相关。lr的初始值是,是一个与batchsize相关的量,如果没有这个设计,小batchsize也会使得FRN性能变差。
-
FRN在Images per GPU 128的情况下,是差于BN的。但是paper里面最大的Images per GPU是32,而在Images per GPU 32的情况下,FRN性能优于BN。所以这个结果是与paper里面结果一致的。
-
Max_Min_FRN效果较FRN差距比较大。但是在小batchsize下,与BN基本持平。与FRN差距较大的原因,初步分析,是由于
-
非常有意思的点是,当我们把方差置为1时,效果仍然不错。而且与关系不大。同时,我们发现这种训练在前期收敛速度特别快,远远超过正常的FRN。这是一个很好的性质。只是在后期,他的精度无法达到FRN的级别。
1.2:对的一种估计
有高斯分布性质,大体可以认为,,这里的指在H,W两个维度上算出的均值方差。
若方差用代替,均值用代替,可以降低运算量。
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
1.3:用可学习变量代替方差
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.40% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 | |
Learnable_V4 | 不收敛,效果很差 |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 | |
Learnable_V4 | 不收敛,效果很差 | |
Learnable_V5 | 90.24% |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 | |
Learnable_V4 | 不收敛,效果很差 | |
Learnable_V5 | 90.55% | |
Learnable_V6 | 90.50% |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 | |
Learnable_V4 | 不收敛,效果很差 | |
Learnable_V5 | 90.55% | |
Learnable_V6 | 90.50% | |
Learnable_V7 | 效果很差 |
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max_Min_FRN | 91.25% | 91.25% |
Max_Min_FRN _V2 | 91.80% | 91.81% |
Learnable_V1 | 90.55% | |
Learnable_V2 | 不收敛,效果很差 | |
Learnable_V3 | 不收敛,效果很差 | |
Learnable_V4 | 不收敛,效果很差 | |
Learnable_V5 | 90.55% | |
Learnable_V6 | 90.50% | |
Learnable_V7 | 效果很差 |
1.4:借用二阶矩导数
其对应的导数如下:
我们将替换成比较容易得到的值,但是保留导数的计算方法,看效果
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
FRN | 92.30% | 92.31% |
效果很差,收敛慢 | ||
效果很差,收敛慢,但是比好 | ||
与差不多,但是比好 | ||
90.3% | ||
90.7% | ||
88% |
分析与的差别,猜测是因为v1.1距离二阶矩更近。接下来做两种验证。第一种是<但是>。第二种是>
由,需要验证该值是偏大还是偏小还是有时偏大有时偏小。假设偏小,与之比较。如果性能优于,则应该继续增大该值;
由实验结果,尝试继续增大该值或减小该值。比大,但是效果比较差。所以尝试一个比小同时比大的值
并没有发现一个比较好的参数形式,能够达到FRN的精度。
1.5 引入lr参量。
这个公式不知怎么回事,会出现NAN。
- 测试1:
由于的时候训练收敛极快,所以epoch < warm_up 时,就用,epoch>warm_up时,
这个训练结果略差于FRN。 - 测试2:
1:时,
2:时,
3:时,
测试结果有好有坏。好处在于当epoch/total_epoch<end时,收敛是非常快的,效果非常好。最优结果达到了92.13%,远超同期的FRN训练效果。但是当进入第三阶段时,性能没有再进一步提升。
推测是由于max函数中的1导致的。因为训练后期,方差会收缩到1以下,FRN会适当的放大该层的值,从而在后面的训练中能够进一步优化该值。
- 测试3
1:时,
2:时,
3:时,
与测试2相比,进入第三阶段后,会放大。
经过实验测试,本方法会比测试2稳定很多,在第三阶段仍然能够出现一定的上涨,稳定的收敛到92.24%。 - 测试4
基于测试2的结果,将max中的1从常数变成逐渐缩小的。
1:时,
2:时,
3:时,
这个实验效果很差,无论是收敛速度还是最终效果都比较差,只有91.75%。但是比较有意思的是,当训练的最后阶段,max值停止变动时,训练效果可以稳定提升。推测出现这种情况的原因时,max不能太小,或者是max变动会导致训练不稳定 - 总结
测试3 的方法究竟有什么好处?更快的收敛究竟意味着什么?
接下来的实验要从三方面入手:
1:该方法是否稳定?
2:该方法究竟有什么好处?
3:该方法能够进一步优化?
针对第一点,比较好的方法就是多个数据集检验。鉴于目前算力,只在svhn和cifar100上跑一下就可以,应该足够说明问题。
针对第二点,目前FRN面临的问题就是训练困难。需要采用warmup和cosine形式的lr下降方式才能work。这种新的训练方式能否降低对这些技巧的依赖,使得该方法更加普适?
针对第三点,这种训练方式也许说明了方差不那么重要。能否用其他的东西来代替方差?另外,这种方法对BN有没有效果?
2:max函数导数修正
接下来,基于最初的FRN版本
尝试对其导数进行修改。因为上式有一半的时间是对tau不进行梯度更新的。借鉴RCF的技术,将tau的梯度为0时的情况变成梯度不为0.
2.1:Max导数修正V1
所以,
这样做的效果不达预期
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% |
2.2:Max导数修正V2
这样做并没有取得好的效果
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% | |
Max导数修正V2 | 92.02% |
2.3:Max导数修正V3
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% | |
Max导数修正V2 | 92.02% | |
Max导数修正V3 | 91.47% |
2.4:Max导数修正V4
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% | |
Max导数修正V2 | 92.02% | |
Max导数修正V3 | 91.47% | |
Max导数修正V4 | 92.13% |
2.5:Max导数修正V5
首先修改正向传播值
这样做,的作用会非常类似于mean。以各个channel的mean为基准修正,以期获得更快的收敛速度
导数的计算方式与V3一样。
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% | |
Max导数修正V2 | 92.02% | |
Max导数修正V3 | 91.47% | |
Max导数修正V4 | 92.13% | |
Max导数修正V5 | 89.35% |
2.6:Max导数修正V6
首先修改正向传播值
这样做,的作用会非常类似于mean。以各个channel的mean为基准修正,以期获得更快的收敛速度
导数与V5的区别是,
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
Max导数修正V1 | 90.49% | |
Max导数修正V2 | 92.02% | |
Max导数修正V3 | 91.47% | |
Max导数修正V4 | 92.13% | |
Max导数修正V5 | 89.35% | |
Max导数修正V6 | 91.00% |
3:BN,LN,GN上对lr方法的测试
经过实验发现,在BN,LN上应用lr测试3的方法,并不work。BN会大大降低训练速度,LN的数值会急剧增大,出现nan值。GN上没有测试,估计也不行。
4:warm up 和cosine decay是必须的
经过实验测试,在没有warm up的时候,FRN是不收敛的,lr测试3的方法也是,所以warm up对新方法来说,仍然是必须的
同时,cosine decay也是必须的
一些尝试
FRN中除以后,对x导数有很大影响。
那么,如果认为只是一个常数,与x无关,那么上式可以写成
经过实验发现,准确率出现很大的降低。
Image per GPU | 128 | 32(batchsize:64,GPU:2) |
---|---|---|
BN | 93.01% | 92.01% |
FRN | 92.30% | 92.31% |
No_sigma_FRN_V1 | 91.51% | 91.50% |
No_sigma_FRN_V2 | 91.71% | 91.71% |
No_grad_FRN | 91.0% |
如果没有导数,甚至还不如直接不除。
4:代码单元测试部分
先检查模块的正确性,再做实验
FRN单元测试
比较了paper中tensorflow版本的输出和自己实现的pytorch版本的输出。
import tensorflow as tf
tf.__version__
def FRNLayer(x, tau, beta, gamma, eps=1e-6):
nu2 = tf.reduce_mean(tf.square(x), axis=[1,2], keepdims=True)
x = x / tf.sqrt(nu2 + tf.abs(eps))
# return tf.maximum(gamma * x + beta, tau)
return tf.maximum(gamma * x + beta,tau)
x = tf.reshape(tf.range(2,34.),[1,4,4,2])
print(x[0,:,:,0])
gamma = tf.Variable(tf.ones([1,1,1,2]))
beta = tf.Variable(tf.zeros([1,1,1,2]))
tau = tf.Variable(tf.zeros([1,1,1,2]))
with tf.GradientTape() as g:
g.watch(x)
y = FRNLayer(x,tau,beta,gamma)
loss = tf.reduce_sum(y)
print('output:',y[0,:,:,0])
print('loss:',loss)
# dl_dgamma = g.gradient(loss,gamma)
# print(dl_dgamma)
# dl_dbeta = g.gradient(loss,beta)
# print(dl_dbeta)
dl_dtau = g.gradient(loss,tau)
print(dl_dtau)
class FilterResponseNormalization(nn.Module):
def __init__(self, num_features, eps=1e-6):
"""
Input Variables:
----------------
beta, gamma, tau: Variables of shape [1, C, 1, 1].
eps: A scalar constant or learnable variable.
"""
super(FilterResponseNormalization, self).__init__()
self.beta = nn.parameter.Parameter(
torch.Tensor(1, num_features, 1, 1), requires_grad=True)
self.gamma = nn.parameter.Parameter(
torch.Tensor(1, num_features, 1, 1), requires_grad=True)
self.tau = nn.parameter.Parameter(
torch.Tensor(1, num_features, 1, 1), requires_grad=True)
self.eps = nn.parameter.Parameter(torch.Tensor([eps]))
self.reset_parameters()
def reset_parameters(self):
nn.init.ones_(self.gamma)
nn.init.zeros_(self.beta)
nn.init.zeros_(self.tau)
def forward(self, x):
"""
Input Variables:
----------------
x: Input tensor of shape [NxCxHxW]
"""
n, c, h, w = x.shape
assert (self.gamma.shape[1], self.beta.shape[1], self.tau.shape[1]) == (c, c, c)
# Compute the mean norm of activations per channel
nu2 = x.pow(2).mean(dim=(2,3), keepdim=True)
# Perform FRN
x = x * torch.rsqrt(nu2 + torch.abs(self.eps))
# Return after applying the Offset-ReLU non-linearity
# return torch.max(self.gamma*x + self.beta, self.tau)
return torch.max(self.gamma * x + self.beta,self.tau)
x = torch.transpose(torch.transpose(torch.arange(2,34.).reshape([1,4,4,2]),1,3),2,3)
print(x[0,0,:,:])
frn = FilterResponseNormalization(2)
y = frn(x)
print('output:',y[0,0,:,:])
print("loss:",loss)
loss = torch.sum(y)
loss.backward()
print(frn.tau.grad)
从导数和输出值来看,pytorch版本的结果是没有问题的。
max_min_FRN单元测试
import torch
x = torch.arange(0,16,dtype=torch.float32).reshape(1,1,4,4)
n, c, h, w = x.shape
print(x.shape)
channel_max = torch.max(torch.max(x,dim=2,keepdim=True)[0],dim=3,keepdim=True)[0]
channel_min = torch.min(torch.min(x,dim=2,keepdim=True)[0],dim=3,keepdim=True)[0]
print(channel_max)
print(channel_min)
Cn = torch.log(torch.tensor(h * w + 0.000001)) * 2
print(Cn)
MyMax模块单元测试
import torch
class MyMax(torch.autograd.Function):
@staticmethod
def forward(self, x, tau):
self.save_for_backward(x, tau)
output = torch.max(x, tau)
return output
@staticmethod
def backward(self, grad_output):
x, tau = self.saved_tensors
dl_dx = grad_output.clone()
dl_dx[x < tau] = 0
dl_dtau = grad_output.clone()
# x_max = torch.max(torch.max(torch.max(x,dim=0,keepdim=True)[0],
# dim=2,keepdim=True)[0],dim=3,keepdim=True)[0]
# x_min = torch.min(torch.min(torch.min(x,dim=0,keepdim=True)[0],
# dim=2,keepdim=True)[0],dim=3,keepdim=True)[0]
# mu = (x_max + x_min)/2
# mu = x.mean([0,2,3],keepdim=True)
A = (x > tau).float() * (x / (tau + 1e-6)) * dl_dtau
print("A")
print(A)
# print("A",A.device)
# num = torch.sum(x > tau, dim=(0, 2, 3), keepdim=True).to(A.device)
# print("num:",num.device)
dl_dtau = dl_dtau*(x < tau).float() + A
# print(dl_dtau.shape)
# dl_dtau[x > tau] = x / (tau + 1e-6)
# dl_dtau = dl_dtau.mean([0, 2, 3], keepdim=True)
return dl_dx, dl_dtau
x = torch.nn.parameter.Parameter(torch.reshape(torch.range(1,8.),[2,2,2,1]))
print(x[0,0,:,:])
tau = torch.nn.parameter.Parameter(4 * torch.ones([1,2,1,1]))
max_ = MyMax.apply
y = max_(x, tau)
print("y",y.shape)
loss = torch.sum(y)
loss.backward()
print("tau:")
print(tau.grad)
print("x:")
print(x.grad[1,1,:,:])