博客星系图开发记录:聚类与可视化实践

这次把博客的文章列表从线性列表扩展成一个可探索的二维”星系图”。思路是先在本地完成向量化、聚类与降维,再把结果作为静态 JSON 输出给 Astro 前端渲染,形成”本地预计算 → 静态部署”的链路。

数据处理流程

整个处理逻辑放在 tools/blog-clustering/generate_embeddings.py,模型缓存统一放在仓库根目录 model/(已在 .gitignore 中忽略)。

流程分四步:扫描 src/content/blog 中的 Markdown/MDX,读取 Front Matter 并过滤 draft 文章,使用 BAAI/bge-m3 生成向量,最后 KMeans 聚类加 t-SNE 降维,输出到 src/data/clusters.json

每条记录包含标题、slug、日期、聚类编号和二维坐标:

[
  {
    "title": "Post Title",
    "slug": "post-slug-url",
    "date": "YYYY-MM-DD",
    "cluster": 0,
    "x": 12.34,
    "y": -5.67
  }
]

前端渲染

src/components/BlogGalaxy.astro 读取 JSON,用 ECharts 绘制散点图。坐标轴隐藏,启用 roam 支持缩放与拖拽,Tooltip 展示标题与日期,点击跳转到 /blog/{slug}。页面入口是 /blog/galaxy,全屏展示,保留顶部导航。

显存适配

我的显卡只有 8GB 显存,脚本默认用了轻量化参数:--batch-size 4--max-length 1024、GPU 走 fp16。显存仍然吃紧的话可以进一步压缩:

python tools/blog-clustering/generate_embeddings.py --batch-size 2 --max-length 512

或者直接走 CPU,速度慢一些但不受显存限制:

python tools/blog-clustering/generate_embeddings.py --device cpu

使用方式

pip install -r tools/blog-clustering/requirements.txt
npm install
npm run update-graph
npm run dev

然后访问 http://localhost:4321/blog/galaxy 就能看到星系图。

几点收获

星系图把文章集合从”列表”变成”空间”,浏览内容的方式会变。这个方案里最值得保留的是把计算密集的部分(向量化、聚类)放在本地预处理,前端只负责渲染静态数据,既保持了 SSG 的简洁,也不用把运行时计算压力交给浏览器。BAAI/bge-m3 对中文支持不错,聚类结果基本符合直觉,t-SNE 的降维效果也比 PCA 更适合这种可视化场景。