PyTorch Serve自定义Handler无推理输出及日志不打印问题求助
PyTorch Serve自定义Handler无输出且日志不打印的排查与修复
核心问题总结
你的Handler代码存在方法参数不匹配、语法错误、类型转换错误三大类问题,同时日志未打印可能是初始化失败或未查看Worker进程日志导致的。以下是具体问题和修复方案:
1. 初始化阶段日志未打印:存在变量名笔误
get_enhancement_model方法里的loadnet是未定义的变量,实际应为ldnet,这会直接导致初始化失败,Worker进程启动异常,但主日志不会显示Worker的错误,需要查看Worker日志文件(路径通常是logs/W-9000-facex_1.0.log)。
修复代码:
def get_enhancement_model(self, device): gan = GAN(device) ldnet = torch.load(MODEL_PATH_RET) name = 'params_ema' if 'params_ema' in ldnet else 'params' gan.load_state_dict(ldnet[name], strict=True) # 修正为ldnet gan.eval() gan = gan.to(device) return gan
2. 推理方法参数不符合TorchServe规范
TorchServe的BaseHandler默认执行流程是:handle() → preprocess() → inference() → postprocess(),但你的方法参数完全不符合规范:
preprocess接收的是请求数据列表(比如[request1, request2]),不是单个imageinference接收的是preprocess返回的张量,不是PIL Image
修复preprocess(先补全缺失的io导入):
import io # 新增导入 def preprocess(self, data): logger.info("\n\n\n Inside preprocess Function") logger.info("Type of received data = %s", type(data)) # 取第一个请求的内容,兼容data/body两种格式 request_data = data[0].get("data") or data[0].get("body") if isinstance(request_data, bytes): image = Image.open(io.BytesIO(request_data)) else: image = Image.open(io.BytesIO(request_data)) # 转换为模型需要的张量格式(适配你的GAN输入要求) image_np = np.array(image) image_tensor = torch.from_numpy(image_np.transpose(2, 0, 1)).float().unsqueeze(0).to(self.device) logger.info("Preprocessing complete.") return image_tensor
修复inference:
def inference(self, input_tensor): logger.info("\n\n\n Inside Inference Function") logger.info("Type of received tensor = %s", type(input_tensor)) output_tensor = self.pi(input_tensor) output_image = self.postprocess(output_tensor) logger.info("Type of Output = %s", type(output_image)) return output_image
3. 推理逻辑中的语法错误
poi方法存在两处明显语法问题:
alc()调用未传参数(原函数需传入人脸关键点等必要参数)self.fc_enh(torch.from_numpy(cropped_face)缺少闭合括号
修复示例:
def poi(self, input_tensor: torch.Tensor): img_np = input_tensor.squeeze(0).detach().cpu().numpy().transpose(1, 2, 0) face_landmarks, _ = self.fc_h.get_face_landmarks(img_np) restored_img = img_np.copy() # 初始化原始图像,避免为空 for face_landmark in face_landmarks: # 传入必要参数给alc(根据你的fb.py逻辑补全) cropped_face, inverse_affine = alc(face_landmark, img_np) # 补全括号并转换为模型适配的张量格式 cropped_tensor = torch.from_numpy(cropped_face).float().unsqueeze(0).to(self.device) restored_face = self.fc_enh(cropped_tensor) restored_face = restored_face.squeeze(0).detach().cpu().numpy().transpose(1, 2, 0) restored_img = pfb(img_np, restored_face, inverse_affine=inverse_affine) return torch.from_numpy(restored_img.transpose(2, 0, 1)).unsqueeze(0).to(self.device), [], []
4. Postprocess返回格式不符合要求
你的postprocess返回PIL Image,但TorchServe无法直接序列化PIL Image返回给客户端,需要转换为字节流。
修复postprocess:
def postprocess(self, output: torch.Tensor) -> bytes: output_np = output.squeeze(0).detach().cpu().numpy().transpose(1, 2, 0) output_np = np.clip(output_np, 0, 255).astype(np.uint8) output_image = Image.fromarray(output_np) # 转换为字节流返回 img_byte_arr = io.BytesIO() output_image.save(img_byte_arr, format='JPEG') img_byte_arr.seek(0) # 同时保存到本地(可选) output_image.save("processed_output.jpg") return img_byte_arr.getvalue()
5. 日志查看建议
主日志ts_log.log仅记录服务器启动信息,Worker进程的错误和自定义日志会写入Worker日志文件(路径:logs/W-<端口>-<模型名>_<版本>.log),比如你的场景对应logs/W-9000-facex_1.0.log,排查时必须查看该文件。
6. 重新打包与启动
修复代码后,重新生成.mar文件(注意添加fb.py到额外文件):
torch-model-archiver --model-name facex --version 1.0 --handler handler.py --serialized-file model.pth --extra-files landmarks.dat,GANModel.py,LD.py,Utils.py,fb.py
重启服务器:
torchserve --stop torchserve --ncs --start --model-store model_store --ts-config config.properties --models facex.mar
内容的提问来源于stack exchange,提问作者Muhammad Danial Khan
相关产品推荐
相关产品推荐

