在 AWS Inferentia2 上经济高效地部署用于宠物行为检测的视觉语言模型

TL;DR · AI 摘要
Tomofun利用AWS Inferentia2芯片将宠物行为检测的视觉语言模型推理成本显著降低,同时保持高准确率和吞吐量,实现规模化实时监控。
核心要点
- 使用AWS Inferentia2的EC2 Inf2实例可大幅降低视觉语言模型的推理成本。
- Furbo在不重写BLIP模型核心代码的情况下完成从GPU到Inferentia2的迁移。
- 方案支持高吞吐、低延迟的实时宠物行为检测,适用于大规模部署。
标题:在 AWS Inferentia2 上经济高效地部署视觉语言模型用于宠物行为检测 | 亚马逊云科技
URL 来源:http://aws.amazon.com/blogs/machine-learning/cost-effective-deployment-of-vision-language-models-for-pet-behavior-detection-on-aws-inferentia2/
发布日期:2026-05-06T07:37:08-08:00
Markdown 内容: Tomofun 是总部位于台湾的宠物科技初创公司,也是 Furbo 宠物摄像头背后的开发团队,正在重新定义宠物主人如何远程与宠物互动。Furbo 将智能摄像头与人工智能相结合,可检测吠叫、奔跑或异常活动等行为,并实时向主人发出警报。这一功能的核心是计算机视觉和视觉语言模型,它们从视频流中解读宠物的行为。
最初,Furbo 的推理工作负载运行在基于 GPU 的 Amazon Elastic Compute Cloud(Amazon EC2)实例上。虽然 GPU 提供了高吞吐量,但由于需要持续运行以支持大规模实时宠物活动警报,因此成本较高。为了降低成本并保持准确性,Tomofun 转而采用由 AWS Inferentia2 驱动的 EC2 Inf2 实例,Inferentia2 是亚马逊专为 AI 设计的芯片。本文将详细探讨以下内容。
挑战:大规模实时视觉语言模型的 GPU 推理成本降低
像 Bootstrapping Language-image Pre-Training (BLIP) 这样的先进视觉语言模型原本运行在 GPU 实例上,但在需要始终在线的大规模实时推理场景下,其成本效益较低。挑战来自两个方面:Tomofun 需要在数十万台设备上维持几乎连续的宠物行为监控,同时还要保证成本效率、模型精度和吞吐量。此外,他们希望在不重写已针对 PyTorch 优化的大量 BLIP 代码库的前提下完成这一目标。
解决方案概述
在深入架构细节之前,下图展示了该系统如何通过 AWS 各项服务实现大规模宠物行为检测的整体流程。

- 摄像头交互 – Furbo 的 API 是 Tomofun 宠物行为检测服务的核心,负责将客户宠物摄像头的图像流调度至 AWS 中的推理端点。图中展示了使用 EC2 Inf2 实例构建的弹性负载均衡(ELB)和 Amazon EC2 自动扩展组架构,可根据实时推理请求量动态扩展。当摄像头捕获一帧画面后,数据会通过 Amazon CloudFront 和 ELB 发送到第一层 EC2 自动扩展组,该组托管着宠物行为检测的 API 服务器。API 层处理每个请求后,会将图像转发给第二层专门用于模型推理的自动扩展组。
- 模型推理 – 图像经过处理后被发送到第二个 EC2 自动扩展组中的推理实例。该组内的容器运行 BLIP 模型,并可在基于 Inferentia2 的 EC2 Inf2 实例上执行。使用 Neuron SDK 编译的 BLIP 模型组件会被加载到 Inf2 实例的容器中。早期实现中,Furbo 的 API 仅将推理调用路由到 GPU 容器,但现在也可以在不更改上游 API 或下游告警逻辑的情况下,将请求定向到基于 Inf2 的容器。这种架构使 Tomofun 能够实时将推理请求在 GPU 和 Inferentia2 后端之间切换,从而保持高可用性,并灵活扩展低成本推理能力,同时为 Furbo 用户保留相同的 API 接口。
- 指标收集 – Amazon CloudWatch 监控整个推理集群的关键运行指标,包括延迟、吞吐量和错误率。这些信号提供了必要的可观测性,以便在流量模式全天变化时及早发现性能下降并确保满足服务水平目标。
- 按需扩展 – ELB 将请求分发到自动扩展组中的可用实例,而自动扩展组则根据 CloudWatch 指标(即传入请求数量)管理实例池的大小。之所以采用这种基于指标的方法,是因为每种实例类型的吞吐量基准已经通过压力测试确定,因此扩展决策可直接依据图像请求数量进行。最终形成了一种能够实时扩展低成本推理能力的架构,在需求增长时仍能保持高可用性。
在 Inferentia2 上优化 BLIP 模型
在深入模型细节前,下图简要展示了 BLIP 架构及其核心组件之间的交互方式。

来源:BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation, 2022 https://arxiv.org/pdf/2201.12086
如图所示,BLIP 由三个组件构成:图像编码器(Image Encoder)、文本编码器(Text Encoder)和文本解码器(Text Decoder)。为了支持 Inferentia2,可以将模型拆分为多个组件,并通过封装适配输入输出形状。Tomofun 对 BLIP 应用了这种方法,为模型的三个组件分别创建了轻量级封装器,从而保持原始架构不变。每个组件都使用 torch_neuronx 独立编译,然后组合成完整的推理流水线,使输入数据可以顺序流动。这种模块化方法在不修改 BLIP 预训练逻辑的前提下实现了与 Inferentia2 的兼容。
原始模型代码
第一步是隔离原始 BLIP 的 Text Encoder,以便在不修改其内部逻辑的情况下进行编译。TextEncoder 类是对原始子模块(model.text_encoder.model)的一个薄层封装,它通过只返回主张量来标准化前向传播输出。这使得该组件易于使用 Neuron 进行追踪和编译,同时保留原始架构。
class TextEncoder(torch.nn.Module):
def __init__(self, model):
super().__init__()
self.model = model
def forward(self, input_ids, attention_mask, encoder_hidden_states, encoder_attention_mask):
output = self.model(
input_ids=input_ids,
attention_mask=attention_mask,
encoder_hidden_states=encoder_hidden_states,
encoder_attention_mask=encoder_attention_mask,
return_dict=False,
)
return output[0]代码
在 编译阶段,原始模型(model.text_encoder.model)被直接传入 torch_neuronx.trace() 并编译为 Neuron 优化的 TorchScript 产物,而不修改预训练的 BLIP 逻辑。
封装代码
由于 torch_neuronx.trace() API 要求输入和输出均为张量元组,因此需要一个封装器。为了避免重写模型,轻量级封装器作为适配层存在,用于重新格式化输入输出,同时保持原始架构不变。这种方法最小化了代码变更,使编译后的组件能够无缝集成到现有推理流水线中。
class TextEncoderWrapper(torch.nn.Module):
def __init__(self, model):
super().__init__()
self.model = TextEncoder(model)
@classmethod
def from_model(cls, model):
wrapper = cls(model)
wrapper.model = model
return wrapper
def forward(self, input_ids, attention_mask, encoder_hidden_states, encoder_attention_mask, return_dict):
output = self.model(input_ids, attention_mask, encoder_hidden_states, encoder_attention_mask)
return (output,)代码
封装器仅在部署时用于加载编译后的模型并格式化 I/O,使其适配现有的 BLIP 流水线。
- 编译:使用原始模型(
model.text_encoder.model) - 部署:使用
TextEncoderWrapper运行编译后的模型
这样既保持了原始代码不变,又让编译后的模型易于接入生产环境。
针对 Inferentia2 的模型编译
在以下代码片段中,model.text_encoder.model 表示未经修改的文本编码器子模块,它被编译为 Neuron 优化的 TorchScript 格式。
def trace_model(model, directory, compiler_args=f"--auto-cast-type fp16 --logfile {LOG_DIR}/log-neuron-cc.txt"):
if os.path.isfile(directory):
print(f"提供的路径 ({directory}) 应为目录而非文件")
return
os.makedirs(directory, exist_ok=True)
os.makedirs(LOG_DIR, exist_ok=True)
# 若模型已编译则跳过
if not os.path.isfile(os.path.join(directory, 'text_encoder.pt')):
print("正在追踪 text_encoder")
# 步骤1:提供具有预期形状和数据类型的伪输入数据
inputs = (
torch.ones((1, 8), dtype=torch.int64),
torch.ones((1, 8), dtype=torch.int64),
torch.ones((1, 577, 768), dtype=torch.float32),
torch.ones((1, 577), dtype=torch.int64),
)
# 步骤2:使用 torch_neuronx.trace() 将模型编译为适用于 Inferentia 的版本
encoder = torch_neuronx.trace(model.text_encoder.model,
inputs,
compiler_args=compiler_args)
# 步骤3:将编译后的模型保存为 TorchScript 产物
torch.jit.save(encoder, os.path.join(directory, 'text_encoder.pt'))
else:
print('跳过 text_encoder.pt')代码
为了将 BLIP 组件编译用于 Inferentia2,Tomofun 定义了一个追踪函数,用于将 GPU 训练的 PyTorch 模型自动转换为适用于 Inferentia 的优化产物。该过程首先准备代表模型输入预期形状和数据类型的伪输入张量,以指导追踪过程。输入定义完成后,函数调用 torch_neuronx.trace() 将 BLIP 子模型编译为可在 Inferentia 上运行的版本,生成原始代码的 Neuron 优化版本。最后,使用 torch.jit.save 保存编译后的产物,使其准备好在 Inf2 实例上部署。这一三步流程——加载封装器、提供伪输入数据、使用 Neuron 编译——确保了 Tomofun 可以在不修改原始模型代码的前提下迁移 BLIP 的 TextDecoder 及其他组件。
在 Inferentia2 上部署模型
在部署阶段,通过封装类加载已编译的子模块,以组装最终的 BLIP 推理流水线。这种分离设计形成了清晰的工作流:原始模型组件在编译期间直接用于 Neuron 优化,而封装类则在推理期间处理输入输出格式,确保与 Inferentia2 兼容。部署阶段的代码如下:
models.text_encoder = TextEncoderWrapper.from_model(
torch.jit.load(os.path.join(directory, 'text_encoder.pt')))
这种设计在不修改原始 BLIP 架构的前提下,通过轻量级封装类满足了 Neuron SDK 的 I/O 接口要求。同时也实现了模块化、组件级别的编译与部署流程,允许每个 BLIP 子模块独立编译和管理。因此,在编译阶段使用 model.text_encoder.model 对于直接进行 Neuron 优化至关重要,而封装器则在部署阶段承担接口适配职责。