与传统的神经网络相比残差神经网络具有更好的深度网络构建能力,能避免因为网络层次过深而造成的梯度弥散和梯度爆炸。
通过在一个浅层网络基础上叠加y=x的层,可以让网络随深度增加而不退化。
残差学习的函数是F(x) = H(x) - x,这里如果F(x) =0,那么就是恒等映射。
resnet"short connections” 的在connection是在恒等映射的情况。
输入和输出的差别H(x)-x就是残差。
这个是通过shortcut connection 实现,通过shortcut将这个block的输入和输出进行一个
element-wise的加叠,这个简单的加法并不会给网络带来多大额外计算量,同时可以大大
增加模型的训练速度,提高训练效果,并且当模型的层数加深时,这个简单的结构能很好的解决退化问题。
a[l+2] 加上了 a[l]的残差块,即:残差网络中,直接将a[l]向后拷贝到神经网络的更深层,在ReLU非线性激活前面
加上a[l],a[l]的信息直接达到网络深层。使用残差块能够训练更深层的网络,构建一个ResNet网络就是通过将很多
这样的残差块堆积在一起,形成一个深度神经网络。
对于大型的网络,无论把残差块添加到神经网络的中间还是末端,都不会影响网络的表现。
可以提升网络效率。
可以看到普通直连的卷积神经网络最大区别在于,ResNet有一个shortcut结构,而传统卷积或多或少存在信息丢失的问题
。
在实际中关于成本考虑,既将两个3*3的卷积替换成1*1+3*3+1*1,如下图,新结构
中的中间3*3的卷积层在一个1*1降维,另一个1*1做还原,既保持精度又减少计算量。
如图结构:
定义残差
class ResNet:
def __init__(self,X_input,kernel_size,in_filter,out_filters,stride):
self.X=X_input
self.X_sortcut=X_input
self.stride=stride
f1,f2,f3=out_filters
self.conv_1=tf.Variable(tf.truncated_normal(shape=[1,1,in_filter,f1],stddev=0.1,mean=0,dtype=tf.float32))
self.conv_b1=tf.Variable(tf.zeros([f1]))
self.conv_2=tf.Variable(tf.truncated_normal(shape=[kernel_size,kernel_size,f1,f2],stddev=0.1,mean=0,dtype=tf.float32))
self.conv_b2=tf.Variable(tf.zeros([f2]))
self.conv_3=tf.Variable(tf.truncated_normal(shape=[1,1,f2,f3],stddev=0.1,mean=0,dtype=tf.float32))
self.conv_b3=tf.Variable(tf.zeros([f3]))
self.b_conv_fin = tf.Variable(tf.zeros([f3]))
def ResNetChoice(self,choice):
if(choice):
# frist
y = tf.nn.relu(tf.nn.conv2d(self.X,self.conv_1,strides=[1,1,1,1],padding="SAME")+self.conv_b1)
#second
y = tf.nn.relu(tf.nn.conv2d(y,self.conv_2,strides=[1,1,1,1],padding="SAME")+self.conv_b2)
#third
y = tf.nn.relu(tf.nn.conv2d(y,self.conv_3,strides=[1,1,1,1],padding="SAME")+self.conv_b3)
#final steap
add=tf.add(y,self.X_sortcut)
add_result = tf.nn.relu(add + self.b_conv_fin)
return add_result
else:
# frist
y = tf.nn.relu(tf.nn.conv2d(self.X, self.conv_1, strides=[1, self.stride,self.stride, 1], padding="SAME") + self.conv_b1)
# second
y = tf.nn.relu(tf.nn.conv2d(y, self.conv_2, strides=[1, 1, 1, 1], padding="SAME") + self.conv_b2)
# third
y = tf.nn.relu(tf.nn.conv2d(y, self.conv_3, strides=[1, 1, 1, 1], padding="SAME") + self.conv_b3)
# final steap
add = tf.nn.conv2d(self.X_sortcut,self.conv_3,strides=[1,1,1,1],padding="SAME")
add = tf.add(y,add)
add_result = tf.nn.relu(add+self.b_conv_fin)
return add_result
网络结构
class Net:
def __init__(self):
# 输入x(数据输入为图片的格式)
self.x=tf.placeholder(tf.float32,[None,28,28,1])
# 输入y(标签)
self.y=tf.placeholder(tf.float32,[None,10])
# ----------------------------卷积初始化--------------------------------
#卷积第一层
self.conv1_w=tf.Variable(tf.random_normal([3,3,1,16],dtype=tf.float32,stddev=0.1))
# 卷积第一层偏移
self.convb1=tf.Variable(tf.zeros([16]))
#--------------------------------全连接初始化---------------------------------------
# 第一层全连接w
self.W = tf.Variable(tf.random_normal([7*7*32,128],dtype=tf.float32,stddev=0.1))
# 第一层全连接b
self.B = tf.Variable(tf.zeros([128]))
# 第二层全连接w
self.W1 = tf.Variable(tf.random_normal([128,10],dtype=tf.float32,stddev=0.1))
# 第二层全连接b
self.B1 = tf.Variable(tf.zeros([10]))
def forward(self):
# ------------------------------卷积层-----------------------------------
# 卷积第一层实现
self.conv1=tf.nn.relu(tf.nn.conv2d(self.x,self.conv1_w,strides=[1,1,1,1],padding="SAME")+self.convb1)
# 第一层池化
self.pool1=tf.nn.max_pool(self.conv1,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
# 残差部分
Rt = ResNet(self.pool1, 3, 16, [8, 8, 16], 1)
x = Rt.ResNetChoice(True)
Rt1 = ResNet(x, 3, 16, [8, 8, 16], 1)
x = Rt1.ResNetChoice(True)
Rt2 = ResNet(x, 3, 16, [8, 8, 16], 1)
x = Rt2.ResNetChoice(True)
Rt3 = ResNet(x, 3, 16, [8, 8, 16], 1)
x = Rt3.ResNetChoice(True)
URt =ResNet(x,3,16,[16,16,32],1)
x = URt.ResNetChoice(False)
self.pool2=tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding="SAME")
# 均值tf.nn.avg_pool
# 归一化层tf.nn.batch_normalization
# 形状处理
self.flat = tf.reshape(self.pool2,[-1,7*7*32])
# ---------------------------------全链接层-------------------------------------------
self.y0 = tf.nn.relu(tf.matmul(self.flat,self.W)+self.B)
self.yo = tf.nn.softmax(tf.matmul(self.y0,self.W1)+self.B1)
def backword(self):
self.cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=self.yo, labels=self.y))
self.optimizer = tf.train.AdamOptimizer(0.003).minimize(self.cross_entropy)
训练:
if __name__ == '__main__':
net = Net()
net.forward()
net.backword()
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for i in range(100000):
xs,ys = mnist.train.next_batch(128)
cg=xs.reshape([128,28,28,1])
rs,loss,_=sess.run([net.acc,net.cross_entropy,net.optimizer],feed_dict={net.x:cg,net.y:ys})
残差神经网络在图像识别领域很有优势