这篇论文是2018年的,目前不再维护,新出了一个DRG,大家有兴趣可以看一下。
1.图片。从图片可以看出输入图片经过目标检测,之后经过HOI检测。
2.HOI检测。全称为Human-Object Interaction Detection,即人和物体的动作交互检测。首先他会检测出与图片中产生动作的人以及和他产生动作交互的物体,同时还要将二者的动作关系预测出来并关联起来。HOI detection的最终目标是给出三元组<human, object, interaction>,即人、物体和它们之间的交互动作。这种检测技术在计算机视觉中有广泛的应用,如监控事件监测和机器人模仿学习等。
3.在图1中,展示了一个HOI检测问题的示例。给定一个输入图像和从一个对象检测器中检测到的实例,目标是识别所有的组(人,动词,对象)。
4.contextual cues。大多数现有的方法都是利用一个人和一个物体的外观特征以及它们之间的空间关系来推断互动的。除了只使用一个人的外观特征外,最近的动作识别算法还利用了来自图像中的上下文线索。
5.图2:上下文特性的示例。除了使用人和对象的边界框外,从图像中捕捉上下文线索的不同方法。
如图2所示,编码上下文的示例包括选择一个辅助框[13],使用人和对象边界框[29]的并集,提取人姿态关键点[6]周围的特征,或者从整个图像[31]中利用全局上下文。看上去不理解,结合图像与文章就可以理解了,第一种就是利用人体姿态提取周围特征画出辅助框。而另一种方法是提取全局特征并选择辅助框。
虽然结合环境通常有助于提高表现,但这些手工设计的注意区域可能并不总是与识别行动/互动相关。例如,关注人类的姿势可能有助于识别行动像“骑”和“扔”,关注交互的点可能有助于识别行动涉及手-对象的交互,如“喝杯子”和“用勺子吃”,关注背景可能有助于区分“打网球拍”和“打棒球棒”。
也就是说你关注不同的区域,会识别出不同的结果,而下面就是利用端到端也就是识别的结果整合在一起(我猜的)。
为了解决这一限制,最近的工作利用端到端可训练的注意力模块来进行动作识别[9]或图像分类[20]。然而,这些方法都是为图像级的分类任务而设计的。
6.所提出的以实例为中心的注意网络(iCAN)动态地为每个被检测到的人或对象实例生成一个注意地图,以突出显示与任务相关的区域。
7.相关工作。Visual relationship detection,
Visual relationship detection,也称为VRD任务,是计算机视觉中的一个重要任务。它的主要目标是检测和定位图像中的多个对象,并对每一对对象之间的交互关系(predicate)进行分类。这种关系通常表示为一个三元组(triplet),即<object1 - predicate - object2>,其中object1和object2表示图像中的两个对象,而predicate描述它们之间的关系或交互。
Visual relationship detection不仅要求识别出图像中的物体以及他们的位置,还需要识别物体之间的关系。这些关系可以是空间关系(如above,next to,below),动词关系(如wear),介词关系(如with),比较关系(如taller than)等等。由于对象的个数和关系的种类都非常多,因此其语义空间比单独的物体或关系的语义空间要大得多。
8.相关工作。Scene graph generation,即场景图生成,是计算机视觉中的一个任务,目标是让计算机自动生成一种语义化的图结构,称为场景图(scene graph)。在这个图结构中,图像中的目标对应图中的节点(graph node),而目标之间的关系则对应图中的边(graph edge)。这些关系可以包括目标之间的空间关系、动作关系、属性关系等。
场景图生成的过程通常包括两个主要步骤:目标检测和关系预测。首先,通过目标检测算法识别出图像中的目标,并为每个目标生成一个节点。然后,通过关系预测算法预测目标之间的关系,并为每对关系生成一条边。这些边可以包含关系的类型(如动作、空间关系等)以及关系的属性(如距离、角度等)
9.相关工作。language prior,在视觉关系检测或场景图生成等计算机视觉任务中,"语言先验"也可以被用来提高模型的性能。由于视觉关系检测或场景图生成的目标之一是理解图像中的物体及其之间的关系,而这些关系往往可以通过自然语言进行描述,因此可以利用自然语言处理中的语言先验知识来帮助模型理解图像中的关系。例如,可以利用已知的语法规则和词汇含义来约束模型生成的关系三元组,从而提高其准确性。
10.“End-to-end trainable attention-based methods” 是一种深度学习方法,它结合了注意力机制和端到端的训练方式。这里的“端到端”训练指的是整个模型从输入到输出都是可训练的,而不需要分阶段或分模块进行训练。这种训练方法使得模型可以优化整个处理流程,而不仅仅是单个组件。
11.我们提出了以实例为中心的HOI检测注意网络(图3)。我们首先概述了我们的方法(第3.1节),然后介绍了以实例为中心的注意模块(第3.2节)。接下来,我们概述了用于特征提取的三个主要流的细节(第3.3节):人类流、对象流和成对流。最后,我们描述了我们的推理过程(第3.4节)。
图3:模型的概述。提出的模型由以下三个主要流组成: (1)基于人体外观检测交互的人流;(2)基于物体外观预测交互的物体蒸汽;(3)成对流,用于编码人和物体边界框之间的空间布局。给定现成的更快的R-CNN检测到的对象实例,我们使用所有的人-对象对生成HOI假设。然后将来自单个流的动作分数融合,产生最终的预测,如图所示。
12.算法概述
人-目标交互检测方法包括两个主要步骤: 1)目标检测和2) HOI预测。首先,给定一个输入图像,我们使用Faster R-CNN [34]来检测所有的人/对象实例。我们将bh表示为一个人的检测边界框,以bo表示物。我们使用sh和so分别表示被检测的人和一个对象的置信度分数。
这个算法里面先定出一个实例对象,人作为主人公,然后通过对人与物之间的得分来进行判是否之间有关系。
算法可以看到结构比较简单,易于理解,下面通过代码进行学习
(1)config.py
# Root directory of project
__C.ROOT_DIR = osp.abspath(osp.join(osp.dirname(__file__), '..', '..'))
# Data directory
__C.DATA_DIR = osp.abspath(osp.join(__C.ROOT_DIR, 'Data'))
在这段代码中,__C.ROOT_DIR
被赋值为一个绝对路径,这个路径指向的是当前文件(__file__
)所在目录的上级目录的上级目录。具体来说:
__file__
:这是一个特殊变量,在Python脚本中,它表示当前文件的路径(通常是相对于脚本被运行的位置)。osp.dirname(__file__)
:os.path.dirname
函数返回指定文件路径的目录名。这里,它返回当前文件所在的目录路径。'..'
:这是一个相对路径指示符,表示上一级目录。在这里,它被使用了两次,意味着我们想要向上移动两级目录。osp.join(osp.dirname(__file__), '..', '..')
:os.path.join
函数被用来合并这些组件,形成一个相对路径,这个路径指向当前文件所在目录的上级目录的上级目录。osp.abspath(...)
:最后,os.path.abspath
函数将这个相对路径转换为绝对路径。这意味着无论当前工作目录是什么,__C.ROOT_DIR
都会是一个从文件系统根目录开始的完整路径。
下一步直接看resnet50-vcoco.py中的resnet50
self.visualize = {}
class MyModel:
def __init__(self):
# 初始化visualize属性为一个空字典
self.visualize = {}
def train(self, data, labels):
# 在训练过程中,将损失函数值存储在visualize字典中
for epoch in range(num_epochs):
loss = self.compute_loss(data, labels)
self.visualize['loss'] = loss
def compute_loss(self, data, labels):
# 计算损失函数的值(这里只是一个示例函数)
return sum((predictions - labels) ** 2) / len(labels)
# 创建一个MyModel实例
model = MyModel()
# 执行训练过程(这里省略了数据和标签的实际传递)
model.train(data, labels)
# 访问存储在visualize字典中的损失函数值
print(model.visualize['loss'])
self.visualize = {}
是Python编程语言中的一行代码,通常出现在类的构造函数(__init__
方法)或其他方法中。这行代码创建了一个名为 visualize
的字典(dictionary)并将其初始化为空。这个字典通常用于存储与可视化相关的信息,例如在机器学习或深度学习的上下文中,可能用于存储训练过程中的损失函数值、准确率、模型权重等,以便后续进行可视化分析.
self.intermediate = {}
在Python的类中,self.intermediate = {}
这行代码的作用是在实例对象上创建一个名为 intermediate
的属性,并将其初始化为一个空字典。这个字典通常用于存储中间结果、临时数据或用于后续处理的任何信息。下面示例:
class MyProcessor:
def __init__(self):
# 初始化intermediate属性为一个空字典
self.intermediate = {}
def process_data(self, data):
# 在处理数据的过程中,将中间结果存储在intermediate字典中
self.intermediate['step1'] = self.perform_step1(data)
self.intermediate['step2'] = self.perform_step2(self.intermediate['step1'])
def perform_step1(self, data):
# 执行第一步处理,并返回结果
# 这里只是一个示例,实际处理逻辑会根据需要编写
return data + 1
def perform_step2(self, step1_result):
# 执行第二步处理,并返回结果
# 这里只是一个示例,实际处理逻辑会根据需要编写
return step1_result * 2
def get_intermediate_results(self):
# 返回存储在intermediate字典中的所有中间结果
return self.intermediate
# 创建一个MyProcessor实例
processor = MyProcessor()
# 处理一些数据
data_to_process = 5
processor.process_data(data_to_process)
# 获取并打印中间结果
intermediate_results = processor.get_intermediate_results()
print(intermediate_results) # 输出:{'step1': 6, 'step2': 12}
self.predictions = {}
在Python的类定义中,self.predictions = {}
表示创建一个名为 predictions
的字典属性,并将其初始化为空。这个 predictions
属性通常用于存储模型的预测结果。在机器学习和深度学习的上下文中,这样的字典可能用于保存不同批次(batch)或不同数据点的预测输出。
self.score_summaries = {}
在Python的类中,self.score_summaries = {}
这行代码的作用是在实例对象上创建一个名为 score_summaries
的属性,并将其初始化为一个空字典。这个字典通常用于存储关于分数或评估结果的摘要信息,比如在机器学习模型中可能用于存储每个批次(batch)或每个周期(epoch)的得分、准确率、损失等。
self.event_summaries = {}
在Python的类中,self.event_summaries = {}
这行代码创建了一个名为 event_summaries
的字典属性,并将其初始化为空。这个字典通常用于存储关于事件或事件处理过程的摘要信息。具体的应用场景可能包括日志记录、事件追踪、性能监控等。
self.losses = {}
在Python的类中,self.losses = {}
这行代码创建了一个名为 losses
的字典属性,并将其初始化为空字典。这个字典通常用于存储在训练机器学习模型或执行其他任务时每个批次或每个周期的损失值。
self.image = tf.placeholder(tf.float32, shape=[1, None, None, 3], name = 'image')
这段代码是定义输入图像的大小通道以及批次,但是在TensorFlow 2.x中,tf.placeholder
已经被移除,取而代之的是使用tf.data.Dataset
或其他方法来定义输入数据。
1
表示批次大小(batch size),这里固定为1。None
表示图像的高度,这里是不确定的,意味着可以是任何值。None
表示图像的宽度,这里也是不确定的。3
表示图像的通道数,通常对应于RGB三个通道。
首先输入图片没有要求是一个三通道,一批次也就是输入一次的数据量1,也就是1张,之后
self.spatial = tf.placeholder(tf.float32, shape=[None, 64, 64, 2], name = 'sp')
这行代码定义了一个名为spatial
的占位符,用于存储一个形状为[None, 64, 64, 2]
的浮点数张量。这个形状的含义如下:
None
:表示批次大小(batch size)是不确定的,可以在运行时指定。64
:表示图像或空间数据的高度为64像素。64
:表示图像或空间数据的宽度为64像素。2
:表示每个像素有2个通道,这通常对应于某种空间特征或特定的数据表示(例如,可以是x和y坐标,或者其它两个相关的特征)。
这个信息在论文中是
self.Hsp_boxes = tf.placeholder(tf.float32, shape=[None, 5], name = 'Hsp_boxes')
这一个暂时不太清楚属于那一部分,我猜这是初始化一个人的框架
self.O_boxes = tf.placeholder(tf.float32, shape=[None, 5], name = 'O_boxes')
这个占位符可能用于表示一批边界框(bounding boxes)的坐标和类别信息。具体来说,[None, 5]
的形状可能意味着每个边界框由5个值表示,例如:
- x1: 边界框左上角的x坐标
- y1: 边界框左上角的y坐标
- x2: 边界框右下角的x坐标
- y2: 边界框右下角的y坐标
- class_id: 边界框内物体的类别ID
self.gt_class_H = tf.placeholder(tf.float32, shape=[None, 29], name = 'gt_class_H')
gt_class_H
可能用于表示真实的目标类别,其中“gt”通常指的是“ground truth”(真实值)。shape=[None, 29]
意味着每个样本可能有29个类别的真实标签。这些标签通常以one-hot编码的形式表示,其中每个样本的29个元素中只有一个是1(表示该样本属于该类别),其余都是0。
例如,如果有一个样本属于第5个类别,那么它的gt_class_H
可能是一个形状为[1, 29]
的张量,内容为[0, 0, 0, 0, 1, 0, 0, ..., 0]
(其中只有第5个元素是1,其余都是0)。
这是对人的标签类别的定义。
self.gt_class_HO = tf.placeholder(tf.float32, shape=[None, 29], name = 'gt_class_HO')
这行代码定义了一个名为gt_class_HO
的TensorFlow占位符。这个占位符是为了在计算图中保留一个位置,以便在运行时填充真实的目标类别数据。
这里面是初始化这些标签,一个是人,一个是人物,另一个是sp
self.gt_class_sp = tf.placeholder(tf.float32, shape=[None, 29], name = 'gt_class_sp')
self.Mask_HO = tf.placeholder(tf.float32, shape=[None, 29], name = 'HO_mask')
在目标检测的上下文中,特别是在处理具有不同方向的目标(如水平方向和垂直方向的目标)时,HO_mask
可能指的是“Horizontal Objects Mask”(水平物体掩码)。这个掩码通常用于指示哪些目标框(bounding boxes)是水平的,哪些可能是垂直的。这对于某些类型的检测任务来说很重要,尤其是当物体可以以不同方向出现时。
具体来说,HO_mask
可能是一个二值化(binary)掩码,其中每个元素对应于一个目标框。如果目标框是水平的,则相应的掩码元素被设置为1;如果目标框是垂直的,则设置为0。在某些实现中,这个掩码可能是一个与边界框坐标张量相同形状的张量,但在其他实现中,它可能是一个单独的张量,其中每个元素代表一个边界框的方向性。
例如,如果你有一个包含多个目标框的批次,并且你希望区分哪些框是水平的,你可能会有一个形状为 [batch_size, num_boxes]
的 HO_mask
张量,其中 batch_size
是批次中的图像数量,num_boxes
是每个图像中的目标框数量。对于每个图像中的每个目标框,HO_mask
中的相应元素将指示该框是否是水平的。
这种掩码在训练过程中非常有用,因为它允许模型学习如何区分不同类型的目标框。在推理时,这个掩码也可以用来过滤或处理特定方向的目标框,以满足特定任务的需求
self.num_classes = 29
这行代码定义了一个实例变量 num_classes
,并将其设置为29。这通常表示数据集中目标类别的总数。在目标检测或图像分类任务中,num_classes
用于指定模型需要预测的不同类别的数量。
这个变量通常用于定义模型的输出层的大小,以便它能够产生与类别数量相对应的预测。在神经网络中,输出层通常是一个全连接层(或称为密集层),其神经元数量与类别数量相同。每个神经元的输出可以解释为输入图像属于相应类别的概率。
此外,num_classes
也可能用于其他目的,例如在计算损失函数时确定交叉熵损失的类别数量,或者在定义数据预处理和增强时指定标签编码的维度等。
self.num_fc = 1024
这个变量名暗示它可能与全连接层(fully connected layer)的神经元数量有关
全连接层,也被称为密集层(dense layer),是神经网络中常见的层类型。在卷积神经网络的末端,全连接层通常用于将前面层的输出转换为固定大小的向量,以便进行分类或其他任务。
num_fc = 1024
表示全连接层中的神经元数量是1024个。这意味着该层将接收输入数据,并通过1024个不同的神经元进行加权求和和激活函数处理,以产生输出。
这个值的选择通常是基于经验、实验或网络架构的需求。较大的 num_fc
值可能会增加模型的复杂性,从而有可能提高模型的性能,但也可能会导致过拟合,特别是当训练数据有限时。相反,较小的值可能会减少模型的复杂性,但也可能限制其表示能力。
self.scope = 'resnet_v1_50'
在提供的代码片段 self.scope = 'resnet_v1_50'
中,self.scope
是一个实例变量,它被赋值为字符串 'resnet_v1_50'
。这个变量名 scope
通常用于指定一个命名空间或作用域,在深度学习框架(如 TensorFlow)中,这可以帮助组织和管理不同的变量和操作。
在 TensorFlow 中,scope
常常与 tf.compat.v1.variable_scope
一起使用,来创建一个变量作用域。这个作用域可以用来封装一组变量,使得这些变量具有相同的名称前缀,方便管理和复用。这对于在模型中重用相同的网络结构(如残差网络 ResNet 的不同层)特别有用。
self.blocks = [resnet_utils.Block('block1', resnet_v1.bottleneck,[(256, 64, 1)] * 2 + [(256, 64, 2)]),
resnet_utils.Block('block2', resnet_v1.bottleneck,[(512, 128, 1)] * 3 + [(512, 128, 2)]),
resnet_utils.Block('block3', resnet_v1.bottleneck,[(1024, 256, 1)] * 5 + [(1024, 256, 1)]),
resnet_utils.Block('block4', resnet_v1.bottleneck,[(2048, 512, 1)] * 3),
resnet_utils.Block('block5', resnet_v1.bottleneck,[(2048, 512, 1)] * 3)]
里面定义了5个残差块,
resnet_v1.bottleneck
指的是 ResNet-v1 中的瓶颈(bottleneck)残差块。瓶颈块由三个卷积层组成:1x1, 3x3, 1x1,其中中间的 3x3 卷积层是主要的特征提取层,而两个 1x1 的卷积层用于调整通道数,从而实现“瓶颈”的效果。我们知道瓶颈是中间细两边粗的,这里很形象的说出残差的含义。
[(256, 64, 1)] * 2 + [(256, 64, 2)]
是一个参数列表,用于配置 resnet_utils.Block
函数中瓶颈块的参数。每个元组 (256, 64, 1)
或 (256, 64, 2)
表示一个瓶颈块的配置,其中:
- 第一个数字(
256
)是该块的输入和输出通道数。 - 第二个数字(
64
)是中间 3x3 卷积层的通道数。 - 第三个数字(
1
或2
)是该块中最后一个卷积层的步长(stride)。
在这段代码中,self.blocks
是一个列表,它定义了 ResNet 架构中不同阶段的块(block)。每个块包含了一系列瓶颈(bottleneck)残差单元,这些单元是 ResNet 的主要构建块之一。每个阶段(block)的命名和配置反映了 ResNet 的典型设计,其中每个阶段的深度(即瓶颈残差单元的数量)和特征维度(通道数和空间尺寸)都有所不同。
下面是每个块的具体配置解释:
- block1:
- 名称:'block1'
- 瓶颈残差单元类型:
resnet_v1.bottleneck
- 配置:
[(256, 64, 1)] * 2 + [(256, 64, 2)]
这意味着block1
包含两个瓶颈残差单元,每个单元的输入/输出通道数为 256,中间 3x3 卷积层的通道数为 64,且步长为 1(无下采样)。随后是一个步长为 2 的瓶颈残差单元,用于将特征图的空间尺寸减半。
- block2:
- 名称:'block2'
- 瓶颈残差单元类型:
resnet_v1.bottleneck
- 配置:
[(512, 128, 1)] * 3 + [(512, 128, 2)]
block2
包含三个步长为 1 的瓶颈残差单元,每个单元的输入/输出通道数为 512,中间 3x3 卷积层的通道数为 128。接着是一个步长为 2 的瓶颈残差单元,用于进一步减小特征图的空间尺寸。
- block3:
- 名称:'block3'
- 瓶颈残差单元类型:
resnet_v1.bottleneck
- 配置:
[(1024, 256, 1)] * 5 + [(1024, 256, 1)]
block3
包含五个步长为 1 的瓶颈残差单元,每个单元的输入/输出通道数为 1024,中间 3x3 卷积层的通道数为 256。注意这里最后一个瓶颈残差单元的步长也是 1,意味着该块不会改变特征图的空间尺寸。
- block4:
- 名称:'block4'
- 瓶颈残差单元类型:
resnet_v1.bottleneck
- 配置:
[(2048, 512, 1)] * 3
block4
包含三个步长为 1 的瓶颈残差单元,每个单元的输入/输出通道数为 2048,中间 3x3 卷积层的通道数为 512。这个块同样不会改变特征图的空间尺寸。
- block5:
- 名称:'block5'
- 瓶颈残差单元类型:
resnet_v1.bottleneck
- 配置:
[(2048, 512, 1)] * 3
block5
的配置与block4
相同,也是由三个步长为 1 的瓶颈残差单元组成,每个单元的输入/输出通道数为 2048,中间 3x3 卷积层的通道数为 512。
这种配置通常对应于较深的 ResNet 版本,如 ResNet-101 或 ResNet-152,其中每个阶段包含多个瓶颈残差单元。每个阶段的输出特征图尺寸和通道数逐渐减小,而网络的深度(即残差块的数量)逐渐增加,以提取更高级别的特征。这种设计使得网络能够同时捕捉细粒度和粗粒度的信息,从而提高其性能。
这里通过查看可以发现最后都要通过池化将特征变成一维,之后经过全连接层得出分类的概率。
def build_base(self):
with tf.variable_scope(self.scope, self.scope):
net = resnet_utils.conv2d_same(self.image, 64, 7, stride=2, scope='conv1')
net = tf.pad(net, [[0, 0], [1, 1], [1, 1], [0, 0]])
net = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID', scope='pool1')
return net
-
def build_base(self):
- 这行定义了一个名为
build_base
的方法,它是某个类的一部分(由于有self
参数)。
- 这行定义了一个名为
-
with tf.variable_scope(self.scope, self.scope):
- 使用
tf.variable_scope
上下文管理器来创建一个变量作用域。这有助于组织和管理网络中的变量。self.scope
是这个变量作用域的名字,它可能是一个字符串,定义了变量作用域的名称。
- 使用
-
net = resnet_utils.conv2d_same(self.image, 64, 7, stride=2, scope='conv1')
- 调用
resnet_utils.conv2d_same
函数(这个函数可能是 ResNet 工具集中定义的),来对输入图像self.image
进行卷积操作。 - 卷积核的大小是 7x7,输出通道数是 64,步长(stride)是 2。
scope='conv1'
参数用于为这个卷积层创建一个命名空间,这样 TensorFlow 可以更好地组织和管理这个层的变量。
- 调用
-
net = tf.pad(net, [[0, 0], [1, 1], [1, 1], [0, 0]])
- 对
net
进行填充(padding)。这里使用了tf.pad
函数,填充的方式是在高度和宽度两侧各填充 1 个像素。这种填充通常用于保持卷积操作后的空间尺寸不变,特别是在使用步长为 2 的卷积或池化层时。
- 对
-
net = slim.max_pool2d(net, [3, 3], stride=2, padding='VALID', scope='pool1')
- 使用 Slim(一个 TensorFlow 的高级 API)的
max_pool2d
函数对net
进行最大池化操作。 - 池化核的大小是 3x3,步长是 2。
padding='VALID'
表示不使用填充。scope='pool1'
参数用于为这个池化层创建一个命名空间。
- 使用 Slim(一个 TensorFlow 的高级 API)的
-
return net
- 返回处理后的
net
,这个net
现在包含了卷积和池化操作的结果,将作为后续网络层的输入。
- 返回处理后的
net = tf.pad(net, [[0, 0], [1, 1], [1, 1], [0, 0]])
中,net
是一个四维张量(可能是图像数据,具有形状[batch_size, height, width, channels]
),而paddings
参数是一个形状为[4, 2]
的张量,它指定了每个维度上的填充大小。
具体地,[[0, 0], [1, 1], [1, 1], [0, 0]]
表示:
- 在第一个维度(通常是批量大小)上不进行填充(即填充大小为
[0, 0]
)。 - 在第二个维度(高度)上,在顶部填充1个单位,在底部填充1个单位。
- 在第三个维度(宽度)上,在左侧填充1个单位,在右侧填充1个单位。
- 在第四个维度(通道数)上不进行填充(即填充大小为
[0, 0]
)。
conv2d_same
这个名字暗示了这个函数可能会使用 "SAME" 的填充方式,这意味着卷积操作后输出的特征图(feature map)将与输入有相同的高度和宽度(如果步长(stride)为1的话;如果步长大于1,则输出的特征图尺寸会减小)。
with slim.arg_scope(resnet_arg_scope(is_training=is_training)):
head, _ = resnet_v1.resnet_v1(net,
self.blocks[cfg.RESNET.FIXED_BLOCKS:-2], # - (Restore_flag - 3)
global_pool=False,
include_root_block=False,
scope=self.scope)
return head
net
: 输入到 ResNet 网络的张量。self.blocks[cfg.RESNET.FIXED_BLOCKS:-2]
: 这里选择了self.blocks
列表中的一部分块来构建 ResNet。cfg.RESNET.FIXED_BLOCKS
定义了开始构建的块的索引,而:-2
表示从列表的倒数第二个元素开始排除所有后续元素。这通常意味着网络将构建到倒数第二个块,保留最后两个块(通常是平均池化和全连接层)以便进行特定任务的定制。global_pool=False
: 指示不在网络的最后进行全局池化。include_root_block=False
: 表示不包括 ResNet 的根块(即初始的 7x7 卷积和 3x3 最大池化层)。scope=self.scope
: 为该部分的网络定义一个命名空间,有助于组织计算图中的操作。