使用 Stars 训练 GitHub 仓库嵌入
Source: Dev.to
(请提供需要翻译的正文内容,我才能为您完成简体中文翻译。)
TL;DR
The Idea – 人们使用 GitHub Stars 作为书签。这是理解哪些仓库在语义上相似的极佳信号。
The Data – 处理了约 1 TB 的 GitHub Archive(BigQuery)原始数据,构建了 4 M 开发者的兴趣矩阵。
The ML – 使用度量学习(EmbeddingBag + MultiSimilarityLoss)为 300 k+ 仓库训练了嵌入向量。
The Frontend – 构建了一个仅客户端的演示,直接在浏览器中通过 WASM 运行向量搜索(KNN),无需后端。
The Result – 系统能够发现不明显的库替代方案,并实现对开发者个人资料的语义比较。
个人动机
完成想法通常比看起来更困难。构建原型很容易,但真正的挑战在于之后:打磨粗糙的边缘、撰写文本以及设置演示。这个项目是我尝试全程完成并解决那些“悬而未决”问题的努力。
我也开始思考我们 GitHub Stars 的本质。我们把它们仅仅当作“以后再看”的书签,但实际上它们是一种有价值的数字资产——我们职业兴趣和技能的快照。我在想:
- 我们能否让这个被动资产发挥作用?
- 我们能否利用数百万开发者积累的知识,构建一个仓库推荐系统,让人们比较自己的技术兴趣?
概念
集群假设
阅读本文的读者在兴趣上比街上随意挑选的陌生人更相似。在我们的宇宙中,相似的事物常常出现在相似的上下文中。
我们直观地感受到这些“隐藏”的偏好。如果你看到一位新同事使用 Vim 编写代码并在没有帮助的情况下退出,你可能已经在脑中构建了他们兴趣的向量:你大概可以和他们讨论在 FreeBSD 上为 KDE2 打补丁,但询问关于 RGB 游戏鼠标的建议可能就不太合适。
仓库表示
我们希望构建一个空间,使语义相似的仓库彼此靠近。
为了说明,想象一个二维空间,其坐标轴为:
- Axis X: 数据(准备 & 分析) ↔ 模型(训练 & 推理)
- Axis Y: 本地 / 单节点 ↔ 大数据 / 集群

实际上,神经网络会自行学习这些(往往难以解释的)特征。数学本质保持不变:相似的仓库会根据学习到的特征被拉拢到同一簇中。
有了这些向量,我们可以:
- 使用 余弦相似度(向量之间的夹角)搜索相似的仓库。
- 通过对用户 已加星 仓库的向量取平均,得到用户兴趣向量。
- 将用户画像相互比较。
信号来源
为了获得高质量的向量,我采用了混合方法。
-
文本(README.md) – 初始化
许多仓库都有 README,这是一个很好的“冷启动”来源。我使用了 Qwen3‑Embedding‑0.6B 模型(支持 Matryoshka Representation Learning),并仅保留前 128 维(最重要的成分)。这些向量作为可训练模型的 初始权重。注意: 这一步约提升最终质量的 10 %。为了保持公共仓库的轻量,我省略了它;模型从随机初始化也能学习,只是稍慢一些。
-
星标矩阵 – 主训练
单靠文本无法体现工具之间的协同使用。协同过滤可以捕捉到这一点。User → Starred repositories A → Pandas, Dask, scikit‑learn, NumPy B → Vue, React, TypeScript, Vite可选方法包括图算法(LightGCN)或矩阵分解。我选择 度量学习,因为它需要的 GPU 资源更少(≈1 块 GPU,约 8 GB),且在管理向量空间时更灵活。
数据准备
Data were sourced from the public GitHub Archive dataset in BigQuery.
Two queries were required:
| Query | Purpose |
|---|---|
| Stars (WatchEvent) | 收集拥有 10 – 800 星标的用户(过滤机器人和不活跃用户),并保留星标顺序。 |
| Meta (PushEvent) | 收集仓库名称、提交日期和描述。 |
The queries processed ~1 TB of data and almost fit within the BigQuery Free Tier. The output was a Parquet file containing ~4 M users and ~2.5 M unique repositories.
训练向量
模型选择
为了让解决方案在浏览器中保持轻量,我排除了 Transformers。
模型是经典的 torch.nn.EmbeddingBag —— 本质上是一个大型查找表:
repo_id → vector[128]
它可以高效地聚合(平均)向量。
采样与损失函数
我们如何告诉网络 Pandas 和 NumPy 是“接近”的?
对于每个用户,我将他们的星标仓库列表拆分成两个随机的、互不重叠的 桶,并将它们用作正样本对。负样本则从其他用户的桶中抽取。损失函数使用 MultiSimilarityLoss,其目标是:
- 拉近 正样本对。
- 推远 负样本对。
这种简单的方案在不进行昂贵图计算的情况下捕获了协同过滤信号。
推理与前端
训练好的嵌入(128 维向量)被导出为静态二进制文件(约 150 MB)。在浏览器中:
- 通过
fetch加载文件并解码为Float32Array。 - 使用 FAISS‑like 产品量化的 WebAssembly(WASM)模块实现快速余弦相似度搜索。
- UI(纯 HTML + CSS + Vanilla JS)让用户:
- 使用 GitHub 进行身份验证 → 获取已加星的仓库 → 计算其兴趣向量。
- 可视化技能分布雷达图。
- 搜索相似仓库或比较个人资料。
所有这些都在客户端运行;无需服务器或 API 密钥。
结果
- 系统会呈现非显而易见的库替代方案(例如,向 Pandas 爱好者推荐 Polars)。
- 用户画像相似性热图揭示了具有共同兴趣的开发者聚类。
欢迎随意探索演示,给仓库加星,或在有建议时打开 issue!
用于桶级聚合的 EmbeddingBag
我们使用 EmbeddingBag 来计算每个桶的聚合嵌入。
| 用户 | 桶 | 桶中的仓库 | 平均值(桶中的嵌入) |
|---|---|---|---|
| A | A1 | NumPy, Dask, SciPy | [0.2, -1.1, 0.9, …] |
| A | A2 | Pandas, SK‑Learn | [0.1, -1.3, 0.6, …] |
| B | B1 | Vue, Vite | [-0.4, 0.6, 0.2, …] |
| B | B2 | React, TypeScript | [-0.3, 0.7, 0.1, …] |
训练目标
我们训练嵌入,使 以下两个条件 同时成立:
- 用户内部凝聚 – 同一用户的桶应尽可能接近(例如,A1 ↔ A2)。
- 用户间分离 – 不同用户的桶应被推得尽可能远(例如,B1 ↔ A2)。
梯度下降在这两种相反的力量之间取得平衡,以最小化整体误差。
损失函数
表现最佳的损失是来自 pytorch-metric-learning 库的 MultiSimilarityLoss。
注: 我们要感谢 StarSpace,它在八年前提出了这一思路。
高级方法 vs. 简约
我们自然会认为用户给仓库加星的顺序(即“星标序列”)会包含强信号,于是我们尝试了 Word2Vec‑style 滑动窗口模型。
令人惊讶的是,最简单的随机划分优于所有更复杂的方法。
可能的原因:
- 时间数据噪声过大。
- 我们未能从中提取有用信息。
我们还尝试了:
- Hard Negative Miners。
- 替代损失函数,例如 NTXentLoss(其内存使用约是 MultiSimilarityLoss 的 4 倍)。
- Cross‑Batch Memory(未见明显收益)。
这些方法都未能超越原始基准。有时 Occam’s razor 确实占上风;而有时,剃刀只是钝的。
质量评估
拥有向量是一回事——它们真的有用吗?
我们没有使用 LLM 生成的合成数据,而是采用了更优雅的真实标签:Awesome Lists(例如 “Awesome Python”、 “Awesome React”)。这些是人工策划的相似库集合。
- 下载列表的 README。
- 提取共现(哪些仓库一起出现)。
- 应用启发式加权。
- 使用 NDCG 指标评估排序。
该流水线让我们能够公平地比较损失函数、超参数和采样策略。
前端:展示与 AI‑辅助开发
即使拥有十年的数据科学经验,我也不是前端专家。挑战在于在没有后端且不是 JS 开发者的情况下构建复杂的客户端逻辑。
所有前端以及“胶水”代码都是在AI 编码代理的帮助下编写的。
架构
- 数据 – 客户端下载压缩的嵌入(FP16,约 80 MB)和元数据,然后将其缓存到 IndexedDB 中。
- 搜索(WASM) – 使用 USearch 库的核心,编译为 WebAssembly。
底层魔法
最初我们尝试了预先计算的 HNSW 索引,但它占用的内存比原始嵌入还要多。
AI 代理通过以下方式实现了 精确搜索(仍在 WASM 中):
- 暴露底层的
_usearch_exact_search方法。 - 生成一个工作线程 (
coreWorker.js),手动管理内存,通过_malloc分配缓冲区,并操作指针。 - 添加即时的 FP16 → FP32 转换器,因为浏览器对原生 FP16 的支持不佳。
最终实现了在约 300 k 向量上进行快速精确搜索,而无需任何 HNSW 索引。
Source: …
用户画像与技能雷达
用户嵌入
- 客户端查询 GitHub API,获取用户已加星的仓库。
- 检索这些仓库的 embeddings。
- 对它们求平均,得到 均值向量——用户兴趣的数字指纹。
由于该向量与仓库嵌入位于同一度量空间,我们可以搜索“最近”的库。
技能雷达(向量解释)
- 提示 LLM 为每个 10 类别(例如 “GenAI”、 “Web3”、 “系统编程”)生成 20 个参考仓库。
- 训练简单的 Logistic Regression(线性探针),以区分这些类别的向量。
- 在浏览器中,将用户向量输入这些探针,得到雷达图的概率得分。
无服务器共享
- 相似度度量: 余弦相似度,经过 Quantile Transformation 转换,使得分数以百分位显示(例如,“95 % 匹配” 表示用户比随机配对中 95 % 的情况更相似于该开发者)。
- 共享机制: 将用户向量压缩后进行 Base64‑编码,并直接嵌入 URL 片段标识符 (
#…) 中。无数据库,无后端——纯客户端计算。
结果:期望 vs. 现实
除了量化指标外,我们还进行了“肉眼测试”。
哪些没有奏效
-
向量算术 类似于 NLP(
King – Man + Woman = Queen)。
假设:Pandas – Python + TypeScript = Danfo.js。
现实: 仓库的向量空间要复杂得多,简单的线性运算并不能得到可解释的结果。 -
明显的聚类 —— 嵌入向量并未形成清晰分离的可视化簇。
哪些奏效了
- 主要目标已实现:搜索能够为用户标星的仓库找到相关的替代方案。
总体而言,系统展示了 简单、无服务器、仅客户端 架构也能从大规模嵌入空间中提供有用的个性化推荐。
小众工具与新颖解决方案
不同于经常偏向最流行解决方案的 LLM,这种基于 IT 专业人士行为 的方法能够发现:
- 小众工具: 专业人士使用但在博客中很少提及的库。
- 新颖解决方案: 最近获得人气且呈现相似“星标模式”的仓库。
- 本地优先: 所有内容均在客户端设备本地运行。
Future Vision
当前演示展示了在没有后端的情况下可以实现的功能,但还有许多其他用例是可以想象的。
Semantic Text Search
可以训练一个文本编码器,并在其上添加投影层映射到仓库嵌入空间,从而实现通过抽象描述搜索工具或人员。
GitHub Tinder (Networking)
通过用户向量,我们可以匹配人员:
- Mentor or co‑founder search – 找到拥有互补技术栈的人。
- Contributor discovery – 识别对相似项目加星但尚未看到你的项目的开发者。
- HR‑Tech – 根据技术兴趣将候选人与职位匹配。
Trend Analytics
加入时间维度后,可在数月或数年间追踪新兴技术和开发者兴趣的变化。