feat(cluster): 输出簇分布图,直观展示簇间距离
在 preview 拼图的同级目录生成 cluster_scatter.png: - 左:聚类空间的 PCA 二维投影,散点按簇着色 + 质心标记,标题标注两个主成分的方差占比 - 右:质心两两距离矩阵(在聚类空间里算,不是投影后),带数值标注 投影用 PCA 而不是 t-SNE/UMAP——后两者为了把簇拉开会扭曲簇间距离, 而这张图的目的就是看距离。 - ClusterResult 新增 points 字段,保留 KMeans 实际使用的空间供可视化复用, 质心与散点用同一组主成分变换,避免质心飘到簇外 - summary.json 新增 centroid_distances - 新增 scatter 配置块(enabled / max_points / point_size / alpha / figsize / dpi) - 中文字体按 PingFang / Noto Sans CJK 等顺序探测,找不到自动退回英文标签, 不会画成豆腐块 - 新增依赖 matplotlib Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4e6c8aa2ea
commit
e4bd794025
@@ -337,6 +337,50 @@ def test_cluster_script_simclr_branch_end_to_end(tiny_image_dir, tmp_path, monke
|
||||
assert (work_dir / "clusters" / "cluster_00").is_dir()
|
||||
|
||||
|
||||
def test_scatter_plot_is_written_next_to_previews(tiny_image_dir, tmp_path, monkeypatch):
|
||||
"""cluster_scatter.png 要和 preview 拼图落在同一级目录。"""
|
||||
sys.path.insert(0, "scripts")
|
||||
import cluster_unlabeled as cu
|
||||
|
||||
work_dir = tmp_path / "run"
|
||||
monkeypatch.setattr(
|
||||
sys, "argv",
|
||||
["cluster_unlabeled.py", "--config", "configs/cluster/film.yaml",
|
||||
"--work-dir", str(work_dir), "--root", str(tiny_image_dir), "--set",
|
||||
"device=cpu", "loader.num_workers=0", "loader.batch_size=4",
|
||||
"num_clusters=2", "n_init=2"],
|
||||
)
|
||||
assert cu.main() == 0
|
||||
|
||||
scatter = work_dir / "cluster_scatter.png"
|
||||
assert scatter.exists() and scatter.stat().st_size > 5000
|
||||
assert (work_dir / "cluster_00_preview.png").parent == scatter.parent
|
||||
|
||||
import json
|
||||
|
||||
summary = json.loads((work_dir / "summary.json").read_text(encoding="utf-8"))
|
||||
matrix = summary["centroid_distances"]
|
||||
assert len(matrix) == 2 and matrix[0][0] == pytest.approx(0.0)
|
||||
assert matrix[0][1] == pytest.approx(matrix[1][0])
|
||||
|
||||
|
||||
def test_scatter_can_be_disabled(tiny_image_dir, tmp_path, monkeypatch):
|
||||
sys.path.insert(0, "scripts")
|
||||
import cluster_unlabeled as cu
|
||||
|
||||
work_dir = tmp_path / "run_off"
|
||||
monkeypatch.setattr(
|
||||
sys, "argv",
|
||||
["cluster_unlabeled.py", "--config", "configs/cluster/film.yaml",
|
||||
"--work-dir", str(work_dir), "--root", str(tiny_image_dir), "--set",
|
||||
"device=cpu", "loader.num_workers=0", "loader.batch_size=4",
|
||||
"num_clusters=2", "n_init=2", "scatter.enabled=False"],
|
||||
)
|
||||
assert cu.main() == 0
|
||||
assert not (work_dir / "cluster_scatter.png").exists()
|
||||
assert (work_dir / "cluster_00_preview.png").exists()
|
||||
|
||||
|
||||
def test_simclr_runs_on_the_resolved_device(tiny_image_dir, tmp_path, monkeypatch):
|
||||
"""method=simclr 在本机自动选中的后端上(MPS / CUDA / CPU)都应能跑通。"""
|
||||
sys.path.insert(0, "scripts")
|
||||
|
||||
Reference in New Issue
Block a user