zoukankan      html  css  js  c++  java
  • AlexNet

    转自:https://blog.csdn.net/u012679707/article/details/80793916

    【深度学习】AlexNet原理解析及实现

        Alex提出的alexnet网络结构模型,在imagenet2012图像分类challenge上赢得了冠军。

        要研究CNN类型DL网络模型在图像分类上的应用,就逃不开研究alexnet,这是CNN在图像分类上的经典模型。

    一、Alexnet结构

    alexNet为8层深度网络,其中5层卷积层和3层全连接层,不计LRN层和池化层。如下图所示:

        

                                                                图 Alexnet结构

    详解各层训练参数的计算:

    前五层:卷积层


    后三层:全连接层

                        

    整体计算图:

      

    二、结构分析

            AlexNet每层的超参数如下图所示,其中输入尺寸为227*227,第一个卷积使用较大的核尺寸11*11,步长为4,有96个卷积核;紧接着一层LRN层;然后是最大池化层,核为3*3,步长为2。这之后的卷积层的核尺寸都比较小,5*5或3*3,并且步长为1,即扫描全图所有像素;而最大池化层依然为3*3,步长为2.

            我们可以发现,前几个卷积层的计算量很大,但参数量很小,只占Alexnet总参数的很小一部分。这就是卷积层的优点!通过较小的参数量来提取有效的特征。

            要注意,论文中指出,如果去掉任何一个卷积层,都会使网络的分类性能大幅下降。

                

    三、AlexNet的新技术点

        AlexNet的新技术点(即大牛论文的contribution),如下:

    (1)ReLU作为激活函数。

        ReLU为非饱和函数,论文中验证其效果在较深的网络超过了SIgmoid,成功解决了SIgmoid在网络较深时的梯度弥散问题

    (2)Dropout避免模型过拟合

        在训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。在alexnet的最后几个全连接层中使用了Dropout。

    (3)重叠的最大池化

        之前的CNN中普遍使用平均池化,而Alexnet全部使用最大池化,避免平均池化的模糊化效果。并且,池化的步长小于核尺寸,这样使得池化层的输出之间会有重叠和覆盖提升了特征的丰富性

    (4)提出LRN层

        提出LRN层,对局部神经元的活动创建竞争机制,使得响应较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

    (5)GPU加速

    (6)数据增强

        随机从256*256的原始图像中截取224*224大小的区域(以及水平翻转的镜像),相当于增强了(256-224)*(256-224)*2=2048倍的数据量。使用了数据增强后,减轻过拟合,提升泛化能力。避免因为原始数据量的大小使得参数众多的CNN陷入过拟合中。

    四、AlexNet的搭建

        利用tensorflow实现ALexNet,环境为:win10+anaconda+python3+CPU(本人仅利用CPU,未使用GPU加速,所以最终模型训练速度较慢)。

        利用tensorboard可视化ALexNet结构为:

                

    (1)首先看一下卷积层的搭建:带有LRN和池化层的卷积层

    1. with tf.name_scope('conv1') as scope:
    2. """
    3. images:227*227*3
    4. kernel: 11*11 *64
    5. stride:4*4
    6. padding:name
    7. #通过with tf.name_scope('conv1') as scope可以将scope内生成的Variable自动命名为conv1/xxx
    8. 便于区分不同卷积层的组建
    9. input: images[227*227*3]
    10. middle: conv1[55*55*96]
    11. output: pool1 [27*27*96]
    12. """
    13. kernel=tf.Variable(tf.truncated_normal([11,11,3,96],
    14. dtype=tf.float32,stddev=0.1),name="weights")
    15. conv=tf.nn.conv2d(images,kernel,[1,4,4,1],padding='SAME')
    16. biases=tf.Variable(tf.constant(0.0, shape=[96], dtype=tf.float32),
    17. trainable=True,name="biases")
    18. bias=tf.nn.bias_add(conv,biases) # w*x+b
    19. conv1=tf.nn.relu(bias,name=scope) # reLu
    20. print_architecture(conv1)
    21. parameters +=[kernel,biases]
    22. #添加LRN层和max_pool层
    23. """
    24. LRN会让前馈、反馈的速度大大降低(下降1/3),但最终效果不明显,所以只有ALEXNET用LRN,其他模型都放弃了
    25. """
    26. lrn1=tf.nn.lrn(conv1,depth_radius=4,bias=1,alpha=0.001/9,beta=0.75,name="lrn1")
    27. pool1=tf.nn.max_pool(lrn1,ksize=[1,3,3,1],strides=[1,2,2,1],
    28. padding="VALID",name="pool1")
    29. print_architecture(pool1)
    (2)卷积层的搭建:不带有LRN和池化层的卷积层
    1. with tf.name_scope('conv3') as scope:
    2. """
    3. input: pool2[13*13*256]
    4. output: conv3 [13*13*384]
    5. """
    6. kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 384],
    7. dtype=tf.float32, stddev=0.1), name="weights")
    8. conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')
    9. biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32),
    10. trainable=True, name="biases")
    11. bias = tf.nn.bias_add(conv, biases) # w*x+b
    12. conv3 = tf.nn.relu(bias, name=scope) # reLu
    13. parameters += [kernel, biases]
    14. print_architecture(conv3)

    3)全连接层的搭建

    1. #全连接层6
    2. with tf.name_scope('fc6') as scope:
    3. """
    4. input:pool5 [6*6*256]
    5. output:fc6 [4096]
    6. """
    7. kernel = tf.Variable(tf.truncated_normal([6*6*256,4096],
    8. dtype=tf.float32, stddev=0.1), name="weights")
    9. biases = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),
    10. trainable=True, name="biases")
    11. # 输入数据变换
    12. flat = tf.reshape(pool5, [-1, 6*6*256] ) # 整形成m*n,列n为7*7*64
    13. # 进行全连接操作
    14. fc = tf.nn.relu(tf.matmul(flat, kernel) + biases,name='fc6')
    15. # 防止过拟合 nn.dropout
    16. fc6 = tf.nn.dropout(fc, keep_prob)
    17. parameters += [kernel, biases]
    18. print_architecture(fc6)

    (4)训练测试:

        因未下载ImageNet数据集(太大),只是简单的测试了一下alexnet的性能。使用的是随机生成的图片来作为训练数据。

    1. def time_compute(session,target,info_string):
    2. num_step_burn_in=10 #预热轮数,头几轮迭代有显存加载、cache命中等问题可以因此跳过
    3. total_duration=0.0 #总时间
    4. total_duration_squared=0.0
    5. for i in range(num_batch+num_step_burn_in):
    6. start_time=time.time()
    7. _ = session.run(target)
    8. duration= time.time() -start_time
    9. if i>= num_step_burn_in:
    10. if i%10==0: #每迭代10次显示一次duration
    11. print("%s: step %d,duration=%.5f "% (datetime.now(),i-num_step_burn_in,duration))
    12. total_duration += duration
    13. total_duration_squared += duration *duration
    14. time_mean=total_duration /num_batch
    15. time_variance=total_duration_squared / num_batch - time_mean*time_mean
    16. time_stddev=math.sqrt(time_variance)
    17. #迭代完成,输出
    18. print("%s: %s across %d steps,%.3f +/- %.3f sec per batch "%
    19. (datetime.now(),info_string,num_batch,time_mean,time_stddev))
    20. def main():
    21. with tf.Graph().as_default():
    22. """仅使用随机图片数据 测试前馈和反馈计算的耗时"""
    23. image_size =224
    24. images=tf.Variable(tf.random_normal([batch_size,image_size,image_size,3],
    25. dtype=tf.float32,stddev=0.1 ) )
    26. fc8,parameters=inference(images)
    27. init=tf.global_variables_initializer()
    28. sess=tf.Session()
    29. sess.run(init)
    30. """
    31. AlexNet forward 计算的测评
    32. 传入的target:fc8(即最后一层的输出)
    33. 优化目标:loss
    34. 使用tf.gradients求相对于loss的所有模型参数的梯度
    35. AlexNet Backward 计算的测评
    36. target:grad
    37. """
    38. time_compute(sess,target=fc8,info_string="Forward")
    39. obj=tf.nn.l2_loss(fc8)
    40. grad=tf.gradients(obj,parameters)
    41. time_compute(sess,grad,"Forward-backward")

    (5)测试结果:

        结构输出   (注意,32是我设置的batch_size,即训练的图片数量为32)

                    

        前向预测用时:


        后向训练(学习)用时:


        可以看出后向训练用时比前向推理用时长很多,大概是5倍。


    【附录】完整代码

    1. # -*- coding:utf-8 -*-
    2. """
    3. @author:Lisa
    4. @file:alexNet.py
    5. @function:实现Alexnet深度模型
    6. @note:learn from《tensorflow实战》
    7. @time:2018/6/24 0024下午 5:26
    8. """
    9. import tensorflow as tf
    10. import time
    11. import math
    12. from datetime import datetime
    13. batch_size=32
    14. num_batch=100
    15. keep_prob=0.5
    16. def print_architecture(t):
    17. """print the architecture information of the network,include name and size"""
    18. print(t.op.name," ",t.get_shape().as_list())
    19. def inference(images):
    20. """ 构建网络 :5个conv+3个FC"""
    21. parameters=[] #储存参数
    22. with tf.name_scope('conv1') as scope:
    23. """
    24. images:227*227*3
    25. kernel: 11*11 *64
    26. stride:4*4
    27. padding:name
    28. #通过with tf.name_scope('conv1') as scope可以将scope内生成的Variable自动命名为conv1/xxx
    29. 便于区分不同卷积层的组建
    30. input: images[227*227*3]
    31. middle: conv1[55*55*96]
    32. output: pool1 [27*27*96]
    33. """
    34. kernel=tf.Variable(tf.truncated_normal([11,11,3,96],
    35. dtype=tf.float32,stddev=0.1),name="weights")
    36. conv=tf.nn.conv2d(images,kernel,[1,4,4,1],padding='SAME')
    37. biases=tf.Variable(tf.constant(0.0, shape=[96], dtype=tf.float32),
    38. trainable=True,name="biases")
    39. bias=tf.nn.bias_add(conv,biases) # w*x+b
    40. conv1=tf.nn.relu(bias,name=scope) # reLu
    41. print_architecture(conv1)
    42. parameters +=[kernel,biases]
    43. #添加LRN层和max_pool层
    44. """
    45. LRN会让前馈、反馈的速度大大降低(下降1/3),但最终效果不明显,所以只有ALEXNET用LRN,其他模型都放弃了
    46. """
    47. lrn1=tf.nn.lrn(conv1,depth_radius=4,bias=1,alpha=0.001/9,beta=0.75,name="lrn1")
    48. pool1=tf.nn.max_pool(lrn1,ksize=[1,3,3,1],strides=[1,2,2,1],
    49. padding="VALID",name="pool1")
    50. print_architecture(pool1)
    51. with tf.name_scope('conv2') as scope:
    52. """
    53. input: pool1[27*27*96]
    54. middle: conv2[27*27*256]
    55. output: pool2 [13*13*256]
    56. """
    57. kernel = tf.Variable(tf.truncated_normal([5, 5, 96, 256],
    58. dtype=tf.float32, stddev=0.1), name="weights")
    59. conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')
    60. biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),
    61. trainable=True, name="biases")
    62. bias = tf.nn.bias_add(conv, biases) # w*x+b
    63. conv2 = tf.nn.relu(bias, name=scope) # reLu
    64. parameters += [kernel, biases]
    65. # 添加LRN层和max_pool层
    66. """
    67. LRN会让前馈、反馈的速度大大降低(下降1/3),但最终效果不明显,所以只有ALEXNET用LRN,其他模型都放弃了
    68. """
    69. lrn2 = tf.nn.lrn(conv2, depth_radius=4, bias=1, alpha=0.001 / 9, beta=0.75, name="lrn1")
    70. pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
    71. padding="VALID", name="pool2")
    72. print_architecture(pool2)
    73. with tf.name_scope('conv3') as scope:
    74. """
    75. input: pool2[13*13*256]
    76. output: conv3 [13*13*384]
    77. """
    78. kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 384],
    79. dtype=tf.float32, stddev=0.1), name="weights")
    80. conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')
    81. biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32),
    82. trainable=True, name="biases")
    83. bias = tf.nn.bias_add(conv, biases) # w*x+b
    84. conv3 = tf.nn.relu(bias, name=scope) # reLu
    85. parameters += [kernel, biases]
    86. print_architecture(conv3)
    87. with tf.name_scope('conv4') as scope:
    88. """
    89. input: conv3[13*13*384]
    90. output: conv4 [13*13*384]
    91. """
    92. kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 384],
    93. dtype=tf.float32, stddev=0.1), name="weights")
    94. conv = tf.nn.conv2d(conv3, kernel, [1, 1, 1, 1], padding='SAME')
    95. biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32),
    96. trainable=True, name="biases")
    97. bias = tf.nn.bias_add(conv, biases) # w*x+b
    98. conv4 = tf.nn.relu(bias, name=scope) # reLu
    99. parameters += [kernel, biases]
    100. print_architecture(conv4)
    101. with tf.name_scope('conv5') as scope:
    102. """
    103. input: conv4[13*13*384]
    104. output: conv5 [6*6*256]
    105. """
    106. kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256],
    107. dtype=tf.float32, stddev=0.1), name="weights")
    108. conv = tf.nn.conv2d(conv4, kernel, [1, 1, 1, 1], padding='SAME')
    109. biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32),
    110. trainable=True, name="biases")
    111. bias = tf.nn.bias_add(conv, biases) # w*x+b
    112. conv5 = tf.nn.relu(bias, name=scope) # reLu
    113. pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
    114. padding="VALID", name="pool5")
    115. parameters += [kernel, biases]
    116. print_architecture(pool5)
    117. #全连接层6
    118. with tf.name_scope('fc6') as scope:
    119. """
    120. input:pool5 [6*6*256]
    121. output:fc6 [4096]
    122. """
    123. kernel = tf.Variable(tf.truncated_normal([6*6*256,4096],
    124. dtype=tf.float32, stddev=0.1), name="weights")
    125. biases = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),
    126. trainable=True, name="biases")
    127. # 输入数据变换
    128. flat = tf.reshape(pool5, [-1, 6*6*256] ) # 整形成m*n,列n为7*7*64
    129. # 进行全连接操作
    130. fc = tf.nn.relu(tf.matmul(flat, kernel) + biases,name='fc6')
    131. # 防止过拟合 nn.dropout
    132. fc6 = tf.nn.dropout(fc, keep_prob)
    133. parameters += [kernel, biases]
    134. print_architecture(fc6)
    135. # 全连接层7
    136. with tf.name_scope('fc7') as scope:
    137. """
    138. input:fc6 [4096]
    139. output:fc7 [4096]
    140. """
    141. kernel = tf.Variable(tf.truncated_normal([4096, 4096],
    142. dtype=tf.float32, stddev=0.1), name="weights")
    143. biases = tf.Variable(tf.constant(0.0, shape=[4096], dtype=tf.float32),
    144. trainable=True, name="biases")
    145. # 进行全连接操作
    146. fc = tf.nn.relu(tf.matmul(fc6, kernel) + biases, name='fc7')
    147. # 防止过拟合 nn.dropout
    148. fc7 = tf.nn.dropout(fc, keep_prob)
    149. parameters += [kernel, biases]
    150. print_architecture(fc7)
    151. # 全连接层8
    152. with tf.name_scope('fc8') as scope:
    153. """
    154. input:fc7 [4096]
    155. output:fc8 [1000]
    156. """
    157. kernel = tf.Variable(tf.truncated_normal([4096, 1000],
    158. dtype=tf.float32, stddev=0.1), name="weights")
    159. biases = tf.Variable(tf.constant(0.0, shape=[1000], dtype=tf.float32),
    160. trainable=True, name="biases")
    161. # 进行全连接操作
    162. fc8 = tf.nn.xw_plus_b(fc7, kernel, biases, name='fc8')
    163. parameters += [kernel, biases]
    164. print_architecture(fc8)
    165. return fc8,parameters
    166. def time_compute(session,target,info_string):
    167. num_step_burn_in=10 #预热轮数,头几轮迭代有显存加载、cache命中等问题可以因此跳过
    168. total_duration=0.0 #总时间
    169. total_duration_squared=0.0
    170. for i in range(num_batch+num_step_burn_in):
    171. start_time=time.time()
    172. _ = session.run(target)
    173. duration= time.time() -start_time
    174. if i>= num_step_burn_in:
    175. if i%10==0: #每迭代10次显示一次duration
    176. print("%s: step %d,duration=%.5f "% (datetime.now(),i-num_step_burn_in,duration))
    177. total_duration += duration
    178. total_duration_squared += duration *duration
    179. time_mean=total_duration /num_batch
    180. time_variance=total_duration_squared / num_batch - time_mean*time_mean
    181. time_stddev=math.sqrt(time_variance)
    182. #迭代完成,输出
    183. print("%s: %s across %d steps,%.3f +/- %.3f sec per batch "%
    184. (datetime.now(),info_string,num_batch,time_mean,time_stddev))
    185. def main():
    186. with tf.Graph().as_default():
    187. """仅使用随机图片数据 测试前馈和反馈计算的耗时"""
    188. image_size =224
    189. images=tf.Variable(tf.random_normal([batch_size,image_size,image_size,3],
    190. dtype=tf.float32,stddev=0.1 ) )
    191. fc8,parameters=inference(images)
    192. init=tf.global_variables_initializer()
    193. sess=tf.Session()
    194. sess.run(init)
    195. """
    196. AlexNet forward 计算的测评
    197. 传入的target:fc8(即最后一层的输出)
    198. 优化目标:loss
    199. 使用tf.gradients求相对于loss的所有模型参数的梯度
    200. AlexNet Backward 计算的测评
    201. target:grad
    202. """
    203. time_compute(sess,target=fc8,info_string="Forward")
    204. obj=tf.nn.l2_loss(fc8)
    205. grad=tf.gradients(obj,parameters)
    206. time_compute(sess,grad,"Forward-backward")
    207. if __name__=="__main__":
    208. main()
    ------------------------------------------------------         END      ----------------------------------------------------------

    参考:

    《tensorflow实战》黄文坚(本文内容及代码大多源于此书,感谢!)

    大牛论文《ImageNet Classification with Deep Convolutional Neural Networks Alex Krizhevsky

    [caffe]深度学习之图像分类模型AlexNet解读  https://blog.csdn.net/sunbaigui/article/details/39938097(参数分析很详细)



  • 相关阅读:
    Spark 内核架构+宽依赖与窄依赖+基于Yarn的两种提交模式
    Spark RDD高级编程:基于排序机制的wordcount程序+二次排序+topn
    Spark RDD持久化原理+共享变量原理(Broadcast Variable和Accumulator)
    Spark RDD工作原理详解+RDD JAVA API编程
    Spark 程序设计详解
    剑指offer 39.知识迁移能力 平衡二叉树
    剑指offer 38.知识迁移能力 二叉树的深度
    ElasticSearch 倒排索引原理+document写入流程+数据恢复
    剑指offer 37.知识迁移能力 数字在排序数组中出现的次数
    剑指offer 36.时间空间效率的平衡 两个链表的第一个公共结点
  • 原文地址:https://www.cnblogs.com/leebxo/p/10207320.html
Copyright © 2011-2022 走看看