我正在尝试使用Sagemaker Batch Transformer对Yolo-NAS模型进行按需推理,利用预训练的模型和权重。但是,我遇到了一个错误信息:
python3: can't open file '//serve': [Errno 2] No such file or directory
我对'//serve'
感到非常困惑,因为它既不是我代码中的引用,也未在任何文档中找到相关信息。
为了提供更多的背景信息以便理解我的情况:
- 我的数据集由单个对象的图像组成(称为“作物”)。
- 我的脚本
seq-gil-transformer.py
位于S3路径s3://benny-test/indexer/yolo-nas/1/code
下。
- 我知道
/opt/ml/processing/input
并非正确的数据路径,目前我正尝试确定应该使用的正确路径,但错误发生在我预期执行逻辑之前。
我还自定义了一个Docker镜像,内容如下:
FROM python:3.10-bullseye
# 更新并安装必要的库
RUN apt update && \
apt install ffmpeg libsm6 libxext6 libgl1 -y && \
rm -rf /var/lib/apt/lists/*
# 添加依赖文件并安装
ADD req.txt req.txt
RUN pip3 install -r req.txt
# 设置入口点
ENTRYPOINT ["python3"]
此外,我有一段自定义的推理代码seq-gil-transformer.py
,用于加载预训练的Yolo-NAS模型,处理输入图像,并执行推理。代码中还包括一个名为ModelWrapper
的模块,它用于包装和控制模型的输出层。
SageMaker部署的Python脚本则负责设置模型、配置Transformer并启动转换作业。
尽管我已经尝试了多种方法,但仍然无法绕过那个神秘的'//serve'
错误。我需要帮助理解这个错误的来源以及如何解决它,以便能够成功地在我的数据上运行模型推理。
import super_gradients
import torch
from torch import nn
import json
from collections import OrderedDict
from typing import List
from pathlib import Path
import PIL
import numpy as np
import time
import cProfile
import trace
import io
import psutil
import numpy as np
import matplotlib.pyplot as plt
import sys
import pandas as pd
import argparse
# 从本地路径获取图片
def get_images(path, device):
# 遍历指定路径下所有.png文件
crops_path_list = Path(path).glob("*.png")
images = [] # 初始化图片列表
for crop_path in crops_path_list:
image = PIL.Image.open(str(crop_path)).convert("RGB") # 打开并转换为RGB格式
image = np.asarray(image) # 转换为numpy数组
image = np.ascontiguousarray(image.transpose(2, 0, 1)) # 调整维度顺序以适应PyTorch
image = torch.from_numpy(np.array(image)).unsqueeze(0).to(dtype=torch.float32, device=device) # 转换为Tensor并发送到指定设备
images.append(image)
return images
# 保存模型
def create_yolo_nas_indexer(device="cpu"):
# 加载预训练的YOLO-NAS模型并保存其backbone部分
yolo_nas = super_gradients.training.models.get("yolo_nas_l", pretrained_weights="coco").to(device)
torch.save(yolo_nas.backbone, f"yolo_nas_l_{device}.tar.gz")
# 加载模型
def load_yolo_nas(device="cpu",output_layers=["stage2","stage3","context_module"]):
# 从保存的文件加载模型,并通过自定义的ModelWrapper包装
yolo_nas = torch.load(f"yolo_nas_l_{device}.pth.tar").to(device)
wraped_yolo_nas = ModelWrapper(yolo_nas,device=device).to(device)
wraped_yolo_nas.add_output_layers(output_layers) # 添加自定义输出层
return wraped_yolo_nas
# 对单个图像进行推理
def primitive_c2v(crop:torch.Tensor, model)->list:
output = model(crop)[0] # 使用ModelWrapper进行前向传播
return output
if __name__ == "__main__":
engine = "cpu"
crops = get_images("/opt/ml/processing/input", engine) # 获取图片
# 准备环境和模型
outputs = []
create_yolo_nas_indexer(engine)
model = load_yolo_nas(engine)
model.eval() # 设置模型为评估模式
list_o = [] # 存储推理结果
# 对每个图像进行推理
for crop in crops:
start_time = time.time()
output = primitive_c2v(crop, model)
output = [l.detach().numpy() for l in output] # 转换输出为numpy数组
list_o.append(output)
# 打印输出长度验证一切正常
for l in list_o:
print(len(l))
# SageMaker部署相关代码
import boto3
import sagemaker
from sagemaker import get_execution_role
# 设置SageMaker会话、角色、存储桶
sagemaker_session = sagemaker.Session()
role = get_execution_role()
bucket = 'benny-test'
# 定义SageMaker模型
model = sagemaker.model.Model(
source_dir="s3://benny-test/indexer/yolo-nas/1/code",
entry_point="seq-gil-transformer.py",
image_uri='*.dkr.ecr.eu-west-1.amazonaws.com/sagemaker:super-gredients-0.1',
role=role,
name="yolo-nas-cpu")
# 创建并启动转换器实例
transformer = model.transformer(
instance_count=1,
instance_type="ml.m5.4xlarge"
)
transformer.transform(
data="s3://benny-test/indexer/Raw/dummy_set/images"
)
transformer.wait() # 等待转换完成
模型包装器参考
这一部分的代码虽然不直接解决问题所需,但我将其包含于此以供参考和可复现性。
以下代码定义了一个名为ModelWrapper
的类,该类扩展自PyTorch的nn.Module
,用于包装和控制模型的前向传播过程,特别是用于在特定层收集输出。这个类还提供了一些辅助功能,比如移除所有已注册的钩子、递归地将模型的所有模块添加到字典中、以及计算输出层的参数等。
def remove_all_hooks_recursive(model: nn.Module) -> None:
# 递归移除模型中所有模块的前向、前向预处理、和后向钩子
for name, child in model.named_children():
if child is not None:
for attr in ["_forward_hooks", "_forward_pre_hooks", "_backward_hooks"]:
if hasattr(child, attr):
setattr(child, attr, OrderedDict())
remove_all_hooks_recursive(child)
def add_all_modules_to_model_dict_recursive(model, module_dict, prefix=''):
"""递归地将模型所有模块添加到一个分层字典中"""
for name, module in model.named_children():
full_name = f"{prefix}.{name}" if prefix else name
full_name = full_name if full_name != "_model" else ""
module_dict[full_name] = module
if isinstance(module, nn.Module):
add_all_modules_to_model_dict_recursive(module, module_dict, full_name)
class StopModel(Exception):
# 自定义异常,用于停止模型前向传播
pass
def forward_hook(model_wrapper, layer_name, model=None):
# 定义前向传播钩子函数
def hook(module, input, output):
model_wrapper.selected_out[layer_name] = output
if model is not None:
_, code = model(output)
model_wrapper.selected_out[f"code_{layer_name}"] = code
if model_wrapper.stop_at_last_hook and layer_name == model_wrapper.last_layer:
raise StopModel()
return hook
class ModelWrapper(nn.Module):
def __init__(self, model, stop_at_last_hook=False, device=None):
super().__init__()
self.configure_with_model(model, stop_at_last_hook, device)
def configure_with_model(self, model, stop_at_last_hook, device):
self.stop_at_last_hook = stop_at_last_hook
self.model = model
self.model.eval()
self.prepare_output_setup()
self.initialize_device(device)
self.setup_hooks_and_calculate_params()
def prepare_output_setup(self):
self.output_layers = []
self.selected_out = OrderedDict()
self.fhooks = []
self.layer_size_dict = {}
self.layer_stride_dict = {}
def initialize_device(self, device):
self.device = device or torch.device('cuda' if torch.cuda.is_available() else 'cpu')
def setup_hooks_and_calculate_params(self):
self.model_dict = self.add_all_modules_to_model_dict()
self.compute_output_layer_parameters()
# ...其他方法如add_output_layers, compute_output_layer_parameters等保持不变...
# 其余方法定义...