• InceptionV3代码解析


    InceptionV3代码解析

    读了Google的GoogleNet以及InceptionV3的论文,决定把它实现一下,尽管很难,但是网上有不少资源,就一条一条的写完了,对于网络的解析都在代码里面了,是在原博主的基础上进行修改的,添加了更多的细节,以及自己的理解。总之,是更详细更啰嗦的一个版本,适合初学者。

      1 import tensorflow as tf
      2 from datetime import datetime
      3 import math
      4 import time
      5  
      6 ##参考tensorflow实战书籍+博客https://blog.csdn.net/superman_xxx/article/details/65451916,不过丰富了很多细节
      7 ##适合像我一样的初学者
      8 slim = tf.contrib.slim
      9 #Slim is an interface to contrib functions, examples and models.
     10 #只是一个接口作用
     11 trunc_normal = lambda stddev: tf.truncated_normal_initializer(0.0, stddev)
     12 #匿名函数 lambda x: x * x  实际上就是:返回x的平方
     13 # tf.truncated_normal_initializer产生截断的正态分布
     14  
     15 ########定义函数生成网络中经常用到的函数的默认参数########
     16 # 默认参数:卷积的激活函数、权重初始化方式、标准化器等
     17 def inception_v3_arg_scope(weight_decay=0.00004,  # 设置L2正则的weight_decay
     18                            stddev=0.1, # 标准差默认值0.1
     19                            batch_norm_var_collection='moving_vars'):
     20  
     21 # 定义batch normalization(批量标准化/归一化)的参数字典
     22   batch_norm_params = {
     23       'decay': 0.9997,  # 定义参数衰减系数
     24       'epsilon': 0.001,
     25       'updates_collections': tf.GraphKeys.UPDATE_OPS,
     26       'variables_collections': {
     27           'beta': None,
     28           'gamma': None,
     29           'moving_mean': [batch_norm_var_collection],
     30           'moving_variance': [batch_norm_var_collection],#值就是前面设置的batch_norm_var_collection='moving_vars'
     31       }
     32   }
     33  
     34 # 给函数的参数自动赋予某些默认值
     35 # slim.arg_scope常用于为tensorflow里的layer函数提供默认值以使构建模型的代码更加紧凑苗条(slim):
     36   with slim.arg_scope([slim.conv2d, slim.fully_connected],
     37                       weights_regularizer=slim.l2_regularizer(weight_decay)):
     38       # 对[slim.conv2d, slim.fully_connected]自动赋值,可以是列表或元组
     39   # 使用slim.arg_scope后就不需要每次都重复设置参数了,只需要在有修改时设置
     40     with slim.arg_scope( # 嵌套一个slim.arg_scope对卷积层生成函数slim.conv2d的几个参数赋予默认值
     41         [slim.conv2d],
     42         weights_initializer=trunc_normal(stddev), # 权重初始化器
     43         activation_fn=tf.nn.relu, # 激活函数
     44         normalizer_fn=slim.batch_norm, # 标准化器
     45         normalizer_params=batch_norm_params) as sc: # 标准化器的参数设置为前面定义的batch_norm_params
     46       return sc # 最后返回定义好的scope
     47  
     48 ########定义函数可以生成Inception V3网络的卷积部分########
     49 #########Inception V3架构见TENSORFLOW实战书-黄文坚 p124-p125页
     50 def inception_v3_base(inputs, scope=None):
     51   '''
     52   Args:
     53   inputs:输入的tensor
     54   scope:包含了函数默认参数的环境
     55   '''
     56   end_points = {} # 定义一个字典表保存某些关键节点供之后使用
     57  
     58   with tf.variable_scope(scope, 'InceptionV3', [inputs]):
     59     with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d], # 对三个参数设置默认值
     60                         stride=1, padding='VALID'):
     61       # 正式定义Inception V3的网络结构。首先是前面的非Inception Module的卷积层
     62       #输入图像尺寸299 x 299 x 3
     63       # slim.conv2d的第一个参数为输入的tensor,第二个是输出的通道数,卷积核尺寸,步长stride,padding模式
     64       net = slim.conv2d(inputs, 32, [3, 3], stride=2, scope='Conv2d_1a_3x3')
     65       # 输出尺寸149 x 149 x 32
     66       '''
     67       因为使用了slim以及slim.arg_scope,我们一行代码就可以定义好一个卷积层
     68       相比AlexNet使用好几行代码定义一个卷积层,或是VGGNet中专门写一个函数定义卷积层,都更加方便
     69       '''
     70       net = slim.conv2d(net, 32, [3, 3], scope='Conv2d_2a_3x3')
     71       # 输出尺寸147 x 147 x 32
     72       net = slim.conv2d(net, 64, [3, 3], padding='SAME', scope='Conv2d_2b_3x3')
     73       # 输出尺寸147 x 147 x 64
     74       net = slim.max_pool2d(net, [3, 3], stride=2, scope='MaxPool_3a_3x3')
     75       # 输出尺寸73 x 73 x 64
     76       net = slim.conv2d(net, 80, [1, 1], scope='Conv2d_3b_1x1')
     77       #输出尺寸 73 x 73 x 80.
     78       net = slim.conv2d(net, 192, [3, 3], scope='Conv2d_4a_3x3')
     79       #输出尺寸 71 x 71 x 192.
     80       net = slim.max_pool2d(net, [3, 3], stride=2, scope='MaxPool_5a_3x3')
     81       # 输出尺寸35 x 35 x 192.
     82  
     83     '''上面部分代码一共有5个卷积层,2个池化层,实现了对图片数据的尺寸压缩,并对图片特征进行了抽象
     84     有个疑问是框架例里给出的表格中是6个卷积和一个池化,并没有1x1的卷积,为什么要这么做,以及scope后面的名字为什么要这样叫。
     85     '''
     86  
     87     '''
     88     接下来就是三个连续的Inception模块组,三个Inception模块组中各自分别有多个Inception Module,这部分是Inception Module V3
     89     的精华所在。每个Inception模块组内部的几个Inception Mdoule结构非常相似,但是存在一些细节的不同
     90     '''
     91     # Inception blocks
     92     with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],  # 设置所有模块组的默认参数
     93                         stride=1, padding='SAME'):  # 将所有卷积层、最大池化、平均池化层步长都设置为1
     94         #注意这个模块已经统一指定了padding='SAME',后面不用再说明
     95         # 第一个模块组包含了三个结构类似的Inception Module
     96         # 第一个模块组第一个Inception Module,Mixed_5b
     97         with tf.variable_scope('Mixed_5b'):  # 第一个Inception Module名称。Inception Module有四个分支
     98             with tf.variable_scope('Branch_0'):  # 第一个分支64通道的1*1卷积
     99                 branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    100                 #输出尺寸35*35*64
    101             with tf.variable_scope('Branch_1'):  # 第二个分支48通道1*1卷积后一层链接一个64通道的5*5卷积
    102                 branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0a_1x1')
    103                 #输出尺寸35*35*48
    104                 branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5')
    105                 #输出尺寸35*35*64
    106             with tf.variable_scope('Branch_2'): #第三个分支64通道1*1卷积后一层链接2个96通道的5*5卷积
    107                 branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    108                 #输出尺寸35*35*64
    109                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
    110                 #输出尺寸35*35*96
    111                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
    112                 # 输出尺寸35*35*96
    113             with tf.variable_scope('Branch_3'):  # 第四个分支为3*3的平均池化后一层连接32通道的1*1卷积
    114                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    115                 # 输出尺寸35*35*192
    116                 branch_3 = slim.conv2d(branch_3, 32, [1, 1], scope='Conv2d_0b_1x1')
    117                 # 输出尺寸35*35*32
    118             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    119             # 将四个分支的输出合并在一起(第三个维度合并,即输出通道上合并)64+64+96+32=256个通道
    120             # 输出尺寸35*35*256
    121  
    122         # 第一个模块组第二个Inception Module 名称是:Mixed_5c
    123         with tf.variable_scope('Mixed_5c'):   #同样有4个分支,唯一不同的是第4个分支最后接的是64输出通道
    124             with tf.variable_scope('Branch_0'):
    125                 branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    126                 # 输出尺寸35*35*64
    127             with tf.variable_scope('Branch_1'):
    128                 branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0b_1x1')
    129                 # 输出尺寸35*35*48
    130                 branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv_1_0c_5x5')
    131                 # 输出尺寸35*35*64
    132             with tf.variable_scope('Branch_2'):
    133                 branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    134                 # 输出尺寸35*35*64
    135                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
    136                 # 输出尺寸35*35*96
    137                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
    138                 # 输出尺寸35*35*96
    139             with tf.variable_scope('Branch_3'):
    140                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    141                 # 输出尺寸35*35*192
    142                 branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
    143                 # 输出尺寸35*35*64
    144             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    145             # 将四个分支的输出合并在一起(第三个维度合并,即输出通道上合并)64+64+96+64=288个通道
    146             # 输出尺寸35*35*288
    147  
    148         # 第一个模块组第3个Inception Module 名称是:Mixed_5d
    149         with tf.variable_scope('Mixed_5d'):
    150             with tf.variable_scope('Branch_0'):
    151                 branch_0 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    152             with tf.variable_scope('Branch_1'):
    153                 branch_1 = slim.conv2d(net, 48, [1, 1], scope='Conv2d_0a_1x1')
    154                 branch_1 = slim.conv2d(branch_1, 64, [5, 5], scope='Conv2d_0b_5x5')
    155             with tf.variable_scope('Branch_2'):
    156                 branch_2 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    157                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0b_3x3')
    158                 branch_2 = slim.conv2d(branch_2, 96, [3, 3], scope='Conv2d_0c_3x3')
    159             with tf.variable_scope('Branch_3'):
    160                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    161                 branch_3 = slim.conv2d(branch_3, 64, [1, 1], scope='Conv2d_0b_1x1')
    162             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    163             # 将四个分支的输出合并在一起(第三个维度合并,即输出通道上合并)64+64+96+64=288个通道
    164             # 输出尺寸35*35*288
    165  
    166  
    167         # 第二个Inception模块组是一个非常大的模块组,包含了5个Inception Mdoule,2-5个Inception Mdoule结构非常相似
    168         #第二个模块组第一个Inception Module 名称是:Mixed_6a
    169         #输入是35*35*288
    170         with tf.variable_scope('Mixed_6a'):  #包含3个分支
    171             with tf.variable_scope('Branch_0'):
    172                 branch_0 = slim.conv2d(net, 384, [3, 3], stride=2,
    173                                        padding='VALID', scope='Conv2d_1a_1x1')
    174                 # padding='VALID'图片尺寸会被压缩,通道数增加
    175                 # 输出尺寸17*17*384
    176             with tf.variable_scope('Branch_1'): #64通道的1*1加2个96通道的3*3卷积
    177                 branch_1 = slim.conv2d(net, 64, [1, 1], scope='Conv2d_0a_1x1')
    178                 #输出尺寸35 * 35 * 64
    179                 branch_1 = slim.conv2d(branch_1, 96, [3, 3], scope='Conv2d_0b_3x3')
    180                 #输出尺寸35*35*96
    181                 branch_1 = slim.conv2d(branch_1, 96, [3, 3], stride=2,
    182                                        padding='VALID', scope='Conv2d_1a_1x1')
    183                 # 图片被压缩/输出尺寸17*17*96
    184             with tf.variable_scope('Branch_2'):
    185                 branch_2 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
    186                                            scope='MaxPool_1a_3x3')
    187                 #输出尺寸17 * 17 * 288(这里大家注意,书上还有很多博客上都是384+96+256=736并不是768,所以最后应该加288)
    188             net = tf.concat([branch_0, branch_1, branch_2], 3)
    189             # 输出尺寸定格在17 x 17 x 768
    190  
    191         # 第二个模块组第二个Inception Module 名称是:Mixed_6b
    192         with tf.variable_scope('Mixed_6b'):  #4个分支
    193             with tf.variable_scope('Branch_0'):
    194                 branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    195                 # 输出尺寸17*17*192
    196             with tf.variable_scope('Branch_1'):
    197                 branch_1 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
    198                 # 输出尺寸17 * 17 * 128
    199                 branch_1 = slim.conv2d(branch_1, 128, [1, 7],
    200                                        scope='Conv2d_0b_1x7')
    201                 # 输出尺寸17 * 17 * 128
    202                 # 串联1*7卷积和7*1卷积合成7*7卷积,减少了参数,减轻了过拟合
    203                 branch_1 = slim.conv2d(branch_1, 192, [7, 1], scope='Conv2d_0c_7x1')
    204                 # 输出尺寸17 * 17 * 192
    205             with tf.variable_scope('Branch_2'):
    206                 branch_2 = slim.conv2d(net, 128, [1, 1], scope='Conv2d_0a_1x1')
    207                 # 输出尺寸17 * 17 * 128
    208                 # 反复将7*7卷积拆分
    209                 branch_2 = slim.conv2d(branch_2, 128, [7, 1], scope='Conv2d_0b_7x1')
    210                 branch_2 = slim.conv2d(branch_2, 128, [1, 7], scope='Conv2d_0c_1x7')
    211                 branch_2 = slim.conv2d(branch_2, 128, [7, 1], scope='Conv2d_0d_7x1')
    212                 branch_2 = slim.conv2d(branch_2, 192, [1, 7], scope='Conv2d_0e_1x7')
    213                 # 这种方法算是利用Factorization into small convolutions 的典范
    214             with tf.variable_scope('Branch_3'):  #3*3的平均池化
    215                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    216                 # 输出尺寸17 * 17 * 768
    217                 branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    218                 # 输出尺寸17 * 17 * 192
    219             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    220             # 输出尺寸定格在17 x 17 x (192*4)=17*17*768
    221  
    222         # 第二个模块组第三个Inception Module 名称是:Mixed_6c
    223         # 同前面一个相似,第二个分支和第三个分支的前几层通道由120升为160
    224         with tf.variable_scope('Mixed_6c'):
    225             with tf.variable_scope('Branch_0'):
    226                 '''
    227                 我们的网络每经过一个inception module,即使输出尺寸不变,但是特征都相当于被重新精炼了一遍,
    228                 其中丰富的卷积和非线性化对提升网络性能帮助很大。
    229                 '''
    230                 branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    231             with tf.variable_scope('Branch_1'):
    232                 branch_1 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
    233                 branch_1 = slim.conv2d(branch_1, 160, [1, 7], scope='Conv2d_0b_1x7')
    234                 branch_1 = slim.conv2d(branch_1, 192, [7, 1], scope='Conv2d_0c_7x1')
    235             with tf.variable_scope('Branch_2'):
    236                 branch_2 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
    237                 branch_2 = slim.conv2d(branch_2, 160, [7, 1], scope='Conv2d_0b_7x1')
    238                 branch_2 = slim.conv2d(branch_2, 160, [1, 7], scope='Conv2d_0c_1x7')
    239                 branch_2 = slim.conv2d(branch_2, 160, [7, 1], scope='Conv2d_0d_7x1')
    240                 branch_2 = slim.conv2d(branch_2, 192, [1, 7], scope='Conv2d_0e_1x7')
    241             with tf.variable_scope('Branch_3'):
    242                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    243                 branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    244             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    245             # 输出尺寸定格在17 x 17 x (192*4)=17*17*768
    246  
    247         # 第二个模块组第四个Inception Module 名称是:Mixed_6d
    248         # 和前面一个完全一样,增加卷积和非线性,提炼特征
    249         with tf.variable_scope('Mixed_6d'):
    250             with tf.variable_scope('Branch_0'):
    251                 branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    252             with tf.variable_scope('Branch_1'):
    253                 branch_1 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
    254                 branch_1 = slim.conv2d(branch_1, 160, [1, 7], scope='Conv2d_0b_1x7')
    255                 branch_1 = slim.conv2d(branch_1, 192, [7, 1], scope='Conv2d_0c_7x1')
    256             with tf.variable_scope('Branch_2'):
    257                 branch_2 = slim.conv2d(net, 160, [1, 1], scope='Conv2d_0a_1x1')
    258                 branch_2 = slim.conv2d(branch_2, 160, [7, 1], scope='Conv2d_0b_7x1')
    259                 branch_2 = slim.conv2d(branch_2, 160, [1, 7], scope='Conv2d_0c_1x7')
    260                 branch_2 = slim.conv2d(branch_2, 160, [7, 1], scope='Conv2d_0d_7x1')
    261                 branch_2 = slim.conv2d(branch_2, 192, [1, 7], scope='Conv2d_0e_1x7')
    262             with tf.variable_scope('Branch_3'):
    263                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    264                 branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    265             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    266             # 输出尺寸定格在17 x 17 x (192*4)=17*17*768
    267  
    268         # 第二个模块组第五个Inception Module 名称是:Mixed_6e
    269         # 也同前面一样,通道数变为192,但是要将Mixed_6e存储在end_points中
    270         with tf.variable_scope('Mixed_6e'):
    271             with tf.variable_scope('Branch_0'):
    272                 branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    273             with tf.variable_scope('Branch_1'):
    274                 branch_1 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    275                 branch_1 = slim.conv2d(branch_1, 192, [1, 7], scope='Conv2d_0b_1x7')
    276                 branch_1 = slim.conv2d(branch_1, 192, [7, 1], scope='Conv2d_0c_7x1')
    277             with tf.variable_scope('Branch_2'):
    278                 branch_2 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    279                 branch_2 = slim.conv2d(branch_2, 192, [7, 1], scope='Conv2d_0b_7x1')
    280                 branch_2 = slim.conv2d(branch_2, 192, [1, 7], scope='Conv2d_0c_1x7')
    281                 branch_2 = slim.conv2d(branch_2, 192, [7, 1], scope='Conv2d_0d_7x1')
    282                 branch_2 = slim.conv2d(branch_2, 192, [1, 7], scope='Conv2d_0e_1x7')
    283             with tf.variable_scope('Branch_3'):
    284                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    285                 branch_3 = slim.conv2d(branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    286             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    287             # 输出尺寸定格在17 x 17 x (192*4)=17*17*768
    288             end_points['Mixed_6e'] = net
    289             # 将Mixed_6e存储于end_points中,作为Auxiliary Classifier辅助模型的分类
    290  
    291         # 第三个Inception模块包含了3个Inception Mdoule,后两个个Inception Mdoule结构非常相似
    292         # 第三个模块组第一个Inception Module 名称是:Mixed_7a
    293         with tf.variable_scope('Mixed_7a'):  # 3个分支
    294             with tf.variable_scope('Branch_0'):
    295                 branch_0 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    296                 # 输出尺寸17*17*192
    297                 branch_0 = slim.conv2d(branch_0, 320, [3, 3], stride=2,
    298                                        padding='VALID', scope='Conv2d_1a_3x3')
    299                 # 压缩图片# 输出尺寸8*8*320
    300             with tf.variable_scope('Branch_1'):
    301                 branch_1 = slim.conv2d(net, 192, [1, 1], scope='Conv2d_0a_1x1')
    302                 branch_1 = slim.conv2d(branch_1, 192, [1, 7], scope='Conv2d_0b_1x7')
    303                 branch_1 = slim.conv2d(branch_1, 192, [7, 1], scope='Conv2d_0c_7x1')
    304                 branch_1 = slim.conv2d(branch_1, 192, [3, 3], stride=2,
    305                                        padding='VALID', scope='Conv2d_1a_3x3')
    306                 # 输出尺寸8*8*192
    307             with tf.variable_scope('Branch_2'):  # 池化层不会对输出通道数产生改变
    308                 branch_2 = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID',
    309                                            scope='MaxPool_1a_3x3')
    310                 # 输出尺寸8*8*768
    311             net = tf.concat([branch_0, branch_1, branch_2], 3)
    312             # 输出图片尺寸被缩小,通道数增加,tensor的总size在持续下降中
    313             # 输出尺寸8*8*(320+192+768)=8*8*1280
    314  
    315         # 第三个模块组第二个Inception Module 名称是:Mixed_7b
    316         '''
    317         这个模块最大的区别是分支内又有分支,network in network in network
    318         '''
    319         with tf.variable_scope('Mixed_7b'): # 4 个分支
    320             with tf.variable_scope('Branch_0'):
    321                 branch_0 = slim.conv2d(net, 320, [1, 1], scope='Conv2d_0a_1x1')
    322                 # 输出尺寸8*8*320
    323             with tf.variable_scope('Branch_1'): #第二个分支里还有分支
    324                 branch_1 = slim.conv2d(net, 384, [1, 1], scope='Conv2d_0a_1x1')
    325                 # 输出尺寸8*8*384
    326                 branch_1 = tf.concat([
    327                     slim.conv2d(branch_1, 384, [1, 3], scope='Conv2d_0b_1x3'),
    328                     slim.conv2d(branch_1, 384, [3, 1], scope='Conv2d_0b_3x1')], 3)
    329                 # 输出尺寸8 * 8 * (384+384)=8*8*768
    330             with tf.variable_scope('Branch_2'): #这个分支更复杂:1*1>3*3>1*3+3*1,总共有三层
    331                 branch_2 = slim.conv2d(net, 448, [1, 1], scope='Conv2d_0a_1x1')
    332                 branch_2 = slim.conv2d(
    333                     branch_2, 384, [3, 3], scope='Conv2d_0b_3x3')
    334                 branch_2 = tf.concat([
    335                     slim.conv2d(branch_2, 384, [1, 3], scope='Conv2d_0c_1x3'),
    336                     slim.conv2d(branch_2, 384, [3, 1], scope='Conv2d_0d_3x1')], 3)
    337                 # 输出尺寸8 * 8 * (384+384)=8*8*768
    338             with tf.variable_scope('Branch_3'):
    339                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    340                 branch_3 = slim.conv2d(
    341                     branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    342                 # 输出尺寸8 * 8 * (384+384)=8*8*192
    343             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    344             # 输出通道数增加到2048 # 输出尺寸8 * 8 * (320+768+768+192)=8*8*2048
    345  
    346         # 第三个模块组第三个Inception Module 名称是:Mixed_7c
    347         #同前一个一样
    348         with tf.variable_scope('Mixed_7c'):
    349             with tf.variable_scope('Branch_0'):
    350                 branch_0 = slim.conv2d(net, 320, [1, 1], scope='Conv2d_0a_1x1')
    351             with tf.variable_scope('Branch_1'):
    352                 branch_1 = slim.conv2d(net, 384, [1, 1], scope='Conv2d_0a_1x1')
    353                 branch_1 = tf.concat([
    354                     slim.conv2d(branch_1, 384, [1, 3], scope='Conv2d_0b_1x3'),
    355                     slim.conv2d(branch_1, 384, [3, 1], scope='Conv2d_0c_3x1')], 3)
    356             with tf.variable_scope('Branch_2'):
    357                 branch_2 = slim.conv2d(net, 448, [1, 1], scope='Conv2d_0a_1x1')
    358                 branch_2 = slim.conv2d(
    359                     branch_2, 384, [3, 3], scope='Conv2d_0b_3x3')
    360                 branch_2 = tf.concat([
    361                     slim.conv2d(branch_2, 384, [1, 3], scope='Conv2d_0c_1x3'),
    362                     slim.conv2d(branch_2, 384, [3, 1], scope='Conv2d_0d_3x1')], 3)
    363             with tf.variable_scope('Branch_3'):
    364                 branch_3 = slim.avg_pool2d(net, [3, 3], scope='AvgPool_0a_3x3')
    365                 branch_3 = slim.conv2d(
    366                     branch_3, 192, [1, 1], scope='Conv2d_0b_1x1')
    367             net = tf.concat([branch_0, branch_1, branch_2, branch_3], 3)
    368             # 输出尺寸8 * 8 * (320+768+768+192)=8*8*2048
    369  
    370         return net, end_points
    371         #Inception V3网络的核心部分,即卷积层部分就完成了
    372     '''
    373     设计inception net的重要原则是图片尺寸不断缩小,inception模块组的目的都是将空间结构简化,同时将空间信息转化为
    374     高阶抽象的特征信息,即将空间维度转为通道的维度。降低了计算量。Inception Module是通过组合比较简单的特征
    375     抽象(分支1)、比较比较复杂的特征抽象(分支2和分支3)和一个简化结构的池化层(分支4),一共四种不同程度的
    376     特征抽象和变换来有选择地保留不同层次的高阶特征,这样最大程度地丰富网络的表达能力。
    377     '''
    378  
    379 ########全局平均池化、Softmax和Auxiliary Logits(之前6e模块的辅助分类节点)########
    380 def inception_v3(inputs,
    381                  num_classes=1000, # 最后需要分类的数量(比赛数据集的种类数)
    382                  is_training=True, # 标志是否为训练过程,只有在训练时Batch normalization和Dropout才会启用
    383                  dropout_keep_prob=0.8, # 节点保留比率
    384                  prediction_fn=slim.softmax, # 最后用来分类的函数
    385                  spatial_squeeze=True, # 参数标志是否对输出进行squeeze操作(去除维度数为1的维度,比如5*3*1转为5*3)
    386                  reuse=None, # 是否对网络和Variable进行重复使用
    387                  scope='InceptionV3'): # 包含函数默认参数的环境
    388  
    389   with tf.variable_scope(scope, 'InceptionV3', [inputs, num_classes], # 定义参数默认值
    390                          reuse=reuse) as scope:
    391       #'InceptionV3'是命名空间
    392     with slim.arg_scope([slim.batch_norm, slim.dropout], # 定义标志默认值
    393                         is_training=is_training):
    394       # 拿到最后一层的输出net和重要节点的字典表end_points
    395       net, end_points = inception_v3_base(inputs, scope=scope) # 用定义好的函数构筑整个网络的卷积部分
    396  
    397       # Auxiliary Head logits作为辅助分类的节点,对分类结果预测有很大帮助,
    398       # 对end_points的结界做平均池化、卷积最后通过1*1的卷积将通道变为1000
    399       with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
    400                           stride=1, padding='SAME'): # 将卷积、最大池化、平均池化步长设置为1
    401         aux_logits = end_points['Mixed_6e'] # 通过end_points取到Mixed_6e
    402         with tf.variable_scope('AuxLogits'):
    403           aux_logits = slim.avg_pool2d(
    404               aux_logits, [5, 5], stride=3, padding='VALID', # 在Mixed_6e之后接平均池化。压缩图像尺寸
    405               scope='AvgPool_1a_5x5')
    406           # 输入图像尺寸17*17*768,输出5*5*768
    407           aux_logits = slim.conv2d(aux_logits, 128, [1, 1], # 卷积。压缩图像尺寸。
    408                                    scope='Conv2d_1b_1x1')
    409           # 输出图像尺寸5*5*128
    410           # Shape of feature map before the final layer.
    411           aux_logits = slim.conv2d(
    412               aux_logits, 768, [5,5],
    413               weights_initializer=trunc_normal(0.01), # 权重初始化方式重设为标准差为0.01的正态分布
    414               padding='VALID', scope='Conv2d_2a_5x5')
    415           # 输出图像尺寸1*1*768
    416           aux_logits = slim.conv2d(
    417               aux_logits, num_classes, [1, 1], activation_fn=None,
    418               normalizer_fn=None, weights_initializer=trunc_normal(0.001),
    419               scope='Conv2d_2b_1x1')
    420           # 输出变为1*1*1000,这里的num_classes表示输出通道数,不用激活和标准化,权重重设为0.001的正态分布
    421           if spatial_squeeze: # tf.squeeze消除tensor中前两个为1的维度。
    422             aux_logits = tf.squeeze(aux_logits, [1, 2], name='SpatialSqueeze')
    423  
    424 #  这里非常值得注意,tf.squeeze(aux_logits, [1, 2])为什么是[1, 2]而不是[0,1],括号里表示为1的维度
    425 #因为训练的时候是输入的批次,第一维不是1,[32,1,1,1000].
    426           end_points['AuxLogits'] = aux_logits
    427           # 最后将辅助分类节点的输出aux_logits储存到字典表end_points中
    428  
    429       # 处理正常的分类预测逻辑
    430       # Final pooling and prediction
    431       # 这一过程的主要步骤:对Mixed_7c的输出进行8*8的全局平均池化>Dropout>1*1*1000的卷积>除去维数为1>softmax分类
    432       with tf.variable_scope('Logits'):
    433         net = slim.avg_pool2d(net, [8, 8], padding='VALID',
    434                               scope='AvgPool_1a_8x8')
    435         #输入为8*8*2048 输出为1 x 1 x 2048
    436         net = slim.dropout(net, keep_prob=dropout_keep_prob, scope='Dropout_1b')
    437         end_points['PreLogits'] = net
    438         # 1*1*2048
    439         logits = slim.conv2d(net, num_classes, [1, 1], activation_fn=None,
    440                              normalizer_fn=None, scope='Conv2d_1c_1x1')
    441         # 激活函数和规范化函数设为空 # 输出通道数1*1*1000
    442         if spatial_squeeze: # tf.squeeze去除输出tensor中维度为1的节点
    443           logits = tf.squeeze(logits, [1, 2], name='SpatialSqueeze')
    444       end_points['Logits'] = logits
    445       end_points['Predictions'] = prediction_fn(logits, scope='Predictions')
    446       # Softmax对结果进行分类预测
    447   return logits, end_points # 最后返回logits和包含辅助节点的end_points
    448 #end_points里面有'AuxLogits'、'Logits'、'Predictions'分别是辅助分类的输出,主线的输出以及经过softmax后的预测输出
    449  
    450 '''
    451 到这里,前向传播已经写完,对其进行运算性能测试
    452 '''
    453 ########评估网络每轮计算时间########
    454 def time_tensorflow_run(session, target, info_string):
    455  
    456   # Args:
    457   # session:the TensorFlow session to run the computation under.
    458   # target:需要评测的运算算子。
    459   # info_string:测试名称。
    460  
    461   num_steps_burn_in = 10
    462   # 先定义预热轮数(头几轮跌代有显存加载、cache命中等问题因此可以跳过,只考量10轮迭代之后的计算时间)
    463   total_duration = 0.0 # 记录总时间
    464   total_duration_squared = 0.0 # 总时间平方和  -----用来后面计算方差
    465  
    466   #迭代计算时间
    467   for i in range(num_batches + num_steps_burn_in): # 迭代轮数
    468     start_time = time.time() # 记录时间
    469     _ = session.run(target) # 每次迭代通过session.run(target)
    470     duration = time.time() - start_time
    471     #每十轮输出一次
    472     if i >= num_steps_burn_in:
    473       if not i % 10:
    474         print ('%s: step %d, duration = %.3f' %
    475                (datetime.now(), i - num_steps_burn_in, duration))
    476       total_duration += duration  # 累加便于后面计算每轮耗时的均值和标准差
    477       total_duration_squared += duration * duration
    478   mn = total_duration / num_batches # 每轮迭代的平均耗时
    479   vr = total_duration_squared / num_batches - mn * mn
    480   # 方差,是把一般的方差公式进行化解之后的结果,值得 借鉴
    481   sd = math.sqrt(vr) # 标准差
    482   print ('%s: %s across %d steps, %.3f +/- %.3f sec / batch' %
    483          (datetime.now(), info_string, num_batches, mn, sd))
    484   #输出的时间是处理一批次的平均时间加减标准差
    485  
    486 # 测试前向传播性能
    487 batch_size = 32 # 因为网络结构较大依然设置为32,以免GPU显存不够
    488 height, width = 299, 299 # 图片尺寸
    489 # 随机生成图片数据作为input
    490 inputs = tf.random_uniform((batch_size, height, width, 3))
    491  
    492 with slim.arg_scope(inception_v3_arg_scope()):
    493     # scope中包含了batch normalization默认参数,激活函数和参数初始化方式的默认值
    494   logits, end_points = inception_v3(inputs, is_training=False)
    495     # inception_v3中传入inputs获取里logits和end_points
    496  
    497 init = tf.global_variables_initializer() # 初始化全部模型参数
    498 sess = tf.Session() # 创建session
    499 sess.run(init)
    500 num_batches=100 # 测试的batch数量
    501 time_tensorflow_run(sess, logits, "Forward")
    502 '''
    503 虽然输入图片比VGGNet的224*224大了78%,但是forward速度却比VGGNet更快。
    504 这主要归功于其较小的参数量,inception V3参数量比inception V1的700万
    505 多了很多,不过仍然不到AlexNet的6000万参数量的一半。相比VGGNet的1.4
    506 亿参数量就更少了。整个网络的浮点计算量为50亿次,比inception V1的15亿
    507 次大了不少,但是相比VGGNet来说不算大。因此较少的计算量让inception V3
    508 网络变得非常实用,可以轻松地移植到普通服务器上提供快速响应服务,甚至
    509 移植到手机上进行实时的图像识别。
    510 '''
    511 #分析结果:前面的输出是当前时间下每10步的计算时间,最后输出的是当前时间下前向传播的总批次以及平均时间+-标准差
  • 相关阅读:
    Redission 中 RPermitExpirableSemaphore 用法
    Linux查询CPU核数
    Http超时时间之SocketTimeout
    In Theano, how to do Reverse-MaxPooling in the Convolutional MaxPooling Auto-Encoder
    如何让Ubuntu中virtualbox虚拟机用上USB设备
    Deep Learning Stuffs(持续更新中……)
    实例描述如何用python组件ctypes调用c的dll中的函数
    放弃MATLAB!简述winpython为什么比MATLAB更方便
    虚拟机Ubuntu10.04无法显示windows中文目录和文件
    W: GPG 错误
  • 原文地址:https://www.cnblogs.com/xiaoboge/p/10457237.html
Copyright © 2020-2023  润新知