- BrainTools - https://www.braintools.ru -

Обучение эмбеддингов GitHub репозиториев

Анализ и сравнение профиля из демо

Анализ и сравнение профиля из демо

TL;DR

  • Идея: Люди используют GitHub Stars как закладки – это отличный сигнал, чтобы понять, какие репозитории похожи друг на друга по смыслу.

  • Данные: Обработал 1 ТБ сырых данных из GitHub Archive (BigQuery), получив матрицу интересов 4 миллионов разработчиков.

  • ML: Обучил эмбеддинги для 300к+ репозиториев, используя Metric Learning (EmbeddingBag + MultiSimilarityLoss).

  • Frontend: Сделал client-only демо, которое крутит векторный поиск (KNN) прямо в браузере через WASM, без участия бекенда.

  • Итог: Система находит неочевидные альтернативы библиотек и позволяет сравнивать профили разработчиков.


Персональная мотивация

Доводить идеи до конца обычно сложнее, чем кажется. Часто хватает сил на прототип, но дальше начинается самое трудное: довести качество, написать текст, оформить демо. Этот проект – моя попытка пройти весь путь и закрыть один из таких “висящих хвостов”.

А еще я задумался о природе наших GitHub Stars. Мы привыкли относиться к ним просто как к закладкам “на будущее”. Но на самом деле – это ценный цифровой актив. Это слепок наших профессиональных интересов и навыков. Мне стало интересно: можно ли заставить этот пассивный актив работать? Использовать накопленные знания миллионов разработчиков, чтобы построить систему рекомендаций репозиториев и дать возможность людям сравнивать их интересы.


Концепт

Cluster hypothesis

Хаброчане, которые сейчас читают эту статью, схожи с вами по интересам гораздо сильнее, чем случайно выбранный человек с улицы. В нашей вселенной похожие вещи часто появляются в одинаковых контекстах.

Мы интуитивно чувствуем эти “скрытые” предпочтения. Вы подошли к новому коллеге, который что-то печатает в Vim и смог самостоятельно из него выйти. Наверняка вы уже построили в голове примерный вектор его интересов: с ним можно обсудить патчинг KDE2 под FreeBSD, но вряд ли стоит спрашивать совета про выбор игровой мыши с RGB-подсветкой.

Repo representation

Перенесем это на репозитории. Мы хотим получить пространство, где близкие по смыслу проекты находятся рядом.

Чтобы упростить, представьте себе 2D пространство с осями:

  • Ось X: Данные (Подготовка & Анализ) vs Модели (Обучение [3] & Inference).

  • Ось Y: Local / Single-node vs Big Data / Cluster.

Пространство

Пространство

В реальности нейросеть сама придумывает эти оси (features), и они не всегда интерпретируемы человеком, но математическая суть сохраняется: похожие инструменты стягиваются в кластеры.

Имея такие вектора, мы можем:

  • Искать похожие репозитории через Cosine Similarity (угол между векторами).

  • Получить вектор интересов пользователя, просто усреднив вектора его лайкнутых репозиториев.

  • Сравнивать профили пользователей между собой.


Источники сигнала (Signal Source)

Чтобы получить такие вектора, я использовал гибридный подход.

1. Текст (README.md) – для инициализации

У многих репозиториев есть README. Это отличный источник для “холодного старта”. Я использовал модель Qwen3-Embedding-0.6B [4], которая поддерживает MRL (Matryoshka Representation Learning), оставив только первые 128D самые важные компоненты. Эти вектора я использовал как начальную инициализацию весов обучаемой модели.
Note: Этот шаг добавляет около 10% к итоговому качеству. Чтобы не усложнять код лишними зависимостями, в публичном репозитории я этот шаг пропустил – модель отлично учится и с нуля

2. Матрица Stars – для основного обучения

Текст – это хорошо, но он не показывает, как инструменты используются вместе. Тут в игру вступает коллаборативная фильтрация.

User

Starred repositories

A

Pandas, Dask, SK-Learn, Numpy

B

Vue, React, TypeScript, Vite

Существует много подходов к обучению на таких данных: графовые алгоритмы (LightGCN) или факторизация матриц. Но я выбрал Metric Learning, так как это требует меньше ресурсов GPU и дает гибкость в управлении пространством.


Подготовка данных

Данные брались из публичного датасета GitHub Archive [5] в BigQuery.
Нам нужно было два запроса:

  1. Stars (WatchEvent): Собрать пользователей, у которых от 10 до 800 лайков (фильтруем малоактивных и ботов). Сохраняем порядок лайков.

  2. Meta (PushEvent): Собрать имена репозиториев, даты коммитов и описание.

Суммарно запросы обрабатывают около 1 ТБ данных и почти укладываются в Free Tier BigQuery. На выходе – Parquet-файл с 4 млн пользователей и 2.5 млн уникальных репозиториев.


Обучение векторов

Выбор модели

Я хотел сделать решение максимально легковесным для браузера, поэтому сразу отсек Трансформеры.
Моя модель – это классический torch.nn.EmbeddingBag. По сути, это просто большая таблица repo_id -> vector[128], которая умеет эффективно агрегировать (усреднять) вектора.

Сэмплирование и Loss Function

Как объяснить нейросети, что Pandas и Numpy – это “близкие” вещи?

Basic sampling method

Я разбил список лайков каждого пользователя на две случайных непересекающихся группы, например:

Пользователь

Группа

Репо в группе

Среднее(Embeddings в группе)

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]

Мы обучаем эмбеддинги так, чтобы одновременно соблюдались два условия:

  1. Группы одного пользователя были как можно ближе (A1 <-стягивались-> A2).

  2. Группы разных пользователей были как можно дальше (B1 <-отталкивались-> A2).

Очевидно модель не сможет идеально выполнить эти два условия одновременно для всех пользователей. Но балансируя между этими двумя силами градиентный спуск пытается подобрать такие вектора для самих репозиториев чтобы ошибка [6] была минимальной.

Advanced methods vs Simplicity

Логично [7] было предположить, что в последовательности лайков (то, в каком порядке вы ставите звезды) есть сильный сигнал. Я пробовал подходы а-ля Word2Vec (скользящее окно), сложные Hard Negative Miners.
Но, как ни странно, простейшее случайное разбиение сработало лучше всего. Вероятно, данные о времени слишком шумные, либо мне не удалось извлечь из этого пользу. Иногда в ML простые эвристики побеждают сложные архитектуры.

В качестве функции потерь лучше всего себя показала MultiSimilarityLoss [8] из библиотеки pytorch-metric-learning, хотя сравнивались и другие альтернативы.


Оценка качества

У нас есть вектора, но как понять, хорошие ли они?
Можно использовать синтетику от LLM, но я нашел более элегантный Ground Truth – Awesome Lists. На GitHub существуют тысячи репозиториев формата “Awesome Python”, “Awesome React”. Это уже готовые, модерируемые людьми кластеры похожих библиотек.
Я скачал README этих списков, нашел коллокации (какие репо встречаются вместе), хитро взвесил силу связи и использовал метрику NDCG для оценки ранжирования. Это позволило честно сравнивать разные лоссы, гиперпараметры и методы семплирования.


Frontend: Showcase и AI-разработка

Несмотря на мой 10-летний опыт [9] в Data Science, во фронтенде я, мягко говоря, не эксперт. Поэтому челлендж был таким: сделать сложную клиентскую логику без бекенда, не будучи JS-разработчиком.

Весь код фронтенда и “клея” был написан с помощью Codex.

Архитектура

  1. Данные: Клиент загружает сжатые эмбеддинги (FP16, ~80 MB) и метадату, кешируя их в IndexedDB.

  2. Поиск (WASM): Используется ядро библиотеки USearch [10], скомпилированное в WebAssembly.

Низкоуровневая магия

Изначально я хотел использовать предрасчитанный HNSW-индекс, но он занимал больше памяти [11] чем чистые эмбединги.
Поэтому я попросил агента попробовать сделать Exact Search (все еще используя WASM).
Агент нашел низкоуровневые методы _usearch_exact_search и сгенерировал воркер (coreWorker.js), который вручную управляет памятью, аллоцирует буферы через _malloc и гоняет указатели.
Браузеры пока плохо умеют в нативный FP16, поэтому агенту так же пришлось написать конвертер FP16 -> FP32 на лету при чтении векторов. Для меня это выглядит как магия, но это работает быстро даже на 300к векторов, даже без HNSW-индексов.

Замыкаем агентный цикл через Chrome MCP

Особую роль сыграл Chrome MCP [12]. Без него использование LLM для веб-разработки выглядит как пинг-понг: “Даешь агенту задание” -> “Проверяешь нормально ли это работает в браузере” -> “Видишь ошибки или недочеты и просишь агента исправить”. И логичной идеей выглядит замкнуть цикл чтобы агент сам мог посмотреть на результатысвоей работы (например WASM ошибки памяти).


Результаты: Ожидания vs Реальность

Помимо метрик, я проверял модель “глазами”.

Что не получилось:
Я надеялся на красивую векторную арифметику, как в NLP (King - Man + Woman = Queen).
Гипотеза: Pandas - Python + TypeScript = Danfo.js.
Реальность: Это не сработало. Векторное пространство репозиториев оказалось устроено сложнее, и простые линейные операции там не дают такой красивой интерпретации.

Помимо этого я надеялся на красивую кластерную структуру эмбедингов, но к сожалению она тоже не выглядит слишком выраженной.

Что получилось:
Главная цель достигнута – поиск находит альтернативы, о которых я не знал, но семантически релевантны.
В отличие от LLM, которые часто имеют bias в сторону самых популярных решений, этот подход, основанный на поведении [13] IT-специалистов, выкапывает:

  1. Нишевые инструменты: Библиотеки, которые используют профи, но о которых мало пишут в блогах.

  2. Свежие решения: Репозитории, которые набрали популярность недавно и имеют схожий паттерн “звездообразования”.

  3. Local-first: Все работает локально на клиентских устройствах.

Final words

После нескольких лет работы с LLM мне хотелось сделать шаг назад, “тряхнуть стариной” и вспомнить классические подходы. Так же хотелось показать что “старые добрые” эмбеддинги все еще могут быть полезными в мире где все в наше время кажется крутиться вокруг GenAI.

Автор: Puzer

Источник [14]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/24026

URLs in this post:

[1] Исходные коды, эмбединги и датасет: https://github.com/Puzer/github-repo-embeddings

[2] Демо, 100% client-side для получения рекомендаций по вашим GitHub Stars: https://puzer.github.io/github_recommender

[3] Обучение: http://www.braintools.ru/article/5125

[4] Qwen3-Embedding-0.6B: https://github.com/QwenLM/Qwen3-Embedding/blob/44548aa5f0a0aed1c76d64e19afe47727a325b8f/examples/qwen3_embedding_transformers.py#L54

[5] GitHub Archive: https://www.gharchive.org/#bigquery

[6] ошибка: http://www.braintools.ru/article/4192

[7] Логично: http://www.braintools.ru/article/7640

[8] MultiSimilarityLoss: https://kevinmusgrave.github.io/pytorch-metric-learning/losses/#multisimilarityloss

[9] опыт: http://www.braintools.ru/article/6952

[10] USearch: https://github.com/unum-cloud/USearch

[11] памяти: http://www.braintools.ru/article/4140

[12] Chrome MCP: https://github.com/ChromeDevTools/chrome-devtools-mcp

[13] поведении: http://www.braintools.ru/article/9372

[14] Источник: https://habr.com/ru/articles/983080/?utm_campaign=983080&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100