【文献阅读】LRTA——图网络解释VQA的答案生成过程(W. Liang等人,NeurIPS,2020)

一、背景

文章题目:《LRTA: A Transparent Neural-Symbolic Reasoning Framework with Modular Supervision for Visual Question Answering》

这篇文章的核心内容只有5页,但是做的挺好,方法很新,读起来也非常容易理解。

文章下载地址https://arxiv.org/pdf/2011.10731.pdf

文章引用格式:Weixin Liang, Feiyang Niu, Aishwarya Reganti, Govind Thattai and Gokhan Tur. "LRTA: A Transparent Neural-Symbolic Reasoning Framework with Modular Supervision for Visual Question Answering." arXiv preprint, arXiv: 2011.10731,

项目地址:目前项目的代码作者还没给,但是给了数据集,参考https://github.com/Aishwarya-NR/LRTA_Perturbed_Dataset

二、文章导读

The predominant approach to visual question answering (VQA) relies on encoding the image and question with a “black-box” neural encoder and decoding a single token as the answer like “yes” or “no”. Despite this approach’s strong quantitative results, it struggles to come up with intuitive, human-readable forms of justification for the prediction process. To address this insufficiency, we reformulate VQA as a full answer generation task, which requires the model to justify its predictions in natural language. We propose LRTA [Look, Read, Think, Answer], a transparent neural-symbolic reasoning framework for visual question answering that solves the problem step-by-step like humans and provides human-readable form of justification at each step. Specifically, LRTA learns to first convert an image into a scene graph and parse a question into multiple reasoning instructions. It then executes the reasoning instructions one at a time by traversing the scene graph using a recurrent neural-symbolic execution module. Finally, it generates a full answer to the given question with natural language justifications. Our experiments on GQA dataset show that LRTA outperforms the state-of-the-art model by a large margin (43.1% v.s. 28.0%) on the full answer generation task. We also create a perturbed GQA test set by removing linguistic cues (attributes and relations) in the questions for analyzing whether a model is having a smart guess with superficial data correlations. We show that LRTA makes a step towards truly understanding the question while the state-of-the-art model tends to learn superficial correlations from the training data.

VQA目前主要是将图像和问题输入到“黑箱子”编码器中进行编码,再将其对应的答案解码得到答案的具体表达。尽管这类方法有很好的效果,但是它与人类可读或可判断的形式来预测答案仍有一定距离。为了解决这类问题,我们将VQA表示为一个完全的答案生成任务,它需要进一步判断模型的预测。作者将其命名为LRTA,一种神经符号的(neural-symbolic)的VQA推理框架,可像人类一样,逐步判断每一步的结果。具体来说,LRTA首先将图像转换为一个场景图,将问题解析为多个推理指令。然后,使用递归neural-symbolic执行模块遍历所有的场景图,执行一次推理指令。最后,它会给出问题的正确答案及语言判断。实验在GQA数据集上表明,LRTA比现在最好的模型还要好。另外作者还对GQA测试集进行了扰动,移除问题中的语言线索,以分析模型是否能够从学习数据之间的表面关系。

三、文章介绍

目前大部分的VQA模型,都是对输入的图像和文本进行编码,这类方法带来复杂的计算,但预测答案往往只有简单的表示,比如用“yes”或者“no”。这类方法的缺点在于没有使用一种人类可以理解的,或者可以判断的形式来展现预测答案。另外最近的一些研究也表明,这类方法仍然具有一些错误行为,比如忽视问题中的某些重要的信息,关注图像中的错误地方,甚至一直坚持一些错误的、具有误导性的关联。

为了解决这种不充分性的问题,我们将VQA表示为一个答案生成任务(full answer generation task)而不是分类任务。它需要模型用自然语言来生成一个正确的完整的答案。我们发现现有的最好模型,很多虽然能够正确回答问题,但实际上的 推理过程都是错误的。为了解决这个问题,作者提出了LRTA (Look Read Think Answer)模型,它能够像人一样逐步来解决问题,首先,看一张图像,然后阅读问题,再之后思考进行多步推理,最后给出答案。从这个角度出发,LRTA具有四个模块:

A scene graph generation module first converts an image into a scene graph; 场景图生成模块,将图像转换为场景图

A semantic parsing module parses each question into multiple reasoning instructions; 语义解析模块,将问题解析为多次推理指令

A neural execution module interprets reason instructions one at a time by traversing the scene graph in a recurrent manner; 神经执行模块,用递归的形式遍历场景图,一次解释一个推理指令。

A natural language generation module generates a full answer containing natural language explanations. 自然语言生成模块,生成一个包含自然语言解释的答案。

四个模块都包含了隐含状态而不是只有显式输出,因此整个框架可以进行端到端训练。此外,由于LRTA可以在测试过程中,每一个模块都可以以人类可读的形式处理,因此我们很容易检查错误答案的问题出在哪里。最后实验在GQA数据集上表明,LRTA比目前最好的模型效果还要好很多(43%比28%的精度)。

总的来说,文章的贡献有三个方面:

• We formulate VQA as a full answer generation problem (instead of short answer classification) to improve explainability and discourage superficial guess for answering the questions.  将VQA作为答案生成问题处理,以提高回答问题的可解释性。

• We propose LRTA, an end-to-end trainable, modular VQA framework facilitating explainability and enhanced error analysis as compared to contemporary black-box approaches. 提出了LRTA模型,相较于传统的黑箱方法,该方法可增强对错误的分析。

• We create a perturbed GQA test set that provides an efficient way to peak into a model’s reasoning capability and validate our approach on the perturbed dataset. The dataset is available for future research - https://github.com/Aishwarya-NR/LRTA_Perturbed_Dataset 。提出了改进的GQA测试数据集。

1. 模型LRTA: Look, Read, Think and Answer

模型的整体结构如下图所示:

正如前面所说,模型结构分为四个模块:

(1)Look: Scene Graph Generation场景图的生成

给定一张图像I,图中的每一个目标就作为场景图中的一个节点,节点之间的边就表示目标之间的关系。首先,建立场景图需要进行目标检测,我们使用DETR模型进行场景中的目标检测,因为该模型移除了一些手动设计的步骤,比如非极大抑制;DETR将从ResNet50中获得图像特征,输入到非自动回归(non-autoregressive)的transformer模型中,可获得N个目标向量,即:

                                              [o1; o2; : : : ; oN] = DETR                                                 (1)

每个目标向量都表示图像中的一个目标;之后,对于每一个目标向量,DETR使用一个解码器,来获得其对应的目标类别;因为N个目标类别的预测是无序的(order-less),因此DETR计算预测的损失是通过计算第一次预测结果和真值之间的匹配情况来进行的,然后再对每一个目标向量的loss进行求和。N设置为100,另外BETR还有另一个标签"no object",代表目标向量不表示任何目标。

由于目标检测并不能学习到目标的属性,还有目标之间的关系。因此我们还在目标检测中添加了额外的属性检测,对于每一种属性的概念(比如颜色),我们创建了所有它可能出现的值(红色,粉色等);为了获得目标之间的关系,我们考虑N(N - 1)个目标向量对,即[e1; e2; : : : ; eN(N-1)],而每一个目标向量对之间的关系编码,我们将向量对输入到归一化层中来获得边向量(edge vector),再将其进行解码,获得对应的关系标签,该过程表示如下:

                                            ei;j = LayerNorm(FeedForward(oi ⊕ oj))                                (2)

目标检测、目标属性、和关系检测都是以一种监督的多任务形式(multi-task manner)行进的。对于没有关系的目标向量对,我们依然使用标签“no relation”。最终的场景图构成,应该是由N个目标向量和N(N-1)个边向量构成的,之后我们将其输入到后面的模块中。

(2)Read: Semantic Parsing语义解析

语义解析就类似于一个编译器,就是将问题字符转化为神经网络可处理的形式。我们使用分层序列生成设计:先用transformer将问题解析为M个指令向量的序列,即[i1; i2; : : : ; iM],其中第i个指令向量恰好对应第i步。为了便于理解,我们用基于transformer的指令向量解码器,将指令向量解码为人类可读的text文本。将M个指令向量输入到下一步Neural Execution Engine处理中,该过程可以表示为:

                                           [i1; i2; : : : ; iM] = Transformer(q1; q2; : : : ; qQ)                              (3)

(3)Think: Visual Reasoning with Neural Execution Engine视觉推理

Neural Execution Engine工作方式是一种循环形式:在第m次过程中,Neural Execution Engine需要第M个指令向量,和输出的场景图的遍历结果。与循环神经网络相似,每一个历史向量,都有着当前步骤所有节点的state,这些state会输入到下一次计算中。neural execution engine使用的是图神经网络,它对图的卷积操作使用的是近邻聚集策略(neighborhood aggregation scheme)。最关键的是,每个节点聚集它的近邻的特征向量来计算它的新特征向量作为下面神经层的输入。因此,在第m次,给定一个中心节点,我们能够通过后向反馈网络获得它周围节点的特征向量:场景图中,周围节点的目标向量记为ok,周围节点和中心节点的边向量记为ek;central,第m-1次的历史向量为hm-1,第m次的指令向量为im,则:

之后将每一个周围节点的特征向量取平均作为中心节点的背景向量。之后,对每一个中心节点进行节点分类,“1”表示第m次中该节点需要被遍历,“0”反之。这个分类过程采用的是softmax,表示如下

所有节点的分类结果会形成一个二值图,将其作为场景图的遍历结果,之后再计算所有目标向量的加权平均,作为历史向量hm,权重就是每一个节点分类的置信分数:

(4)Answer: Full Answer Generation答案生成

VQA之前总是将问题生成作为一个分类问题,而我们将其作为一个自然语言生成问题来处理,为了实现这一步,LRTA用了transformer模型,它需要前面所有的历史向量,最后生成的答案字符可以表示为:

                                        (a1; a2; : : : ; aA) = Transformer(h1 ⊕ h2 ⊕ · · · ⊕ hM)                         (8)

(5)End-to-End Training: From Pixels to Answers端到端训练

我们通过隐含状态(hidden states)连接4个模块,因此可以进行端到端的训练,loss采用四个模块loss的求和。

2. 实验

设置:数据集使用GQA

比较实验:如下图所示:

LRTA比LXMERT的结果要明显的更好。

对于真值场景图的验证结果如下,这里对LTRA使用visual oracle在第一步中进行验证:

验证结果表明,在短答案和所有答案中,结果表现都很好。

之后作者再对GQA数据集进行扰动,扰动后进行精度评定的结果如下:

四、小结

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 限制每个状态的扩展不超过一次的带有权值迭代的Anytime repair Astar算法(LRTA*)是一个基于Astar算法的增强版本,用于解决路径规划问题。Java是一种面向对象的编程语言,我们可以使用它来实现这种算法。下面是一个简单的实现示例: 首先,我们需要定义一个节点类,用于存储状态和计算节点的代价。 ```java public class Node { private int[] state; private double cost; public Node(int[] state, double cost) { this.state = state; this.cost = cost; } public int[] getState() { return state; } public double getCost() { return cost; } } ``` 接下来,我们需要定义一个LRTAstar类,用于执行算法。 ```java import java.util.*; public class LRTAstar { private static final int MAX_ITERATIONS = 1000; private static final double INFINITY = Double.MAX_VALUE; private int[] startState; private int[] goalState; private Map<Integer, List<Integer>> successors; private Map<Integer, Double> heuristic; private Map<Integer, Double> gValues; public LRTAstar(int[] startState, int[] goalState, Map<Integer, List<Integer>> successors, Map<Integer, Double> heuristic) { this.startState = startState; this.goalState = goalState; this.successors = successors; this.heuristic = heuristic; this.gValues = new HashMap<>(); gValues.put(Arrays.hashCode(startState), 0.0); } public List<Integer> search() { List<Integer> path = new ArrayList<>(); int[] currentState = startState; double gValue = 0.0; int iterations = 0; while (!Arrays.equals(currentState, goalState) && iterations < MAX_ITERATIONS) { List<Integer> nextStates = successors.get(Arrays.hashCode(currentState)); double minValue = INFINITY; int[] nextState = null; for (int state : nextStates) { double value = gValues.getOrDefault(state, INFINITY) + heuristic.getOrDefault(state, INFINITY); if (value < minValue) { minValue = value; nextState = new int[] {state}; } } if (nextState == null) { return null; } path.add(nextState[0]); gValue += heuristic.getOrDefault(Arrays.hashCode(nextState), INFINITY); gValues.put(Arrays.hashCode(currentState), gValue); currentState = nextState; if (!Arrays.equals(currentState, goalState)) { double hValue = heuristic.getOrDefault(Arrays.hashCode(currentState), INFINITY); gValue += hValue; int[] parentState = currentState; double parentGValue = gValues.getOrDefault(Arrays.hashCode(parentState), INFINITY); for (int i = 0; i < MAX_ITERATIONS; i++) { double minValue = INFINITY; nextState = null; for (int state : nextStates) { double value = gValues.getOrDefault(state, INFINITY) + heuristic.getOrDefault(state, INFINITY); if (value < minValue) { minValue = value; nextState = new int[] {state}; } } if (nextState == null) { return null; } double hValue = heuristic.getOrDefault(Arrays.hashCode(nextState), INFINITY); double fValue = minValue + hValue; if (fValue >= parentGValue) { break; } parentState = nextState; parentGValue = gValues.getOrDefault(Arrays.hashCode(parentState), INFINITY); } currentState = parentState; gValue = parentGValue - heuristic.getOrDefault(Arrays.hashCode(currentState), INFINITY); } iterations++; } if (Arrays.equals(currentState, goalState)) { return path; } else { return null; } } } ``` 在LRTAstar类中,我们首先定义了一些常量,例如最大迭代次数和无限大的值。然后,我们定义了一个构造函数,该函数接受起始状态,目标状态,后继状态和启发式函数作为输入,并初始化gValues映射。 接下来,我们定义了一个search方法,该方法执行LRTAstar算法。我们使用一个while循环迭代,直到当前状态等于目标状态或达到最大迭代次数。在每个迭代中,我们首先计算下一个状态的代价,并将其添加到路径中。然后,我们更新gValues映射和当前状态,并检查当前状态是否等于目标状态。如果当前状态不等于目标状态,则我们使用另一个while循环来查找当前状态的最佳邻居,并使用任意时间修复策略来更新路径和gValue。最后,我们递增迭代次数,并返回找到的路径或null。 最后,我们可以使用以下示例代码来测试LRTAstar类。 ```java import java.util.*; public class Main { public static void main(String[] args) { int[] startState = new int[] {0, 0}; int[] goalState = new int[] {3, 3}; Map<Integer, List<Integer>> successors = new HashMap<>(); successors.put(Arrays.hashCode(new int[] {0, 0}), Arrays.asList(Arrays.hashCode(new int[] {1, 0}), Arrays.hashCode(new int[] {0, 1}))); successors.put(Arrays.hashCode(new int[] {1, 0}), Arrays.asList(Arrays.hashCode(new int[] {2, 0}), Arrays.hashCode(new int[] {1, 1}), Arrays.hashCode(new int[] {0, 0}))); successors.put(Arrays.hashCode(new int[] {0, 1}), Arrays.asList(Arrays.hashCode(new int[] {1, 1}), Arrays.hashCode(new int[] {0, 2}), Arrays.hashCode(new int[] {0, 0}))); successors.put(Arrays.hashCode(new int[] {2, 0}), Arrays.asList(Arrays.hashCode(new int[] {3, 0}), Arrays.hashCode(new int[] {2, 1}), Arrays.hashCode(new int[] {1, 0}))); successors.put(Arrays.hashCode(new int[] {1, 1}), Arrays.asList(Arrays.hashCode(new int[] {2, 1}), Arrays.hashCode(new int[] {1, 2}), Arrays.hashCode(new int[] {1, 0}), Arrays.hashCode(new int[] {0, 1}))); successors.put(Arrays.hashCode(new int[] {0, 2}), Arrays.asList(Arrays.hashCode(new int[] {1, 2}), Arrays.hashCode(new int[] {0, 1}))); successors.put(Arrays.hashCode(new int[] {3, 0}), Arrays.asList(Arrays.hashCode(new int[] {2, 0}), Arrays.hashCode(new int[] {3, 1}))); successors.put(Arrays.hashCode(new int[] {2, 1}), Arrays.asList(Arrays.hashCode(new int[] {3, 1}), Arrays.hashCode(new int[] {2, 2}), Arrays.hashCode(new int[] {2, 0}), Arrays.hashCode(new int[] {1, 1}))); successors.put(Arrays.hashCode(new int[] {1, 2}), Arrays.asList(Arrays.hashCode(new int[] {2, 2}), Arrays.hashCode(new int[] {1, 1}), Arrays.hashCode(new int[] {0, 2}))); successors.put(Arrays.hashCode(new int[] {3, 1}), Arrays.asList(Arrays.hashCode(new int[] {2, 1}), Arrays.hashCode(new int[] {3, 2}), Arrays.hashCode(new int[] {3, 0}))); successors.put(Arrays.hashCode(new int[] {2, 2}), Arrays.asList(Arrays.hashCode(new int[] {3, 2}), Arrays.hashCode(new int[] {2, 1}), Arrays.hashCode(new int[] {1, 2}))); successors.put(Arrays.hashCode(new int[] {3, 2}), Arrays.asList(Arrays.hashCode(new int[] {2, 2}), Arrays.hashCode(new int[] {3, 1}))); Map<Integer, Double> heuristic = new HashMap<>(); heuristic.put(Arrays.hashCode(new int[] {0, 0}), 6.0); heuristic.put(Arrays.hashCode(new int[] {1, 0}), 5.0); heuristic.put(Arrays.hashCode(new int[] {0, 1}), 5.0); heuristic.put(Arrays.hashCode(new int[] {2, 0}), 4.0); heuristic.put(Arrays.hashCode(new int[] {1, 1}), 3.0); heuristic.put(Arrays.hashCode(new int[] {0, 2}), 4.0); heuristic.put(Arrays.hashCode(new int[] {3, 0}), 3.0); heuristic.put(Arrays.hashCode(new int[] {2, 1}), 2.0); heuristic.put(Arrays.hashCode(new int[] {1, 2}), 2.0); heuristic.put(Arrays.hashCode(new int[] {3, 1}), 2.0); heuristic.put(Arrays.hashCode(new int[] {2, 2}), 1.0); heuristic.put(Arrays.hashCode(new int[] {3, 2}), 0.0); LRTAstar lrtaStar = new LRTAstar(startState, goalState, successors, heuristic); List<Integer> path = lrtaStar.search(); if (path != null) { for (int state : path) { System.out.println(Arrays.toString(NodeUtils.getState(state))); } } else { System.out.println("No path found."); } } } ``` 在这个示例中,我们定义了一个简单的4x4网格世界,并使用它来测试LRTAstar算法。我们定义了起始状态,目标状态,后继状态和启发式函数,并创建一个LRTAstar对象。然后,我们调用search方法来执行算法并打印找到的路径。在这个例子中,输出应该是: ``` [0, 1] [0, 2] [1, 2] [2, 2] [3, 2] [3, 3] ``` 这表明从起始状态到目标状态的最佳路径是[0, 1], [0, 2], [1, 2], [2, 2], [3, 2], [3, 3]。 ### 回答2: Anytime repair A*算法是一种启发式搜索算法,用于解决搜索问题,它在处理大规模问题时能得到较好的效果。迭代意味着算法可以在有限的时间内进行多次迭代,每次迭代都会得到一个更好的解决方案。而限制每个状态的扩展不超过一次可以减少算法运行的时间和空间复杂度。 使用Java语言实现限制每个状态的扩展不超过一次的带有权值迭代的Anytime repair A*算法,可以按照以下步骤进行: 1. 定义搜索问题的状态表示和目标状态。 2. 定义启发函数,用来估计每个状态到目标状态的代价。 3. 创建一个优先队列,用来存储待扩展的状态。状态的优先级由启发函数和已搜索到的代价决定。 4. 创建一个哈希表,用来保存已扩展的状态及其对应的代价。 5. 初始化起始状态,并将其加入到优先队列和哈希表中。 6. 进入迭代循环,直到达到停止条件(例如达到一定的时间限制或找到满足目标的解决方案): a. 从优先队列中取出优先级最高的状态。 b. 检查该状态是否已经被扩展过,如果是则跳过。 c. 若未扩展过,将该状态标记为已扩展,并将其相邻的状态加入到优先队列中。 d. 如果优先队列不为空,返回步骤a继续迭代;否则表示无解或达到停止条件。 7. 根据需要返回结果(例如返回搜索到的最优解)。 其中,限制每个状态的扩展不超过一次的核心思想是通过哈希表来记录已扩展的状态,以避免重复扩展相同的状态。 此外,带有权值迭代的Anytime repair A*算法还可以通过设置不同的权值来调整搜索的策略,以获得更好的性能和解决方案。 以上是用Java实现限制每个状态的扩展不超过一次的带有权值迭代的Anytime repair A*算法的简要步骤和思路。具体的实现代码可以根据具体问题进行进一步细化和调整。 ### 回答3: 限制每个状态的扩展不超过一次的带有权值迭代的Anytime repair Astar算法可以用Java语言实现。 首先,我们需要定义一个类来表示搜索状态,包括状态的值、权值、父状态和估计代价等信息。该类可以命名为Node。 然后,我们需要实现一个优先级队列来存储Open列表中的节点。Java中的PriorityQueue类可以满足此要求,我们可以根据节点的估计代价设定优先级。 接下来,我们可以实现算法的核心部分——Anytime repair Astar算法的主体函数。在函数中,我们首先需要创建Open和Closed列表,并将初始状态加入Open列表。然后,进入一个循环,直到找到解或者Open列表为空。 在每次循环中,我们从Open列表中选择估计代价最小的节点进行扩展。根据限制条件,我们仅对当前最优节点进行扩展一次。当扩展一个节点时,我们需要生成其所有邻居节点,并计算它们的权值和估计代价。对于已经在Closed列表中的节点,我们可以直接跳过。对于新生成的节点,我们将其加入Open列表。 当找到解时,我们可以回溯路径并输出结果。如果Open列表为空,则意味着无解。 最后,我们可以实现主函数,读取输入和调用主体函数进行搜索。在主函数中,我们可以设定限制条件,并设定权值的迭代次数。随着迭代次数的增加,我们可以逐渐优化搜索效果。 以上就是用Java实现限制每个状态的扩展不超过一次的带有权值迭代的Anytime repair Astar算法的基本思路和步骤。根据具体需求,我们还可以对算法进行更加详细和精细的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全部梭哈迟早暴富

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值