VisProg解析:根据自然语言指令解决复杂视觉任务

本文涉及的产品
NLP自然语言处理_高级版,每接口累计50万次
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: VisProg是一个神经符号系统,能够根据自然语言指令生成并执行Python程序来解决复杂的视觉任务,提供可解释的解决方案。

VisProg:根据自然语言指令解决复杂视觉任务

1. 介绍

VisProg 是一种神经符号系统,可以根据自然语言指令解决复杂的组合视觉任务。VisProg 使用 GPT3 的上下文学习能力来生成 Python 程序,然后执行这些程序以获得解决方案和全面且可解释的基本原理。生成的程序的每一行都可以调用几个现成的计算机视觉模型、图像处理例程或Python函数之一来产生可由程序的后续部分使用的中间输出。

相关论文:Paper for VisProg

2. 安装和配置 VisProg

首先,如果你的系统上还没有安装 Conda,请前往 Anaconda 安装适合你的平台的 Conda 工具。

然后,使用以下命令安装和配置 VisProg:

# 克隆 VisProg 仓库
git clone https://github.com/allenai/visprog.git

# 切换到仓库目录
cd visprog

# 创建 conda 环境并安装依赖
conda env create -f environment.yaml

# 激活 VisProg 环境
conda activate visprog

3. 使用VisProg

在VSCODE中打开文件,填入对应的OpenAI密钥
在这里插入图片描述
笔者在测试时Conda环境有相关依赖需要补充安装:
在visprog环境下执行

pip install appdirs
pip install ipywidgets

4. VisProg 解读

VisProg的关键组成部分是一系列名为interpreter的类,这些类在visprog/engine/step_interpreters.py文件中定义。

例如,EvalInterpreter 类解析和执行 ‘EVAL’ 步骤。它首先使用 parse 方法来解析步骤,然后使用 execute 方法来执行该步骤。如果 inspect 参数为 True,execute 方法还会生成描述该步骤的 HTML 字符串。

class EvalInterpreter():
    step_name = 'EVAL'

    def __init__(self):
        print(f'Registering {self.step_name} step')

# 解析步骤
    def parse(self,prog_step):
        parse_result = parse_step(prog_step.prog_str)
        step_name = parse_result['step_name']
        output_var = parse_result['output_var']
        step_input = eval(parse_result['args']['expr'])
        assert(step_name==self.step_name)
        return step_input, output_var

# 生成 HTML 字符串    
    def html(self,eval_expression,step_input,step_output,output_var):
        eval_expression = eval_expression.replace('{','').replace('}','')
        step_name = html_step_name(self.step_name)
        var_name = html_var_name(output_var)
        output = html_output(step_output)
        expr = html_arg_name('expression')
        return f"""<div>{var_name}={step_name}({expr}="{eval_expression}")={step_name}({expr}="{step_input}")={output}</div>"""

# 执行步骤
    def execute(self,prog_step,inspect=False):
        step_input, output_var = self.parse(prog_step)
        prog_state = dict()
        for var_name,var_value in prog_step.state.items():
            if isinstance(var_value,str):
                if var_value in ['yes','no']:
                    prog_state[var_name] = var_value=='yes'
                elif var_value.isdecimal():
                    prog_state[var_name] = var_value
                else:
                    prog_state[var_name] = f"'{var_value}'"
            else:
                prog_state[var_name] = var_value

        eval_expression = step_input

        if 'xor' in step_input:
            step_input = step_input.replace('xor','!=')

        step_input = step_input.format(**prog_state)
        step_output = eval(step_input)
        prog_step.state[output_var] = step_output
        if inspect:
            html_str = self.html(eval_expression, step_input, step_output, output_var)
            return step_output, html_str

        return step_output

Breadcrumbsvisprog/engine /step_interpreters.py中的parse_step函数从步骤字符串中解析出步骤名称、输出变量和参数。它使用了 Python 的 tokenize 库来解析步骤字符串。

def parse_step(step_str,partial=False):
    tokens = list(tokenize.generate_tokens(io.StringIO(step_str).readline))
    output_var = tokens[0].string
    step_name = tokens[2].string
    parsed_result = dict(
        output_var=output_var,
        step_name=step_name)
    if partial:
        return parsed_result

    arg_tokens = [token for token in tokens[4:-3] if token.string not in [',','=']]
    num_tokens = len(arg_tokens) // 2
    args = dict()
    for i in range(num_tokens):
        args[arg_tokens[2*i].string] = arg_tokens[2*i+1].string
    parsed_result['args'] = args
    return parsed_result

5. Notebooks 介绍

VisProg 还包含一些 Jupyter 笔记本,用于展示其在不同任务上的应用:

  • notebooks/ok_det.ipynb:这个 notebook 与 “外部知识对象标记(Outside Knowledge Object Tagging)” 相关。它包含了一些用于通过自然语言处理技术对外部知识对象进行标注的代码和示例。

  • notebooks/image_editing.ipynb:这个 notebook 与 “自然语言图像编辑(Natural Language Image Editing)” 相关。它包含了一些用于根据自然语言指令对图像进行编辑和处理的代码和示例。

  • notebooks/nlvr.ipynb:这个 notebook 与 “自然语言视觉推理(Natural Language Visual Reasoning)” 相关。它包含了一些用于处理自然语言与图像之间的推理任务的代码和示例。

  • notebooks/gqa.ipynb:这个 notebook 与 “视觉问答(Visual Question Answering)” 相关。它包含了一些用于处理视觉问答任务的代码和示例。

整体流程梳理

此处以 notebooks/gqa.ipynb为例子,梳理VisProg的程序整体流程:

# 导入必要的库
import os
import sys
from PIL import Image
from IPython.core.display import HTML
from functools import partial

# 添加上级目录到系统路径,使得可以导入在上级目录中的模块
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)

# 设置OpenAI Key环境变量
%env OPENAI_API_KEY=<Enter you key here> 

# 从自定义模块导入函数和类
from engine.utils import ProgramGenerator, ProgramInterpreter
from prompts.gqa import create_prompt

# 创建ProgramGenerator和ProgramInterpreter实例对象
# 这些对象将被用于,生成程序和解释程序
# partial方法调用create_prompt生成测试用的Prompts,这些prompts作为generator的输入
interpreter = ProgramInterpreter(dataset='gqa')
prompter = partial(create_prompt,method='all')
generator = ProgramGenerator(prompter=prompter)

# 读取一张图片,并将其大小缩放为合适的大小,并转换为 RGB 格式
# 将要进行推理的图像加载到内存中,以便在后续的操作中使用
image = Image.open('../assets/camel1.png')
image.thumbnail((640,640),Image.Resampling.LANCZOS)
init_state = dict(IMAGE=image.convert('RGB'))
image

# 指定自然语言问题/陈述/指令:定义你的自然语言问题、陈述或指令,这将作为输入提供给程序生成器以生成相应的程序
question = "How many people or animals are in the image?"
# 使用程序生成器对象,将指定的问题/陈述/指令作为输入,生成相应的程序。
prog,_ = generator.generate(dict(question=question))
print(prog)
# 利用程序解释器对象,对生成的程序进行解释和执行,返回结果
result, prog_state, html_str = interpreter.execute(prog,init_state,inspect=True)

# 输出程序的结果
result

# 输出HTML字符串
# 将返回的结果以及执行过程的可视化(比如执行追踪)展示出来,以便更好地理解和分析程序的执行情况
# 该做法也是OpenAI Code Interpreter的做法,使用可视化增强程序的可解释性
HTML(html_str)

对于其中出现的ProgramInterpreter.executecreate_promptProgramGenerator.generate进一步解释如下:

visprog/engine/utils.py/ProgramInterpreter
class ProgramInterpreter:
    def __init__(self, dataset='nlvr'):
        """
        初始化 ProgramInterpreter 类的实例。

        参数:
        dataset (str): 一个字符串,用来注册程序步骤的解释器。

        属性:
        step_interpreters (dict): 字典,存储了与每个程序步骤名称对应的解释器。
        """
        # .step_interpreter中包含了各类图像处理的解释器
        self.step_interpreters = register_step_interpreters(dataset)

    def execute_step(self, prog_step, inspect):
        """
        执行一个程序步骤,并返回结果。

        参数:
        prog_step (Program): 需要执行的程序步骤。
        inspect (bool): 是否需要返回可供检查的结果。

        返回值:
        根据 inspect 的值,可能会返回步骤的输出结果,也可能会返回一个包含步骤的输出结果和 HTML 字符串的元组。
        """
        # 解析程序步骤的字符串形式,获取步骤名称
        step_name = parse_step(prog_step.prog_str, partial=True)['step_name']
        print(step_name)
        # 从步骤解释器字典中获取对应的解释器,然后用它执行程序步骤
        return self.step_interpreters[step_name].execute(prog_step, inspect)

    def execute(self, prog, init_state, inspect=False):
        """
        执行一个完整的程序,并返回结果。

        参数:
        prog (str or Program): 需要执行的程序,可以是字符串形式,也可以是 Program 类的实例。
        init_state (dict): 程序的初始状态。
        inspect (bool): 是否需要返回可供检查的结果。

        返回值:
        根据 inspect 的值,可能会返回程序的输出结果和状态,也可能会返回一个包含程序的输出结果、状态和 HTML 字符串的元组。
        """
        # 如果程序是字符串形式,则转化为 Program 类的实例
        if isinstance(prog, str):
            prog = Program(prog, init_state)
        else:
            assert(isinstance(prog, Program))

        # 将程序的每个指令都转化为 Program 类的实例
        prog_steps = [Program(instruction, init_state=prog.state) \
            for instruction in prog.instructions]

        html_str = '<hr>'
        for prog_step in prog_steps:
            if inspect:
                # 如果需要返回可供检查的结果,则执行每个步骤时都返回步骤的输出结果和 HTML 字符串
                step_output, step_html = self.execute_step(prog_step, inspect)
                html_str += step_html + '<hr>'
            else:
                # 否则,只返回步骤的输出结果
                step_output = self.execute_step(prog_step, inspect)

        # 返回程序的结果
        if inspect:
            return step_output, prog.state, html_str

        return step_output, prog.state
visprog/engine/utils.py/ProgramGenerator
class ProgramGenerator():
    def __init__(self, prompter, temperature=0.7, top_p=0.5, prob_agg='mean'):
        """
        初始化 ProgramGenerator 类的实例。

        参数:
        prompter (function): 函数,用于生成 prompt。
        temperature (float): 控制生成的文本的随机性的参数,值越高,结果越随机。
        top_p (float): 控制生成的文本的多样性的参数,值越高,结果越多样。
        prob_agg (str): 用于计算输出文本概率的聚合函数,可以是 'mean' 或 'sum'。

        属性:
        prompter (function): 存储输入的 prompter。
        temperature (float): 存储输入的 temperature。
        top_p (float): 存储输入的 top_p。
        prob_agg (str): 存储输入的 prob_agg。
        """
        openai.api_key = os.getenv("OPENAI_API_KEY")
        self.prompter = prompter
        self.temperature = temperature
        self.top_p = top_p
        self.prob_agg = prob_agg

    def compute_prob(self, response):
        """
        计算生成的文本的概率。

        参数:
        response (openai.completion_v1.Completion): OpenAI API 返回的响应。

        返回值:
        float: 生成的文本的概率。
        """
        eos = ''
        for i, token in enumerate(response.choices[0]['logprobs']['tokens']):
            if token == eos:
                break

        if self.prob_agg == 'mean':
            agg_fn = np.mean
        elif self.prob_agg == 'sum':
            agg_fn = np.sum
        else:
            raise NotImplementedError

        return np.exp(agg_fn(response.choices[0]['logprobs']['token_logprobs'][:i]))

    def generate(self, inputs):
        """
        根据输入生成一个程序。

        参数:
        inputs (dict): 字典,包含了生成 prompt 所需的输入信息。

        返回值:
        tuple: 包含生成的程序和程序的概率的元组。
        """
        response = openai.Completion.create(
            model="text-davinci-003",
            prompt=self.prompter(inputs),
            temperature=self.temperature,
            max_tokens=512,
            top_p=self.top_p,
            frequency_penalty=0,
            presence_penalty=0,
            n=1,
            logprobs=1
        )

        prob = self.compute_prob(response)
        prog = response.choices[0]['text'].lstrip('\n').rstrip('\n')
        return prog, prob
visprog/prompts/gqa.py/create_prompt
def create_prompt(inputs, num_prompts=8, method='random', seed=42, group=0):
    """
    创建一个提示字符串,该字符串包含一个问题和一些之前生成的程序示例。

    参数:
    inputs (dict): 一个字典,包含需要插入到提示中的值。它应该有一个名为'question'的键,对应的值将被插入到提示的最后一个问题中。
    num_prompts (int, 可选): 如果 method='random',这个参数决定了选择多少个随机的程序示例来构成提示。默认值为8。
    method (str, 可选): 选择程序示例的方法。如果为'random',将会随机选择;如果为'all',将使用所有程序示例。默认值为'random'。
    seed (int, 可选): 用于随机数生成器的种子,以便于复现。默认值为42。
    group (int, 可选): 未使用的参数,保留给可能的未来扩展。默认值为0。

    返回值:
    str: 生成的提示字符串,它包含一些程序示例,然后是一个问题,最后是"Program:",表明下一部分应该是一个程序。
    """
    if method == 'all':
        # 如果方法为 'all',则选择所有的程序示例
        prompt_examples = GQA_CURATED_EXAMPLES
    elif method == 'random':
        # 如果方法为 'random',则随机选择一些程序示例
        random.seed(seed)  # 设置随机数生成器的种子,以便复现
        prompt_examples = random.sample(GQA_CURATED_EXAMPLES, num_prompts)  # 随机选择 num_prompts 个程序示例
    else:
        # 如果 method 不是 'all' 或 'random',则抛出错误
        raise NotImplementedError

    # 将选择的程序示例合并为一个字符串,每个示例之间用换行符分隔
    prompt_examples = '\n'.join(prompt_examples)

    # 在前面添加一些指示性的文字
    prompt_examples = f'Think step by step to answer the question.\n\n{prompt_examples}'

    # 在最后添加问题和 "Program:"
    return prompt_examples + "\nQuestion: {question}\nProgram:".format(**inputs)

6. 结论

本文主要介绍了VisProg,这是一种神经符号系统,可以根据自然语言指令解决复杂的组合视觉任务。VisProg利用GPT3的上下文学习能力生成Python程序,通过执行这些程序来找到解决方案,并提供全面且可解释的解答。

安装和配置VisProg主要涉及克隆VisProg仓库,创建和激活Conda环境,并安装相关依赖。

VisProg的主要组件是一系列名为interpreter的类,这些类定义在visprog/engine/step_interpreters.py文件中。每个类都有解析和执行步骤的方法,如果inspect参数为True,execute方法还会生成描述该步骤的HTML字符串。

VisProg还提供了一些Jupyter笔记本,展示了在不同任务上的应用,包括外部知识对象标记、自然语言图像编辑、自然语言视觉推理和视觉问答等任务。

目录
相关文章
|
12天前
|
JSON JavaScript 前端开发
Javaweb中Vue指令的详细解析与应用
Vue指令提供了一种高效、声明式的编码方式,使得开发者可以更专注于数据和业务逻辑,而不是DOM操作的细节。通过熟练使用Vue指令,可以极大地提高开发效率和项目的可维护性。
12 3
|
18天前
|
JavaScript 前端开发 UED
Javaweb中Vue指令的详细解析与应用
Vue指令是Vue框架中非常强大的特性之一,它提供了一种简洁、高效的方式来增强HTML元素和组件的功能。通过合理使用这些指令,可以使你的JavaWeb应用更加响应用户的操作,提高交互性和用户体验。而且,通过创建自定义指令,你可以进一步扩展Vue的功能,使其更贴合你的应用需求。
12 1
|
2月前
|
图形学 数据可视化 开发者
超实用Unity Shader Graph教程:从零开始打造令人惊叹的游戏视觉特效,让你的作品瞬间高大上,附带示例代码与详细步骤解析!
【8月更文挑战第31天】Unity Shader Graph 是 Unity 引擎中的强大工具,通过可视化编程帮助开发者轻松创建复杂且炫酷的视觉效果。本文将指导你使用 Shader Graph 实现三种效果:彩虹色渐变着色器、动态光效和水波纹效果。首先确保安装最新版 Unity 并启用 Shader Graph。创建新材质和着色器图谱后,利用节点库中的预定义节点,在编辑区连接节点定义着色器行为。
135 0
|
2月前
|
机器学习/深度学习 存储 自然语言处理
自然语言处理中的情感分析技术:深入解析与应用前景
【8月更文挑战第4天】情感分析技术作为自然语言处理领域的重要分支,具有广泛的应用前景和重要的研究价值。通过不断的技术创新和应用实践,我们可以期待情感分析在未来发挥更大的作用,为我们的生活和工作带来更多便利和效益。
129 10
|
2月前
|
图形学 开发者
【Unity光照艺术手册】掌握这些技巧,让你的游戏场景瞬间提升档次:从基础光源到全局光照,打造24小时不间断的视觉盛宴——如何运用代码与烘焙创造逼真光影效果全解析
【8月更文挑战第31天】在Unity中,合理的光照与阴影设置对于打造逼真环境至关重要。本文介绍Unity支持的多种光源类型,如定向光、点光源、聚光灯等,并通过具体示例展示如何使用着色器和脚本控制光照强度,模拟不同时间段的光照变化。此外,还介绍了动态和静态阴影、全局光照及光照探针等高级功能,帮助开发者创造丰富多样的光影效果,提升游戏沉浸感。
46 0
|
2月前
|
图形学 C# 开发者
Unity粒子系统全解析:从基础设置到高级编程技巧,教你轻松玩转绚丽多彩的视觉特效,打造震撼游戏画面的终极指南
【8月更文挑战第31天】粒子系统是Unity引擎的强大功能,可创建动态视觉效果,如火焰、爆炸等。本文介绍如何在Unity中使用粒子系统,并提供示例代码。首先创建粒子系统,然后调整Emission、Shape、Color over Lifetime等模块参数,实现所需效果。此外,还可通过C#脚本实现更复杂的粒子效果,增强游戏视觉冲击力和沉浸感。
98 0
|
2月前
|
C# Windows 开发者
超越选择焦虑:深入解析WinForms、WPF与UWP——谁才是打造顶级.NET桌面应用的终极利器?从开发效率到视觉享受,全面解读三大框架优劣,助你精准匹配项目需求,构建完美桌面应用生态系统
【8月更文挑战第31天】.NET框架为开发者提供了多种桌面应用开发选项,包括WinForms、WPF和UWP。WinForms简单易用,适合快速开发基本应用;WPF提供强大的UI设计工具和丰富的视觉体验,支持XAML,易于实现复杂布局;UWP专为Windows 10设计,支持多设备,充分利用现代硬件特性。本文通过示例代码详细介绍这三种框架的特点,帮助读者根据项目需求做出明智选择。以下是各框架的简单示例代码,便于理解其基本用法。
80 0
|
3月前
|
机器学习/深度学习 数据采集 自然语言处理
自然语言处理中的文本分类技术深度解析
【7月更文挑战第31天】文本分类作为自然语言处理领域的重要技术之一,正不断推动着智能信息处理的发展。随着深度学习技术的不断成熟和计算资源的日益丰富,我们有理由相信,未来的文本分类技术将更加智能化、高效化、普适化,为人类社会带来更加便捷、精准的信息服务。
|
3月前
|
数据采集 自然语言处理 机器人
使用生成器来提高自然语言处理任务的性能
使用生成器来提高自然语言处理任务的性能
|
2月前
|
存储 并行计算 API
ViperGPT解析:结合视觉输入与文本查询生成和执行程序
ViperGPT是一个创新的混合视觉和语言处理模型,通过生成和执行代码来解决视觉查询问题,具有高度模块化、灵活性和优秀的外部知识查询能力。
50 0

推荐镜像

更多