当前位置:   article > 正文

tensorflow实战 resnet笔记_残差模块tensorflow

残差模块tensorflow

resnet简介

知识点简介

1.两层及三层的ResNet残差学习模块:
在这里插入图片描述
2.下图为ResNet不同层数时的网络配置,[ ]表示一个残差学习模块。
在这里插入图片描述
3.34层的残差网络如下:
在这里插入图片描述

代码中blocks的搭建过程

1.在主函数中运行resnet_v2_152(),用来创建152层的resnet网络。

with slim.arg_scope(resnet_arg_scope(is_training=False)):
    net, end_points = resnet_v2_152(inputs, 1000)

  • 1
  • 2
  • 3

2.在resnet_v2_152()函数中,
首先定义一些参数,num_classes是分类数;global_pool是确定是否添加全局池化层;reuse是否参数重用。
然后定义blocks的整体结构,分为四个block,每个block中有n个三层的残差学习模块,分别为3、8、36、3个。在blocks中,每一组的最后一个三层残差学习模块的第二层的步长为2,其他所有层步长均为1。每个残差学习型模块由一个三元组表示,第一个数字表示该残差学习模块第三层的输出通道数,第2个数字表示前两层的输出通道数,第三个数表示第二层的步长。
最后调用resnet_v2()函数,传入输入图片,定义好的blocks,以及提前定义好的一些参数。

def resnet_v2_152(inputs,
                  num_classes=None,
                  global_pool=True,
                  reuse=None,
                  scope='resnet_v2_152'):
    blocks = [
        Block('block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]),
        Block('block2', bottleneck, [(512, 128, 1)] * 7 + [(512, 128, 2)]),
        Block('block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]),
        Block('block4', bottleneck, [(2048, 512, 1)] * 3)]

    return resnet_v2(inputs, blocks, num_classes, global_pool,
                     include_root_block=True, reuse=reuse, scope=scope)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3.resnet_v2()函数是用来搭建整体网络的,包含blocks前面的卷积和池化层以及blocks后面的卷积和softmax层等结构。
首先根据include_root_block,决定是否创建ResNet最前面的64输出通道的步长为2的77卷积。
然后是通过stack_blocks_dense()函数组装blocks结构,得到经过完整的blocks后的输出,并进行batch_norm,因为在每个三层的残差学习模块的第三层上,都经过激活函数和BN操作,所以需要一次batch_norm,并指定激活函数。
最后根据标记global_pool,决定是否添加全局平均池化层;根据标记num_classes决定是否添加一个1
1卷积。

# 使用前面定义好的函数stack_blocks_dense将残差学习模块组生成好,得到其输出
# net就是经过blocks前面网络组合层后的输出。
net = stack_blocks_dense(net, blocks)
net = slim.batch_norm(net, activation_fn=tf.nn.relu, scope='postnorm')
  • 1
  • 2
  • 3
  • 4

4.在stack_blocks_dense()函数中,
遍历blocks中的四个block;
在每个block中遍历其args属性,即[(256, 64, 1)] * 2 + [(256, 64, 2)]等;
然后获取每个三层残差学习模块的三个数值;
调用block中unit_fn属性指定好的默认函数bottleneck(),并将前面或得到的三个数值传入该函数,用来搭建该残差学习模块的三层。
这样一来,第二层循环是用来组建每个block中的n个残差学习单元,第一层循环是用来组建所有block。

def stack_blocks_dense(net, blocks, outputs_collections=None):
    # 遍历每一个Block
    for block in blocks:
        # 使用两个tf.variable_scope将残差学习单元命名为block1/unit_1的形式
        with tf.variable_scope(block.scope, 'block', [net]) as sc:
            for i, unit in enumerate(block.args):
                with tf.variable_scope('unit_%d' % (i + 1), values=[net]):
                    unit_depth, unit_depth_bottleneck, unit_stride = unit
                    # 使用unit_fn函数(即残差学习单元生成函数)顺序创建并连接所有的残差学习单元
                    net = block.unit_fn(net,  # block.unit_fn即调用了bottleneck()函数
                                        depth=unit_depth,  # 第三层卷积输出通道数
                                        depth_bottleneck=unit_depth_bottleneck,  # 前两层卷积输出通道数depth_bottleneck
                                        stride=unit_stride)  # 中间那层卷积的步长      #其余参数是固定不变的
                    # net = bottleneck(net,
                    #                 depth=unit_depth,
                    #                 depth_bottleneck=unit_depth_bottleneck,
                    #                 stride=unit_stride)
            # 将输出net添加到collection中
            net = slim.utils.collect_named_outputs(outputs_collections, sc.name, net)
    # 返回最后的net作为函数结果
    return net
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

5.在bottleneck()函数中,
首先,因为上一个残差学习模块只进行了输入和输出的相加,没有进行非线性和BN处理,所以要先调用slim.batch_norm()函数进行这两项处理。
然后,因为要用input和输出先加,所以要先匹配通道数和每一个通道上的长和宽(shortcut处理)。如果通道数一致,就直接调用定义好的subsample()函数来修改输入的长和宽;如果不一致,就要调用slim.conv2d()函数同时修改通道数和大小,注意此时没有正则化的激活函数。
最后,修改好input通道数和大小后,组建三层残差学习模块,第一层直接调用slim.conv2d创建;第二层因为要判断步长是否为1,所以使用定义好的**conv2d_same()**来搭建;第三层的关键点在于不能进行非正则化和BN处理,因为要与处理好的输入相加。

def bottleneck(inputs,  # 输入
               depth, depth_bottleneck, stride,  # 这三个参数是Blocks类中的args
               outputs_collections=None,  # 收集end_points
               scope=None):  # 名称
    with tf.variable_scope(scope, 'bottleneck_v2', [inputs]) as sc:
        # 获取输入的最后一个维度,即输出通道数。  min_rank=4限定最少为4个维度
        depth_in = slim.utils.last_dimension(inputs.get_shape(), min_rank=4)
        # 对输入进行BN(Batch Normalization)操作,并使用ReLU函数进行预激活Preactivate
        preact = slim.batch_norm(inputs, activation_fn=tf.nn.relu, scope='preact')

        '''shortcut处理!!!'''
        '''定义shortcut,即旁路的弯曲的支线'''
        # 如果残差单元的输入通道depth_in与第三层卷积的输出通道depth一致
        if depth == depth_in:
            # 使用subsample按步长为stride对inputs进行空间降采样(因为输出通道一致了,还要确保空间尺寸和残差一致,因为残差中间那层的卷积步长为stride,tensor尺寸可能会缩小)
            shortcut = subsample(inputs, stride, 'shortcut')
        else:  # 输出通道不一致
            # 使用1×1的卷积核改变其通道数,并使用与步长为stride确保空间尺寸与残差一致
            shortcut = slim.conv2d(preact, depth, [1, 1], stride=stride, normalizer_fn=None,
                                   activation_fn=None, scope='shortcut')

        '''残差residual,三层卷积'''
        residual = slim.conv2d(preact, depth_bottleneck, [1, 1], stride=1, scope='conv1')

        residual = conv2d_same(residual, depth_bottleneck, 3, stride, scope='conv2')  # 步长为stride,并进行补零操作

        residual = slim.conv2d(residual, depth, [1, 1], stride=1, normalizer_fn=None,  # 最后一层卷积没有正则项也没有激活函数
                               activation_fn=None, scope='conv3')

        output = shortcut + residual

        return slim.utils.collect_named_outputs(outputs_collections, sc.name, output)  # 将结果添加进collection并返回
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

6.在处理输入时用到的subsample()函数作用解析:
当传输发生在同一个block的不同残差学习模块之间时,不存在步长不为1的层,并且input和第三层的输出通道一致,所以直接返回input;
当传输发生在不同block的交界处,因为在上一个残差学习模块中经过了一个步长为2的卷积层,所以尺寸输出尺寸缩小了,这时输入也需要进行相应的尺寸缩小。

def subsample(inputs, factor, scope=None):
    # 如果采样因子为1,之间返回输出
    if factor == 1:
        return inputs
    # 否则,最大池化处理,步长为采样因子
    else:
        return slim.max_pool2d(inputs, [1, 1], stride=factor, scope=scope)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.在组建每一个残差学习模块第二层时,用到的**conv2d_same()**的作用解析:
当不是每个block中的最后一个残差学习单元时,第二层的步长均为1,这时直接使用slim.conv2d()进行卷积,保证大小不变即可。
当是每个block中的最后一个残差学习单元时,第二层步长为2,这是进行合理的填充,使尺寸刚好变为原来的一般。

def conv2d_same(inputs, num_outputs, kernel_size, stride, scope=None):
    if stride == 1:
        return slim.conv2d(inputs, num_outputs, kernel_size, stride=1, padding='SAME', scope=scope)
    else:
        # 步长不为1,则显示地pad zero
        pad_total = kernel_size - 1
        pad_beg = pad_total // 2
        pad_end = pad_total - pad_beg
        # 补零操作,在第二和第三个维度上进行补零操作
        inputs = tf.pad(inputs, [[0, 0], [pad_beg, pad_end], [pad_beg, pad_end], [0, 0]])
        return slim.conv2d(inputs, num_outputs, kernel_size, stride=stride, padding='VALID', scope=scope)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

7.参考流程图:该图取自博客
紫色显示在该位置调用其他方法,绿色表示在该处返回参数。
在这里插入图片描述

代码难点记录

1.collections.namedtuple()函数的作用是创建一个自定义的元组子类。可以根据你想让该类元组表示怎样的含义来定义。
比如你想创建一个坐标点元组子类,可以通过语句pt = collections.namedtuple('point', ['x', 'y'])来创建该坐标点元组类。如果你想要获取一个该元组类的对象,点A,可以通过语句A = pt(2,3)来获取。这样就可以通过p.x等方式获取该点的信息。
又比如你想创建一个像素点上的RGB数值的元组子类,可以通过语句co = collections.namedtuple('Color', 'red green blue')来创建三原色元组类。同样,可以通过语句x = co(128,64,32)来获取像素点的三色值。
下面的语句是创建一个类的语句,它的输入是一个用namedtuple自定义的元组子类,该元组名为’Block’,包含三个值’scope’, ‘unit_fn’, ‘args’,如果你想得到一个该类的对象就必须传入一个给元组子类的对象,并且包含这三个值。

class Block(collections.namedtuple('Block', ['scope', 'unit_fn', 'args'])):
  • 1

2.参数理解:自定义元组中三个参数’scope’, ‘unit_fn’, 'args’的含义。
重点理解第三个参数args,其元组的第一个值是bottleneck残差学习单元最后一层的输出通道数,第二个值是前两层的输出通道数,第三个参数是中间层的步长。

class Block(collections.namedtuple('Block', ['scope', 'unit_fn', 'args'])):
    'a named tuple decribing a ResNet block.'


'''一个典型的Block
    需要输入参数,分别是scope、unit_fn、args
    以Block('block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)])为例,它可以定义一个典型的Block
    其中
        1、block1就是这个Block的名称(或scope)
        2、bottleneck是ResNet V2中的残差学习单元
        3、[(256, 64, 1)] * 2 + [(256, 64, 2)]是这个Block的args,args是一个列表,其中每一个元素都对应一个bottleneck残差学习单元,
        前面两个都是(256, 64, 1),最后一个是(256, 64, 2)。每个元素都是一个三元tuple,即(depth, depth_bottleneck, stride)
        比如(256, 64, 3),代表构建的bottleneck残差学习单元(每个残差学习单元包含三个卷积层)中,第三层卷积输出通道数为256,
        前两层卷积输出通道数depth_bottleneck为64,且中间那层的步长stride为3

'''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.slim.utils.collect_named_outputs(outputs_collections, sc.name, output)的作用是,给output取一个别名sc.name,并存入outputs_collections中。

4.其他难点在代码中blocks的搭建过程均有提到。

完整代码

import tensorflow as tf
import collections
import time
from datetime import datetime
import math

slim = tf.contrib.slim

'''使用collections.namedtuple设计ResNet基本Block模块组的named tuple,并用它创建Block类'''

# b = collections.namedtuple('point', ['x', 'y'])

class Block(collections.namedtuple('Block', ['scope', 'unit_fn', 'args'])):
    'a named tuple decribing a ResNet block.'


'''一个典型的Block
    需要输入参数,分别是scope、unit_fn、args
    以Block('block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)])为例,它可以定义一个典型的Block
    其中
        1、block1就是这个Block的名称(或scope)
        2、bottleneck是ResNet V2中的残差学习单元
        3、[(256, 64, 1)] * 2 + [(256, 64, 2)]时这个Block的args,args是一个列表,其中每一个元素都对应一个bottleneck残差学习单元,
        前面两个都是(256, 64, 1),最后一个是(256, 64, 2)。每个元素都是一个三元tuple,即(depth, depth_bottleneck, stride)
        比如(256, 64, 3),代表构建的bottleneck残差学习单元(每个残差学习单元包含三个卷积层)中,第三层卷积输出通道数为256,
        前两层卷积输出通道数depth_bottleneck为64,且中间那层的步长stride为3

'''

'''降采样的方法'''


def subsample(inputs, factor, scope=None):
    # 如果采样因子为1,之间返回输出
    if factor == 1:
        return inputs
    # 否则,最大池化处理,步长为采样因子
    else:
        return slim.max_pool2d(inputs, [1, 1], stride=factor, scope=scope)


'''创建卷积层'''


def conv2d_same(inputs, num_outputs, kernel_size, stride, scope=None):
    if stride == 1:
        return slim.conv2d(inputs, num_outputs, kernel_size, stride=1, padding='SAME', scope=scope)
    else:
        # 步长不为1,则显示地pad zero
        pad_total = kernel_size - 1
        pad_beg = pad_total // 2
        pad_end = pad_total - pad_beg
        # 补零操作,在第二和第三个维度上进行补零操作
        inputs = tf.pad(inputs, [[0, 0], [pad_beg, pad_end], [pad_beg, pad_end], [0, 0]])
        return slim.conv2d(inputs, num_outputs, kernel_size, stride=stride, padding='VALID', scope=scope)


'''堆叠Blocks的函数,net为输入,bloks为Block类的列表,outputs_collections时用来收集各个end_points'''


@slim.add_arg_scope
def stack_blocks_dense(net, blocks, outputs_collections=None):
    # 遍历每一个Block
    for block in blocks:
        # 使用两个tf.variable_scope将残差学习单元命名为block1/unit_1的形式
        with tf.variable_scope(block.scope, 'block', [net]) as sc:
            for i, unit in enumerate(block.args):
                with tf.variable_scope('unit_%d' % (i + 1), values=[net]):
                    unit_depth, unit_depth_bottleneck, unit_stride = unit
                    # 使用unit_fn函数(即残差学习单元生成函数)顺序创建并连接所有的残差学习单元
                    net = block.unit_fn(net,  # block.unit_fn即调用了bottleneck()函数
                                        depth=unit_depth,  # 第三层卷积输出通道数
                                        depth_bottleneck=unit_depth_bottleneck,  # 前两层卷积输出通道数depth_bottleneck
                                        stride=unit_stride)  # 中间那层卷积的步长      #其余参数是固定不变的
                    # net = bottleneck(net,
                    #                 depth=unit_depth,
                    #                 depth_bottleneck=unit_depth_bottleneck,
                    #                 stride=unit_stride)
            # 将输出net添加到collection中
            net = slim.utils.collect_named_outputs(outputs_collections, sc.name, net)
    # 返回最后的net作为函数结果
    return net


'''创建ResNet通用的arg_scope,  arg_scope的功能是定义某些函数的参数默认值'''


def resnet_arg_scope(is_training=True,  # 训练标记
                     weight_decay=0.0001,  # 权重衰减速率
                     batch_norm_decay=0.997,  # BN的衰减速率
                     batch_norm_epsilon=1e-5,  # BN的epsilon
                     batch_norm_scale=True):  # BN的scale

    batch_norm_parmas = {
        'is_training': is_training,
        'decay': batch_norm_decay,
        'epsilon': batch_norm_epsilon,
        'scale': batch_norm_scale,
        'updates_collections': tf.GraphKeys.UPDATE_OPS
    }

    with slim.arg_scope(
            [slim.conv2d],
            weights_regularizer=slim.l2_regularizer(weight_decay),  # 权重正则设为L2正则
            weights_initializer=slim.variance_scaling_initializer(),  # 权重初始化器
            activation_fn=tf.nn.relu,  # 激活函数
            normalizer_fn=slim.batch_norm,  # 标准化器设为BN(batch_norm的缩写)
            normalizer_params=batch_norm_parmas):
        with slim.arg_scope([slim.batch_norm], **batch_norm_parmas):  # 设置BN的默认参数
            with slim.arg_scope([slim.max_pool2d], padding='SAME') as arg_sc:  # 最大池化的padding默认设为SAME
                return arg_sc


'''bottleneck残差学习单元'''
'''
知识点:
并不是所有的方法都能用arg_scope设置默认参数, 只有用@slim.add_arg_scope修饰过的方法才能使用arg_scope. 
例如conv2d方法, 它就是被修饰过的(见源码). 
所以, 要使slim.arg_scope正常运行起来, 需要两个步骤:
    1、用@add_arg_scope修饰目标函数
    2、用with arg_scope(...) 设置默认参数.
'''


@slim.add_arg_scope
def bottleneck(inputs,  # 输入
               depth, depth_bottleneck, stride,  # 这三个参数是Blocks类中的args
               outputs_collections=None,  # 收集end_points
               scope=None):  # 名称
    with tf.variable_scope(scope, 'bottleneck_v2', [inputs]) as sc:
        # 获取输入的最后一个维度,即输出通道数。  min_rank=4限定最少为4个维度
        depth_in = slim.utils.last_dimension(inputs.get_shape(), min_rank=4)
        # 对输入进行BN(Batch Normalization)操作,并使用ReLU函数进行预激活Preactivate
        preact = slim.batch_norm(inputs, activation_fn=tf.nn.relu, scope='preact')

        '''定义shortcut,即旁路的弯曲的支线'''
        # 如果残差单元的输入通道depth_in与第三层卷积的输出通道depth一致
        if depth == depth_in:
            # 使用subsample按步长为stride对inputs进行空间降采样(因为输出通道一致了,还要确保空间尺寸和残差一致,因为残差中间那层的卷积步长为stride,tensor尺寸可能会缩小)
            shortcut = subsample(inputs, stride, 'shortcut')
        else:  # 输出通道不一致
            # 使用1×1的卷积核改变其通道数,并使用与步长为stride确保空间尺寸与残差一致
            shortcut = slim.conv2d(preact, depth, [1, 1], stride=stride, normalizer_fn=None,
                                   activation_fn=None, scope='shortcut')

        '''残差residual,三层卷积'''
        residual = slim.conv2d(preact, depth_bottleneck, [1, 1], stride=1, scope='conv1')

        residual = conv2d_same(residual, depth_bottleneck, 3, stride, scope='conv2')  # 步长为stride,并进行补零操作

        residual = slim.conv2d(residual, depth, [1, 1], stride=1, normalizer_fn=None,  # 最后一层卷积没有正则项也没有激活函数
                               activation_fn=None, scope='conv3')

        output = shortcut + residual

        return slim.utils.collect_named_outputs(outputs_collections, sc.name, output)  # 将结果添加进collection并返回


'''生成ResNet的主函数,
只要预先定义好网络的残差学习模块组blocks,它就可以生成对应的完整的ResNet'''


def resnet_v2(inputs,  # 输入
              blocks,  # 定义好的Block类列表
              num_classes=None,  # 最后输出的类数
              global_pool=True,  # 是否加上最后一层全局平均池化
              include_root_block=True,  # 是否加上ResNet网络最前面通常使用的7*7卷积和最大池化
              reuse=None,  # 是否重用
              scope=None):  # 整个网络的名称

    with tf.variable_scope(scope, 'resnet_v2', [inputs], reuse=reuse) as sc:

        end_points_collection = sc.original_name_scope + '_end_points'
        # 将slim.conv2d, bottleneck, stack_blocks_dense这三个函数的参数outputs_collections默认设为end_points_collection
        with slim.arg_scope([slim.conv2d, bottleneck, stack_blocks_dense], outputs_collections=end_points_collection):

            net = inputs

            # 根据该标记,创建ResNet最前面的64输出通道的步长为2的7*7卷积
            if include_root_block:
                with slim.arg_scope([slim.conv2d], activation_fn=None, normalizer_fn=None):
                    net = conv2d_same(net, 64, 7, stride=2, scope='conv1')

                net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')

            # 使用前面定义好的函数stack_blocks_dense将残差学习模块组生成好,得到其输出
            net = stack_blocks_dense(net, blocks)
            net = slim.batch_norm(net, activation_fn=tf.nn.relu, scope='postnorm')

            # 根据标记添加全局平均池化层
            if global_pool:
                net = tf.reduce_mean(net, [1, 2], name='pool5', keep_dims=True)
            # 根据是否有分类数,添加一个1*1卷积
            if num_classes is not None:
                net = slim.conv2d(net, num_classes, [1, 1], activation_fn=None, normalizer_fn=None, scope='logits')

            # 通过该方法将collection转化为python的dict词典
            end_points = slim.utils.convert_collection_to_dict(end_points_collection)

            # 根据是否有分类数,添加Softmax层输出网络结果
            if num_classes is not None:
                end_points['predictions'] = slim.softmax(net, scope='predictions')

            return net, end_points


'''设计层数为152层的ResNet
Resnet不断使用步长为2的层来缩减尺寸,同时输出通道数也在持续增加
'''


def resnet_v2_152(inputs,
                  num_classes=None,
                  global_pool=True,
                  reuse=None,
                  scope='resnet_v2_152'):
    blocks = [
        Block('block1', bottleneck, [(256, 64, 1)] * 2 + [(256, 64, 2)]),
        Block('block2', bottleneck, [(512, 128, 1)] * 7 + [(512, 128, 2)]),
        Block('block3', bottleneck, [(1024, 256, 1)] * 35 + [(1024, 256, 2)]),
        Block('block4', bottleneck, [(2048, 512, 1)] * 3)]

    return resnet_v2(inputs, blocks, num_classes, global_pool,
                     include_root_block=True, reuse=reuse, scope=scope)


'''评估ResNet_V2每轮计算所用时间'''


def time_tensorflow_run(session, target, info_string):  # target:需要评测的运算算字, info_string:测试的名称
    num_steps_burn_in = 10  # 给程序热身,头几轮迭代有显存的加载、cache命中等问题因此可以跳过,我们只考量10轮迭代之后的计算时间
    total_duration = 0.0  # 总时间
    total_duration_squared = 0.0  # 平方和

    # 循环计算每一轮耗时
    for i in range(num_batches + num_steps_burn_in):
        start_time = time.time()
        _ = session.run(target)
        duration = time.time() - start_time

        if i >= num_steps_burn_in:  # 程序热身完成后,记录时间

            if not i % 10:  # 每10轮 显示  当前时间,迭代次数(不包括热身),用时
                print('%s: step %d, duration = %.3f' % (datetime.now(), i - num_steps_burn_in, duration))

            # 累加total_duration和total_duration_squared
            total_duration += duration
            total_duration_squared += duration * duration

    # 循环结束后,计算每轮迭代的平均耗时mn和标准差sd,最后将结果显示出来
    mn = total_duration / num_batches
    vr = total_duration_squared / num_batches - mn * mn
    sd = math.sqrt(vr)
    print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' % (datetime.now(), info_string, num_batches, mn, sd))


batch_size = 16
height, width = 224, 224
inputs = tf.random_uniform((batch_size, height, width, 3))
with slim.arg_scope(resnet_arg_scope(is_training=False)):
    net, end_points = resnet_v2_152(inputs, 1000)

init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
num_batches = 100
time_tensorflow_run(sess, net, "Forward")


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/195926
推荐阅读
相关标签
  

闽ICP备14008679号