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

本文涉及的产品
NLP 自学习平台,3个模型定制额度 1个月
云解析 DNS,旗舰版 1个月
NLP自然语言处理_高级版,每接口累计50万次
简介: 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笔记本,展示了在不同任务上的应用,包括外部知识对象标记、自然语言图像编辑、自然语言视觉推理和视觉问答等任务。

目录
相关文章
|
6天前
|
NoSQL Java Linux
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结
141 75
|
2月前
|
数据采集 自然语言处理 机器人
如何使用生成器来提高自然语言处理任务的性能?
如何使用生成器来提高自然语言处理任务的性能?
|
13天前
|
人工智能 自然语言处理 PyTorch
BrushEdit:腾讯和北京大学联合推出的图像编辑框架,通过自然语言指令实现对图像的编辑和修复
BrushEdit是由腾讯、北京大学等机构联合推出的先进图像编辑框架,结合多模态大型语言模型和双分支图像修复模型,支持基于指令引导的图像编辑和修复。
58 12
BrushEdit:腾讯和北京大学联合推出的图像编辑框架,通过自然语言指令实现对图像的编辑和修复
|
19天前
|
人工智能 自然语言处理 前端开发
Director:构建视频智能体的 AI 框架,用自然语言执行搜索、编辑、合成和生成等复杂视频任务
Director 是一个构建视频智能体的 AI 框架,用户可以通过自然语言命令执行复杂的视频任务,如搜索、编辑、合成和生成视频内容。该框架基于 VideoDB 的“视频即数据”基础设施,集成了多个预构建的视频代理和 AI API,支持高度定制化,适用于开发者和创作者。
76 9
Director:构建视频智能体的 AI 框架,用自然语言执行搜索、编辑、合成和生成等复杂视频任务
|
1月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
55 12
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
探索深度学习与自然语言处理的前沿技术:Transformer模型的深度解析
109 0
|
2月前
|
机器学习/深度学习 存储 自然语言处理
从理论到实践:如何使用长短期记忆网络(LSTM)改善自然语言处理任务
【10月更文挑战第7天】随着深度学习技术的发展,循环神经网络(RNNs)及其变体,特别是长短期记忆网络(LSTMs),已经成为处理序列数据的强大工具。在自然语言处理(NLP)领域,LSTM因其能够捕捉文本中的长期依赖关系而变得尤为重要。本文将介绍LSTM的基本原理,并通过具体的代码示例来展示如何在实际的NLP任务中应用LSTM。
202 4
|
2月前
|
JSON JavaScript 前端开发
Javaweb中Vue指令的详细解析与应用
Vue指令提供了一种高效、声明式的编码方式,使得开发者可以更专注于数据和业务逻辑,而不是DOM操作的细节。通过熟练使用Vue指令,可以极大地提高开发效率和项目的可维护性。
27 3
|
3月前
|
JavaScript 前端开发 UED
Javaweb中Vue指令的详细解析与应用
Vue指令是Vue框架中非常强大的特性之一,它提供了一种简洁、高效的方式来增强HTML元素和组件的功能。通过合理使用这些指令,可以使你的JavaWeb应用更加响应用户的操作,提高交互性和用户体验。而且,通过创建自定义指令,你可以进一步扩展Vue的功能,使其更贴合你的应用需求。
23 1
|
4月前
|
图形学 数据可视化 开发者
超实用Unity Shader Graph教程:从零开始打造令人惊叹的游戏视觉特效,让你的作品瞬间高大上,附带示例代码与详细步骤解析!
【8月更文挑战第31天】Unity Shader Graph 是 Unity 引擎中的强大工具,通过可视化编程帮助开发者轻松创建复杂且炫酷的视觉效果。本文将指导你使用 Shader Graph 实现三种效果:彩虹色渐变着色器、动态光效和水波纹效果。首先确保安装最新版 Unity 并启用 Shader Graph。创建新材质和着色器图谱后,利用节点库中的预定义节点,在编辑区连接节点定义着色器行为。
338 0

推荐镜像

更多