视觉问答

您所在的位置:网站首页 vgg特征提取 视觉问答

视觉问答

2023-03-23 20:18| 来源: 网络整理| 查看: 265

一、背景

本教程用于记录自己学习视觉问答代码编写的学习过程。

二、VQA关键部分代码

标准VQA模型包括3个模块,分别是图像特征提取模块,文本特征提取模块,以及特征融合后的分类模块。标准VQA模型如下图所示:

1. 图像特征提取

一般我们用预训练好的CNN模型,这里常用的包括vgg16/19,resnet-152/101,faster rcnn。主要的是这三类,当然你也可以自己写cnn或者用其他模型。

(1)使用预训练的vgg16/19来提取图像特征

这里以vgg16为例。我们需要下载预训练好的vgg16和相应的python文件。

常用的预训练好的vgg16有两种格式:

①预训练的vgg16.tfmodel文件和vgg16.py文件:这里的tfmodel可以从这里下载:https://github.com/ry/tensorflow-vgg16,如果你没有安装caffe,那么就用种子下载训练好的文件吧:

下载上面的几个文件,然后种子文件用迅雷就可以下载。

②vgg16.npy文件和vgg16.py文件:另外一种就是npy文件格式,我个人也是用这种格式比较多。文件我传到了自己的网盘上(链接:https://pan.baidu.com/s/1o2-h5Vq6Ff4mCAHWMV7e8w     提取码:3pcx)。

③vgg19.npy文件和vgg19.py文件:和vgg16类似,文件的下载地址可以参考我这一篇博客:对抗生成网络学习(八)——DeblurGAN实现运动图像的去模糊化(tensorflow实现)。

由于自己不太用tfmodel文件,所以这里先主要介绍npy文件如何提取图像特征。

这里先声明一下,这个vgg16.py文件我简单修改过,初始化的时候需要传入一个config类,这个类里面定义了很多参数,需要用到config的地方包括:

class vgg_16(): def __init__(self, config): self.model_path = config.vgg_path self.image_height = config.image_height self.image_width = config.image_width self.data_dict = np.load(self.model_path, encoding='latin1').item() print("vgg16.npy file loaded")

接下来加载vgg16,首先是vgg16初始化,然后用到一个占位符,传入vgg中建立模型。

vgg = vgg_16(config) images = tf.placeholder("float", [None, image_height, image_width, dim_image]) with tf.name_scope("content_vgg"): vgg.build(images)

这时候如果我需要提取一张图中的特征,就可以用下面的方法,这里我取出了‘fc7’层的特征,最终的特征保存在img_vgg_faeture变量中:

img = resize(img, (image_height, image_width)) img = img.reshape((1, image_height, image_width, dim_image)) sess = tf.Session() sess.run(tf.global_variables_initializer()) img_vgg_feature = self.sess.run(vgg.fc7, feed_dict={images: img}) (2)使用预训练的resnet-152/101来提取图像特征

非常幸运的是,tensorflow里面集成好了resnet-50/101/152,我们可以直接调用。调用需要用到的库包括:

from tensorflow.contrib.slim.nets import resnet_v2 from tensorflow.contrib.slim.python.slim.nets.resnet_utils import resnet_arg_scope

调用方法:

with slim.arg_scope(resnet_arg_scope(is_training=False)): net, end_points = resnet_v2.resnet_v2_152(data_set)

resnet-50和101都是一样的调用思路,net是网络最终的返回,大小是(1,1,1,2048),endpoint里面保存了resnet各个网络层节点信息。加入我们要用resnet提取图片特征,抽出中间的一个层,那么可以用如下的方法:

# 随机读取一张图片,将其变成448*448的 image = io.imread('0.png') image = transform.resize(image, (448, 448)) # 由于一张图像是一个三维立方体,我们要将其变为四维张量 data_set = np.empty((1, 448, 448, 3), dtype="float32") data_set[0, :, :, :] = image # 构建resnet网络 with slim.arg_scope(resnet_arg_scope): net, end_points = resnet_v2.resnet_v2_152(data_set) # 运行网络 init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) sess.run(net) # 随便记录其中中间的某一层 show_img = end_points['resnet_v2_152/block1/unit_1/bottleneck_v2/conv1'][0].eval() # 输出一下这一层的大小,是(112,112,64) print(show_img.shape) # 随便拿一个feature map输出一下 io.imshow(show_img[:, :, 0]) io.show()

这个feature map输出的结果和原图如下所示:

最后还有resnet_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默认1e-5 batch_norm_scale=True): # BN的scale默认值 batch_norm_params = { # 定义batch normalization(标准化)的参数字典 '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.arg_scope将[slim.conv2d]的几个默认参数设置好 [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 normalizer_params=batch_norm_params): with slim.arg_scope([slim.batch_norm], **batch_norm_params): with slim.arg_scope([slim.max_pool2d], padding='SAME') as arg_sc: # ResNet原论文是VALID模式,SAME模式可让特征对齐更简单 return arg_sc # 最后将基层嵌套的arg_scope作为结果返回

(3)使用预训练的faster rcnn来提取图像特征

2. 文本特征提取

文本特征常用的提取方法一般有两种,LSTM或者GRU(其实GRU是LSTM的一个变种),但是在做文本特征提取之前,我们需要先将文本转成向量。

(1)embedding嵌入

这一步的目的是将文本转化为向量,当然文本转向量的方法有很多种,一种最简单的API如下:

embeddings = tf.Variable( tf.random_uniform([self.vocabulary_size, self.word_embedding_dim], -1.0, 1.0))

也就是我从[-1 1]之间随机初始化,初始化的参数服从均匀分布,生成一个[vocabulary_size, word_embedding_size]的向量。这里的vocabulary_size是所有unique word的数量,word_embedding_size就是每个单词要嵌入为多少长度的向量,一般设置300的比较多,这样做的意思就是说,我现在将所有单词建议一个映射表,每一个单词对应的300维的向量,当然这个映射表是随机建立的。

下面要考虑的就是对unique word建立id表了,也就是对每个单词建立一个编号,根据这个编号,我们才能找到他在映射表中对应的向量:

ques_word_list = [] ques_word_list.append(ques_list[q].split(' '))

上面这个问题就是找到ques_list中第q个问题,然后根据" "将问题中的所有单词分开,添加到ques_word_list中。

添加的ques_word_list一般情况是个二维的,就是[ques_num, word_in_each_question],需要将其变为一维的:

unique_word_in_q = [i for item in ques_word_list for i in item] # 二维变一维

接下来就是统计这个数组中的唯一单词数量了:

all_word = list(set(list(set(unique_word_in_q)))

最后就可以根据这个all_word构建一个word和id的编号表了:

word2id = {word: index+1 for index, word in enumerate(all_word)}

(2)word2vec可以看:

(3)word2glove可以看:https://nlp.stanford.edu/projects/glove/

三、其他 1. 注意力机制

使用注意力的方式有很多种,下面介绍一些常用的

(1)SAN中的注意力

SAN中的注意力比较简单,就是将问题向量作为一个query,来找图像向量中的关键部分。算法也很简单,两个向量结合在做一个tanh,最后再加上权重和bias做一次softmax就行了,代码如下:

def compute_attention(self, image_tensor, question_tensor, out_dim, dropout=True): # 先将嵌入拉伸为1维向量 img = tf.nn.tanh(tf.layers.dense(image_tensor, out_dim)) ques = tf.nn.tanh(tf.layers.dense(question_tensor, out_dim)) # 连接问题和图像 ques = tf.expand_dims(ques, axis=-2) IQ = tf.nn.tanh(img + ques) if dropout: IQ = tf.nn.dropout(IQ, self.drop_out_rate) # 再将连接好的拉伸为1维向量,再reshape temp = tf.layers.dense(IQ, 1) temp = tf.reshape(temp, [-1, temp.shape[1]]) # softmax获得注意力 p = tf.nn.softmax(temp) p_exp = tf.expand_dims(p, axis=-1) att_layer = tf.reduce_sum(p_exp * image_tensor, axis=1) # 最终的注意力结果 final_out = att_layer + question_tensor return p, final_out

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3