最近复现了几个经典的卷积网络,拿自己的数据集试了了试,不得不承认的是卷积神经网络在图像处理方面确实有着得天独厚的优势,写这篇博客讲述早期最经典的卷积神经网络Alex_Net以及附上一些自己的理解
2012年,Hinton的学生在ILSVRC 2012中以显著的优势赢得了比赛,top-5的错误率降低至了16.4%,相比于第二名的26.2%有了巨大的提升,这可以说是神经网络在低谷期以后的第一次发声,确立了深度学习在计算机视觉领域的统治地位。AlexNet可以算是LeNet的一种更深更宽的版本,首次应用了ReLU、Dropout和LRN等trick,同时也使用了GPU进行加速运算。
AlexNet包含了6亿3000万个链接,6000万个参数和65万个神经元,拥有5个卷积层,其中3个卷积层后面有最大池化层,最后还有3个全链接层。本文的内容参考AlexNet的论文
ImageNet classification with deep convolutional neural networks
同时在文末用tensorflow代码对其进行复现
一、AlexNet的新技术
AlexNet主要用到了一下几个比较新的技术点
· 使用ReLU作为CNN的激活函数,并验证了其效果在较深的网络中超过了传统的sigmoid,论文中有下图
· 训练时使用了dropout随机忽略一部分神经元,这已经被证明是避免过拟合的有效手段
· 在CNN中使用重叠的最大池化,在此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊效果。并且,AlexNet中提出让步长比池化核的尺寸小,这样池化层之间会有重叠和覆盖,提升了特征的丰富性
·提出了Local Response Normalization(LRN)层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力
·利用GPU强大的并行计算能力,加速卷积网络的训练
·数据增强,随机的从256x256的原始图像中截取224x224大小的区域,为训练数据增加了2058倍的数量,使用数据增强可以大大减轻过拟合,提升泛化性能。进行预测时,则提取图片的四个角加中间五个位置,并进行左右翻转,一共获得10张图片,对他们进行预测并对10次结果求均值
·ALexNet论文中还提到了会对图像的RGB数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%
二、AlexNet模型结构
整个AlexNet有8个需要训练参数的层(不包括池化层和LRN层),前5层为卷积层,后三层为全链接层
我们可以发现在前面几个卷积层,虽然计算量很大,但参数量较小,都在1M左右甚至更小,只占网络总参数量很小的一部分,这就是卷积很有用的一个地方,可以通过较小的参数量提取有效的特征
三、AlexNet的Tensorflow实现
首先给出完整代码链接
https://github.com/LiangjunFeng/Alex_Net/blob/master/Alex_net.py
这里主要讲述AlexNet模型部分的代码,数据预处理部分请见上述链接,数据集较大,想要完整数据集的可以留言给我,邮件发送
import tensorflow as tf
def print_activations(t):
print(t.op.name,' ',t.get_shape().as_list()) #输出每层网络结构的函数
config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC'
my_graph = tf.Graph()
sess = tf.InteractiveSession(graph=my_graph,config=config) #创建tensorflow图与会话
with my_graph.as_default():
x = tf.placeholder(tf.float32,[None,image_size*image_size*3])
x = tf.reshape(x,[-1,image_size,image_size,3])
y = tf.placeholder(tf.float32,[None,190]) #为输入数据和标签创立占符
#conv1,第一个卷积层
c1_kernel = tf.get_variable('weights1',
shape=[11,11,3,64],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d()) #卷积核
c1_conv = tf.nn.conv2d(x,c1_kernel,[1,4,4,1],padding = 'SAME')
c1_biases = tf.Variable(tf.constant(0.0,shape=[64],dtype = tf.float32),trainable = True,name='biases')
c1_bias = tf.nn.bias_add(c1_conv,c1_biases) #加入偏置
conv1 = tf.nn.relu(c1_bias,name = 'conv1') #ReLU激活函数
print_activations(conv1) #输出网络结构
#pool1 最大池化层
pool1 = tf.nn.max_pool(conv1,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID',name='pool1')
print_activations(pool1)
#conv2 第二个卷积层
c2_kernel = tf.get_variable('weights2',
shape=[5,5,64,192],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c2_conv = tf.nn.conv2d(pool1,c2_kernel,[1,1,1,1],padding='SAME')
c2_biases = tf.Variable(tf.constant(0.0,shape=[192],dtype=tf.float32),trainable=True,name='biases')
c2_bias = tf.nn.bias_add(c2_conv,c2_biases)
conv2 = tf.nn.relu(c2_bias,name='conv2')
print_activations(conv2)
#pool2 第二个最大池化层
pool2 = tf.nn.max_pool(conv2,ksize=[1,3,3,1],strides=[1,2,2,1],padding='VALID',name='pool2')
print_activations(pool2)
#conv3 第三个卷积层
c3_kernel = tf.get_variable('weights3',
shape=[3,3,192,384],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c3_conv = tf.nn.conv2d(pool2,c3_kernel,[1,1,1,1],padding='SAME')
c3_biases = tf.Variable(tf.constant(0.0,shape=[384],dtype=tf.float32),trainable = True,name='biases')
c3_bias = tf.nn.bias_add(c3_conv,c3_biases)
conv3 = tf.nn.relu(c3_bias,name='conv3')
print_activations(conv3)
# conv4 第四个卷积层
c4_kernel = tf.get_variable('weights4',
shape=[3, 3, 384, 256],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c4_conv = tf.nn.conv2d(conv3, c4_kernel, [1, 1, 1, 1], padding='SAME')
c4_biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),trainable=True, name='biases')
c4_bias = tf.nn.bias_add(c4_conv, c4_biases)
conv4 = tf.nn.relu(c4_bias, name='conv4')
print_activations(conv4)
# conv5 第五个卷积层
c5_kernel = tf.get_variable('weights5',
shape=[3, 3, 256, 256],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d())
c5_conv = tf.nn.conv2d(conv4, c5_kernel, [1, 1, 1, 1], padding='SAME')
c5_biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),trainable=True, name='biases')
c5_bias = tf.nn.bias_add(c5_conv, c5_biases)
conv5 = tf.nn.relu(c5_bias, name='conv5')
print_activations(conv5)
# pool5 最大池化层
pool5 = tf.nn.max_pool(conv4,ksize=[1, 3, 3, 1],strides=[1, 2, 2, 1],padding='VALID',name='pool5')
print_activations(pool5)
# flat1 第一个全链接层
f1_weight = tf.get_variable('weights6',
shape=[6*6*256,4096],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
f1_biases = tf.Variable(tf.constant(0.1,shape=[4096]))
pool5_flat = tf.reshape(pool5,[-1,6*6*256])
flat1 = tf.nn.relu(tf.matmul(pool5_flat,f1_weight)+f1_biases,name = 'flat1')
print_activations(flat1)
# flat2 第二个全链接层
f2_weight = tf.get_variable('weights7',
shape=[4096,4096],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
f2_biases = tf.Variable(tf.constant(0.1,shape=[4096]))
flat2 = tf.nn.relu(tf.matmul(flat1,f2_weight)+f2_biases,name = 'flat2')
print_activations(flat2)
# output 输出层
op_weight = tf.get_variable('weights8',
shape=[4096,190],
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer()) #使用的数据集共190个类别,所以190个输出节点
op_biases = tf.Variable(tf.constant(0.1,shape=[190]))
y_conv = tf.nn.softmax(tf.matmul(flat2,op_weight)+op_biases,name = 'output')
print_activations(y_conv)
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y * tf.log(tf.clip_by_value(y_conv, 1e-10, 1.0)),reduction_indices=[1])) #定义损失函数
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #确定优化算法
correct_prediction = tf.equal(tf.arg_max(y_conv,1),tf.arg_max(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) #精度定义
sess.run(tf.global_variables_initializer())
for i in range(num_batches):
rand_index = np.random.choice(35221,size=(batch_size))
train_step.run(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})
if i%100 == 0: #每100个batch输出训练精度与loss
rand_index = np.random.choice(35221,size=(3000))
train_accuracy = accuracy.eval(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})
print('step %d, training accuracy %g'%(i,train_accuracy))
print('step %d, training accuracy %g'%(cross_entropy.eval(feed_dict={x:traindata[rand_index],y:trainlabel[rand_index]})))
print("test accuracy %g"%accuracy.eval(feed_dict={x:testdata,y:testlabel}))#输出测试精度