把 data/film 下所有图片拉平做聚类,不使用任何目录名,簇数由 num_clusters 决定。 预处理针对该批数据的实际情况(14.5k 张已裁好的缺陷小图,中位 152x113, 宽高比 0.24~39.9):尺寸与亮度都是 ROI 裁剪和光照的产物、不是缺陷属性, 因此拉伸到 128x128(不用 letterbox,其填充比例等价于编码了宽高比)并逐图 z-score。 - cluster: torch 实现的 kmeans++/Lloyd(含空簇处理与 n_init)、PCA、轮廓系数、 NMI 稳定性、多 stage 特征提取;不引 scikit-learn - 白化默认关闭:CNN 特征谱极陡(λ1/λ32≈165x),白化会把噪声方向抬到与信号同权, 实测最大簇一度吃掉 96% 的样本 - simclr: NT-Xent 自监督预训练,用项目自己的卷积骨架,权重可直接初始化 DefectNet; 属于训练,走 require_cuda() 守卫 - 产出 assignments.csv、summary.json、按簇的软链目录与代表样本拼图,供人工核对 全量 14,579 张:轮廓系数 0.326、换种子稳定性 0.964,CPU 约 100 秒。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
31 lines
977 B
Python
31 lines
977 B
Python
"""SimCLR 的投影头。
|
|
|
|
对比损失只作用在投影后的空间;聚类时**丢掉投影头、用骨架特征**——
|
|
这是 SimCLR 的既有结论:投影头会丢掉对下游任务有用但对比任务用不上的信息。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import torch
|
|
from torch import nn
|
|
|
|
from models.registry import HEADS
|
|
|
|
|
|
@HEADS.register("projection")
|
|
class ProjectionHead(nn.Module):
|
|
def __init__(self, in_channels: int, hidden_channels: int = 256, out_channels: int = 128) -> None:
|
|
super().__init__()
|
|
self.pool = nn.AdaptiveAvgPool2d(1)
|
|
self.mlp = nn.Sequential(
|
|
nn.Linear(in_channels, hidden_channels),
|
|
nn.BatchNorm1d(hidden_channels),
|
|
nn.ReLU(inplace=True),
|
|
nn.Linear(hidden_channels, out_channels),
|
|
)
|
|
|
|
def forward(self, feat: torch.Tensor) -> torch.Tensor:
|
|
if feat.dim() == 4:
|
|
feat = self.pool(feat).flatten(1)
|
|
return self.mlp(feat)
|