chore: 数据迁到 data/raw/ 后同步所有路径引用
原始数据从 data/<数据集> 移到了 data/raw/<数据集>。
- 聚类配置的 dataset.root:film / pearlescent 均改为 data/raw/ 下
- 检测配置的 img_dir 与 label_dir 注释同步
- README、data/README、算法文档、流程图(含重新渲染的 PNG)与各脚本
docstring 里的路径示例一并更新
- data/README 补充目录约定:原始数据只读放 data/raw/,
export_clusters.py 的派生数据集放在 data/ 下与 raw/ 平级
- 顺手把 unlabeled 数据集加进 datasets/__init__.py:此前它没在包初始化时注册,
build_dataset({"name": "unlabeled"}) 只有在别处先 import 过该模块时才可用
迁移后重跑全量聚类,结果与迁移前逐项一致(轮廓 0.3255、稳定性 0.9641、
簇大小 [4507, 3795, 4849, 1428])。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5f31ed0a92
commit
5d395b7c25
@@ -56,7 +56,7 @@ uv run python train.py --config configs/train/baseline.yaml --device cpu
|
||||
每行 `class_id cx cy w h`(相对宽高归一化到 [0,1]),空文件或缺失 `.txt` 即无缺陷样本。
|
||||
|
||||
```
|
||||
data/film/
|
||||
data/raw/film/
|
||||
├── images/{train,val}/0001.png
|
||||
├── labels/{train,val}/0001.txt
|
||||
└── data.yaml # path / train / val / nc / names
|
||||
@@ -65,15 +65,15 @@ data/film/
|
||||
```bash
|
||||
# 1. labelme / VOC -> YOLO txt(顺带生成 data.yaml)
|
||||
uv run python scripts/convert_annotations.py --format labelme \
|
||||
--input data/film/labelme --images data/film/images \
|
||||
--output data/film/labels --data-yaml data/film/data.yaml
|
||||
--input data/raw/film/labelme --images data/raw/film/images \
|
||||
--output data/raw/film/labels --data-yaml data/raw/film/data.yaml
|
||||
|
||||
# 2. 切分 train / val(默认建软链,不复制数据)
|
||||
uv run python scripts/split_dataset.py --images data/film/images \
|
||||
--labels data/film/labels --val-ratio 0.2 --data-yaml data/film/data.yaml
|
||||
uv run python scripts/split_dataset.py --images data/raw/film/images \
|
||||
--labels data/raw/film/labels --val-ratio 0.2 --data-yaml data/raw/film/data.yaml
|
||||
```
|
||||
|
||||
配置里的 `classes` 既可以直接写类别列表,也可以写成 `data/film/data.yaml`,由框架读 `names`。
|
||||
配置里的 `classes` 既可以直接写类别列表,也可以写成 `data/raw/film/data.yaml`,由框架读 `names`。
|
||||
读取时也接受 YOLO 分割格式的多边形行(取外接矩形)。细节见 [data/README.md](data/README.md)
|
||||
与 [datasets/yolo.py](datasets/yolo.py)。
|
||||
|
||||
@@ -187,7 +187,7 @@ uv run python scripts/profile_inference.py --config configs/train/baseline.yaml
|
||||
|
||||
### 调用 ImageNet 预训练模型
|
||||
|
||||
`data/film` 里大量样本还没人工分完。这条流水线把目录下**所有图片拉平**(不看任何
|
||||
`data/raw/film` 里大量样本还没人工分完。这条流水线把目录下**所有图片拉平**(不看任何
|
||||
目录名),做无监督聚类,产出可人工核对的分组:
|
||||
|
||||
```bash
|
||||
@@ -443,7 +443,7 @@ resnet50 每 epoch 的耗时与 batch 基本无关(算力受限),所以显
|
||||
只是 NT-Xent 的负样本变少。
|
||||
|
||||
注意默认的 `method: pretrained` 用的**也是 resnet18**(ImageNet 权重,不训练,约 51 秒出结果);
|
||||
上面这个配置是在 `data/film` 上自监督训练它。两者实测对比(14,579 张,k=4):
|
||||
上面这个配置是在 `data/raw/film` 上自监督训练它。两者实测对比(14,579 张,k=4):
|
||||
|
||||
| 方案 | 轮廓 | 稳定性 | 成本 |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 未分类样本的无监督聚类。
|
||||
#
|
||||
# 预处理决策(针对 data/film 的实际情况:14.5k 张已裁好的缺陷小图,
|
||||
# 预处理决策(针对 data/raw/film 的实际情况:14.5k 张已裁好的缺陷小图,
|
||||
# 中位尺寸 152x113,宽高比 0.24~39.9,尺寸几乎每张都不同):
|
||||
#
|
||||
# 前提:图片的尺寸和宽高比是上游 ROI 裁剪环节的产物,不是缺陷属性。
|
||||
@@ -63,7 +63,7 @@ fit_max_samples: 0
|
||||
|
||||
dataset:
|
||||
name: unlabeled
|
||||
root: data/film
|
||||
root: data/raw/film
|
||||
gray: false
|
||||
exclude: [images, labels] # 跳过已经整理成 YOLO 结构的部分
|
||||
transforms:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 用 resnet18 做域内自监督(SimCLR)再聚类。
|
||||
#
|
||||
# 与默认配置的区别只有骨架:configs/cluster/film.yaml 的 method=pretrained 用的也是
|
||||
# resnet18,但那是 ImageNet 权重、不训练;这里是在 data/film 上自监督训练它。
|
||||
# resnet18,但那是 ImageNet 权重、不训练;这里是在 data/raw/film 上自监督训练它。
|
||||
#
|
||||
# 用法:
|
||||
# uv run python scripts/cluster_unlabeled.py --config configs/cluster/film_simclr_resnet18.yaml \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 未分类样本的无监督聚类。
|
||||
#
|
||||
# 预处理决策(针对 data/film 的实际情况:14.5k 张已裁好的缺陷小图,
|
||||
# 预处理决策(针对 data/raw/film 的实际情况:14.5k 张已裁好的缺陷小图,
|
||||
# 中位尺寸 152x113,宽高比 0.24~39.9,尺寸几乎每张都不同):
|
||||
#
|
||||
# 前提:图片的尺寸和宽高比是上游 ROI 裁剪环节的产物,不是缺陷属性。
|
||||
@@ -63,7 +63,7 @@ fit_max_samples: 0
|
||||
|
||||
dataset:
|
||||
name: unlabeled
|
||||
root: data/pearlescent
|
||||
root: data/raw/pearlescent
|
||||
gray: false
|
||||
exclude: [images, labels] # 跳过已经整理成 YOLO 结构的部分
|
||||
transforms:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 薄膜缺陷数据集(YOLO 风格标注:labels/*.txt,每行 `class_id cx cy w h`,归一化坐标)。
|
||||
# 类别顺序即标注里的 class_id,改动顺序等于改变标签含义,必须重新训练。
|
||||
# 也可以写成 data.yaml 的路径,从其中的 names 读类别:classes: data/film/data.yaml
|
||||
# 也可以写成 data.yaml 的路径,从其中的 names 读类别:classes: data/raw/film/data.yaml
|
||||
classes: [scratch, bubble, particle, crease, stain]
|
||||
|
||||
# 图像级标签形式;模型的分类头、分类损失、分类指标都跟随它。
|
||||
@@ -18,8 +18,8 @@ data:
|
||||
train:
|
||||
dataset:
|
||||
name: film_defect
|
||||
img_dir: data/film/images/train
|
||||
label_dir: null # 缺省按 YOLO 约定取 data/film/labels/train
|
||||
img_dir: data/raw/film/images/train
|
||||
label_dir: null # 缺省按 YOLO 约定取 data/raw/film/labels/train
|
||||
gray: false
|
||||
max_objects: 64
|
||||
transforms:
|
||||
@@ -38,7 +38,7 @@ data:
|
||||
val:
|
||||
dataset:
|
||||
name: film_defect
|
||||
img_dir: data/film/images/val
|
||||
img_dir: data/raw/film/images/val
|
||||
label_dir: null
|
||||
gray: false
|
||||
transforms:
|
||||
|
||||
+23
-16
@@ -2,20 +2,27 @@
|
||||
|
||||
存放数据集本体或指向数据集的软链接;除本文件外的内容都不入库(见 .gitignore)。
|
||||
|
||||
标注采用 **YOLO 风格**:`images/` 下每张图在同层 `labels/` 里有一个同名 `.txt`。
|
||||
**原始数据一律放在 `data/raw/<数据集名>/` 下**,派生数据(如按聚类结果导出的数据集)
|
||||
放在 `data/` 下与 `raw/` 平级,两者一眼可分:
|
||||
|
||||
```
|
||||
data/
|
||||
└── film/
|
||||
├── images/ # 原图(或指向 NAS 的软链:ln -s /mnt/nas/film/images data/film/images)
|
||||
│ ├── train/0001.png
|
||||
│ └── val/0002.png
|
||||
├── labels/ # 与 images 目录结构一一对应
|
||||
│ ├── train/0001.txt
|
||||
│ └── val/0002.txt
|
||||
└── data.yaml # path / train / val / nc / names
|
||||
├── raw/ # 原始数据,只读
|
||||
│ └── film/
|
||||
│ ├── images/ # 原图(或指向 NAS 的软链:ln -s /mnt/nas/film/images data/raw/film/images)
|
||||
│ │ ├── train/0001.png
|
||||
│ │ └── val/0002.png
|
||||
│ ├── labels/ # 与 images 目录结构一一对应
|
||||
│ │ ├── train/0001.txt
|
||||
│ │ └── val/0002.txt
|
||||
│ └── data.yaml # path / train / val / nc / names
|
||||
└── film_r18_cluster_k4/ # 派生:scripts/export_clusters.py 的产物
|
||||
├── 0/ 1/ 2/ 3/
|
||||
└── manifest.json
|
||||
```
|
||||
|
||||
标注采用 **YOLO 风格**:`images/` 下每张图在同层 `labels/` 里有一个同名 `.txt`。
|
||||
|
||||
标注文件每行一个目标,坐标**相对图像宽高归一化**到 [0, 1],中心点格式:
|
||||
|
||||
```
|
||||
@@ -31,18 +38,18 @@ data/
|
||||
`data.yaml` 与 ultralytics 通用:
|
||||
|
||||
```yaml
|
||||
path: data/film
|
||||
path: data/raw/film
|
||||
train: images/train
|
||||
val: images/val
|
||||
nc: 5
|
||||
names: {0: scratch, 1: bubble, 2: particle, 3: crease, 4: stain}
|
||||
```
|
||||
|
||||
配置里的 `classes` 既可以直接写类别列表,也可以写成 `data/film/data.yaml` 由框架读取 `names`。
|
||||
配置里的 `classes` 既可以直接写类别列表,也可以写成 `data/raw/film/data.yaml` 由框架读取 `names`。
|
||||
|
||||
## 还没分类的样本
|
||||
|
||||
`data/film` 下按人工整理留下的目录(`grease`、`wrinkle`、`反面纵黑点` ……)只是历史产物,
|
||||
`data/raw/film` 下按人工整理留下的目录(`grease`、`wrinkle`、`反面纵黑点` ……)只是历史产物,
|
||||
不是最终类别。要重新梳理类别时用无监督聚类,它会忽略这些目录名、把所有图拉平处理:
|
||||
|
||||
```bash
|
||||
@@ -57,10 +64,10 @@ uv run python scripts/cluster_unlabeled.py --config configs/cluster/film.yaml
|
||||
```bash
|
||||
# labelme / VOC -> YOLO txt(顺带生成 data.yaml)
|
||||
uv run python scripts/convert_annotations.py --format labelme \
|
||||
--input data/film/labelme --images data/film/images \
|
||||
--output data/film/labels --data-yaml data/film/data.yaml
|
||||
--input data/raw/film/labelme --images data/raw/film/images \
|
||||
--output data/raw/film/labels --data-yaml data/raw/film/data.yaml
|
||||
|
||||
# 划分 train / val(默认建软链,不复制数据)
|
||||
uv run python scripts/split_dataset.py --images data/film/images \
|
||||
--labels data/film/labels --val-ratio 0.2 --data-yaml data/film/data.yaml
|
||||
uv run python scripts/split_dataset.py --images data/raw/film/images \
|
||||
--labels data/raw/film/labels --val-ratio 0.2 --data-yaml data/raw/film/data.yaml
|
||||
```
|
||||
|
||||
@@ -5,6 +5,7 @@ from datasets.film_defect import FilmDefectDataset, collate_fn
|
||||
from datasets.registry import DATASETS
|
||||
from datasets.target_encoder import CenterTargetEncoder
|
||||
from datasets.transforms import build_transforms
|
||||
from datasets.unlabeled import UnlabeledImageDataset, unlabeled_collate
|
||||
from datasets.yolo import format_label_lines, load_classes, parse_label_file, write_data_yaml
|
||||
|
||||
__all__ = [
|
||||
@@ -13,6 +14,8 @@ __all__ = [
|
||||
"FilmDefectDataset",
|
||||
"collate_fn",
|
||||
"DATASETS",
|
||||
"UnlabeledImageDataset",
|
||||
"unlabeled_collate",
|
||||
"CenterTargetEncoder",
|
||||
"build_transforms",
|
||||
"parse_label_file",
|
||||
|
||||
@@ -31,7 +31,7 @@ from utils.labels import MULTILABEL, check_label_mode
|
||||
@DATASETS.register("film_defect")
|
||||
class FilmDefectDataset(Dataset):
|
||||
"""Args:
|
||||
img_dir: 图片目录(YOLO 约定,如 `data/film/images/train`),递归查找图片。
|
||||
img_dir: 图片目录(YOLO 约定,如 `data/raw/film/images/train`),递归查找图片。
|
||||
classes: 类别名列表,或指向 YOLO data.yaml 的路径(读其中的 `names`)。
|
||||
列表顺序即类别下标顺序,必须与标注 txt 里的 class_id 对应。
|
||||
label_dir: 标注目录;缺省按 YOLO 约定把路径里的 `images` 换成 `labels`。
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
目录约定(与 ultralytics 一致,`images` 与 `labels` 同层对应):
|
||||
|
||||
```
|
||||
data/film/
|
||||
data/raw/film/
|
||||
├── images/train/0001.png
|
||||
├── labels/train/0001.txt # 与图片同名,后缀换成 .txt
|
||||
├── images/val/...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 未分类样本的无监督聚类:算法与数学说明
|
||||
|
||||
本文档说明 `scripts/cluster_unlabeled.py` 背后的完整算法:每一步在做什么数学变换、
|
||||
为什么这样选、以及在 `data/film` 上的实测依据。
|
||||
为什么这样选、以及在 `data/raw/film` 上的实测依据。
|
||||
|
||||
代码入口:[`scripts/cluster_unlabeled.py`](../scripts/cluster_unlabeled.py)、
|
||||
[`engine/cluster.py`](../engine/cluster.py)、[`engine/simclr.py`](../engine/simclr.py)、
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
## 1. 问题设定与记号
|
||||
|
||||
给定一组**无标注**图像 $\mathcal{I} = \{I_1, \dots, I_N\}$(`data/film` 下递归扫描到
|
||||
给定一组**无标注**图像 $\mathcal{I} = \{I_1, \dots, I_N\}$(`data/raw/film` 下递归扫描到
|
||||
$N = 14{,}579$ 张 PNG),在没有任何类别标签的前提下,把它们划分成 $k$ 个互不相交的簇
|
||||
|
||||
$$
|
||||
@@ -76,7 +76,7 @@ flowchart TB
|
||||
classDef out fill:#FFEDD5,stroke:#EA580C,stroke-width:2px,color:#7C2D12
|
||||
classDef metric fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#78350F
|
||||
|
||||
scan["data/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
scan["data/raw/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
|
||||
subgraph PRE["① 预处理 — 抹掉采集因子"]
|
||||
direction LR
|
||||
@@ -487,7 +487,7 @@ $$
|
||||
|
||||
**为什么这个指标关键**:数据里真有 $k$ 团结构时,不同初始化会收敛到同一套划分,NMI ≈ 1;若是在硬切一团连续的数据,边界位置随初值乱跳,NMI 会很低。它比轮廓系数更能识破"强行分 $k$ 类"的情形。实测中它一眼揪出了白化的问题(稳定性 0.01)。
|
||||
|
||||
> **不使用外部标签。** `data/film` 下的目录名(`grease`、`wrinkle`、`反面纵黑点`……)
|
||||
> **不使用外部标签。** `data/raw/film` 下的目录名(`grease`、`wrinkle`、`反面纵黑点`……)
|
||||
> 是人工整理的历史产物,既不完整也未必正确,因此既不参与聚类、也不用于评估。
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 无监督聚类流水线
|
||||
|
||||
从 `data/film` 的原始图片到可人工核对的分组结果:六个阶段的数据流、张量形状与每步的数学操作。
|
||||
从 `data/raw/film` 的原始图片到可人工核对的分组结果:六个阶段的数据流、张量形状与每步的数学操作。
|
||||
虚线是可选的 SimCLR 自监督分支(`method: simclr`,CUDA 与 Apple MPS 均可)。
|
||||
|
||||
```mermaid
|
||||
@@ -13,7 +13,7 @@ flowchart TB
|
||||
classDef out fill:#FFEDD5,stroke:#EA580C,stroke-width:2px,color:#7C2D12
|
||||
classDef metric fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#78350F
|
||||
|
||||
scan["data/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
scan["data/raw/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
|
||||
subgraph PRE["① 预处理 — 抹掉采集因子"]
|
||||
direction LR
|
||||
|
||||
@@ -7,7 +7,7 @@ flowchart TB
|
||||
classDef out fill:#FFEDD5,stroke:#EA580C,stroke-width:2px,color:#7C2D12
|
||||
classDef metric fill:#FEF3C7,stroke:#D97706,stroke-width:2px,color:#78350F
|
||||
|
||||
scan["data/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
scan["data/raw/film 递归扫描<br/>N = 14,579 张 PNG<br/>尺寸各异 84×83 ~ 4464×1139"]:::input
|
||||
|
||||
subgraph PRE["① 预处理 — 抹掉采集因子"]
|
||||
direction LR
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 194 KiB |
@@ -2,8 +2,8 @@
|
||||
"""把 labelme / VOC 标注批量转换成 YOLO 风格的 .txt。
|
||||
|
||||
python scripts/convert_annotations.py --format labelme \
|
||||
--input data/film/labelme --images data/film/images \
|
||||
--output data/film/labels --data-yaml data/film/data.yaml
|
||||
--input data/raw/film/labelme --images data/raw/film/images \
|
||||
--output data/raw/film/labels --data-yaml data/raw/film/data.yaml
|
||||
|
||||
每张图输出一个同名 .txt,每行 `class_id cx cy w h`(相对宽高归一化);
|
||||
类别顺序默认按扫描到的名字排序,也可以用 `--classes scratch bubble ...` 固定。
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
"""按比例切分成 YOLO 的 train / val / test 目录结构。
|
||||
|
||||
python scripts/split_dataset.py --images data/film/images --labels data/film/labels \
|
||||
--val-ratio 0.2 --data-yaml data/film/data.yaml
|
||||
python scripts/split_dataset.py --images data/raw/film/images --labels data/raw/film/labels \
|
||||
--val-ratio 0.2 --data-yaml data/raw/film/data.yaml
|
||||
|
||||
默认建软链(不复制数据),产出:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user