论文:Why do deep convolutional networks generalize so poorly to small image transformations?
论文: https://arxiv.org/pdf/1805.12177v1.pdf [第一版]
论文: https://arxiv.org/pdf/1805.12177.pdf [第四版,内容上有稍微的不同]
Trick:
这个比Adobe家的文章出来的早,了解一下就行,最终也没有给出有效的解决方案
要点记录:
1:很多作者已经证明卷积架构并不具有不变性。【这里不是说conv-layer】
2:convolutional architecture不具有不变性的原因:忽略了经典的采样理论
3:数据增广并不能带来不变性的原因:cnn只对与训练集中典型数据相似的数据才有不变性
说实话:这个差异也太大了吧。。。
-----------------------------------------------------------------------------------------------------------------
要点记录:
1:图示轻微的输入变动会使得输出变化很大
-----------------------------------------------------------------------------------------------------------------
要点记录:
1:网络越深,平移性越差
思考启发:
1:人脸识别中112x112的人脸训练和测试都是对齐的,减少了输入的微小扰动
2:活体识别中也有采用对齐后的图像,降低了网络学习的难度,专注与真假的细节学习
-----------------------------------------------------------------------------------------------------------------
优化方案:
- 将下采样替换为:stride 1 maxpooling + stride 2 conv.也就是采样之前先blur,但是对于大数据效果甚微。
- data augmentation:多增加额外的augmentation。
- 减少二次采样操作:二次采样会导致可能不满足采样定律,所以减少二次采样可以保持不变性。
-----------------------------------------------------------------------------------------------------------------
参考:
[论文理解] Why do deep convolutional networks generalize so poorly to small image transformations? - aoru45 - 博客园
要点记录:
1:这篇博文里面对conv,pool的高低频率进行了可视化,值的学习和记录
以下文字引自原博主:
"""
上图是我做的三个实验,分别是原图、pooling后的图、conv2d(stride=4)后的图,下面是对应的频谱图。可以看出来,pooling、conv下采样层都是保留高频部分,丢弃低频部分。
可见,pooling层和conv层所做的下采样都是保留高频,丢弃低频成分。因此信号的频率都是高频,而采样频率是和stride相关的,可以理解为每隔多少像素采样一次,因此如果stride太大,采样频率就会过小,这样就不满足采样定理了。
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
import skimage.measure
import torch
import torch.nn as nn
import torch.nn.functional as F
class FFT(object):
def __init__(self):
pass
def __call__(self,x):
f = np.fft.fft2(x)
fshift = np.fft.fftshift(f)
s1 = np.log(np.abs(fshift))
return s1
@staticmethod
def pooling(feature_map,kernel_size= 2):
inpt = torch.tensor(feature_map[np.newaxis,np.newaxis,:,:],dtype = torch.float32)
out = F.max_pool2d(inpt,kernel_size = kernel_size)
return out.numpy()[0,0,:,:]
#return skimage.measure.block_reduce(feature_map, (kernel_size,kernel_size), np.max)
@staticmethod
def conv2d(feature_map,kernel_size= 2,stride = 1):
inpt = torch.tensor(feature_map[np.newaxis,np.newaxis,:,:],dtype = torch.float32)
kernel = torch.ones((1,1,kernel_size,kernel_size)) / kernel_size
out = F.conv2d(inpt,weight = kernel,stride = stride)
return out.numpy()[0,0,:,:]
if __name__ == "__main__":
fft = FFT()
img = cv2.imread('/home/xueaoru/图片/test002.jpg', 0)
img2 = fft.pooling(img,4)
img3 = fft.conv2d(img,kernel_size = 5,stride = 4)
s1 = fft(img)
s2 = fft(img2)
s3 = fft(img3)
plt.subplot(231)
plt.imshow(img, 'gray')
plt.title('original')
plt.subplot(232)
plt.imshow(img2, 'gray')
plt.title('original2')
plt.subplot(233)
plt.imshow(img3, 'gray')
plt.title('original3')
plt.subplot(234)
plt.imshow(s1,'gray')
plt.title('center1')
plt.subplot(235)
plt.imshow(s2,'gray')
plt.title('center2')
plt.subplot(236)
plt.imshow(s3,'gray')
plt.title('center3')
plt.show()



