登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
登录
注册
代码拉取完成,页面将自动刷新
开源项目
>
人工智能
>
机器学习/深度学习
&&
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
88
Star
640
Fork
1.3K
Ascend
/
pytorch
代码
Issues
40
Pull Requests
378
Wiki
统计
流水线
服务
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
YOLOV8-OBB使用torch_npu推理,新图片后处理阶段(尤其是 NMS)耗时1-2分钟
TODO
#ICTXIJ
训练问题
魏鹏程
创建于
2025-08-20 20:29
一、问题现象(附报错日志上下文): 使用torch_npu适配昇腾NPU的推理服务。目前遇到问题:第一次新的图片调用会特别慢(1-2分钟),后续同样的图片请求调用就很快(100-200毫秒)。 日志: ==== INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... ..... ...........WARNING ⚠️ NMS time limit 2.050s exceeded [W820 16:00:11.976227569 compiler_depend.ts:79] Warning: [Check][offset] Check input storage_offset[%ld] = 0 failed, result is untrustworthy1 (function operator()) 0: 544x960 None34738.4ms Speed: 6296.3ms preprocess, 34738.4ms inference, 28121.1ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 153.188 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.86 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.82 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.72 .INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:64253 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... 0: 544x960 None10.8ms Speed: 11.2ms preprocess, 10.8ms inference, 20.3ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 0.044 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.86 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.82 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65306 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... .........WARNING ⚠️ NMS time limit 2.050s exceeded 0: 544x960 None10.0ms Speed: 10.4ms preprocess, 10.0ms inference, 88658.4ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 88.680 秒 INFO:__main__:检测到 9 个旋转框物体 ....INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.87 INFO:__main__:检测到物体 - 类别: N_螺丝刀, 置信度: 0.68 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.56 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:55492 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... 0: 544x960 None10.1ms Speed: 10.7ms preprocess, 10.1ms inference, 20.6ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 0.043 秒 INFO:__main__:检测到 9 个旋转框物体 INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.87 INFO:__main__:检测到物体 - 类别: N_螺丝刀, 置信度: 0.68 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.56 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:54722 - "POST /gqj_check HTTP/1.1" 200 OK ==== ``` # import os # os.environ["ASCEND_OP_DEBUG"] = "1" # 启用算子调试模式 # os.environ["ASCEND_SLOG_PRINT_TO_STDOUT"] = "1" # 直接在控制台输出算子日志 # os.environ["ASCEND_PRINT_OP_PATH"] = "op_exec.log" # 优先设置环境变量 from fastapi import FastAPI, File, UploadFile, HTTPException, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import JSONResponse import cv2 import numpy as np from ultralytics import YOLO import logging import os from datetime import datetime from PIL import Image import io import argparse import json import time import requests from urllib.parse import urlparse from pydantic import BaseModel from typing import Optional, Dict, Any, List, Union import setproctitle import torch_npu # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="YOLOv8-OBB Object Detection API") # 挂载静态文件目录,用于存放和提供检测后的图片 # /img/some_image.jpg 将会是图片的访问URL RESULT_DIR = 'results' os.makedirs(RESULT_DIR, exist_ok=True) app.mount("/gqj_check/img", StaticFiles(directory=RESULT_DIR), name="results") # 自定义物品名称映射 CUSTOM_NAMES = { "A_安全帽": "安全帽", "B_安全带": "安全带", "C_绝缘靴": "绝缘靴", "D_绝缘手套": "绝缘手套", "E_验电器": "验电器", "F_工具包": "工具包", "G_钳子": "钳子", "H_力矩扳手": "力矩扳手", "I_钢丝刷": "钢丝刷", "J_头灯": "头灯", "K_力矩头": "力矩头", "L_防护旗": "防护旗", "M_扳手": "扳手", "N_螺丝刀": "螺丝刀", "O_脚扣": "脚扣", "P_水平尺": "水平尺", "Q_手锤子": "手锤子", "R_接地线杆": "接地线杆" } # 全局模型变量 model = None config = { "debug_dir": "debug_output", "log_file": "api_calls.log" } class ImageUrl(BaseModel): image_url: str class DetectionItem(BaseModel): class_name: str count: int confidence: float class ApiResponse(BaseModel): code: int msg: str request_time: str end_time: str total_time: str status: Optional[str] data: List[DetectionItem] inference_time: Optional[str] image_url: Optional[str] source_url: Optional[str] def load_model(model_path, npu_id=0): # 新增npu_id参数,指定卡编号 global model try: # 绑定到指定NPU卡(0为卡编号) import torch_npu torch_npu.npu.set_device(npu_id) # 关键:绑定到特定NPU卡 model = YOLO(model_path) model.to(f"npu:{npu_id}") # 迁移模型到指定卡 # 验证设备 if hasattr(model, 'parameters'): first_param = next(model.parameters(), None) if first_param is not None: device = first_param.device logger.info(f"模型已绑定到 {device}(NPU卡{npu_id})") logger.info("OBB模型加载成功并绑定到指定NPU卡") # 关键优化1:设置模型为eval模式并禁用梯度计算 model.model.eval() for param in model.model.parameters(): param.requires_grad = False # 关键优化2:预编译常见尺寸的计算图 logger.info("开始预编译计算图...") common_sizes = [(960, 960), (640, 640), (1280, 1280)] import torch with torch.no_grad(): for size in common_sizes: try: # 创建固定尺寸的dummy输入 dummy_input = torch.randn(1, 3, size[0], size[1]).to(f"npu:{npu_id}") # 多次推理以确保计算图完全编译 for _ in range(3): _ = model.model(dummy_input) logger.info(f"预编译尺寸 {size} 完成") # 清理显存 del dummy_input torch_npu.npu.empty_cache() except Exception as e: logger.warning(f"预编译尺寸 {size} 失败: {e}") logger.info("计算图预编译完成") except Exception as e: logger.error(f"模型加载失败: {str(e)}") raise def log_api_call(request_time: datetime, original_path: str, result_path: str, result_info: Dict[str, Any], request_source: str = "file", request_id: Optional[str] = None): """记录API调用信息到日志文件""" log_entry = { "timestamp": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "request_id": request_id, "request_source": request_source, "original_file": original_path, "result_file": result_path, "result_info": result_info } try: with open(config["log_file"], "a", encoding="utf-8") as f: f.write(json.dumps(log_entry, ensure_ascii=False) + "\n") logger.info(f"API调用记录已保存: {config['log_file']}") except Exception as e: logger.error(f"记录API调用失败: {str(e)}") def process_image(image: Image.Image, request_time: datetime, original_filename: Optional[str] = None, request_source: str = "file") -> Dict[str, Any]: """处理图像并返回检测结果""" # 创建输出目录 os.makedirs(config["debug_dir"], exist_ok=True) # 生成文件名 timestamp = request_time.strftime("%Y%m%d_%H%M%S_%f") if original_filename: # 从URL获取文件名 base_name = os.path.basename(original_filename) name, ext = os.path.splitext(base_name) original_filename = f"{name}_{timestamp}{ext}" else: original_filename = f"original_{timestamp}.jpg" original_path = os.path.join(config["debug_dir"], original_filename) result_filename = f"detection_{timestamp}.jpg" result_path = os.path.join(RESULT_DIR, result_filename) # 保存原始图像 image.save(original_path) # 将图像转换为OpenCV格式 (BGR) opencv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) original_height, original_width = opencv_image.shape[:2] # 记录原始尺寸 logger.info(f"原始图像尺寸: {original_width}x{original_height}") # 关键修改:强制缩放到固定正方形960x960,消除动态shape opencv_image = cv2.resize(opencv_image, (960, 960)) logger.info(f"已将图像强制调整为固定尺寸: 960x960") # 开始计时 start_inference_time = time.time() # 运行OBB推理 logger.info("开始OBB推理...") results = model.predict( source=opencv_image, # device='npu', nms=True, # 启用NMS save=False, conf=0.7, imgsz=960, # 使用固定正方形尺寸 # agnostic_nms=True, max_det=50, iou=0.55 ) # 结束计时 end_inference_time = time.time() inference_time = end_inference_time - start_inference_time logger.info(f"OBB推理完成,总用时: {inference_time:.3f} 秒") # 初始化默认结果 class_counts = {} detected_objects = [] annotated_frame = opencv_image # 默认使用原始图像 # 检查推理结果是否有效 if results is None or len(results) == 0: logger.warning("未检测到任何物体(results为空)") else: r = results[0] # 获取第一个结果 # 对于OBB模型,检测结果在r.obb中 if r.obb is not None: logger.info(f"检测到 {len(r.obb)} 个旋转框物体") # 处理OBB检测结果 for i in range(len(r.obb)): try: class_id = int(r.obb.cls[i]) confidence = float(r.obb.conf[i]) class_name = r.names[class_id] logger.info(f"检测到物体 - 类别: {class_name}, 置信度: {confidence:.2f}") # 只处理置信度大于0.5的检测结果 if confidence > 0.5 and class_name in CUSTOM_NAMES: display_name = CUSTOM_NAMES[class_name] class_counts[display_name] = class_counts.get(display_name, 0) + 1 detected_objects.append({ "class_name": display_name, "confidence": confidence }) except Exception as e: logger.error(f"处理OBB检测结果时出错: {str(e)}") continue # 绘制检测结果(即使没有检测到物体也绘制) try: # 对于OBB模型,使用plot()绘制旋转框 annotated_frame = r.plot() except Exception as e: logger.error(f"绘制OBB检测结果时出错: {str(e)}") annotated_frame = opencv_image # 出错时使用原始图像 # 保存结果图像 cv2.imwrite(result_path, annotated_frame) # 优化结果结构 detection_result = [] for class_name, count in class_counts.items(): # 找到该类别的所有检测结果 class_detections = [d for d in detected_objects if d["class_name"] == class_name] # 取最高置信度 max_confidence = max([d["confidence"] for d in class_detections]) if class_detections else 0 detection_result.append({ "utensilName": class_name, "identifyCount": count, "confidence": max_confidence }) return { "detection_result": detection_result, "inference_time": inference_time, "original_path": original_path, "result_path": result_path } def create_success_response(request: Request, request_time: datetime, end_time: datetime, result: Dict[str, Any], source_url: Optional[str] = None) -> Dict[str, Any]: """创建统一的成功响应格式,将data中的字段提升一级""" total_time = end_time - request_time base_url = str(request.base_url) result_filename = os.path.basename(result["result_path"]) debug_image_url = f"{base_url}gqj_check/img/{result_filename}" return { "code": 200, "msg": "成功", "request_time": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "end_time": end_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "total_time": f"{total_time.total_seconds():.3f}秒", "status": "success", "data": result["detection_result"], "cost_time": f"{result['inference_time']:.3f}", "debug_image_url": debug_image_url, **({"source_url": source_url} if source_url else {}) } def create_error_response(status_code: int, msg: str, request_time: datetime, end_time: datetime) -> Dict[str, Any]: """创建统一的错误响应格式""" total_time = end_time - request_time return { "code": status_code, "msg": msg, "request_time": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "end_time": end_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "total_time": f"{total_time.total_seconds():.3f}秒", "data": [] } @app.post("/gqj_check/localfile") async def detect_objects(request: Request, file: UploadFile = File(...)): request_time = datetime.now() if model is None: end_time = datetime.now() return JSONResponse( content=create_error_response(500, "模型未加载", request_time, end_time), status_code=500 ) try: # 读取上传的图像 contents = await file.read() image = Image.open(io.BytesIO(contents)) # 处理图像 result = process_image(image, request_time, file.filename) end_time = datetime.now() # 准备返回结果 response_data = create_success_response(request, request_time, end_time, result) # 记录API调用 log_api_call(request_time, result["original_path"], result["result_path"], response_data) return JSONResponse(content=response_data) except Exception as e: end_time = datetime.now() logger.error(f"OBB检测失败: {str(e)}", exc_info=True) return JSONResponse( content=create_error_response(500, str(e), request_time, end_time), status_code=500 ) @app.post("/gqj_check") async def detect_objects_by_url(request: Request, url_data: ImageUrl): url = url_data.image_url request_time = datetime.now() if model is None: end_time = datetime.now() return JSONResponse( content=create_error_response(500, "模型未加载", request_time, end_time), status_code=500 ) try: # 验证URL格式 try: result = urlparse(url) if not all([result.scheme, result.netloc]): raise ValueError("无效的URL格式") except ValueError as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"URL格式错误: {str(e)}") # 下载图片 # 找到下载图片的代码块,替换为以下内容 logger.info(f"从URL下载图片: {url},预计大小可能较大,延长超时时间") try: # 连接超时5秒(快速判断是否能建立连接),读取超时60秒(给大图片足够下载时间) response = requests.get(url, timeout=(5, 60)) # 关键修改:延长读取超时到60秒 response.raise_for_status() # 检查HTTP响应状态码 # 记录下载成功的信息(便于排查) content_length = response.headers.get('Content-Length', '未知') logger.info(f"图片下载成功,大小: {content_length} 字节") except requests.exceptions.ConnectTimeout: end_time = datetime.now() raise HTTPException(status_code=408, detail="连接图片服务器超时,请检查网络") except requests.exceptions.ReadTimeout: end_time = datetime.now() raise HTTPException(status_code=408, detail="下载图片超时,图片可能过大或网络缓慢") except requests.exceptions.RequestException as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"访问图片URL失败: {str(e)}") # 检查内容类型 content_type = response.headers.get('Content-Type', '') if not content_type.startswith('image/'): end_time = datetime.now() raise HTTPException(status_code=400, detail=f"URL内容类型不是图片: {content_type}") # 打开图片 try: image = Image.open(io.BytesIO(response.content)) except Exception as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"无法解析图片: {str(e)}") # 提取文件名 original_filename = None if 'Content-Disposition' in response.headers: # 尝试从Content-Disposition头中获取文件名 import re cd = response.headers['Content-Disposition'] fname = re.findall('filename="?([^"]+)"?', cd) if fname: original_filename = fname[0] if not original_filename: # 从URL中提取文件名 path = result.path original_filename = os.path.basename(path) if not original_filename or '.' not in original_filename: original_filename = f"image_{request_time.strftime('%Y%m%d_%H%M%S')}.jpg" # 处理图像 result = process_image(image, request_time, original_filename, "url") end_time = datetime.now() # 准备返回结果 response_data = create_success_response(request, request_time, end_time, result, url) # 记录API调用 log_api_call(request_time, result["original_path"], result["result_path"], response_data, "url") return JSONResponse(content=response_data) except HTTPException as e: logger.error(f"URL检测失败: {e.detail}") return JSONResponse( content=create_error_response(e.status_code, e.detail, request_time, datetime.now()), status_code=e.status_code ) except Exception as e: end_time = datetime.now() logger.error(f"OBB检测失败: {str(e)}", exc_info=True) return JSONResponse( content=create_error_response(500, str(e), request_time, end_time), status_code=500 ) def warmup_scenarios(model, npu_id=0): logger.info("开始多场景预热...") # 创建不同尺寸的合成图像进行预热 import torch warmup_configs = [ (960, 960, 5), # (width, height, iterations) (640, 640, 3), (1280, 1280, 3), ] with torch.no_grad(): for width, height, iterations in warmup_configs: logger.info(f"预热尺寸: {width}x{height}") # 创建合成图像 synthetic_img = np.random.randint(0, 255, (height, width, 3), dtype=np.uint8) for i in range(iterations): try: results = model.predict( source=synthetic_img, conf=0.6, imgsz=max(width, height), verbose=False, save=False, nms=True, agnostic_nms=False, max_det=30, iou=0.6 ) logger.info(f"预热迭代 {i+1}/{iterations} 完成") # 清理 del results torch_npu.npu.empty_cache() except Exception as e: logger.warning(f"预热失败: {e}") logger.info(f"尺寸 {width}x{height} 预热完成") logger.info("多场景预热完成") if __name__ == "__main__": # 设置自定义进程名 setproctitle.setproctitle("gqj_check_api") # 设置进程名称 parser = argparse.ArgumentParser(description='YOLOv8-OBB Object Detection API') parser.add_argument('--model_path', type=str, required=True, help='OBB模型文件的路径') parser.add_argument('--port', type=int, default=8001, help='服务启动的端口号') parser.add_argument('--output_dir', type=str, default="debug_output", help='输出目录的路径') parser.add_argument('--log_file', type=str, default="api_calls.log", help='日志文件的路径') parser.add_argument('--npu_id', type=int, default=0, help='NPU卡编号(默认0)') # 新增参数 args = parser.parse_args() # 更新配置 config["debug_dir"] = args.output_dir config["log_file"] = args.log_file # 创建输出目录 os.makedirs(args.output_dir, exist_ok=True) # 加载模型并启动服务 load_model(args.model_path,npu_id=args.npu_id) warmup_scenarios(model, args.npu_id) import uvicorn uvicorn.run(app, host="0.0.0.0", port=args.port) ``` 二、软件版本: 1. 硬件环境 服务器:华为鲲鹏 920-3226 处理器(ARM 架构)+ Atlas 300I Duo NPU:8 个华为昇腾 310P3 NPU 2. 软件版本 组件版本安装路径 操作系统内核 4.19.90-89.24.v2401.ky10.aarch64 - CANN 开发套件 Ascend-cann-toolkit_8.1.RC1_linux-aarch64 Ascend-cann-kernels-310p_8.1.RC1_linux-aarch64 Ascend-cann-nnal_8.1.RC1_linux-aarch64 /usr/local/Ascend/ascend-toolkit/8.1.RC1/aarch64-linux NPU 驱动 Ascend-hdk-310p-npu-driver_25.0.rc1.1_linux-aarch64.run - NPU 固件 Ascend-hdk-310p-npu-firmware_7.7.0.1.231.run - 内核开发包 kernel-devel-4.19.90-89.11.v2401.ky10.aarch64 kernel-headers-4.19.90-89.11.v2401.ky10.aarch64 - 昇腾 PyTorch torch_npu-2.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - 推理工具 aclruntime-0.0.2-cp310-cp310-linux_aarch64.whl ais_bench-0.0.2-py3-none-any.whl 三、测试步骤: 通过AI说问题是每个不同尺寸的图片都会触发NPU重新编译计算图。  四、日志信息: (yolo_npu) [root@bogon gqj_check]# python gqj_check_npu.py --model_path best.pt --port 8001 --output_dir debug_output --npu_id 0 ..INFO:__main__:模型已绑定到 npu:0(NPU卡0) INFO:__main__:OBB模型加载成功并绑定到指定NPU卡 INFO:__main__:开始预编译计算图... ....INFO:__main__:预编译尺寸 (960, 960) 完成 ....INFO:__main__:预编译尺寸 (640, 640) 完成 ...INFO:__main__:预编译尺寸 (1280, 1280) 完成 .INFO:__main__:计算图预编译完成 INFO:__main__:开始多场景预热... INFO:__main__:预热尺寸: 960x960 ........INFO:__main__:预热迭代 1/5 完成 INFO:__main__:预热迭代 2/5 完成 INFO:__main__:预热迭代 3/5 完成 INFO:__main__:预热迭代 4/5 完成 INFO:__main__:预热迭代 5/5 完成 INFO:__main__:尺寸 960x960 预热完成 INFO:__main__:预热尺寸: 640x640 ........INFO:__main__:预热迭代 1/3 完成 INFO:__main__:预热迭代 2/3 完成 INFO:__main__:预热迭代 3/3 完成 INFO:__main__:尺寸 640x640 预热完成 INFO:__main__:预热尺寸: 1280x1280 .INFO:__main__:预热迭代 1/3 完成 INFO:__main__:预热迭代 2/3 完成 INFO:__main__:预热迭代 3/3 完成 INFO:__main__:尺寸 1280x1280 预热完成 INFO:__main__:多场景预热完成 INFO: Started server process [1851047] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit) INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... ........WARNING ⚠️ NMS time limit 2.050s exceeded [W820 18:55:47.435100569 compiler_depend.ts:79] Warning: [Check][offset] Check input storage_offset[%ld] = 0 failed, result is untrustworthy1 (function operator()) INFO:__main__:OBB推理完成,总用时: 76.958 秒 INFO:__main__:检测到 8 个旋转框物体 .INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.98 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:50167 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... INFO:__main__:OBB推理完成,总用时: 0.056 秒 INFO:__main__:检测到 8 个旋转框物体 INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.98 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65432 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... ........WARNING ⚠️ NMS time limit 2.050s exceeded INFO:__main__:OBB推理完成,总用时: 72.287 秒 INFO:__main__:检测到 12 个旋转框物体 .INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.96 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.85 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.76 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.71 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65440 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... INFO:__main__:OBB推理完成,总用时: 0.056 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.96 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.85 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.76 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.71 INFO:__main__:API调用记录已保存: api_calls.log
一、问题现象(附报错日志上下文): 使用torch_npu适配昇腾NPU的推理服务。目前遇到问题:第一次新的图片调用会特别慢(1-2分钟),后续同样的图片请求调用就很快(100-200毫秒)。 日志: ==== INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... ..... ...........WARNING ⚠️ NMS time limit 2.050s exceeded [W820 16:00:11.976227569 compiler_depend.ts:79] Warning: [Check][offset] Check input storage_offset[%ld] = 0 failed, result is untrustworthy1 (function operator()) 0: 544x960 None34738.4ms Speed: 6296.3ms preprocess, 34738.4ms inference, 28121.1ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 153.188 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.86 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.82 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.72 .INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:64253 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... 0: 544x960 None10.8ms Speed: 11.2ms preprocess, 10.8ms inference, 20.3ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 0.044 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.86 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.82 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65306 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... .........WARNING ⚠️ NMS time limit 2.050s exceeded 0: 544x960 None10.0ms Speed: 10.4ms preprocess, 10.0ms inference, 88658.4ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 88.680 秒 INFO:__main__:检测到 9 个旋转框物体 ....INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.87 INFO:__main__:检测到物体 - 类别: N_螺丝刀, 置信度: 0.68 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.56 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:55492 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:开始OBB推理... 0: 544x960 None10.1ms Speed: 10.7ms preprocess, 10.1ms inference, 20.6ms postprocess per image at shape (1, 3, 544, 960) INFO:__main__:OBB推理完成,总用时: 0.043 秒 INFO:__main__:检测到 9 个旋转框物体 INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.97 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.88 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.87 INFO:__main__:检测到物体 - 类别: N_螺丝刀, 置信度: 0.68 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.56 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:54722 - "POST /gqj_check HTTP/1.1" 200 OK ==== ``` # import os # os.environ["ASCEND_OP_DEBUG"] = "1" # 启用算子调试模式 # os.environ["ASCEND_SLOG_PRINT_TO_STDOUT"] = "1" # 直接在控制台输出算子日志 # os.environ["ASCEND_PRINT_OP_PATH"] = "op_exec.log" # 优先设置环境变量 from fastapi import FastAPI, File, UploadFile, HTTPException, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import JSONResponse import cv2 import numpy as np from ultralytics import YOLO import logging import os from datetime import datetime from PIL import Image import io import argparse import json import time import requests from urllib.parse import urlparse from pydantic import BaseModel from typing import Optional, Dict, Any, List, Union import setproctitle import torch_npu # 配置日志 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="YOLOv8-OBB Object Detection API") # 挂载静态文件目录,用于存放和提供检测后的图片 # /img/some_image.jpg 将会是图片的访问URL RESULT_DIR = 'results' os.makedirs(RESULT_DIR, exist_ok=True) app.mount("/gqj_check/img", StaticFiles(directory=RESULT_DIR), name="results") # 自定义物品名称映射 CUSTOM_NAMES = { "A_安全帽": "安全帽", "B_安全带": "安全带", "C_绝缘靴": "绝缘靴", "D_绝缘手套": "绝缘手套", "E_验电器": "验电器", "F_工具包": "工具包", "G_钳子": "钳子", "H_力矩扳手": "力矩扳手", "I_钢丝刷": "钢丝刷", "J_头灯": "头灯", "K_力矩头": "力矩头", "L_防护旗": "防护旗", "M_扳手": "扳手", "N_螺丝刀": "螺丝刀", "O_脚扣": "脚扣", "P_水平尺": "水平尺", "Q_手锤子": "手锤子", "R_接地线杆": "接地线杆" } # 全局模型变量 model = None config = { "debug_dir": "debug_output", "log_file": "api_calls.log" } class ImageUrl(BaseModel): image_url: str class DetectionItem(BaseModel): class_name: str count: int confidence: float class ApiResponse(BaseModel): code: int msg: str request_time: str end_time: str total_time: str status: Optional[str] data: List[DetectionItem] inference_time: Optional[str] image_url: Optional[str] source_url: Optional[str] def load_model(model_path, npu_id=0): # 新增npu_id参数,指定卡编号 global model try: # 绑定到指定NPU卡(0为卡编号) import torch_npu torch_npu.npu.set_device(npu_id) # 关键:绑定到特定NPU卡 model = YOLO(model_path) model.to(f"npu:{npu_id}") # 迁移模型到指定卡 # 验证设备 if hasattr(model, 'parameters'): first_param = next(model.parameters(), None) if first_param is not None: device = first_param.device logger.info(f"模型已绑定到 {device}(NPU卡{npu_id})") logger.info("OBB模型加载成功并绑定到指定NPU卡") # 关键优化1:设置模型为eval模式并禁用梯度计算 model.model.eval() for param in model.model.parameters(): param.requires_grad = False # 关键优化2:预编译常见尺寸的计算图 logger.info("开始预编译计算图...") common_sizes = [(960, 960), (640, 640), (1280, 1280)] import torch with torch.no_grad(): for size in common_sizes: try: # 创建固定尺寸的dummy输入 dummy_input = torch.randn(1, 3, size[0], size[1]).to(f"npu:{npu_id}") # 多次推理以确保计算图完全编译 for _ in range(3): _ = model.model(dummy_input) logger.info(f"预编译尺寸 {size} 完成") # 清理显存 del dummy_input torch_npu.npu.empty_cache() except Exception as e: logger.warning(f"预编译尺寸 {size} 失败: {e}") logger.info("计算图预编译完成") except Exception as e: logger.error(f"模型加载失败: {str(e)}") raise def log_api_call(request_time: datetime, original_path: str, result_path: str, result_info: Dict[str, Any], request_source: str = "file", request_id: Optional[str] = None): """记录API调用信息到日志文件""" log_entry = { "timestamp": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "request_id": request_id, "request_source": request_source, "original_file": original_path, "result_file": result_path, "result_info": result_info } try: with open(config["log_file"], "a", encoding="utf-8") as f: f.write(json.dumps(log_entry, ensure_ascii=False) + "\n") logger.info(f"API调用记录已保存: {config['log_file']}") except Exception as e: logger.error(f"记录API调用失败: {str(e)}") def process_image(image: Image.Image, request_time: datetime, original_filename: Optional[str] = None, request_source: str = "file") -> Dict[str, Any]: """处理图像并返回检测结果""" # 创建输出目录 os.makedirs(config["debug_dir"], exist_ok=True) # 生成文件名 timestamp = request_time.strftime("%Y%m%d_%H%M%S_%f") if original_filename: # 从URL获取文件名 base_name = os.path.basename(original_filename) name, ext = os.path.splitext(base_name) original_filename = f"{name}_{timestamp}{ext}" else: original_filename = f"original_{timestamp}.jpg" original_path = os.path.join(config["debug_dir"], original_filename) result_filename = f"detection_{timestamp}.jpg" result_path = os.path.join(RESULT_DIR, result_filename) # 保存原始图像 image.save(original_path) # 将图像转换为OpenCV格式 (BGR) opencv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) original_height, original_width = opencv_image.shape[:2] # 记录原始尺寸 logger.info(f"原始图像尺寸: {original_width}x{original_height}") # 关键修改:强制缩放到固定正方形960x960,消除动态shape opencv_image = cv2.resize(opencv_image, (960, 960)) logger.info(f"已将图像强制调整为固定尺寸: 960x960") # 开始计时 start_inference_time = time.time() # 运行OBB推理 logger.info("开始OBB推理...") results = model.predict( source=opencv_image, # device='npu', nms=True, # 启用NMS save=False, conf=0.7, imgsz=960, # 使用固定正方形尺寸 # agnostic_nms=True, max_det=50, iou=0.55 ) # 结束计时 end_inference_time = time.time() inference_time = end_inference_time - start_inference_time logger.info(f"OBB推理完成,总用时: {inference_time:.3f} 秒") # 初始化默认结果 class_counts = {} detected_objects = [] annotated_frame = opencv_image # 默认使用原始图像 # 检查推理结果是否有效 if results is None or len(results) == 0: logger.warning("未检测到任何物体(results为空)") else: r = results[0] # 获取第一个结果 # 对于OBB模型,检测结果在r.obb中 if r.obb is not None: logger.info(f"检测到 {len(r.obb)} 个旋转框物体") # 处理OBB检测结果 for i in range(len(r.obb)): try: class_id = int(r.obb.cls[i]) confidence = float(r.obb.conf[i]) class_name = r.names[class_id] logger.info(f"检测到物体 - 类别: {class_name}, 置信度: {confidence:.2f}") # 只处理置信度大于0.5的检测结果 if confidence > 0.5 and class_name in CUSTOM_NAMES: display_name = CUSTOM_NAMES[class_name] class_counts[display_name] = class_counts.get(display_name, 0) + 1 detected_objects.append({ "class_name": display_name, "confidence": confidence }) except Exception as e: logger.error(f"处理OBB检测结果时出错: {str(e)}") continue # 绘制检测结果(即使没有检测到物体也绘制) try: # 对于OBB模型,使用plot()绘制旋转框 annotated_frame = r.plot() except Exception as e: logger.error(f"绘制OBB检测结果时出错: {str(e)}") annotated_frame = opencv_image # 出错时使用原始图像 # 保存结果图像 cv2.imwrite(result_path, annotated_frame) # 优化结果结构 detection_result = [] for class_name, count in class_counts.items(): # 找到该类别的所有检测结果 class_detections = [d for d in detected_objects if d["class_name"] == class_name] # 取最高置信度 max_confidence = max([d["confidence"] for d in class_detections]) if class_detections else 0 detection_result.append({ "utensilName": class_name, "identifyCount": count, "confidence": max_confidence }) return { "detection_result": detection_result, "inference_time": inference_time, "original_path": original_path, "result_path": result_path } def create_success_response(request: Request, request_time: datetime, end_time: datetime, result: Dict[str, Any], source_url: Optional[str] = None) -> Dict[str, Any]: """创建统一的成功响应格式,将data中的字段提升一级""" total_time = end_time - request_time base_url = str(request.base_url) result_filename = os.path.basename(result["result_path"]) debug_image_url = f"{base_url}gqj_check/img/{result_filename}" return { "code": 200, "msg": "成功", "request_time": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "end_time": end_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "total_time": f"{total_time.total_seconds():.3f}秒", "status": "success", "data": result["detection_result"], "cost_time": f"{result['inference_time']:.3f}", "debug_image_url": debug_image_url, **({"source_url": source_url} if source_url else {}) } def create_error_response(status_code: int, msg: str, request_time: datetime, end_time: datetime) -> Dict[str, Any]: """创建统一的错误响应格式""" total_time = end_time - request_time return { "code": status_code, "msg": msg, "request_time": request_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "end_time": end_time.strftime("%Y-%m-%d %H:%M:%S.%f"), "total_time": f"{total_time.total_seconds():.3f}秒", "data": [] } @app.post("/gqj_check/localfile") async def detect_objects(request: Request, file: UploadFile = File(...)): request_time = datetime.now() if model is None: end_time = datetime.now() return JSONResponse( content=create_error_response(500, "模型未加载", request_time, end_time), status_code=500 ) try: # 读取上传的图像 contents = await file.read() image = Image.open(io.BytesIO(contents)) # 处理图像 result = process_image(image, request_time, file.filename) end_time = datetime.now() # 准备返回结果 response_data = create_success_response(request, request_time, end_time, result) # 记录API调用 log_api_call(request_time, result["original_path"], result["result_path"], response_data) return JSONResponse(content=response_data) except Exception as e: end_time = datetime.now() logger.error(f"OBB检测失败: {str(e)}", exc_info=True) return JSONResponse( content=create_error_response(500, str(e), request_time, end_time), status_code=500 ) @app.post("/gqj_check") async def detect_objects_by_url(request: Request, url_data: ImageUrl): url = url_data.image_url request_time = datetime.now() if model is None: end_time = datetime.now() return JSONResponse( content=create_error_response(500, "模型未加载", request_time, end_time), status_code=500 ) try: # 验证URL格式 try: result = urlparse(url) if not all([result.scheme, result.netloc]): raise ValueError("无效的URL格式") except ValueError as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"URL格式错误: {str(e)}") # 下载图片 # 找到下载图片的代码块,替换为以下内容 logger.info(f"从URL下载图片: {url},预计大小可能较大,延长超时时间") try: # 连接超时5秒(快速判断是否能建立连接),读取超时60秒(给大图片足够下载时间) response = requests.get(url, timeout=(5, 60)) # 关键修改:延长读取超时到60秒 response.raise_for_status() # 检查HTTP响应状态码 # 记录下载成功的信息(便于排查) content_length = response.headers.get('Content-Length', '未知') logger.info(f"图片下载成功,大小: {content_length} 字节") except requests.exceptions.ConnectTimeout: end_time = datetime.now() raise HTTPException(status_code=408, detail="连接图片服务器超时,请检查网络") except requests.exceptions.ReadTimeout: end_time = datetime.now() raise HTTPException(status_code=408, detail="下载图片超时,图片可能过大或网络缓慢") except requests.exceptions.RequestException as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"访问图片URL失败: {str(e)}") # 检查内容类型 content_type = response.headers.get('Content-Type', '') if not content_type.startswith('image/'): end_time = datetime.now() raise HTTPException(status_code=400, detail=f"URL内容类型不是图片: {content_type}") # 打开图片 try: image = Image.open(io.BytesIO(response.content)) except Exception as e: end_time = datetime.now() raise HTTPException(status_code=400, detail=f"无法解析图片: {str(e)}") # 提取文件名 original_filename = None if 'Content-Disposition' in response.headers: # 尝试从Content-Disposition头中获取文件名 import re cd = response.headers['Content-Disposition'] fname = re.findall('filename="?([^"]+)"?', cd) if fname: original_filename = fname[0] if not original_filename: # 从URL中提取文件名 path = result.path original_filename = os.path.basename(path) if not original_filename or '.' not in original_filename: original_filename = f"image_{request_time.strftime('%Y%m%d_%H%M%S')}.jpg" # 处理图像 result = process_image(image, request_time, original_filename, "url") end_time = datetime.now() # 准备返回结果 response_data = create_success_response(request, request_time, end_time, result, url) # 记录API调用 log_api_call(request_time, result["original_path"], result["result_path"], response_data, "url") return JSONResponse(content=response_data) except HTTPException as e: logger.error(f"URL检测失败: {e.detail}") return JSONResponse( content=create_error_response(e.status_code, e.detail, request_time, datetime.now()), status_code=e.status_code ) except Exception as e: end_time = datetime.now() logger.error(f"OBB检测失败: {str(e)}", exc_info=True) return JSONResponse( content=create_error_response(500, str(e), request_time, end_time), status_code=500 ) def warmup_scenarios(model, npu_id=0): logger.info("开始多场景预热...") # 创建不同尺寸的合成图像进行预热 import torch warmup_configs = [ (960, 960, 5), # (width, height, iterations) (640, 640, 3), (1280, 1280, 3), ] with torch.no_grad(): for width, height, iterations in warmup_configs: logger.info(f"预热尺寸: {width}x{height}") # 创建合成图像 synthetic_img = np.random.randint(0, 255, (height, width, 3), dtype=np.uint8) for i in range(iterations): try: results = model.predict( source=synthetic_img, conf=0.6, imgsz=max(width, height), verbose=False, save=False, nms=True, agnostic_nms=False, max_det=30, iou=0.6 ) logger.info(f"预热迭代 {i+1}/{iterations} 完成") # 清理 del results torch_npu.npu.empty_cache() except Exception as e: logger.warning(f"预热失败: {e}") logger.info(f"尺寸 {width}x{height} 预热完成") logger.info("多场景预热完成") if __name__ == "__main__": # 设置自定义进程名 setproctitle.setproctitle("gqj_check_api") # 设置进程名称 parser = argparse.ArgumentParser(description='YOLOv8-OBB Object Detection API') parser.add_argument('--model_path', type=str, required=True, help='OBB模型文件的路径') parser.add_argument('--port', type=int, default=8001, help='服务启动的端口号') parser.add_argument('--output_dir', type=str, default="debug_output", help='输出目录的路径') parser.add_argument('--log_file', type=str, default="api_calls.log", help='日志文件的路径') parser.add_argument('--npu_id', type=int, default=0, help='NPU卡编号(默认0)') # 新增参数 args = parser.parse_args() # 更新配置 config["debug_dir"] = args.output_dir config["log_file"] = args.log_file # 创建输出目录 os.makedirs(args.output_dir, exist_ok=True) # 加载模型并启动服务 load_model(args.model_path,npu_id=args.npu_id) warmup_scenarios(model, args.npu_id) import uvicorn uvicorn.run(app, host="0.0.0.0", port=args.port) ``` 二、软件版本: 1. 硬件环境 服务器:华为鲲鹏 920-3226 处理器(ARM 架构)+ Atlas 300I Duo NPU:8 个华为昇腾 310P3 NPU 2. 软件版本 组件版本安装路径 操作系统内核 4.19.90-89.24.v2401.ky10.aarch64 - CANN 开发套件 Ascend-cann-toolkit_8.1.RC1_linux-aarch64 Ascend-cann-kernels-310p_8.1.RC1_linux-aarch64 Ascend-cann-nnal_8.1.RC1_linux-aarch64 /usr/local/Ascend/ascend-toolkit/8.1.RC1/aarch64-linux NPU 驱动 Ascend-hdk-310p-npu-driver_25.0.rc1.1_linux-aarch64.run - NPU 固件 Ascend-hdk-310p-npu-firmware_7.7.0.1.231.run - 内核开发包 kernel-devel-4.19.90-89.11.v2401.ky10.aarch64 kernel-headers-4.19.90-89.11.v2401.ky10.aarch64 - 昇腾 PyTorch torch_npu-2.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - 推理工具 aclruntime-0.0.2-cp310-cp310-linux_aarch64.whl ais_bench-0.0.2-py3-none-any.whl 三、测试步骤: 通过AI说问题是每个不同尺寸的图片都会触发NPU重新编译计算图。  四、日志信息: (yolo_npu) [root@bogon gqj_check]# python gqj_check_npu.py --model_path best.pt --port 8001 --output_dir debug_output --npu_id 0 ..INFO:__main__:模型已绑定到 npu:0(NPU卡0) INFO:__main__:OBB模型加载成功并绑定到指定NPU卡 INFO:__main__:开始预编译计算图... ....INFO:__main__:预编译尺寸 (960, 960) 完成 ....INFO:__main__:预编译尺寸 (640, 640) 完成 ...INFO:__main__:预编译尺寸 (1280, 1280) 完成 .INFO:__main__:计算图预编译完成 INFO:__main__:开始多场景预热... INFO:__main__:预热尺寸: 960x960 ........INFO:__main__:预热迭代 1/5 完成 INFO:__main__:预热迭代 2/5 完成 INFO:__main__:预热迭代 3/5 完成 INFO:__main__:预热迭代 4/5 完成 INFO:__main__:预热迭代 5/5 完成 INFO:__main__:尺寸 960x960 预热完成 INFO:__main__:预热尺寸: 640x640 ........INFO:__main__:预热迭代 1/3 完成 INFO:__main__:预热迭代 2/3 完成 INFO:__main__:预热迭代 3/3 完成 INFO:__main__:尺寸 640x640 预热完成 INFO:__main__:预热尺寸: 1280x1280 .INFO:__main__:预热迭代 1/3 完成 INFO:__main__:预热迭代 2/3 完成 INFO:__main__:预热迭代 3/3 完成 INFO:__main__:尺寸 1280x1280 预热完成 INFO:__main__:多场景预热完成 INFO: Started server process [1851047] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit) INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... ........WARNING ⚠️ NMS time limit 2.050s exceeded [W820 18:55:47.435100569 compiler_depend.ts:79] Warning: [Check][offset] Check input storage_offset[%ld] = 0 failed, result is untrustworthy1 (function operator()) INFO:__main__:OBB推理完成,总用时: 76.958 秒 INFO:__main__:检测到 8 个旋转框物体 .INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.98 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:50167 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://119.45.231.153:8100/ar-oss/8881.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 406415 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... INFO:__main__:OBB推理完成,总用时: 0.056 秒 INFO:__main__:检测到 8 个旋转框物体 INFO:__main__:检测到物体 - 类别: K_力矩头, 置信度: 0.98 INFO:__main__:检测到物体 - 类别: J_头灯, 置信度: 0.93 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: H_力矩扳手, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: R_接地线杆, 置信度: 0.72 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65432 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... ........WARNING ⚠️ NMS time limit 2.050s exceeded INFO:__main__:OBB推理完成,总用时: 72.287 秒 INFO:__main__:检测到 12 个旋转框物体 .INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.96 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.85 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.76 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.71 INFO:__main__:API调用记录已保存: api_calls.log INFO: 192.168.0.9:65440 - "POST /gqj_check HTTP/1.1" 200 OK INFO:__main__:从URL下载图片: http://42.236.74.152:8551/ar-oss/8882.jpg,预计大小可能较大,延长超时时间 INFO:__main__:图片下载成功,大小: 399137 字节 INFO:__main__:原始图像尺寸: 1920x1080 INFO:__main__:已将图像强制调整为固定尺寸: 960x960 INFO:__main__:开始OBB推理... INFO:__main__:OBB推理完成,总用时: 0.056 秒 INFO:__main__:检测到 12 个旋转框物体 INFO:__main__:检测到物体 - 类别: G_钳子, 置信度: 0.96 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.92 INFO:__main__:检测到物体 - 类别: I_钢丝刷, 置信度: 0.91 INFO:__main__:检测到物体 - 类别: D_绝缘手套, 置信度: 0.90 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.89 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.85 INFO:__main__:检测到物体 - 类别: L_防护旗, 置信度: 0.84 INFO:__main__:检测到物体 - 类别: A_安全帽, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: M_扳手, 置信度: 0.83 INFO:__main__:检测到物体 - 类别: C_绝缘靴, 置信度: 0.76 INFO:__main__:检测到物体 - 类别: E_验电器, 置信度: 0.71 INFO:__main__:API调用记录已保存: api_calls.log
评论 (
2
)
登录
后才可以发表评论
状态
TODO
TODO
ACCEPTED
Analysing
Feedback
WIP
Replied
CLOSED
DONE
REJECTED
负责人
未设置
标签
未设置
项目
未立项任务
未立项任务
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
分支 (79)
标签 (179)
master
v2.6.0
v2.8.0
v2.1.0
v2.5.1
v2.7.1
v2.5.1-7.1.0
v2.6.0-7.1.0
v2.1.0-7.1.0
revert-merge-23967-master
revert-merge-23966-v2.8.0
revert-merge-23965-v2.7.1
revert-merge-23964-v2.6.0
revert-merge-23962-v2.5.1
revert-merge-23789-v2.1.0
v2.1.0-7.0.0
v2.4.0-7.0.0
v2.4.0
v2.3.1
v2.3.1-7.0.0
v2.5.1-7.0.0
v2.4.0-6.0.0
v2.3.1-6.0.0
v2.1.0-6.0.0
v2.1.0-6.0.rc3
v2.3.1-6.0.rc3
v2.4.0-6.0.rc3
v2.2.0
v1.11.0-6.0.rc1
v2.1.0-6.0.rc1
v2.2.0-6.0.rc1
v1.11.0-6.0.rc2
v2.1.0-6.0.rc2
v2.2.0-6.0.rc2
v2.3.1-6.0.rc2
v1.11.0
v2.1.0-5.0.0
v2.0.1-5.0.0
v1.11.0-5.0.0
v2.0.1
v2.1.0-5.0.rc3
v1.11.0-5.0.rc3
v2.0.1-5.0.rc3
v1.11.0-5.0.rc3.3
v1.8.1
v1.11.0-x1
v1.8.1-5.0.rc3
v1.11.0-5.0.rc2.2
v1.11.0-zj
v1.11.0-5.0.rc2.1
v2.0.1-5.0.rc2
v1.11.0-5.0.rc2
v1.8.1-5.0.rc2
v2.0.0-5.0.rc2
v1.8.1-5.0.rc1
v1.11.0-5.0.rc1
v1.11.0-yd
v1.11.0-xf
v1.11.0-infer
v1.11.0-bigkernel
v1.11.0-host_api
v1.8.1-3.0.0
v1.11.0-5.0.rc2.t100
v1.8.1-5.0.rc2.t100
v1.8.1-3.0.0-dev
v1.11.0-3.0.0
v2.0-dev
v1.8.1-3.0.rc3
v1.5.0-3.0.0
v1.5.0
v1.8.1-3.0.rc1
v1.11.0-3.0.rc3
v1.8.1-3.0.rc2
v1.5.0-3.0.rc3
v1.5.0-3.0.rc2
2.0.4.tr5
v1.5.0-3.0.rc1
2.0.2.tr5
2.0.3.tr5
v7.0.0.1-pytorch2.4.0
v7.0.0.1-pytorch2.1.0
v7.2.RC1.alpha001-pytorch2.8.0
v7.2.RC1.alpha001-pytorch2.7.1
v7.2.RC1.alpha001-pytorch2.6.0
v7.2.RC1.alpha001-pytorch2.5.1
v7.2.RC1.alpha001-pytorch2.1.0
v7.1.0.1-pytorch2.6.0
v7.1.0.1-pytorch2.5.1
v7.1.0.1-pytorch2.1.0
v7.1.0-pytorch2.6.0
v7.1.0-pytorch2.5.1
v7.1.0-pytorch2.1.0
v7.1.RC1.alpha003-pytorch2.6.0
v7.1.RC1.alpha003-pytorch2.5.1
v7.1.RC1.alpha003-pytorch2.1.0
v7.1.RC1.alpha002-pytorch2.7.1
v7.1.RC1.alpha002-pytorch2.6.0
v7.1.RC1.alpha002-pytorch2.5.1
v7.1.RC1.alpha002-pytorch2.4.0
v7.1.RC1.alpha002-pytorch2.3.1
v7.1.RC1.alpha002-pytorch2.1.0
v6.0.0.1-pytorch2.4.0
v6.0.0.1-pytorch2.3.1
v6.0.0.1-pytorch2.1.0
v7.1.RC1.alpha001-pytorch2.6.0
v7.1.RC1.alpha001-pytorch2.5.1
v7.1.RC1.alpha001-pytorch2.4.0
v7.1.RC1.alpha001-pytorch2.3.1
v7.1.RC1.alpha001-pytorch2.1.0
v7.0.0-pytorch2.5.1
v7.0.0-pytorch2.4.0
v7.0.0-pytorch2.3.1
v7.0.RC1.alpha002-pytorch2.6.0
v7.0.0-pytorch2.1.0
v7.0.RC1.alpha002-pytorch2.5.1
v7.0.RC1.alpha002-pytorch2.4.0
v7.0.RC1.alpha002-pytorch2.3.1
v7.0.RC1.alpha002-pytorch2.1.0
v7.0.RC1.alpha001-pytorch2.5.1
v7.0.RC1.alpha001-pytorch2.1.0
v7.0.RC1.alpha001-pytorch2.4.0
v7.0.RC1.alpha001-pytorch2.3.1
v6.0.0-pytorch2.4.0
v6.0.0-pytorch2.3.1
v6.0.0-pytorch2.1.0
v6.0.0.alpha003-pytorch2.4.0
v6.0.0.alpha003-pytorch2.3.1
v6.0.0.alpha003-pytorch2.1.0
v6.0.0.alpha002-pytorch2.4.0
v6.0.0.alpha002-pytorch2.3.1
v6.0.0.alpha002-pytorch2.1.0
v6.0.0.alpha001-pytorch2.5.1
v6.0.rc3-pytorch2.4.0
v6.0.rc3-pytorch2.3.1
v6.0.rc3-pytorch2.1.0
v6.0.0.alpha001-pytorch2.4.0
v6.0.0.alpha001-pytorch2.3.1
v6.0.0.alpha001-pytorch2.1.0
v6.0.rc2.1-pytorch1.11.0
v6.0.rc2.1-pytorch2.3.1
v6.0.rc2.1-pytorch2.2.0
v6.0.rc2.1-pytorch2.1.0
v6.0.rc3.alpha003-pytorch2.3.1
v6.0.rc3.alpha003-pytorch2.1.0
v6.0.rc3.alpha001-pytorch2.4.0
v6.0.rc3.alpha002-pytorch2.3.1
v6.0.rc3.alpha002-pytorch2.2.0
v6.0.rc3.alpha002-pytorch2.1.0
v6.0.rc3.alpha002-pytorch1.11.0
v6.0.rc2-pytorch2.1.0
v6.0.rc2-pytorch2.3.1
v6.0.rc2-pytorch2.2.0
v6.0.rc2-pytorch1.11.0
v6.0.rc3.alpha001-pytorch2.3.1
v6.0.rc3.alpha001-pytorch2.2.0
v6.0.rc3.alpha001-pytorch2.1.0
v6.0.rc3.alpha001-pytorch1.11.0
v6.0.rc2.alpha002-pytorch2.3.1
v6.0.rc2.alpha003-pytorch1.11.0
v6.0.rc2.alpha003-pytorch2.2.0
v6.0.rc2.alpha003-pytorch2.1.0
v6.0.rc1.1-pytorch2.2.0
v6.0.rc1.1-pytorch2.1.0
v6.0.rc1.1-pytorch1.11.0
v5.0.1.2-pytorch1.11.0
v5.0.1.2-pytorch2.1.0
v5.0.1.2-pytorch2.0.1
v6.0.rc2.alpha002-pytorch2.2.0
v6.0.rc2.alpha002-pytorch2.1.0
v6.0.rc2.alpha002-pytorch1.11.0
v6.0.rc1-pytorch2.2.0
v6.0.rc1-pytorch2.1.0
v6.0.rc1-pytorch1.11.0
v6.0.rc2.alpha001-pytorch2.2.0
v6.0.rc2.alpha001-pytorch2.1.0
v6.0.rc2.alpha001-pytorch1.11.0
v6.0.rc1.alpha003-pytorch2.0.1
v6.0.rc1.alpha003-pytorch2.1.0
v5.0.1.1-pytorch2.0.1
v5.0.1.1-pytorch1.11.0
v5.0.1.1-pytorch2.1.0
v6.0.rc1.alpha003-pytorch1.11.0
v6.0.rc1.alpha002-pytorch2.1.0
v6.0.rc1.alpha002-pytorch1.11.0
v6.0.rc1.alpha002-pytorch2.0.1
v6.0.rc1.alpha001-pytorch2.2.0
v5.0.1-pytorch2.1.0
v5.0.1-pytorch2.0.1
v5.0.1-pytorch1.11.0
v6.0.RC1.alpha001-pytorch2.0.1
v6.0.RC1.alpha001-pytorch2.1.0
v6.0.RC1.alpha001-pytorch1.11.0
v5.0.0-pytorch2.1.0
v5.0.0-pytorch2.0.1
v5.0.0-pytorch1.11.0
v5.0.0.alpha003-pytorch2.1.0
v5.0.0.alpha003-pytorch2.0.1
v5.0.0.alpha003-pytorch1.11.0
v5.0.rc3.3-pytorch1.11.0
v5.0.rc3.2-pytorch1.11.0
v5.0.0.alpha002-pytorch2.1.0
v5.0.0.alpha002-pytorch2.0.1
v5.0.0.alpha002-pytorch1.11.0
v5.0.rc3.1-pytorch1.11.0
v5.0.0.alpha001-pytorch2.1.0
v5.0.0.alpha001-pytorch2.0.1
v5.0.0.alpha001-pytorch1.11.0
v5.0.rc3-pytorch2.1.0
v5.0.rc3-pytorch2.0.1
v5.0.rc3-pytorch1.11.0
v5.0.rc3.alpha003-pytorch2.0.1
v5.0.rc3.alpha003-pytorch1.11.0
v5.0.rc3.alpha003-pytorch1.8.1
v5.0.rc2.2-pytorch1.11.0
v5.0.rc2.1-pytorch1.11.0
v5.0.rc3.alpha002-pytorch2.0.1
v5.0.rc3.alpha002-pytorch1.11.0
v5.0.rc3.alpha002-pytorch1.8.1
v5.0.rc2-pytorch2.0.1
v5.0.rc2-pytorch1.11.0
v5.0.rc2-pytorch1.8.1
v5.0.rc3.alpha001-pytorch1.8.1
v5.0.rc3.alpha001-pytorch1.11.0
v5.0.rc2.alpha003-pytorch1.11.0
v5.0.rc2.alpha003-pytorch1.8.1
v5.0.rc2.alpha002-pytorch1.11.0
v5.0.rc2.alpha002-pytorch1.8.1
v5.0.rc1.alpha003-pytorch1.11.0
v5.0.rc1.alpha003-pytorch1.8.1
v5.0.rc1-pytorch1.11.0
v5.0.rc1-pytorch1.8.1
v5.0.rc1.alpha002-pytorch1.11.0
v5.0.rc1.alpha002-pytorch1.8.1
v5.0.rc1.alpha001-pytorch1.11.0
v5.0.rc1.alpha001-pytorch1.8.1
v3.0.0-pytorch1.11.0
v3.0.0-pytorch1.8.1
v3.0.0-pytorch1.5.0
v3.0.alpha006-pytorch1.8.1
v3.0.alpha005-pytorch1.8.1
v3.0.alpha003-pytorch1.8.1
v3.0.rc3-pytorch1.11.0
v3.0.rc3-pytorch1.8.1
v3.0.rc3-pytorch1.5.0
v3.0.rc2-pytorch1.8.1
v3.0.rc2-pytorch1.5.0
v3.0.rc1-pytorch1.8.1
v3.0.rc1-pytorch1.5.0
v2.0.4
v2.0.4-rc2
v2.0.4-rc1
v2.0.3.1
v2.0.3
v2.0.3-rc4
v2.0.3-rc3
v2.0.3-rc2
v2.0.3-rc1
v2.0.2
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
预计工期
(小时)
参与者(1)
Python
1
https://gitee.com/ascend/pytorch.git
git@gitee.com:ascend/pytorch.git
ascend
pytorch
pytorch
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册