diff --git a/configs/cluster/film.yaml b/configs/cluster/film.yaml index d0f5390..e4ae9b8 100644 --- a/configs/cluster/film.yaml +++ b/configs/cluster/film.yaml @@ -20,7 +20,7 @@ # 聚类被形变干扰,可选的替代是在原始分辨率上取固定大小的中心 patch # (不缩放、不填充),配置里把 Resize 换成 CenterCrop 即可。 -work_dir: runs/cluster +work_dir: runs/film seed: 42 device: auto # auto 按 cuda > mps > cpu 挑最快的可用后端;也可写 cuda / mps / cpu diff --git a/configs/cluster/pearlescent.yaml b/configs/cluster/pearlescent.yaml new file mode 100644 index 0000000..fb08c84 --- /dev/null +++ b/configs/cluster/pearlescent.yaml @@ -0,0 +1,124 @@ +# 未分类样本的无监督聚类。 +# +# 预处理决策(针对 data/film 的实际情况:14.5k 张已裁好的缺陷小图, +# 中位尺寸 152x113,宽高比 0.24~39.9,尺寸几乎每张都不同): +# +# 前提:图片的尺寸和宽高比是上游 ROI 裁剪环节的产物,不是缺陷属性。 +# 所以预处理的目标是**把这个采集噪声抹掉**,只留下缺陷的纹理/形态。 +# +# 1. 直接 Resize 拉伸到 128x128,不用 letterbox —— letterbox 的填充比例 +# 恰好等价于编码了宽高比,灰边占多少就等于把采集噪声明着喂给网络; +# 拉伸则让所有图变成同一个画幅,宽高比不再是可用信号。 +# 2. use_shape_features 关掉 —— 同理,尺寸不进 embedding。 +# 3. 逐图标准化(PerImageStandardize)而不是固定的 ImageNet mean/std —— +# 整体亮度来自光照和相机增益,同样不是缺陷属性。用固定归一化时, +# 4 个簇里有 3 个花在"背景偏亮/偏暗"上;改成逐图 z-score 后, +# 簇变成"条纹状 / 团块状 / 弱纹理",轮廓 0.256->0.322,稳定性 0.966->0.995。 +# 4. 不下采样到 224 —— 这批图本来就小,放大只会糊,128 已经够。 +# +# 代价:拉伸会让纹理各向异性形变(40:1 的长条尤其明显)。如果后续发现 +# 聚类被形变干扰,可选的替代是在原始分辨率上取固定大小的中心 patch +# (不缩放、不填充),配置里把 Resize 换成 CenterCrop 即可。 + +work_dir: runs/pearlescent +seed: 42 +device: auto # auto 按 cuda > mps > cpu 挑最快的可用后端;也可写 cuda / mps / cpu + +num_clusters: 20 # 想分几类改这里(或 --num-clusters 6) + +# pretrained: ImageNet 预训练卷积骨架直接抽特征,不训练,CPU 也能跑,几分钟出结果。 +# simclr: 在本目录数据上自监督训练项目自己的卷积骨架,域适配更好;Mac 上走 MPS 即可。 +method: pretrained + +# method=pretrained 用的骨架(method=simclr 的骨架配在 simclr.backbone,两者字段完全不同, +# 各自独立以免 _base_ 合并时串味) +backbone: + name: torchvision + arch: resnet18 + pretrained: true + in_channels: 3 + +image_size: [128, 128] +use_shape_features: false # 尺寸/宽高比是采集产物,不作为聚类特征;true 仅供对照实验 + +# 取哪几个 stage 的特征拼起来(torchvision resnet 的 features 下标:4~7 = layer1~4)。 +# 实测(2000 张随机样本,k=4): +# 只用最后一层 layer4 轮廓 0.191 换种子稳定性 0.410 +# layer2+3 轮廓 0.256 稳定性 0.989 <- 默认 +# layer2+3+4 轮廓 0.220 稳定性 0.627 +# 缺陷之间的差别主要在纹理(颗粒感、条纹方向、边缘锐度),中间层保留得比最后一层好; +# 最后一层的 ImageNet 语义("这是猫还是车")对这批图基本是噪声。 +feature_stages: [5, 6] + +# 特征降维。whiten 默认关闭:CNN 特征只有前若干主成分携带结构, +# 白化会把剩下几十个噪声方向抬到同权,簇立刻塌成"一个大簇 + 几个碎簇" +# (实测开白化时最大簇占 96%,关掉后降到 38%)。 +pca_dim: 32 +whiten: false +n_init: 10 + +# 只用一部分样本拟合质心以节省时间(0 = 用全部);纯随机抽取,不按目录分层—— +# 目录名是人工整理的历史产物,不参与聚类。拟合完仍会把全部样本分配到最近的簇。 +fit_max_samples: 0 + +dataset: + name: unlabeled + root: data/pearlescent + gray: false + exclude: [images, labels] # 跳过已经整理成 YOLO 结构的部分 + transforms: + - {name: Resize, size: [128, 128]} # 拉伸,不保长宽比:见文件开头的说明 + - {name: ToTensor} + - {name: PerImageStandardize} # 逐图 z-score,抹掉整体亮度/对比度 + +loader: + batch_size: 128 + num_workers: 8 + +# method=simclr 时每个视图各自过一遍的随机增强(正样本对就是同一张图的两个视图) +view_transforms: + - {name: RandomResizedCrop, size: [128, 128], scale: [0.5, 1.0]} + - {name: RandomHorizontalFlip, p: 0.5} + - {name: RandomVerticalFlip, p: 0.5} + - {name: ColorJitter, brightness: 0.4, contrast: 0.4} + - {name: GaussianBlur, p: 0.3} + # 注意:主 transforms 里的 PerImageStandardize 会在这些增强之后执行, + # 所以 ColorJitter 的亮度扰动最终会被抵消掉,起作用的主要是裁剪/翻转/模糊。 + +simclr: + # 自监督用项目自己的纯卷积骨架:练出来的权重可以直接拿去初始化 DefectNet 的 backbone + backbone: + name: tiny_conv + in_channels: 3 + widths: [32, 64, 128, 256] + depths: [1, 2, 2, 2] + block: dw + feature_stages: null # tiny_conv 不是 nn.Sequential,取最终输出 + epochs: 100 + batch_size: 128 + temperature: 0.2 # 越小越关注难负样本 + amp: auto # CUDA 上开、MPS 上关 + save_interval: 5 # 每 5 个 epoch 落一次盘,中途中断也能拿到权重 + + # 训练可视化,字段与 train.swanlab 相同。默认 local:不需要登录, + # 日志写到 /swanlog,另开终端 `uv run swanlab watch runs/simclr/swanlog` 看板。 + swanlab: + enabled: true + project: pearlescent-simclr + name: null # 缺省用 work_dir 目录名 + mode: local # online(云端) | local(本地看板) | offline(仅存盘) | disabled + tags: [simclr, tiny_conv] + + log_interval: 20 + # checkpoint 与 resume 二选一,语义不同: + # checkpoint: 有权重就**跳过训练**,直接拿它抽特征聚类(换簇数时用这个) + # resume: 载入骨架+投影头+优化器+调度器,从上次的 epoch **接着训练** + checkpoint: null # 例:runs/simclr/simclr_backbone.pth + resume: null # 例:runs/simclr/simclr_last.pth + projection: {hidden_channels: 256, out_channels: 128} + optimizer: {name: adamw, lr: 0.001, weight_decay: 0.00001} + scheduler: {name: cosine} + +# 输出:每簇软链多少张图 / 预览拼图放多少张 +link_limit: 300 +preview_count: 100