结合论文和代码来给大家详细介绍一下这篇论文
摘要
在许多实际的应用程序中,相关对象具有多个标签,并且可以表示为一个实例包,多实例多标签(MIML)学习为处理这类任务提供了一个框架,并在各个领域都表现出了良好的性能,在MIML设置中,实例的特征表示通常对最终性能有很大的影响;受近年来深度学习研究的启发,本文提出了一种利用深度神经网络生成MIML实例表示的深度MIML网络。深度MIML结构的子概念学习部分保留了MIML算法的实例-标签关系发现能力;也就是说,它可以自动定位能够触发标签的关键输入实例。通过对不同数据域的实验验证了DeepMIML网络的有效性。
介绍
在许多实际应用程序中,感兴趣的对象有其固有的结构,可以将其表示为一个实例包,并且在包级别上关联多个标签,例如,在文本分类中,每个文档可能有作为实例的句子,并在文档级别分配多个标签。
具体的来说,从MIML的角度来看,训练数据
{
(
X
1
,
Y
1
)
,
.
.
.
(
X
m
,
Y
m
)
}
\{(X_{1},Y_{1}),...(X_{m},Y_{m})\}
{(X1,Y1),...(Xm,Ym)}由
m
m
m个实例包组成,且每一个实例包
X
i
X_{i}
Xi都能被表示成
z
i
z_{i}
zi个实例如
{
x
i
,
1
,
x
i
,
2
,
.
.
.
x
i
,
z
i
}
\{x_{i,1},x_{i,2},...x_{i,zi}\}
{xi,1,xi,2,...xi,zi}.输出
Y
i
Y_{i}
Yi是所有可能标签
{
y
1
,
y
2
,
.
.
.
y
L
}
\{y_{1},y_{2},...y_{L}\}
{y1,y2,...yL}的子集,在这里,
L
L
L是所有可能标签的总数
在本文中,我们提出了深度MIML网络。这是一个用于MIML的深度神经网络模型。这个模型天生具有深度模型的表示学习能力,在DeepMIML中,我们不需要使用另一个实例生成器来生成实例描述。相反,模型本身将完成实例表示的生成和后续的学习过程。此外,通过精心设计的子概念层,MIML具有明显的优势,即,发现输入模式与输出语义标签之间的潜在关系
提出的方法
在本节中,我们将首先介绍2D子概念层,它对单个实例和输出标签之间的关系进行建模。其次,我们将这个想法扩展到一个三维的子概念层,它可以在一个MIML的视角中使用。最后,我们介绍了深度MIML网络,并讨论了它发现实例标签关系的能力。
用于单一示例的2D子概念层
在语义丰富的任务当中,标签可能传递着复杂的信息,这样我们提出一个新的二维神经网络层(子概念层),它可以被训练为每个实例和标签的子概念之间的匹配分数建模,具体来说,一旦获得了示例
x
x
x的表示形式,我们提出一个全连接2D层(子概念层),大小为
L
∗
K
L*K
L∗K,在这里,
L
L
L是分类的个数,也就是所有标签的总数,而
K
K
K是子概念的个数.对于一个给定的示例向量
x
x
x,2D子概念层的结点
(
i
,
j
)
(i,j)
(i,j)表示的是这个示例
x
x
x和第
j
j
j个标签的第
i
i
i个子概念之间的匹配得分,这样的话,第
(
i
,
j
)
(i,j)
(i,j)个结点有如下的激活形式:
c
i
,
j
=
f
(
w
i
,
j
x
+
b
i
,
j
)
(
1
)
\quad \quad \quad \quad \quad \quad \quad\quad\quad c_{i,j}=f(w_{i,j}x+b_{i,j}) \quad \quad \quad\quad\quad \quad \quad\quad \quad \quad \quad (1)
ci,j=f(wi,jx+bi,j)(1)
其中
f
(
.
)
f(.)
f(.)是激活函数,且权向量
w
i
,
j
w_{i,j}
wi,j是第
j
j
j个标签的第
i
i
i个子概念的匹配模板,且我们选择的激活函数为修正线性单元(
R
e
L
U
ReLU
ReLU):
f
(
z
)
=
m
a
x
(
0
,
z
)
(
2
)
\quad\quad \quad \quad \quad\quad\quad \quad \quad \quad f(z)=max(0,z) \quad\quad \quad \quad\quad \quad \quad \quad \quad \quad \quad (2)
f(z)=max(0,z)(2)
2D子概念层就是用于为单个实例和所有标签的所有子概念之间进行分数的匹配,为了得到一个标签级别的预测,也就是单个示例对应的所有标签的预测,后面采用一个列级别的池化操作,最终产生一个
L
∗
1
L*1
L∗1的得分层,得分层上的每一行都代表着这个示例
x
x
x和某一个标签对应的匹配得分
大致来说,就是
x
x
x是输入的一个实例,经过一个全连接层,
L
L
L是对应的所有标签的个数,
K
K
K是每个标签对应的子概念(也可以叫做属性值),然后通过列最大池化,也就是寻找每一个标签对应所有
K
K
K的预测值,并找到一个最大的值当做对此标签的预测值,最终形成一个
L
∗
1
L*1
L∗1的向量,也就是说,池化层用来捕获输入实例与每个标签的最终匹配分数之间的关系.当然,对每一个标签来说,实例
x
x
x对应的子概念的个数可以少于
K
K
K个,并不会影响最终的结果(因为是取最大值),所以列池化具有鲁棒性.
与其他常见的网络结构相比,有几点值得强调:
∙
\bullet
∙ 与通常的2D卷积特征映射(带有局部连接过滤器)不同,我们在这里提出的2D子概念层与输入实例向量是完全连接的,激活可以表示为每个标签的子概念与实例之间的匹配得分。
∙
\bullet
∙ 与通常的一维全连通层不同,二维子概念层以一种可解释的方式排列。也就是说,层是一个直接的结果,每个列是每个标签的得分向量,而列向量中的每个元素是该特定标签的子概念的匹配得分。这使得层更容易直观地解释,更重要的是,它可以用来发现实例-标签关系。
∙
\bullet
∙ 最大池操作通常用于向下采样,以减少参数的总数。然而,我们在这里使用的最大池操作是定位最大匹配分数,它可以在最大池化层中表示。
简单地说,2D子概念层后面的池化层捕获输入模式与每个标签的最终匹配分数之间的关系。此外,每个子概念的匹配分数可以在实例-标签关系发现中进一步使用,这将在后面的部分中进行讨论。
用于多个示例的3D子概念层
当输入有多个实例时,多个实例组成一个包作为输入(这里我们假设每个包具有相同数量的实例,对于具有不同数量实例的包,则用0填充),我们可以将二维子概念层的思想推广到MIML中。其基本思想是通过叠加多个2D层来将二维子概念层扩展成三维张量层,张量的每个“切片”是每个实例的二维子概念层,如前一节所述.
也就是说,给定一个包
X
i
X_{i}
Xi,我们为每一个包中的示例
x
k
,
i
x_{k,i}
xk,i构建一个2D子概念层,并且将这些2D子概念层堆叠为3D子概念层,3D子概念层的通道数是包中实例的个数,则3D子概念层中第
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)个结点的激活对应的是包
X
i
X_{i}
Xi中第
k
k
k个实例
x
k
,
i
x_{k,i}
xk,i与标签
j
j
j中的第
i
i
i个子概念相对应的匹配分数,且相同子概念上的每个实例的匹配权值是不同的。
为了研究实例标签的关系,我们执行了两次池操作,具体来说,首先通过一个垂直最大池化,将标签中子概念获得的最大值提取出来,从而获得一个二维的层,大小为
L
∗
M
L*M
L∗M,
L
L
L是所有标签的个数,
M
M
M是实例的个数,对于这个2D池化层有一个清晰的解释:每个位置的节点
(
i
,
j
)
(i,j)
(i,j)为标签
j
j
j上的实例
i
i
i建模匹配得分。因此,我们将第一个池化层称为实例-标签得分层,通过检查这一层上面的值可以来理解实例与标签的对应关系,可以得知对应的标签中分数最高的子概念是哪个。
其次再通过一个水平最大池化,将一个标签对应的所有示例的预测值中最大值提取出来作为标签对包的预测值,最终获得一个
L
∗
1
L*1
L∗1的的分层,这个层就是获得整个示例包对应的每一个标签的匹配分数
深度MIML网络
当输入一张图片的时候,如果采用VGG16的网络,最终是采用全连接层获得一个1维的向量,然而,这种方法将输入看作一个整体,因此,输入图像的分布式稠密一维表示表示不能很好地显示局部信息(实例),那我们需要对图片进行转化,变成多个实例作为一个包输入,因此,我们采取如下办法:在深度卷积神经网络结构中,在最后一个完全连接的1D层之前的那一层是一个3D卷积张量层,通常是形状为1414512的图形(例如VGG-16 网络),那如果我们把维度为512的14*14(196)个向量中的每一个向量当做图片中的一个实例表示,那么就有196个实例组合成一个包,这样输入就相当于196个实例,因此,在MIML透视图中,我们使用卷积层中的表示,而不是FC层中的表示,以获得大量实例表示。
这样,DeepMIML网络就可以从原始的输入(如一张图片)自动生成示例包(包中有196个实例),在示例级别上学习每个标签的每个子概念的评分函数,并且最终在包级上进行最终预测。
如上图所示,原始的输入数据首先被输入到一个实例生成器设备中,当输入数据为图片时,可以采用类似的VGG网络去掉全连接层从而获得实例包的表示,接着,我们将一个3D子概念层和两个池化层连接到到此实例生成器后,最后添加一个与标签数量相等的全连接层。
有三点值得强调:
∙
\bullet
∙首先,可以为特定的任务使用更复杂的实例生成器。例如,对于图像任务,可以首先应用一些方法(让图片在进入卷积网络之前创建一些小的边界框)来获得更好的实例表示。
∙
\bullet
∙其次,在某些情况下,我们无法访问原始数据,并且输入已经被编码到实例包中,这样我们就直接使用DeepMIML框架把输入投射到3D子概念层
∙
\bullet
∙通过改变子概念的维度,可以很容易将网络退化为单实例多标签(2D子概念层)或者多示例单标签(
L
=
1
L=1
L=1,输入为一个实例包)
实例-标签的关系发现
主要是用于定位能够触发输出标签的关键实例的发现过程,也就是说,输出某个标签,我们可以从3D子概念层当中找到是哪个实例对应的得分最高,也可以找到对应的哪个子概念得分最高。
具体来说,在MS-COCO数据集上,每个输入图像被转化成包含196个实例的包,并且有80个标签可以被标记,则相应的示例-标签的分层的大小为19680,它给出了每个实例和每个标签之间的所有匹配的分,这样就可以发现标签和实例之间的关系,同时,可以检测触发一个特定标签的关键实例,通过反向跟踪实例在池化层中匹配得分最高的位置。对于一个VGG16架构,关键实例的中心像素定位公式为:
(
x
,
y
)
=
(
c
o
n
v
x
∗
16
+
8
,
c
o
n
v
y
∗
16
+
8
)
(
3
)
\quad \quad \quad \quad \quad(x,y)=(conv_{x}*16+8,conv_{y}*16+8) \quad\quad \quad \quad \quad(3)
(x,y)=(convx∗16+8,convy∗16+8)(3) 其中
c
o
n
v
x
,
c
o
n
v
y
conv_{x},conv_{y}
convx,convy都是1414实例包中的2D索引,这样可以达到与注意机制类似的效果。
实验
我们主要是介绍图像任务,下图为实例和标签的匹配图,采用的是MS-COCO数据集,对于要预测的给定输入图像,从VGG-16网络中提取196(14*14)个实例,其中有80个候选标签,实例标签评分层的激活可以被可视化并解释为:每一列是所有实例和一个特定标签之间的匹配分数。例如,突出显示的绿色列是所有实例与标签“Bottle”的匹配分数。同样,每一行都可以解释为一个特定实例和所有可能的标签之间的匹配分数。
∙
\bullet
∙用于图像任务的DeepMIML:我们使用MS-COCO数据集包含82,783张用于训练的图像,总共有80个标签。为了获得每个图像一个包的实例,我们使用了一个预训练VGG-16网络向上到最后一个卷积层.
如上图所示,展示了通过发现测试集上的实例-标签关系而得到的预测结果。通过子概念层可以很容易地实现注意机制,如前一节所讨论的。
表3为比较结果。与使用普通的VGG网络相比,通过简单地添加一个额外的子概念层,我们确实在精度方面获得了性能提升
与更复杂的CNN-RNN模型相比,我们的方法表现出次优性能。这是因为我们在这里使用的实例生成器非常简单和直接,像停车计时器这样的小对象无法有效地编码到实例中。另一方面,对于图像任务,CNN-RNN是一种很好的状态-技术模型,不能很容易地应用于其他非cv任务。
实验步骤
步骤一:!!!注意,代码只能在linux上面运行,我居然尝试在win上面运行,发现不对,代码网址为https://github.com/kingfengji/DeepMIML,网址都是网上找的,要是不能转载麻烦提醒我一下!谢谢!
步骤一:首先配置环境(按照代码网址中的步骤来,环境一定要用python2.7否则会出错)用win10系统的可以考虑自带的ubuntu18.04的版本,教程为https://www.lizenghai.com/archives/26669.html
1)创建conda虚拟环境conda create -n python2 python=2.7
并激活conda activate python2
2)kera+theano的安装如下所示:https://blog.csdn.net/qq_37816453/article/details/81903339?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
步骤二:下载数据集,然后大体说明一下数据集以及需要的几个数据:
`dict_keys(['info', 'images', 'licenses', 'annotations', 'categories'])`
info:#没啥用
{'description': 'This is stable 1.0 version of the 2014 MS COCO dataset.',
'url': 'http://mscoco.org',
'version': '1.0',
'year': 2014,
'contributor': 'Microsoft COCO group',
'date_created': '2015-01-27 09:11:52.357475'
}
images: #里面的id就是annotations里面的'image_id',对照着图片的末尾数字
{'license': 3,
'file_name': 'COCO_val2014_000000016744.jpg',
'coco_url': 'http://mscoco.org/images/16744',
'height': 335,
'width': 500,
'date_captured': '2013-11-20 14:29:03',
'flickr_url': 'http://farm3.staticflickr.com/2393/2228750191_11de3ec047_z.jpg',
'id': 16744
}
annotations:#一张图片可能对应有多个annotation,
{"segmentation": [[26.23, 243.33, 44.67, 253.38, 54.73, 262.88, 65.9, 301.98, 60.87, 304.22, 67.02, 324.89, 67.57, 350.59, 71.48, 364.55, 72.6, 373.49, 68.13, 385.78, 57.52, 387.46, 59.19, 410.36, 58.64, 421.53, 56.96, 427.0, 0.0, 427.0, 0.0, 310.36, 0.0, 191.93, 2.21, 190.81, 17.3, 191.93, 22.32, 203.1, 23.44, 214.84, 22.88, 226.57, 21.21, 239.98]],
"area": 12421.789699999998,
"iscrowd": 0,
"image_id": 170785, #是对应的图片id号,
"bbox": [0.0, 190.81, 72.6, 236.19],
"category_id": 1,#对应的类别
"id": 1311645 #自身对应的annonation的id号}
categories:#有很多超类,包含很多类
[{'supercategory': 'person', 'id': 1, 'name': 'person'}, # 第一个数据
{'supercategory': 'vehicle', 'id': 2, 'name': 'bicycle'},
{'supercategory': 'vehicle', 'id': 3, 'name': 'car'},
...
不断重前面内容....... COCO 数据集一有 90个数字,但是只有80个类别,id是不连续的
...
{'supercategory': 'indoor', 'id': 90, 'name': 'toothbrush'} # 最后一个数据
步骤三:对数据进行自定义,代码文件为coco_dataset.py
,首先每张图片有相对应的标签(一共有87432张图片),我们为每一张图片都建立一个801的向量,分别对应一个标签,若图片有某些标签,则在相应位置标1,最终获得一个图片数量标签个数的矩阵,里面存放着每一个图片所对应的标签
# 获得annotations文件的存放路径
def _get_ann_file(self):
# 加载图片的id号
def _load_image_set_index(self):
#获得图片的路径从而读入图片
def image_at(self, i):
#找到id号对应的图片的绝对路径
def image_path_at(self, i):
#获得所有实例包对应的标签,大小为(82783L, 80L)
def gt_bag_labels(self):
#我们某张图片对应的多个annonation,并把对应的annotation里面的id号取出来,然后建立一个80位的数组,若图片对应某个标签,则把标签加进去
def _load_coco_annotation(self, index):
步骤四:对数据进行分批装载,代码文件为coco_data_layer.py
,主要是对图片进行预处理(更换大小,变换维度顺序,获得CHW),一批有32张图
#返回下一个minibatch的roidb索引,也就是下一批图片所对应的索引值
def _get_next_minibatch_inds(self):
#输出一批图片,data_x存放32张图片,data_y存放32张图片所对应的标签,
#'data_x'大小 (32L, 3L, 224L, 224L) 'data_y'大小(32L, 80L)
def _get_next_minibatch(self):
#不断获得每一批数据
def generate(self):
步骤五:DeepMIML模型介绍
#L是标签个数,K是子概念个数,base_model是VGG16网络删掉后面7层的然后加一些层
def create_miml_model(base_model, L, K, name="miml"):
# input: feature_map.shape = (n_bags, C, H, W)
#把最后一层输出出来
_, C, H, W = model.layers[-1].output_shape
#(C, H, W)->(通道数,高度,宽度) 512,14,14
print("Creating miml... input feature_map.shape={},{},{}".format(C, H, W))
#由于每张图片通过VGG以后变成14*14个512维的向量,所以把14*14=196作为每一个包里面的示例的个数
n_instances = H * W #196
# shape -> (n_bags, (L * K), n_instances, 1)
#有1600个1*1的卷积核,输出为1600*14*14
model.add(Convolution2D(L * K, 1, 1, name=MIML_FIRST_LAYER_NAME))
#改变为 shape -> (n_bags, L, K, n_instances)
model.add(Reshape((L, K, n_instances), name=MIML_CUBE_LAYER_NAME))
# shape -> (n_bags, L, 1, n_instances)
model.add(MaxPooling2D((K, 1), strides=(1, 1)))
# softmax
model.add(Reshape((L, n_instances)))
model.add(Permute((2, 1)))
model.add(Activation("softmax"))
model.add(Permute((2, 1)))
model.add(Reshape((L, 1, n_instances), name=MIML_TABLE_LAYER_NAME))
# shape -> (n_bags, L, 1, 1)
model.add(MaxPooling2D((1, n_instances), strides=(1, 1)))
# shape -> (n_bags, L)
model.add(Reshape((L,), name=MIML_OUTPUT_LAYER_NAME))
return model
步骤六:模型训练
#自定义数据集
dataset = COCODataset("E:\DeepMIML-master\data\coco", "train", "2014")
#分批装载数据和处理
data_layer = COCODataLayer(dataset, batch_size=batch_size)
#采用VGG16网络对输入图片进行预训练
vgg_model_path = "models/imagenet/vgg/vgg16_weights.h5"
base_model = VGG_16(vgg_model_path)
#把后面的7层删掉
base_model = Sequential(layers=base_model.layers[: -7])
#此时是512*14*14 用512个1*1的卷积核,,通过1×1卷积操作提取出输入张量的重要特征(相当于降维)
base_model.add(Convolution2D(512, 1, 1, activation="relu"))
"""
对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。
注意是暂时,对于随机梯度下降来说,
由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。
"""
base_model.add(Dropout(0.5))
deepmiml = DeepMIML(L=L, K=K, base_model=base_model)
print("Compiling Deep MIML Model...")
deepmiml.model.compile(optimizer="adadelta", loss=loss, metrics=["accuracy"])
print("Start Training...")
#samples_per_epoch->87432
samples_per_epoch = data_layer.num_images
"""
使用 Python 生成器(或 Sequence 实例)逐批生成的数据,按批次训练模型。
data_layer.generate():每一次生成一批的数据进行训练
steps_per_epoch:在声明一个 epoch 完成并开始下一个 epoch 之前从 generator 产生的总步数(批次样本)
epochs: 整数。训练模型的迭代总轮数
"""
deepmiml.model.fit_generator(data_layer.generate(),
#steps_per_epoch=samples_per_epoch,
samples_per_epoch=samples_per_epoch,
#epoch=nb_epoch)
nb_epoch=nb_epoch)
结论
在这篇论文中,我们提出了一个通用的深层模型,能够解决多领域的MIML问题。与以往依赖于手动设计实例生成器的MIML研究不同,我们提出的DeepMIML天生具有深度模型的表示学习能力,能够自动学习实例描述。子概念层很容易被合并到其他深度模型中, 使得DeepMIML能够继承的能力用于发现输入模式和输出语义标签之间的关系。在各种数据集上的实验证明了该方法的有效性