当我们从GitHub上下载了一些包,需要调用时,或者当需要在电脑里运行多个版本的某程序,以便后续进行不同版本的模型测试和魔改时,如果是通过将你需要的包的路径添加到系统里的方式,会很麻烦,因为每次换版本都要修改系统里的路径,有没有较为快捷的方法,例如在调用YOLO时,只需要下载好各版本的YOLO放在本地,调用的时候只需要使用类似 from ./yolo8 import YOLO,或 from ./yolo11 import YOLO便可调用yolov8和yolov11。下面便提供一种方案:
例1,
以自写程序为例,首先,我们创建一个符合Python包规范的目录结构:
my_project/
│── main.py
│
└── my_package/
├── __init__.py
├── module1.py
├── module2.py
└── subpackage/
├── __init__.py
└── module3.py
文件内容实现
1. 基础模块文件
my_package/module1.py
def greet():
return "Hello from module1"
my_package/module2.py
from .module1 import greet # 相对导入同目录下的module1
def greet_twice():
return greet() + " and " + greet()
my_package/subpackage/module3.py
from ..module1 import greet # 相对导入上级目录的module1
def greet_from_sub():
return "Subpackage says: " + greet()
2. 主程序文件
main.py
from my_package.module2 import greet_twice
from my_package.subpackage.module3 import greet_from_sub
print(greet_twice())
print(greet_from_sub())
3.运行结果
当运行main.py时,输出应该是:
Hello from module1 and Hello from module1
Subpackage says: Hello from module1
例2,
以YOLO为例。首先,下载好不同版本的yolo放在本地(文件名中可以有下划线但是不能有横杠,如ultralytics-main,import的时候会识别不到,但是ultralytics_main则可以。),如下图:
下载好官方训练好的模型yolo8n.pt或者yolo11n.pt,链接https://github.com/ultralytics/ultralytics,在主程序yolo_test.py中写入如下代码:
from ultralytics_main_v11.ultralytics.models.yolo import YOLO
# 如果models文件夹下有 __init__.py, 也可以使用from ultralytics_main_v11.ultralytics.models import YOLO
model = YOLO(r'./yolov11n.pt')
results = model(r"./bus.jpg", conf=0.05, device='cpu', save=True)
运行便可,运行后,主文件夹中会出现新文件夹runs,里面有测试的结果。后续如果需要对yolo结构进行修改,则只需要在对应的文件夹(如ultralytics_main_v8)下修改便可。
注意:在下载的文件包如ultralytics_main_v11中,有很多多代码都有相互调用
(如from ultralytics.models.yolo import YOLO),这时应把所有这样的调用代码都改成相对路径(如from ultralytics_main_v11.ultralytics.models.yolo import YOLO),否则调用的是系统默认的也就是已安装的那个版本的YOLO,例如系统里装的是YOLOv8,则只有主文程序使用相对路径调用YOLOv11的程序,YOLOv11中的子程序中的调用包的代码并没有改成相对路径,则会报错:ImportError: cannot import name 'YOLOE' from 'ultralytics.models',因为子程序调用的是YOLOv8的程序,YOLOv8中并无YOLOE的模块。
另外,修改好所有文件的相对路径之后,此时如果使用
from ultralytics_main_v11.ultralytics import YOLO
model_yaml = r"./yolov11s.yaml"
data_yaml = r"./data.yaml"
pre_model = r"./yolov11n.pt"
model = YOLO(model_yaml, task='detect').load(pre_model)
# 训练参数
results = (model.train( data=data_yaml, device=0, epochs=2,imgsz=640, batch=32,workers=2))
的方式调用模型和数据,则可正常运行,如果是通过
from ultralytics_main_v11.ultralytics.models.yolo import YOLO
model = YOLO(r'./yolo11n.pt')
results = model(r"./bus.jpg", conf=0.05, device=0, save=True)
来调用官方下载的预训练模型,如yolov11n.pt,仍然会报错,因为官方在保存.pt 文件时,使用 torch.save() 保存对象,如果保存的是类的实例(例如模型),会将该类的 模块路径和类名 一起序列化。例如:
from ultralytics.nn.task import DetectionModel
model = DetectionModel(...)
torch.save(model, 'model.pt')
这会在 .pt 文件中记录类的完整路径 ultralytics.nn.task.DetectionModel。所以,即使你没有直接写依赖,它也会在反序列化时寻找原始模块结构。这时只需要在脚本开头写上
import sys
sys.path.insert(0, './ ultralytics_main_v11')
则可正常运行。
(如果改用 state_dict 保存模型则不会保存路径依赖
保存时:
torch.save(model.state_dict(), 'model_weights.pt')
加载时:
model = YourModelClass() # 定义好模型结构
model.load_state_dict(torch.load('model_weights.pt'))
另外2:使用相对路径调用yolo时,有可能会出现所有的输出都会打印两遍的情况,因为这种自定义路径加载的方式有可能 使 logging 被初始化多次,解绝的办法是:
方法一:在程序开始位置清理所有日志器(包括子模块)YOLOv8 的日志器可能在内部被多次注册(比如模型加载一次,推理又注册一次),而且是 自定义的 logger,需要逐个处理:
import logging
# 清理所有 Logger(包括 ultralytics 子模块的)
for name in logging.root.manager.loggerDict:
logging.getLogger(name).handlers.clear()
注意这段代码必须在模型加载,也就是
from ultralytics_main_v11.ultralytics.models.yolo import YOLO
之后。
方法二:手动设置 ultralytics logger 的级别与 handler
找到 ultralytics/yolo/utils/__init__.py 或 ultralytics/utils/__init__.py(可能在你项目的 ultralytics_main_v11/ 下面),它里面有类似:
LOGGER = logging.getLogger("ultralytics")
你可以在你程序最前面这样加(避免重复 handler):
import logging
ultra_logger = logging.getLogger("ultralytics")
ultra_logger.handlers.clear()
ultra_logger.propagate = False # 防止向上冒泡