给LLM Agent应用插上视觉模型的翅膀,一文搞懂ONNX如何加载头部姿态评估模型

现在,生活中处处都是AI的影子,手机中照片自动优化,自动抠图,照片自动根据人脸分组。抖音直播中送的眼镜,甚至是现在的文生图、图生图,都是AI模型。所以,我认为每个工程师都应该了解和使用模型,看到喜欢的模型,就动动手用起来,丰富自己的工具栈。

如果将视觉模型按照LLM工具调用方式嵌入到大语言模型应用中,给大语言模型插上视觉模型必定能让你的LLM应用更为有趣。

或许有人告诉你模型太复杂了,机器学习太复杂了,还要学习线性代数、矩阵运算、概率论等等。这简直令人头大,然而我们今天要说的是加载和使用模型。你只需熟悉Python或者Java或者C#再或者JS、C++等就能跑起来,没有那么多高深的知识,它已经大大简化了我们使用模型的门槛。

书接上文《LLM多智能体AutoGen教程 7: 什么你还在自己查阅论文?快用AutoGen自动获取多篇论文并撰写报告》,说到最近需要调研头部姿态评估模型。论文挑了几篇,使用kimi阅读总结做了大致的了解,在Github上找到对应的Repo开始测试。就以这个6DRepNet为例,论文全称:6D Rotation Representation for Unconstrained Head Pose Estimation。

这篇论文由 Thorsten Hempel、Ahmed A. Abdelrahman 和 Ayoub Al-Hamadi 撰写,来自德国马格德堡的奥托·冯·格里克大学电气工程与信息技术学院。 论文提出了一种用于无约束头部姿态估计的新方法。作者针对旋转标签的歧义问题,引入了旋转矩阵的形式主义,并提出了一种连续的6D旋转矩阵表示,以便进行高效且稳健的直接回归。这种方法可以学习到完整的旋转外观,超越了之前方法的能力,这些方法通常将姿态预测限制在狭窄的角度范围内以获得满意的结果。此外,作者还提出了一种基于测地线距离的损失函数,以SO(3)流形几何学为依据对网络进行惩罚。在公共AFLW2000和BIWI数据集上的实验表明,所提出的方法显著优于其他最先进的方法,提高了高达20%的性能。 关键点包括:

  • 提出了一种无标记的头部姿态估计方法,使用旋转矩阵表示进行准确的姿态回归。
  • 通过压缩的6D形式高效回归,然后将其转换为旋转矩阵。
  • 引入了基于测地线距离的损失函数,以更好地反映SO(3)流形的几何特性。
  • 在AFLW2000和BIWI数据集上的性能显著优于其他方法。
  • 开源了代码和模型,以促进研究实验和实际应用开发。 论文还详细介绍了所提出方法的实现细节,包括使用Pytorch框架、RepVGG作为主干网络、训练策略、数据集以及与其他最先进方法的比较。

6DRepNet中给出的预训练模型是以pth结尾的Pytorch导出模型,Pytorch虽然也简单,但我们不打算使用,因为Pytorch更偏向于为算法工程师提供训练模型流程。今天我们要介绍的是ONNX,全称Open Neural Network Exchange,开放神经网络交换,是一种用于表示机器学习模型的开放格式,定义了一组通用的运算符------即机器学习和深度学习模型的构建模块,并提供了一种通用的文件格式,使得 AI 开发者能够在各种框架、工具、运行时和编译器中使用模型。简单的说,它使得模型便于在不同机器学习框架中转换、加载使用,关键是支持在云端、移动设备和嵌入式设备中部署。

从上述描述来看,似乎我们想要学习Pytoch然后将其转换为ONNX模型。大可不必,网络上很多模型都提供了ONNX模型,我们简单的搜索就能找到。你可以从GitHub上的PINTO_model_zoo这个共享链接6DRepNet360中下载到所需的ONNX模型。ONNX本身也提供了一个ONNX Zoo:github.com/onnx/models...

那么ONNX模型怎么使用呢?

ONNX

作为工程师,你把ONNX当做一个黑盒子,只需要知道输入和输出是什么了?模型本身是自解释的,当你有了ONNX模型,你只需要使用https://netron.app/即可查看模型的输入输出,仍然以6drepnet360为例。打开netron之后,点击Open Model并选中你本地的模型,他会自动解析出类似下图的模型属性和输入输出。

以上是采用netron在线app查看onnx模型的输入输出和属性,我们也可以使用程序进行打印查看。这要说的就是onnx包。

首先我们安装onnx包。

bash 复制代码
pip install onnx

然后加载模型并输出它的input和output要求。

python 复制代码
import onnx
model = onnx.load("models/sixdrepnet360_Nx3x224x224.onnx")
print(model.graph.input)
print(model.graph.output)

输入参数要求如下,N表示输入的batch数量,后面3表示RGB3个通道,224和224表示高和宽:

python 复制代码
[name: "input"
type {
  tensor_type {
    elem_type: 1
    shape {
      dim {
        dim_param: "N"
      }
      dim {
        dim_value: 3
      }
      dim {
        dim_value: 224
      }
      dim {
        dim_value: 224
      }
    }
  }
}
]

输出参数如下,输入batch的数量N和输出3个yaw pitch roll。:

python 复制代码
[name: "yaw_pitch_roll"
type {
  tensor_type {
    elem_type: 1
    shape {
      dim {
        dim_param: "N"
      }
      dim {
        dim_param: "3"
      }
    }
  }
}
]

onnx经常用于导出模型。但对于推理,我们更常用的其实onnx的运行时:onnxruntime。它不仅提供Python的包,还有C#、JS、Java、C++、OC和Ruby等,让我们一起学习一下Python的onnxruntime。

首先,安装onnxruntime包。

pip install onnxruntime

接下来,我们使用InferenceSession开始加载模型。

python 复制代码
import onnxruntime as ort
session = ort.InferenceSession("models/sixdrepnet360_Nx3x224x224.onnx", provider=['CoreMLExecutionProvider'])

使用session的方法get_inputsget_outputs查看模型的输入输出等信息。

python 复制代码
> len(session.get_inputs())
1
> session.get_inputs()[0].name
'input'
> session.get_inputs()[0].type
'tensor(float)'
> session.get_inputs()[0].shape
['N', 3, 224, 224]
> session.get_outputs()[0].name
'yaw_pitch_roll'
> session.get_outputs()[0].shape
['N', '3']
> session.get_outputs()[0].type
'tensor(float)'

既然输入输出已定,那么如何运行呢?使用session的run方法执行推理即可。

python 复制代码
def run(self, output_names, input_feed, run_options=None):
  pass
  • output_names - 输出的名称列表,可不写,他会自动提取
  • input_feed - 输入字典,形如{ input_name: input_value }

因此,我们可以使用numpy按照输入要求随机构造一个输入数据。

python 复制代码
import numpy as np
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
input_name = session.get_inputs()[0].name
outputs = session.run(None, {input_name: input_data})
print(outputs)
# [array([[-17.788296, 171.02617 , 171.44977 ]], dtype=float32)]

用一个随机数据输入输出看起来没啥意思,我们使用opencv读取图片来试试,就以小可爱川普为例。

按照6DRepNet中头部姿态估算的要求,输入图像需要先检测人脸,然后将人脸扣出,之后做一些图像预处理即可输入模型进行推理。上图是已经通过SCRFD人脸检测模型抓取的人脸。

使用OpenCV读取图像,并做一些预处理将输入图像调整为CHW并处理为输入带有批处理维度的[1, 3, 224, 224]。

python 复制代码
# 读取图像
image_path = 'images/trump-face.jpg'
image = cv2.imread(image_path)

resized_image = cv2.resize(image, (224, 224))
# 进行标准化
resized_image = resized_image.astype(np.float32) / 255.0
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
resized_image = (resized_image - mean) / std

# 调整形状到 (1, 3, 224, 224)
resized_image = np.transpose(resized_image, (2, 0, 1))
resized_image = np.expand_dims(resized_image, axis=0)
resized_image = resized_image.astype(np.float32)

outputs = session.run(None, {input_name: resized_image})
print(outputs)
# [array([[-12.90943 ,  12.171669, -89.99999 ]], dtype=float32)]

作为LLM工具

待更新。

总结

本文通过介绍ONNX和如何使用onnxruntime加载模型和推理,通过加载头部姿态模型6DRepNet、预处理图像和推理演示了使用onnxruntime的全过程。从我为数不多的测试来看,我个人觉得在图像模糊的时候,对于头部的姿态估算准确度不是很好。

本文是一个简单的介绍,主要针对非算法工程师如何也能玩转模型,同时也能方便读者在有需要的时候,可以使用LLM的工具调用能力调用丰富的视觉类小模型。

相关推荐
学习OK呀1 分钟前
日常docker的实操命令场景
后端
雾原4 分钟前
Nginx高频用途的详细配置和性能调优
后端
类似不类似4 分钟前
快速配置linux远程开发-go语言
开发语言·后端·golang
前端付豪4 分钟前
1、为什么浏览器要有渲染流程? ——带你一口气吃透 Critical Rendering Path
前端·后端·浏览器
元亓亓亓6 分钟前
LeetCode热题100--560.和为K的子数组(前缀和)--中等
算法·leetcode·职场和发展
前端付豪7 分钟前
3、Node.js异步编程彻底吃透
前端·后端·node.js
AI视觉网奇7 分钟前
python 求内轮廓
python·opencv·计算机视觉
lczdyx8 分钟前
从Flask到智能体:装饰器模式在AI系统中的架构迁移实践
人工智能·python·语言模型·架构·flask·装饰器模式
老胖闲聊8 分钟前
Flask 请求数据获取方法详解
后端·python·flask
Phoebe鑫12 分钟前
数据结构每日一题day12(链表)★★★★★
数据结构·算法·链表