这文章是理解ResNet神经网络,并且使用简单使用python实战ResNet的残差块以及其他部分。
ResNet残差神经网络
正如之前文章所提到,普通的神经网络,如果超过很多层,比如超过25层之后,模型的accuracy将会下降,按理说,神经网络越深,模型的效果应该越好,但是现实却是相反的情况。这可能是因为梯度消失问题所导致的。 于是有人提出残差块,在某一层中间插入多个残差块,便可以训练超过2000层的神经网络,而且效果越来越好。ResNet网络的提出,使得我们可以训练很深的神经网络,并取得不错的效果。
2015年,ResNet首次被提出,把经过卷积之后的features map和input相加得到新的部分。便是残差块。左图是首次提出的残差块图。2015年的残差块的x是先跟weights相乘之后,再做BN归一化和relu激活。2016年 微软提出新的残差块,对输入input-x提前处理,经过BN归一化和relu 激活之后,再跟weight相乘。
现在大多人都用微软proposed后的残差块(pre-trained activation),即右图。我们的实战也会是用右图的残差块。
使用python实现残差块
def Unit(x,filters):
‘’‘
param x - 输入
param filters - 特征数量
’‘’
res = x
#第一层
out = BatchNormalization()(x) #先对x做BN归一化处理
out = Activation("relu")(out) #再激活x,然后做卷积
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
#第二层,跟第一层同理处理
out = BatchNormalization()(out)
out = Activation("relu")(out)
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
#最后跟res-即原来的input x相加
out = keras.layers.add([res,out])
return out
但如果我们想加入pooling,那么经过pool之后的x和res的维度就不一样,无法相加。所以我们需要对res也做维度处理。使得两者维度相同。
加载packages
import keras
from keras.datasets import cifar10
from keras.layers import Dense,Conv2D,MaxPooling2D,Flatten,AveragePooling2D,Dropout,BatchNormalization,Activation
from keras.models import Model,Input
from keras.optimizers import Adam
from keras.callbacks import LearningRateScheduler
from keras.callbacks import ModelCheckpoint
from math import ceil
import os
from keras.preprocessing.image import ImageDataGenerator
def Unit(x,filters,pool=False):
‘’‘
param x - 输入
param filters - 特征数量
param pool - False or True 表示是否要pooling
’‘’
res = x
if pool:
x = MaxPooling2D(pool_size=(2, 2))(x)
#困惑点-尽管padding = ‘same’,由于strides是(2,2),所以出来的宽度和长度是 W/strides
res = Conv2D(filters=filters,kernel_size=[1,1],strides=(2,2),padding="same")(res)
#由于strides=2 所以形状缩小了一倍
out = BatchNormalization()(x)
out = Activation("relu")(out)
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
out = BatchNormalization()(out)
out = Activation("relu")(out)
out = Conv2D(filters=filters, kernel_size=[3, 3], strides=[1, 1], padding="same")(out)
out = keras.layers.add([res,out])
return out
现在把完善好的残差块组合入模型里面
手写一个小模型
def MiniModel(input_shape):
‘’‘
param input_shape: 输入的shape
’‘’
images = Input(input_shape) #表示接受的image的shape将会是input_shape
#<tf.Tensor 'input_2:0' shape=(None, 32, 32, 3) dtype=float32>
#开始带入网络
#第一层卷积
net = Conv2D(filters=32, kernel_size=[3, 3], strides=[1, 1], padding="same")(images)
#然后放入残差块,stacking堆积三次
net = Unit(net,32) #32代表filters数量
net = Unit(net,32)
net = Unit(net,32)
#再次堆积 但输入和输出的维度将会减少一倍when pool = True
net = Unit(net,64,pool=True)
net = Unit(net,64)
net = Unit(net,64)
#再次堆积
net = Unit(net,128,pool=True)
net = Unit(net,128)
net = Unit(net,128)
#再次堆积
net = Unit(net, 256,pool=True)
net = Unit(net, 256)
net = Unit(net, 256)
############# 残差块 堆积结束
#下一层进行BN处理
net = BatchNormalization()(net)
#激活
net = Activation("relu")(net)
#随机丢掉25%的神经元
net = Dropout(0.25)(net)
#进行average pooling 把形状缩小为原来1/4
net = AveragePooling2D(pool_size=(4,4))(net)
############# 卷积结束
#现在开始铺平,并开始预测y
#铺平卷积网络
net = Flatten()(net)
#用softmax预测 数量为10
net = Dense(units=10,activation="softmax")(net)
############# 预测结束,把网络放入tf的Model里面
model = Model(inputs=images,outputs=net)
return model
模型我们已经手写好了,现在开始我们要准备数据,然后开始进行训练。
数据准备 – 使用cifar10数据集
#(1)加载 cifar10 dataset
(train_x, train_y) , (test_x, test_y) = cifar10.load_data()
#(2)数据normalize化,并把数据类型改为float32
train_x = train_x.astype('float32') / 255
test_x = test_x.astype('float32') / 255
#(3)减去平均值
train_x = train_x - train_x.mean()
test_x = test_x - test_x.mean()
#(4)除 std值
train_x = train_x / train_x.std(axis=0)
test_x = test_x / test_x.std(axis=0)
#(5)ImageDataGenerator()是keras.preprocessing.image模块中的图片生成器,
#同时也可以在batch中对数据进行增强,扩充数据集大小,这里我们是对data进行了水平翻转和角度转移
datagen = ImageDataGenerator(rotation_range=10,
width_shift_range=5. / 32,
height_shift_range=5. / 32,
horizontal_flip=True)
#把train data放入图片生成器 ,只对train data进行生成更多的图片,对 test data 不做处理
datagen.fit(train_x)
#########################对x的处理结束
#现在对Y做处理,把Y变成vector形式
#Encode the labels to vectors
train_y = keras.utils.to_categorical(train_y,10)
test_y = keras.utils.to_categorical(test_y,10)
#即Y变成了N个[0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]形式的vector
开始训练模型
input_shape = (32,32,3)
model = MiniModel(input_shape)
#compile模型
model.compile(optimizer=Adam(0.001),loss="categorical_crossentropy",metrics=["accuracy"])
#batch_size确定每个小批次中的样品数量。它的最大值是所有样本的数量
epochs = 50 #参数epoch决定网络中所有图像的训练次数
steps_per_epoch = ceil(50000/128) #由样本数量/batch size
#放入训练过程的模型中
model.fit_generator(datagen.flow(train_x, train_y, batch_size=128),
validation_data=[test_x,test_y],
epochs=epochs,steps_per_epoch=steps_per_epoch, verbose=1, workers=4)
训练结束
用test data评估
accuracy = model.evaluate(x=test_x,y=test_y,batch_size=128)
model.save("cifar10model.h5")