博客星系图开发记录:聚类与可视化实践
这次把博客的文章列表从「线性列表」扩展成一个可探索的二维“星系图”。核心思路是先在本地完成向量化、聚类与降维,再把结果作为静态 JSON 输出给 Astro 前端渲染,最终实现“本地预计算 → 静态部署”的完整链路。
目标与约束
- 平台:Astro SSG,最终部署到 Cloudflare Pages。
- 计算:本地 GPU 进行预计算(CUDA 优先)。
- 运行方式:生成一次 JSON,前端只做渲染。
工具结构
这次的逻辑拆成独立工具目录,方便维护和扩展:
tools/blog-clustering/
├─ generate_embeddings.py
├─ requirements.txt
└─ README.md
模型下载缓存统一放在仓库根目录 model/(已在 .gitignore 中忽略)。
数据处理流程
流程大致分为四步:
- 扫描
src/content/blog中的 Markdown / MDX - 读取 Front Matter,过滤
draft文章 - 使用
BAAI/bge-m3生成向量 - KMeans 聚类 + t-SNE 降维,输出到
src/data/clusters.json
最终 JSON 结构如下:
[
{
"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
小结
星系图把文章集合从“列表”变成“空间”,让内容探索更有趣。接下来可以继续扩展,比如:
- 在图上增加筛选(标签/时间/系列)
- 为聚类添加自定义配色方案
- 增加聚类说明与统计
如果你也在做内容可视化,希望这次记录能带来一些灵感。