编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

机器学习:使用MXNet Gluon进行视觉问答的关系网络

wxchong 2024-07-23 21:04:23 开源技术 35 ℃ 0 评论

概述

视觉问题回答(VQA)是一种多模式任务,通过字幕或问卷将文本和图像联系起来。例如,有了一条繁忙的高速公路图片,可能会有一个问题:“那里有多少辆红色汽车?”或“摩托车比汽车还多吗?”。这是一项非常具有挑战性的任务,因为它需要对文本和图像以及它们之间的关系有高层次的理解。今天我们将介绍2016年Deep Mind提出的 Relational Network 架构,该架构比以前的所有方法都要大幅度提升。

机器学习中的关系网络(Relational Network)的主要思想不仅在概念上易于理解,而且还广泛适用于其他领域。

数据描述

在本文中,我们将使用CLEVR的简化版本:sort-of-CLEVR3,其中包含10,000个图像,包含20个问题。图像包含来自两个候选形状(正方形或圆形)和6种颜色候选(红色,绿色,蓝色,橙色,灰色和黄色)的随机生成的对象。对于给定的图像,其中10个问题是关系问题,另外10个问题是非关系问题。

即,通过显示的示例图像,我们可以生成非关系问题,例如:

  • 绿色物体是否放在图像的左侧?=>是
  • 橙色物体是否放在图像的顶部?=>不

和关系问题:

  • 最接近红色物体的物体的形状是什么?=>方
  • 距离橙色物体最远的物体的形状是什么?=>圈

网络架构

关系网络的通用公式可以写成如下:

首先,我们将数据转换为对象(Oi and Oj)。然后,在将函数g应用于每个对象组合之后,我们在将它们传递给另一个函数f之前将它们相加。

关系网络中最重要的事情是考虑一个对象而不管域。因此,我们可以在一个网络内针对不同的域进行学习。因此,

VQA问题中关系网络的过程如下:

1.生成要素图

  • 问题:使用LSTM嵌入
  • 图像:CNN(4个卷积层)

2.应用RN(关系网络)步骤

  • 生成带有问题的对象对:我们为从问题和图像中提取的对象进行所有可能的组合。
  • 添加粒子图像特征的协调位置信息。
  • 应用前4层MLP(函数g)
  • 对元素g的输出应用元素
  • 应用接下来的两层MLP(函数f)

为了更好地理解,让我们来看一个简单的例子。假设有三个3x3 CNN特征映射和一个1x4查询特征向量。然后,我们最终得到9 * 9 = 81个特征向量组合。请注意,由于我们创建了81个新数据对,因此第一个维度将比原始批量大小膨胀81倍。在应用函数g之后,那些膨胀的批次将通过元素总和减少到原始批量大小,然后传递给函数f。假设每个单独的图像对象的长度为3,问题对象的长度为4,我们可以将结构描述如下:

坐标位置矢量是一种提供像素位置信息的矢量。坐标位置的结构如下:

def cvt_coord(i):

return [(i/5-2)/2., (i%5-2)/2.]

Python代码

让我们用Gluon实现关系网络模型。对于数据生成,我们使用此代码(https://github.com/kimhc6028/relational-networks/blob/master/sort_of_clevr_generator.py) 生成输入数据。首先,我们使用sort_of_clevr_generator.py创建输入数据。在执行生成代码之前,我们需要安装必要的包。

pip install opencv-python

python sort_of_clevr_generator.py

运行代码后,您将看到以下消息。生成的图像之一显示在下图中。该问题作为One-hot编码特征向量提供。因此,我们不在示例代码中考虑LSTM编码部分。

building test datasets...

building train datasets...

saving datasets...

datasets saved at ./data/sort-of-clevr.pickle

为了加快培训速度,我们将使用多个gpu。为此,您可以设置context参数,如下所示:

GPU_COUNT = 2

def setting_ctx(GPU_COUNT):

if GPU_COUNT > 0 :

ctx = [mx.gpu(i) for i in range(GPU_COUNT)]

else :

ctx = [mx.cpu()]

return ctx

ctx = setting_ctx(GPU_COUNT)

然后我们需要为关系网络定义一个类。它由三部分组成:卷积,DNN(g)和分类器(f)。卷积部分有4个卷积层,可以定义如下:

class ConvInputModel(nn.HybridSequential):

def __init__(self,**kwargs):

super(ConvInputModel,self).__init__(**kwargs)

with self.name_scope():

self.add(

nn.Conv2D(channels=24, kernel_size=3, strides=2, padding=1, activation='relu'),

nn.BatchNorm(),

nn.Conv2D(channels=24, kernel_size=3, strides=2, padding=1, activation='relu'),

nn.BatchNorm(),

nn.Conv2D(channels=24, kernel_size=3, strides=2, padding=1, activation='relu'),

nn.BatchNorm(),

nn.Conv2D(channels=24, kernel_size=3, strides=2, padding=1, activation='relu'),

nn.BatchNorm()

)

分类器(函数f)非常简单,只有2个全连接层,Python代码如下所示:

class FCOutputModel(nn.HybridSequential):

def __init__(self,**kwargs):

super(FCOutputModel,self).__init__(**kwargs)

with self.name_scope():

self.add(

nn.Dense(256, activation='relu'),

nn.Dropout(0.5),

nn.Dense(10)

)

关系网络体系结构的核心可以分为以下几个步骤:

  • 从卷积编码器生成要素图。
  • 添加坐标信息以考虑像素位置。
  • 连接所有可能的图像特征映射对,并连接问题特征。然后,应用FCN(函数g)。
  • 计算元素总和以缩放回原始批量大小并使用分类器(函数f)。

Python代码如下:

class RN_Model(nn.HybridBlock):

def __init__(self, **kwargs):

super(RN_Model, self).__init__(**kwargs)

with self.name_scope():

self.conv = ConvInputModel()

self.g_fc1 = nn.Dense(256, activation='relu', flatten=False)

self.g_fc2 = nn.Dense(256, activation='relu', flatten=False)

self.g_fc3 = nn.Dense(256, activation='relu', flatten=False)

self.g_fc4 = nn.Dense(256, activation='relu', flatten=False)

self.f_fc1 = nn.Dense(256, activation='relu')

self.fcout = FCOutputModel()

def hybrid_forward(self, F, x, qst, coord_tensor):

#input size = (64 * 3 * 75 * 75)

x = self.conv(x) ## x = (64 * 24 * 5 * 5)

x_flat = x.reshape(shape=(0, 0, -3))

x_flat = F.swapaxes(x_flat,1,2) ## (64 * 25 * 24)

##add coordinates

x_flat = F.concat(x_flat, coord_tensor,dim=2)

##add question

qst = qst.expand_dims(1)

qst = F.repeat(qst,repeats=25,axis=1)

qst = qst.expand_dims(2)

# cast all pairs against each other

x_i = x_flat.expand_dims(1)

x_i = F.repeat(x_i,repeats=25,axis=1)

x_j = x_flat.expand_dims(2)

x_j = F.concat(x_j,qst,dim=3)

x_j = F.repeat(x_j,repeats=25,axis=2)

#concatenate all

x_full = F.concat(x_i,x_j,dim=3)

x_ = self.g_fc1(x_full.reshape((0, -1, 63)))

x_ = self.g_fc2(x_)

x_ = self.g_fc3(x_)

x_ = self.g_fc4(x_)

x_g = x_.sum(1)

##### f part #######

x_f = self.f_fc1(x_g)

return self.fcout(x_f)

使用adam优化器和softmax交叉熵损失

#set optimizer

trainer = gluon.Trainer(model.collect_params(),optimizer='adam')

#define loss function

loss = gluon.loss.SoftmaxCrossEntropyLoss()

现在,我们使用split_and_load()函数加载数据,以便轻松使用多个GPU资源。它会自动划分输入数据并将每个子集分配给不同的GPU。

for i, (rel_img, rel_qst, rel_label, norel_img, norel_qst, norel_label) in enumerate(dataloader_train):

# Relational data

rel_imgs = gluon.utils.split_and_load(rel_img, ctx)

rel_qsts = gluon.utils.split_and_load(rel_qst, ctx)

rel_labels = gluon.utils.split_and_load(rel_label, ctx)

# No-relationtional data

norel_imgs = gluon.utils.split_and_load(norel_img, ctx)

norel_qsts = gluon.utils.split_and_load(norel_qst, ctx)

norel_labels = gluon.utils.split_and_load(norel_label, ctx)

with autograd.record():

rel_losses = [loss(model(X,Y, coord),Z) for X, Y, coord, Z in zip(rel_imgs, rel_qsts, coord_tensors, rel_labels )]

for l in rel_losses:

l.backward()

trainer.step(rel_img.shape[0])

实施细节可以在这里找到(https://github.com/seujung/relational-network-gluon/blob/master/relation_reasoning_code_use_multi_gpu.ipynb)。

结果

在关系问题中,我们在非关系问题和91%准确度(对于测试数据)中实现了99%的准确性。

结论

  • 关系网络使用简单的算法结构在VQA域中表现出了很好的性能。
  • Gluon提供了一个简单直接的API来实现模型训练的多gpu使用。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表