在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,:,:])