D-FINE
一个检测 transformer,它把检测框回归重新表述成每条框边上的概率分布,并在各解码器层之间逐步细化。LibreYOLO 支持它做目标检测和实例分割。
- 任务
- detection, instance segmentation
- 尺寸
- n, s, m, l, x at 640 px
- 安装
pip install libreyolo- 支持层级
- 核心,自 v1.1.0 起。核心的可训练检测器:功能在同一个发布波次里紧跟旗舰。
- 许可
- 代码采用 Apache-2.0,权重采用 Apache-2.0。商用
安装
D-FINE 不需要任何可选 extra。它导入的一切都在基础安装里。
pip install libreyolo用 lora=True 做适配器(adapter)微调是个例外,它需要 lora extra。
pip install "libreyolo[lora]"预测
权重在首次使用时从 Hugging Face 下载,并缓存在本地。
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreDFINEn.pt")result = model(SAMPLE_IMAGE, save=True) for box in result.boxes: print(box.cls, box.conf, box.xyxy)libreyolo predict model=LibreDFINEn.pt source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg save=Truefrom libreyolo import LibreYOLO, SAMPLE_IMAGE # 文件名里的 -seg 后缀会选中 mask head,所以这里不需要传# task 参数model = LibreYOLO("LibreDFINEn-seg.pt")result = model(SAMPLE_IMAGE, save=True) print(result.masks.data.shape)返回的 Results 对象和每个家族返回的都是同一个,所以换成另一个检测器只是一行的
改动。-seg 文件名自己就能解析到分割任务,此时 result.masks 会在检测框之外带上
实例掩码。conf 和 max_det 过滤的是 query 的选择;iou 为了 API 一致性而被接受,
但没有任何作用,因为解码器是一个集合预测器,没有 NMS 步骤。数据源、流式处理和结果
处理见预测。
变体
五种尺寸。它们都在相同的输入分辨率下运行,所以下表用参数量和精度来区分它们。
| 检查点 | 输入(px) | mAP 50-95 | 参数量(M) |
|---|---|---|---|
| LibreDFINEl | 640 | 60.0 | 31.24 |
| LibreDFINEm | 640 | 57.8 | 19.59 |
| LibreDFINEn | 640 | 45.8 | 3.78 |
| LibreDFINEs | 640 | 53.4 | 10.32 |
| LibreDFINEx | 640 | 61.4 | 62.62 |
COCO val2017, 500 images。数据由 LibreYOLO 基准测试工具测得,并发布在 Vision Analysis 上,可在此比较不同硬件和运行时的延迟,并查看完整的运行记录。
分割复用了检测的骨干、编码器和解码器,再加上一个 mask head,所以 -seg 检查点
(checkpoint)接受的参数和它的检测版兄弟完全一样。LibreYOLO 的 RT-DETRv4 家族写成
了 D-FINE 包装类的子类:它继承了这条解码器路线,然后把自己的任务列表钉回检测,因为
它不带 mask head。
训练
两个任务的训练都从已发布的检查点开始。
from libreyolo import LibreYOLO model = LibreYOLO("LibreDFINEn.pt")model.train(data="my-dataset.yaml", epochs=50, imgsz=640, batch=8, lr0=2e-4)libreyolo train model=LibreDFINEn.pt data=my-dataset.yaml \ epochs=50 imgsz=640 batch=8 lr0=2e-4# 从已发布的分割权重继续训练,其中包含 mask headlibreyolo train model=LibreDFINEn-seg.pt data=my-dataset.yaml \ task=segment epochs=50 imgsz=640# 检测权重不带 mask head,所以这是一次显式迁移:head 一开始# 未经训练,只有训练之后才有用;在这里传 task=segment# 就是对这次迁移的授权libreyolo train model=LibreDFINEn.pt data=my-dataset.yaml \ task=segment epochs=50 imgsz=640from libreyolo import LibreYOLO model = LibreYOLO("LibreDFINEn.pt")model.train(data="my-dataset.yaml", epochs=50, lora=True)libreyolo train model=LibreDFINEn.pt data=my-dataset.yaml \ epochs=50 device=0,1 batch=16默认情况下,训练器以 lr0=2e-4、amp=True 和 amp_dtype="float16" 运行 132 轮,批大小为 16,连续 50 轮没有改善后提前停止。libreyolo train 对两个任务使用同一套起始配方:AdamW 加平坦余弦调度,weight_decay=1e-4,2 轮预热,梯度范数裁剪为 0.1,并且不使用 mosaic、MixUp、HSV 或仿射数据增强。不传 name= 时,它的运行结果写入 runs/train/dfine_exp。检测权重可以作为分割训练的起点,但必须显式授权迁移,因为 mask head 尚未训练,否则会返回无意义的掩码。在 CLI 中传入 task=segment 即可授权。Python 路径的要求更严格:必须直接构造 LibreDFINE 并传入 allow_detect_to_segment_transfer=True,因为 LibreYOLO() 工厂不接受这个参数;直接构造不会下载权重,所以权重文件必须已在磁盘上。
lora=True 适用于检测。分割训练会拒绝它,并转而指向 freeze='backbone',因为
mask head 还没有和适配器一起测试过。在 Apple silicon 上,训练器会把整个训练过程搬到
CPU:Integral 的分箱矩阵乘法在反向传播时会撞上 Metal 编译失败。MPS 上的推理不受影响。
数据集、数据增强、多卡训练和日志记录器见训练。
验证
val() 返回一个以指标名为键的字典,并在 verbose 保持开启时打印每个类别的结果。
from libreyolo import LibreYOLO model = LibreYOLO("LibreDFINEn.pt")metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/mAP50-95"])print(metrics["metrics/mAP50"])libreyolo val model=LibreDFINEn.pt data=my-dataset.yamlfrom libreyolo import LibreYOLO model = LibreYOLO("LibreDFINEn-seg.pt")metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/mAP50-95(M)"]) # 掩码print(metrics["metrics/mAP50-95(B)"]) # 检测框对 -seg 检查点来说,普通的 metrics/mAP50-95 键里放的是掩码分数,同一次运行还会
在 (B) 下报告检测框、在 (M) 下报告掩码,所以一次跑完两者都有。
导出
| 任务 | ONNX | TorchScript | ExecuTorch | TensorRT | OpenVINO | Paddle | MNN | RKNN | ncnn | TFLite | CoreML | Core AI |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Detection | Detection to ONNX:支持 | Detection to TorchScript:支持 | Detection to ExecuTorch:不支持 | Detection to TensorRT:支持 | Detection to OpenVINO:支持 | Detection to Paddle:支持 | Detection to MNN:支持 | Detection to RKNN:不支持 | Detection to ncnn:不支持 | Detection to TFLite:不支持 | Detection to CoreML:不支持 | Detection to Core AI:支持 |
| Instance segmentation | Instance segmentation to ONNX:支持 | Instance segmentation to TorchScript:支持 | Instance segmentation to ExecuTorch:不支持 | Instance segmentation to TensorRT:支持 | Instance segmentation to OpenVINO:支持 | Instance segmentation to Paddle:不支持 | Instance segmentation to MNN:不支持 | Instance segmentation to RKNN:不支持 | Instance segmentation to ncnn:不支持 | Instance segmentation to TFLite:不支持 | Instance segmentation to CoreML:不支持 | Instance segmentation to Core AI:不支持 |
导出的产物按文件后缀经由 LibreYOLO() 重新加载,所以一个 .onnx 或 .engine 文件
的表现和检查点一样,返回同样的 Results。OpenVINO、Paddle、MNN 和 Core AI 这几条
路径导出时用的是固定画布,而不是动态形状。导出列出了每种格式接受的
参数,以及其中少数几种额外增加的参数。
from libreyolo import LibreYOLO model = LibreYOLO("LibreDFINEn.pt")model.export(format="onnx", imgsz=640)model.export(format="tensorrt", imgsz=640, half=True)libreyolo export model=LibreDFINEn.pt format=onnx imgsz=640libreyolo export model=LibreDFINEn.pt format=tensorrt imgsz=640 half=Truefrom libreyolo import LibreYOLO, SAMPLE_IMAGE # 工厂按文件后缀分发,所以导出的产物加载方式和任何检查点一样,# 返回的也是同一个 Results 对象model = LibreYOLO("LibreDFINEn.onnx")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy)ONNX 输入与输出
| 张量 | images float32 [batch, 3, height, width] |
|---|---|
| 颜色 | RGB |
| 缩放 | 像素 / 255 |
| 归一化 | 无 |
| 缩放方式 | 拉伸到 宽 x 高(不填充,不保持宽高比), Pillow bilinear |
如何复现 Pillow 的双线性缩放
LibreYOLO 使用 Pillow 的 BILINEAR 滤波器(Image.resize)缩放。它不是 OpenCV 的 INTER_LINEAR 或 INTER_AREA:缩小时,Pillow 会按缩小倍数加宽滤波器,而 OpenCV 的缩放会改变检测结果,并使框移动数个像素。要在不使用 Pillow 的情况下复现它,先缩放一个轴,舍入到 8 位,再缩放另一个轴并再次舍入。对每个轴,令 s = in_size / out_size,f = max(s, 1)。输出像素 i 的中心为 c = (i + 0.5) * s,读取源像素 j,范围从 int(c - f + 0.5) 到 int(c + f + 0.5) - 1(限制在图像内),每个像素的权重为 max(0, 1 - |j + 0.5 - c| / f),再除以权重之和。Pillow 使用定点运算,并根据图像形状选择轴的顺序,因此浮点权重在少数像素上可能与它相差一个等级。这不会改变我们检查中的结果。
Square input only. Export rejects a non-square imgsz, and height and width are 640 by default.
Batch size 1 only. The graph declares a dynamic batch axis, but batches of 2 or more fail in 1.7.0.
Divide by 255 after the resize. There is no mean or std normalization.
Detection,默认导出
| 输出 | 形状 | 含义 |
|---|---|---|
pred_logits | [batch, queries, nc] | One logit per query and class. Sigmoid is not applied. queries is 300 for every size. |
pred_boxes | [batch, queries, 4] | cx, cy, w, h of each query, normalized 0 to 1 of the input. The input is the whole image stretched, so these are also fractions of the original image. |
后处理
- Apply sigmoid to pred_logits.
- Flatten the [queries, nc] scores and take the top 300 (max_det, 300 by default). For flat index k, the query is k // nc and the class is k % nc. One query can yield several classes.
- Keep the pairs whose score is above the confidence threshold (0.25 by default).
- Convert the query's box to x1 = cx - w / 2, y1 = cy - h / 2, x2 = cx + w / 2, y2 = cy + h / 2. Multiply x by the original image width and y by the original image height.
- There is no NMS and the iou threshold is not used. D-FINE predicts a set of boxes, and export with nms=True raises NotImplementedError.
- LibreYOLO does not clip these boxes, so an edge can sit slightly outside the image.
Instance segmentation,默认导出
| 输出 | 形状 | 含义 |
|---|---|---|
pred_logits | [batch, queries, nc] | One logit per query and class. Sigmoid is not applied. queries is 300 for every size. |
pred_boxes | [batch, queries, 4] | cx, cy, w, h of each query, normalized 0 to 1 of the input. The input is the whole image stretched, so these are also fractions of the original image. |
pred_masks | [batch, queries, mask_h, mask_w] | One mask per query, sigmoid already applied. mask_h is height / 4 and mask_w is width / 4, 160 x 160 at 640 x 640. |
后处理
- Apply sigmoid to pred_logits.
- Flatten the [queries, nc] scores and take the top 300 (max_det, 300 by default). For flat index k, the query is k // nc and the class is k % nc. One query can yield several classes.
- Keep the pairs whose score is above the confidence threshold (0.25 by default).
- Convert the query's box to x1 = cx - w / 2, y1 = cy - h / 2, x2 = cx + w / 2, y2 = cy + h / 2. Multiply x by the original image width and y by the original image height.
- For each kept pair, take its query's mask. Resize it with bilinear interpolation and half-pixel centers (align_corners=False, no antialiasing) to height x width, then again to the original image size.
- A pixel belongs to the mask when its value is 0.5 or more.
- Clear every mask pixel outside the box. Pixel (x, y) stays only when x1 <= x < x2 and y1 <= y < y2, in original-image pixels.
- There is no NMS and the iou threshold is not used. D-FINE predicts a set of boxes, and export with nms=True raises NotImplementedError.
- LibreYOLO does not clip the boxes of a .pt predict, so an edge can sit slightly outside the image. Its ONNX backend clips them to the image.
要检查你自己的实现,请用 LibreYOLO 运行同一个文件:LibreYOLO("model.onnx").predict("image.jpg") 会返回参考结果。 已用 LibreYOLO 1.7.0 验证。
检查点
这个家族已发布的全部权重文件。
| 文件 | 输入(px) | 权重许可 |
|---|---|---|
| Detection | ||
| LibreDFINEn.pt | 640 | apache-2.0 |
| LibreDFINEs.pt | 640 | apache-2.0 |
| LibreDFINEm.pt | 640 | apache-2.0 |
| LibreDFINEl.pt | 640 | apache-2.0 |
| LibreDFINEx.pt | 640 | apache-2.0 |
| Instance segmentation | ||
| LibreDFINEn-seg.pt | 640 | apache-2.0 |
| LibreDFINEs-seg.pt | 640 | apache-2.0 |
| LibreDFINEm-seg.pt | 640 | apache-2.0 |
| LibreDFINEl-seg.pt | 640 | apache-2.0 |
| LibreDFINEx-seg.pt | 640 | apache-2.0 |
上面的每个文件目前都在 LibreYOLO 组织中,并会在首次使用时下载。
许可证
请检查你所下载的具体权重在 Hugging Face 仓库中的许可。LibreYOLO 组织里的每个检查点都附有许可,同一家族内也不一定相同。该仓库是权威来源;以下摘要说明本页上次验证时适用的情况。
这里只说明涉及的许可证,不构成法律意见。如果答案对商用很重要,请自行阅读许可证并咨询法律顾问。
- 原始工作
- D-FINE, University of Science and Technology of China
- 上游许可
- Apache-2.0
- LibreYOLO 代码
- MIT
- 权重
- 采用 Apache-2.0 许可,重新发布在 huggingface.co/LibreYOLO
- 解读
- Apache-2.0 is a permissive license, so these weights can be used in commercial and closed-source products. It asks you to keep its license text and attribution notices with any copy of the weights you redistribute, and it grants a patent license. It places no obligation on your own application code, and weights you train yourself on your own data are yours. The segmentation weights carry a second Apache-2.0 upstream, ArgoHA/D-FINE-seg, under the same terms.
分割权重还有第二个上游:它们的掩码解码器、掩码匹配和掩码损失函数来自 ArgoHA/D-FINE-seg,同样采用 Apache-2.0 许可,其维护者已同意在署名的前提下复用。
引用
@misc{peng2024dfine,
title={D-FINE: Redefine Regression Task in DETRs as Fine-grained Distribution Refinement},
author={Yansong Peng and Hebei Li and Peixi Wu and Yueyi Zhang and Xiaoyan Sun and Feng Wu},
year={2024},
eprint={2410.13842},
archivePrefix={arXiv},
primaryClass={cs.CV}
}复制自作者在 github.com/Peterande/D-FINE#citation 上提供的引用块。