- BrainTools - https://www.braintools.ru -
Идея: Люди используют GitHub Stars как закладки – это отличный сигнал, чтобы понять, какие репозитории похожи друг на друга по смыслу.
Данные: Обработал 1 ТБ сырых данных из GitHub Archive (BigQuery), получив матрицу интересов 4 миллионов разработчиков.
ML: Обучил эмбеддинги для 300к+ репозиториев, используя Metric Learning (EmbeddingBag + MultiSimilarityLoss).
Frontend: Сделал client-only демо, которое крутит векторный поиск (KNN) прямо в браузере через WASM, без участия бекенда.
Итог: Система находит неочевидные альтернативы библиотек и позволяет сравнивать профили разработчиков.
Доводить идеи до конца обычно сложнее, чем кажется. Часто хватает сил на прототип, но дальше начинается самое трудное: довести качество, написать текст, оформить демо. Этот проект – моя попытка пройти весь путь и закрыть один из таких “висящих хвостов”.
А еще я задумался о природе наших GitHub Stars. Мы привыкли относиться к ним просто как к закладкам “на будущее”. Но на самом деле – это ценный цифровой актив. Это слепок наших профессиональных интересов и навыков. Мне стало интересно: можно ли заставить этот пассивный актив работать? Использовать накопленные знания миллионов разработчиков, чтобы построить систему рекомендаций репозиториев и дать возможность людям сравнивать их интересы.
Хаброчане, которые сейчас читают эту статью, схожи с вами по интересам гораздо сильнее, чем случайно выбранный человек с улицы. В нашей вселенной похожие вещи часто появляются в одинаковых контекстах.
Мы интуитивно чувствуем эти “скрытые” предпочтения. Вы подошли к новому коллеге, который что-то печатает в Vim и смог самостоятельно из него выйти. Наверняка вы уже построили в голове примерный вектор его интересов: с ним можно обсудить патчинг KDE2 под FreeBSD, но вряд ли стоит спрашивать совета про выбор игровой мыши с RGB-подсветкой.
Перенесем это на репозитории. Мы хотим получить пространство, где близкие по смыслу проекты находятся рядом.
Чтобы упростить, представьте себе 2D пространство с осями:
Ось X: Данные (Подготовка & Анализ) vs Модели (Обучение [3] & Inference).
Ось Y: Local / Single-node vs Big Data / Cluster.
В реальности нейросеть сама придумывает эти оси (features), и они не всегда интерпретируемы человеком, но математическая суть сохраняется: похожие инструменты стягиваются в кластеры.
Имея такие вектора, мы можем:
Искать похожие репозитории через Cosine Similarity (угол между векторами).
Получить вектор интересов пользователя, просто усреднив вектора его лайкнутых репозиториев.
Сравнивать профили пользователей между собой.
Чтобы получить такие вектора, я использовал гибридный подход.
У многих репозиториев есть README. Это отличный источник для “холодного старта”. Я использовал модель Qwen3-Embedding-0.6B [4], которая поддерживает MRL (Matryoshka Representation Learning), оставив только первые 128D самые важные компоненты. Эти вектора я использовал как начальную инициализацию весов обучаемой модели.
Note: Этот шаг добавляет около 10% к итоговому качеству. Чтобы не усложнять код лишними зависимостями, в публичном репозитории я этот шаг пропустил – модель отлично учится и с нуля
Текст – это хорошо, но он не показывает, как инструменты используются вместе. Тут в игру вступает коллаборативная фильтрация.
|
User |
Starred repositories |
|---|---|
|
A |
Pandas, Dask, SK-Learn, Numpy |
|
B |
Vue, React, TypeScript, Vite |
Существует много подходов к обучению на таких данных: графовые алгоритмы (LightGCN) или факторизация матриц. Но я выбрал Metric Learning, так как это требует меньше ресурсов GPU и дает гибкость в управлении пространством.
Данные брались из публичного датасета GitHub Archive [5] в BigQuery.
Нам нужно было два запроса:
Stars (WatchEvent): Собрать пользователей, у которых от 10 до 800 лайков (фильтруем малоактивных и ботов). Сохраняем порядок лайков.
Meta (PushEvent): Собрать имена репозиториев, даты коммитов и описание.
Суммарно запросы обрабатывают около 1 ТБ данных и почти укладываются в Free Tier BigQuery. На выходе – Parquet-файл с 4 млн пользователей и 2.5 млн уникальных репозиториев.
Я хотел сделать решение максимально легковесным для браузера, поэтому сразу отсек Трансформеры.
Моя модель – это классический torch.nn.EmbeddingBag. По сути, это просто большая таблица repo_id -> vector[128], которая умеет эффективно агрегировать (усреднять) вектора.
Как объяснить нейросети, что Pandas и Numpy – это “близкие” вещи?
Я разбил список лайков каждого пользователя на две случайных непересекающихся группы, например:
|
Пользователь |
Группа |
Репо в группе |
Среднее(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] |
Мы обучаем эмбеддинги так, чтобы одновременно соблюдались два условия:
Группы одного пользователя были как можно ближе (A1 <-стягивались-> A2).
Группы разных пользователей были как можно дальше (B1 <-отталкивались-> A2).
Очевидно модель не сможет идеально выполнить эти два условия одновременно для всех пользователей. Но балансируя между этими двумя силами градиентный спуск пытается подобрать такие вектора для самих репозиториев чтобы ошибка [6] была минимальной.
Логично [7] было предположить, что в последовательности лайков (то, в каком порядке вы ставите звезды) есть сильный сигнал. Я пробовал подходы а-ля Word2Vec (скользящее окно), сложные Hard Negative Miners.
Но, как ни странно, простейшее случайное разбиение сработало лучше всего. Вероятно, данные о времени слишком шумные, либо мне не удалось извлечь из этого пользу. Иногда в ML простые эвристики побеждают сложные архитектуры.
В качестве функции потерь лучше всего себя показала MultiSimilarityLoss [8] из библиотеки pytorch-metric-learning, хотя сравнивались и другие альтернативы.
У нас есть вектора, но как понять, хорошие ли они?
Можно использовать синтетику от LLM, но я нашел более элегантный Ground Truth – Awesome Lists. На GitHub существуют тысячи репозиториев формата “Awesome Python”, “Awesome React”. Это уже готовые, модерируемые людьми кластеры похожих библиотек.
Я скачал README этих списков, нашел коллокации (какие репо встречаются вместе), хитро взвесил силу связи и использовал метрику NDCG для оценки ранжирования. Это позволило честно сравнивать разные лоссы, гиперпараметры и методы семплирования.
Несмотря на мой 10-летний опыт [9] в Data Science, во фронтенде я, мягко говоря, не эксперт. Поэтому челлендж был таким: сделать сложную клиентскую логику без бекенда, не будучи JS-разработчиком.
Весь код фронтенда и “клея” был написан с помощью Codex.
Данные: Клиент загружает сжатые эмбеддинги (FP16, ~80 MB) и метадату, кешируя их в IndexedDB.
Поиск (WASM): Используется ядро библиотеки USearch [10], скомпилированное в WebAssembly.
Изначально я хотел использовать предрасчитанный HNSW-индекс, но он занимал больше памяти [11] чем чистые эмбединги.
Поэтому я попросил агента попробовать сделать Exact Search (все еще используя WASM).
Агент нашел низкоуровневые методы _usearch_exact_search и сгенерировал воркер (coreWorker.js), который вручную управляет памятью, аллоцирует буферы через _malloc и гоняет указатели.
Браузеры пока плохо умеют в нативный FP16, поэтому агенту так же пришлось написать конвертер FP16 -> FP32 на лету при чтении векторов. Для меня это выглядит как магия, но это работает быстро даже на 300к векторов, даже без HNSW-индексов.
Особую роль сыграл Chrome MCP [12]. Без него использование LLM для веб-разработки выглядит как пинг-понг: “Даешь агенту задание” -> “Проверяешь нормально ли это работает в браузере” -> “Видишь ошибки или недочеты и просишь агента исправить”. И логичной идеей выглядит замкнуть цикл чтобы агент сам мог посмотреть на результатысвоей работы (например WASM ошибки памяти).
Помимо метрик, я проверял модель “глазами”.
Что не получилось:
Я надеялся на красивую векторную арифметику, как в NLP (King - Man + Woman = Queen).
Гипотеза: Pandas - Python + TypeScript = Danfo.js.
Реальность: Это не сработало. Векторное пространство репозиториев оказалось устроено сложнее, и простые линейные операции там не дают такой красивой интерпретации.
Помимо этого я надеялся на красивую кластерную структуру эмбедингов, но к сожалению она тоже не выглядит слишком выраженной.
Что получилось:
Главная цель достигнута – поиск находит альтернативы, о которых я не знал, но семантически релевантны.
В отличие от LLM, которые часто имеют bias в сторону самых популярных решений, этот подход, основанный на поведении [13] IT-специалистов, выкапывает:
Нишевые инструменты: Библиотеки, которые используют профи, но о которых мало пишут в блогах.
Свежие решения: Репозитории, которые набрали популярность недавно и имеют схожий паттерн “звездообразования”.
Local-first: Все работает локально на клиентских устройствах.
После нескольких лет работы с 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
Нажмите здесь для печати.