resnet
pytorch 实现resnet系列, 数据集使用Dogs VS Cats
1. 数据集
Dogs Vs Cats是kaggle竞赛中经典的入门级数据集,训练集包含25000张图片,官方名称为train.zip
, 测试集包含张图片,官方名称为test1.zip
.数据分为两类:dog
和 cat
训练集
# 解压图片 user$ unzip train.zip
训练集的25000张图片解压后均在
train
目录下,命名方式为class.id.jpg
, 其中class
确定了该图片的类别, id是图片的唯一ID,例如:dog.151521.jpg
.测试集
# 解压图片 user$ unzip test1.zip
测试集解压后的所有图片均在
test1
目录下, 没有标签。命名方式为id.jpg
.
为了方便训练和做统计工作, 我们事先对图片的信息做一个统计。包括图片名称、路径、类别、长宽、均值和标准差。
root = '/mnt/data/codewang/dog_cat'
test_data = root + '/test1'
train_data = root + '/train'
def allImages(path):
info = pd.DataFrame(columns=['name', 'class', 'height', 'width', 'path'])
with tqdm(os.listdir(path)) as bar:
for file in bar:
if file.endswith('.jpg'):
file_path = path + '/' + file
img = Image.open(file_path)
info = info.append({
'name' : file,
'class' : file.split('.')[0],
'height' : img.height,
'width' : img.width,
'path' : file_path
}, ignore_index=True)
return info
统计图片的尺寸分布
import seaborn as sns; sns.set(style="white", color_codes=True)
# 带分布的散点关系图
# 有两张离群图,暂时不统计
plot_data = info[info['width'] < 600]
g = sns.jointplot(x='height', y='width', data=plot_data)
# dog cat占比
_ = sns.countplot(x="class", data=info)
统计均值和方差
def calc_mean(file_list):
mean = np.zeros(3)
for i, file in enumerate(file_list):
_mean = (cv2.imread(file)/255).mean(axis=(0,1))
mean+=_mean
print('%d / %d' % (i, len(file_list)), end='\r')
print('')
return mean / len(file_list)
def calc_var(file_list, mean):
var = np.zeros(3)
for i, file in enumerate(file_list):
_var = (((cv2.imread(file)/255) - mean)**2).mean(axis=(0,1))
var += _var
print('%d / %d' % (i, len(file_list)), end='\r')
print('')
return var / len(file_list)
mean = calc_mean(info['path'])
var = calc_var(info['path'], mean)
std = np.sqrt(var)
# outputs
# 24999 / 25000
# 24999 / 25000
#
# mean : array([0.41695606, 0.45508163, 0.48832284])
# var : array([0.06703861, 0.06574376, 0.06910507])
# std : array([0.25891816, 0.25640547, 0.26287844])
2. Resual Block
Resual Block的结构如下图所示,是由He kaiming在论文
Resnet在2015年ImangeNet的classification任务上获得了冠军.
可以看到与传统的卷积块的不同就是增加了一个shortcut
.He kaiming 经过试验发现随着网络的层数的增加网络的性能并没有像想象中那样随之增长, 因此他认为深层的网络并不是学习能力不强,而是没有学习好
Recent evidence reveals that network depth is of crucial importance, and leading results on the challenging ImageNet dataset all exploit "very deep" models, with a depth of sixteen to thirty. Many other non-trivial visual recognition tasks have also greatly benefited from very deep models.
Driven by the significance of depth, a questions arises: **Is learning better networks as easy as stacking more layers ?** An obstacle to answering this question was **the notorious problem of vanishing/exploding gradients**
障碍我回答这个问题的是:臭名昭著的梯度消失/爆炸问题。
This problem , however , has been largely addressed by normalization initialization and intermediated normalization layers , which enable networks with tens of layers to start converging(收敛) for stochastic gradient descent(SGD) with back-propagation.
但是梯度消失/爆炸的问题已经很大程度上被参数初始化方法(Xaver、He) 和 Batch Normalization Layers解决了。
with the network depth increasing, accuracy gets saturated (which might be unsurprising) and then degrades rapidly. such degradation is not caused by overfitting. And adding more layers to suitably deep model leads to highter training error.
随着网络层数的增加,正确率组件趋近于饱和,然后开始下降。并且增加网络的层数还导致训练误差增加,所以说导致正确率下降的原因不是过拟合。
Let us consider a shallower architecture and its deeper conterpart taht adds more layers onto it. There exists a solution by construction to the deeper model: the added layers are identity mapping, and the other layers are copied from the learned shallower model
我们假设一个浅层的网络,对其增加一些层使其变为深层的模型。为了还原它我么有一个从结构上下手的方法就是让他增加的层是恒等变换
总结下来就是resnet想要解决的并不是梯度消失和梯度爆炸问题,而是深层网络如何更好的学习。但是通过增加shortcut
可以使得网路自己选择多深。因为增加了shortcut
之后网络完全可以将这一层变为一个恒等变换。
我们从数学的角度来看看为什么resual block有效:
在没有shortcut
时,网络的每一层学习一个映射 , 增加了shortcut
的模型,每一层学习也是一个映射
也就是说增加了shortcut
后网络学习的是x到y的变换量,即残差,这样网络就对输入量的变化更加敏感,那么就能产生更有效的梯度。输入量本身就被归一化到了0~1, 其变化量本身就很小
3. ResNet
同样的为了减少参数量ResNet用到了1x1卷积,称为bottleneck
结构。除此之外相对于VGG-19,ResNet去掉了最后的Flatten层和连个全连接层,用GAP和一个1000分类的全连接层代替,以减少参数量。
其中resnet18和resnet34使用的是不同的resual块,由于resnet50既以上层数比较多,为了减少参数使用了bottlenect的块。
同时注意到基本的块相当于给vgg块加上一个shoutcut
,所以resnet18其实相当于给vgg16加了shourcut