Stars를 이용한 GitHub Repository Embeddings 학습
I’m ready to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have the text, I’ll translate it into Korean while preserving all formatting, markdown, and technical terms.
TL;DR
The Idea – 사람들은 GitHub Stars를 북마크처럼 사용한다. 이는 레포지토리 간 의미적으로 유사한지를 파악하는 데 매우 좋은 신호이다.
The Data – GitHub Archive (BigQuery)에서 약 1 TB의 원시 데이터를 처리해 4 M 개발자의 관심 매트릭스를 구축했다.
The ML – 메트릭 학습(EmbeddingBag + MultiSimilarityLoss)을 이용해 300 k + 레포지토리의 임베딩을 학습했다.
The Frontend – 백엔드 없이 WASM을 통해 브라우저에서 직접 벡터 검색(KNN)을 실행하는 클라이언트‑전용 데모를 만들었다.
The Result – 시스템은 눈에 띄지 않는 라이브러리 대안을 찾아주고, 개발자 프로필을 의미적으로 비교할 수 있게 한다.
개인 동기
아이디어를 완성하는 것은 보통 생각보다 어렵습니다. 프로토타입을 만드는 것은 쉽지만, 진짜 고난은 그 이후에 시작됩니다: 거친 부분을 다듬고, 텍스트를 작성하고, 데모를 설정하는 일 말이죠. 이 프로젝트는 제가 전체 과정을 마무리하고 그 “느슨한 끝” 중 하나를 정리하려는 시도입니다.
저는 또한 우리 GitHub Stars의 본질에 대해 생각하기 시작했습니다. 우리는 이를 단순히 “나중에”를 위한 북마크로 여기지만, 실제로는 귀중한 디지털 자산—우리의 전문적인 관심사와 기술을 담은 스냅샷입니다. 그래서 궁금했습니다:
- 이 수동적인 자산을 활용할 수 있을까?
- 수백만 개발자들의 축적된 지식을 이용해 레포지토리 추천 시스템을 구축하고, 사람들이 자신의 기술적 관심사를 비교할 수 있게 할 수 있을까?
The Concept
Cluster Hypothesis
이 글을 읽는 사람들은 거리에서 무작위로 선택된 사람보다 당신과 관심사가 훨씬 더 비슷합니다. 우리 우주에서는 비슷한 것들이 종종 비슷한 맥락에서 나타납니다.
우리는 직관적으로 이러한 “숨겨진” 선호도를 감지합니다. 새로운 동료가 Vim을 사용하고 도움 없이 종료하는 모습을 보면, 이미 그들의 관심사에 대한 정신적 벡터를 구축했을 가능성이 높습니다. 그와는 FreeBSD에서 KDE2를 패치하는 이야기를 할 수 있겠지만, RGB 게이밍 마우스에 대한 조언을 구하는 것은 맞지 않을 수도 있습니다.
Repo Representation
우리는 의미적으로 유사한 저장소들이 서로 가깝게 위치하는 공간을 원합니다.
예를 들어, 다음과 같은 축을 가진 2‑D 공간을 상상해 보세요:
- Axis X: Data (Preparation & Analysis) ↔ Models (Training & Inference)
- Axis Y: Local / Single‑node ↔ Big Data / Cluster

실제로 신경망은 이러한 (종종 해석하기 어려운) 특징들을 스스로 학습합니다. 수학적인 본질은 동일합니다: 유사한 저장소들은 학습된 특징에 따라 클러스터로 모여집니다.
이 벡터들을 사용하면 다음을 할 수 있습니다:
- 코사인 유사도(벡터 사이 각도)를 이용해 유사한 저장소 검색
- 사용자가 starred한 저장소들의 벡터를 평균내어 사용자‑관심 벡터 획득
- 사용자 프로필 간 비교
Signal Source
품질 좋은 벡터를 얻기 위해 하이브리드 접근 방식을 사용했습니다.
-
Text (README.md) – Initialization
많은 저장소에 README가 있으며, 이는 훌륭한 “콜드‑스타트” 소스입니다. 저는 Qwen3‑Embedding‑0.6B 모델( Matryoshka Representation Learning 지원)을 사용했고, 가장 중요한 128 D만 남겼습니다. 이 벡터들은 학습 가능한 모델의 초기 가중치 역할을 했습니다.Note: 이 단계는 최종 품질에 약 10 % 정도 기여합니다. 공개 저장소를 가볍게 유지하기 위해 생략했으며, 모델은 무작위 초기화에서도 충분히 학습되지만 약간 더 느립니다.
-
Stars Matrix – Main Training
텍스트만으로는 도구들이 어떻게 함께 사용되는지 알 수 없습니다. 협업 필터링이 이를 포착합니다.User → Starred repositories A → Pandas, Dask, scikit‑learn, NumPy B → Vue, React, TypeScript, Vite그래프 알고리즘(LightGCN)이나 행렬 분해와 같은 접근법이 있지만, 저는 메트릭 러닝을 선택했습니다. 이는 GPU 자원을 적게 사용(≈1 GPU, ~8 GB)하면서 벡터 공간을 유연하게 관리할 수 있기 때문입니다.
데이터 준비
데이터는 BigQuery에 있는 공개 GitHub Archive 데이터셋에서 가져왔습니다.
두 개의 쿼리가 필요했습니다:
| Query | 목적 |
|---|---|
| Stars (WatchEvent) | 10 – 800개의 스타를 가진 사용자(봇 및 비활성 사용자를 필터링)들을 스타 순서를 유지하면서 수집합니다. |
| Meta (PushEvent) | 저장소 이름, 커밋 날짜 및 설명을 수집합니다. |
쿼리는 약 1 TB의 데이터를 처리했으며, 거의 BigQuery 무료 티어 내에 들어갔습니다. 출력은 약 4 M명의 사용자와 약 2.5 M개의 고유 저장소를 포함한 Parquet 파일이었습니다.
Training Vectors
Model Choice
브라우저에서 가볍게 동작하도록 하기 위해 Transformer는 제외했습니다.
모델은 고전적인 torch.nn.EmbeddingBag이며, 본질적으로 큰 조회 테이블입니다:
repo_id → vector[128]
벡터를 효율적으로 집계(평균)할 수 있습니다.
Sampling & Loss Function
Pandas와 NumPy가 “가깝다”는 것을 네트워크에 어떻게 알려줄까요?
각 사용자의 별표된 레포지토리 목록을 두 개의 무작위, 겹치지 않는 버킷으로 나누고 이를 양성 쌍으로 사용했습니다. 부정 샘플은 다른 사용자의 버킷에서 추출했습니다. 손실 함수는 MultiSimilarityLoss였으며, 다음을 장려합니다:
- 양성 쌍을 당기기.
- 부정 쌍을 밀어내기.
이 간단한 방식은 비용이 많이 드는 그래프 연산 없이 협업 필터링 신호를 포착합니다.
추론 및 프론트‑엔드
훈련된 임베딩(128‑dim 벡터)은 정적 바이너리 파일(~150 MB)로 내보내집니다. 브라우저에서:
- 파일은
fetch를 통해 로드되고Float32Array로 디코딩됩니다. - WebAssembly (WASM) 모듈이 FAISS‑like 제품 양자화를 사용해 빠른 코사인‑유사도 검색을 구현합니다.
- UI(순수 HTML + CSS + Vanilla JS)는 사용자가 다음을 할 수 있게 합니다:
- GitHub에 인증 → 별표된 레포지토리 가져오기 → 관심 벡터 계산.
- 스킬 분포 레이더 차트를 시각화합니다.
- 유사한 레포지토리를 검색하거나 프로필을 비교합니다.
이 모든 작업은 클라이언트 측에서 실행되며, 서버나 API 키가 필요하지 않습니다.
결과
- 시스템은 눈에 띄지 않는 라이브러리 대안을 제시합니다 (예: Pandas 팬에게 Polars를 추천).
- 사용자 프로필 유사도 히트맵은 공통 관심사를 가진 개발자 클러스터를 보여줍니다.
데모를 자유롭게 탐색하고, 저장소에 별을 달거나, 제안이 있으면 이슈를 열어 주세요!
버킷별 집계를 위한 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 입니다.
Note: 우리는 이 아이디어를 8년 전 소개한 StarSpace 에 감사의 뜻을 전합니다.
Source: …
고급 방법 vs. 단순성
사용자가 레포지토리에 별을 다는 순서 (즉, “별의 시퀀스”)가 강력한 신호를 제공할 것이라고 자연스럽게 생각했으며, 그래서 Word2Vec‑style sliding‑window 모델을 실험했습니다.
놀랍게도, 가장 단순한 랜덤 분할이 모든 복잡한 접근법보다 성능이 뛰어났습니다.
가능한 이유:
- 타이밍 데이터가 너무 노이즈가 많다.
- 우리는 그로부터 유용한 정보를 추출하지 못했다.
또한 다음을 시도했습니다:
- Hard Negative Miners.
- NTXentLoss와 같은 대체 손실 함수 (MultiSimilarityLoss보다 약 4배 더 많은 메모리를 사용).
- Cross‑Batch Memory (눈에 띄는 이점 없음).
이들 중 어느 것도 원래 베이스라인을 능가하지 못했습니다. 때때로 오컴의 면도날이 진정으로 승리하고, 또 다른 경우에는 면도날이 그냥 무뎌진 것일 뿐입니다.
품질 평가
벡터를 갖는 것은 한 가지이지만, 실제로 좋은가요?
LLM에서 만든 합성 데이터 대신, 우리는 더 우아한 정답을 사용했습니다: Awesome Lists (예: “Awesome Python”, “Awesome React”). 이것들은 유사한 라이브러리를 인간이 큐레이션한 클러스터입니다.
- 리스트들의 README를 다운로드했습니다.
- 동시 출현(어떤 저장소가 함께 나타나는지) 정보를 추출했습니다.
- 휴리스틱 가중치를 적용했습니다.
- NDCG 지표로 순위를 평가했습니다.
이 파이프라인을 통해 손실 함수, 하이퍼파라미터, 샘플링 전략을 공정하게 비교할 수 있었습니다.
Frontend: Showcase & AI‑Assisted Development
데이터 과학 분야에서 10년 경력이 있더라도 저는 프론트엔드 전문가가 아닙니다. 도전 과제는 백엔드 없이 그리고 JS 개발자가 아니면서 복잡한 클라이언트‑사이드 로직을 구축하는 것이었습니다.
모든 프론트엔드 및 “글루” 코드는 AI Coding Agent의 도움을 받아 작성되었습니다.
아키텍처
- Data – 클라이언트는 압축된 임베딩(FP16, ~80 MB)과 메타데이터를 다운로드한 뒤 IndexedDB에 캐시합니다.
- Search (WASM) – USearch 라이브러리의 핵심을 사용하며, WebAssembly로 컴파일되었습니다.
저수준 매직
초기에 사전 계산된 HNSW 인덱스를 시도했지만, 원시 임베딩보다 더 많은 메모리를 사용했습니다.
AI 에이전트는 Exact Search(여전히 WASM) 를 다음과 같이 구현했습니다:
- 저수준
_usearch_exact_search메서드를 노출했습니다. - 메모리를 수동으로 관리하고
_malloc을 통해 버퍼를 할당하며 포인터를 조작하는 워커(coreWorker.js)를 생성했습니다. - 브라우저가 네이티브 FP16을 잘 처리하지 못하기 때문에 실시간 FP16 → FP32 변환기를 추가했습니다.
그 결과, HNSW 인덱스 없이도 약 30만 개의 벡터에 대해 빠른 정확 검색이 가능해졌습니다.
사용자 프로필 및 스킬 레이더
사용자 임베딩
- 클라이언트가 사용자가 별표한 저장소를 조회하기 위해 GitHub API에 요청합니다.
- 해당 저장소들의 임베딩을 가져옵니다.
- 이를 평균내어 평균 벡터를 얻습니다 – 사용자의 관심사를 나타내는 디지털 지문입니다.
이 벡터는 저장소 임베딩과 동일한 메트릭 공간에 존재하므로, “가장 가까운” 라이브러리를 검색할 수 있습니다.
스킬 레이더 (벡터 해석)
- LLM에 10개 카테고리(예: “GenAI”, “Web3”, “System Programming”)마다 20개의 참고 저장소를 생성하도록 프롬프트합니다.
- 이러한 카테고리의 벡터를 구분하기 위해 간단한 **로지스틱 회귀 (선형 프로브)**를 학습시킵니다.
- 브라우저에서 사용자 벡터를 이러한 프로브에 통과시켜 레이더 차트용 확률 점수를 얻습니다.
서버리스 공유
- 유사도 측정: 코사인 유사도, Quantile Transformation을 통해 변환되어 점수가 백분위로 표시됩니다 (예: “95 % Match”는 사용자가 무작위 쌍의 95 %보다 해당 개발자와 더 유사함을 의미합니다).
- 공유 메커니즘: 사용자 벡터를 압축하고 Base64‑인코딩한 뒤 URL 프래그먼트 식별자(
#…)에 직접 삽입합니다. 데이터베이스도, 백엔드도 없이 순수 클라이언트‑사이드 수학만 사용합니다.
결과: 기대 vs 현실
정량적 지표를 넘어, 우리는 “눈으로 보는 테스트”를 수행했습니다.
작동하지 않은 부분
-
벡터 연산 (NLP에서 흔히 보는
King – Man + Woman = Queen와 유사).
가설:Pandas – Python + TypeScript = Danfo.js.
현실: 저장소 벡터 공간은 훨씬 복잡해서 단순한 선형 연산으로는 해석 가능한 결과를 얻을 수 없습니다. -
뚜렷한 클러스터링 – 임베딩이 명확히 구분된 시각적 클러스터를 형성하지 않습니다.
작동한 부분
- 주요 목표를 달성했습니다: 검색이 사용자가 별표한 저장소에 대한 관련 대안을 찾아줍니다.
전체적으로, 이 시스템은 단순하고 서버리스이며 클라이언트‑전용 아키텍처가 대규모 임베딩 공간에서 유용하고 개인화된 추천을 제공할 수 있음을 보여줍니다.
틈새 도구 및 최신 솔루션
LLM과 달리, 종종 가장 인기 있는 솔루션에 편향되는 경우가 많은데, 이 접근 방식은 IT 전문가들의 행동을 기반으로 다음을 밝혀냅니다:
- Niche Tools: 전문가들이 사용하지만 블로그에서는 거의 다루지 않는 라이브러리.
- Fresh Solutions: 최근 인기를 얻었으며 유사한 “별표 패턴”을 공유하는 저장소.
- Local‑first: 모든 것이 클라이언트 장치에서 로컬로 실행됩니다.
미래 비전
현재 데모는 백엔드 없이도 가능한 것을 보여주지만, 상상할 수 있는 다른 많은 사용 사례가 있습니다.
의미 텍스트 검색
텍스트 인코더를 리포지토리 임베딩 공간으로 투사하는 레이어와 함께 훈련시켜, 추상적인 설명으로 도구나 사람을 검색할 수 있게 할 수 있습니다.
GitHub 틴더 (네트워킹)
사용자 벡터를 사용하면 사람들을 매칭할 수 있습니다:
- 멘토 또는 공동 창업자 검색 – 보완적인 스택을 가진 사람을 찾습니다.
- 기여자 발견 – 비슷한 프로젝트에 스타를 찍었지만 아직 여러분의 프로젝트를 보지 않은 개발자를 식별합니다.
- HR‑Tech – 기술적 관심사에 기반해 후보자를 포지션과 매칭합니다.
트렌드 분석
시간 차원을 추가하면 수개월 또는 수년에 걸쳐 떠오르는 기술과 개발자 관심사의 변화를 추적할 수 있습니다.