Training an LLM-RecSys Hybrid for Steerable Recs with Semantic IDs
Юджин Ян описывает эксперимент по обучению гибрида LLM и рекомендательной системы на основе семантических ID — иерархических токенов, заменяющих хеш-идентификаторы товаров. Используя данные Amazon Reviews 2023 (66k товаров, 78,6k пользовательских последовательностей), он обучает RQ-VAE для генерации семантических ID, а затем дообучает Qwen3-8B на 4,2 млн диалоговых примеров, чтобы модель стала «двуязычной» — понимала и английский, и семантические ID. Сравнение SASRec на обычных ID и на семантических ID показало, что базовая версия точнее, но семантическая умеет работать с cold-start. Главное открытие — с моделью можно общаться: она объясняет рекомендации, учитывает ограничения по платформе/жанру и придумывает названия для бандлов. Всё это без отдельных классификаторов намерений, роутеров или retrieval — единый интерфейс объединяет поиск, рекомендации и чат.
Обучение гибрида LLM-RecSys для управляемых рекомендаций на семантических ID
Когда я впервые услышал про Semantic IDs, меня это зацепило. Идея проста: вместо случайных хеш-ID для видео, песен или товаров можно использовать семантически осмысленные токены, которые LLM может понимать на родном языке. Я задумался: а можно ли обучить гибрид LLM и рекомендера на тех богатых поведенческих данных, благодаря которым современные рекомендательные системы так эффективны?
К моему удивлению — можно! В итоге получается языковая модель, которая умеет разговаривать одновременно на английском и на «языке» ID товаров — не через retrieval или другие инструменты, а как единая «двуязычная» модель, где товары (то есть семантические ID) являются частью её словаря. Как рекомендательная модель, она может предлагать товары на основе истории взаимодействий. Но главное удивление и открытие новых возможностей — это когда я обнаружил, что с моделью можно просто общаться в чате, чтобы управлять её рекомендациями, и она способна рассуждать о своих выборах, давать объяснения и креативно придумывать названия для бандлов товаров.
Вот демо-видео. И вот код для подготовки данных, обучения модели и общения с ней. (Замечание: это небольшая модель с очень простой донастройкой, поэтому промптинг играет большую роль. Также она не такая универсальная и устойчивая, как большинство LLM, из-за ограниченного файнтюнинга.)
Стоп, а зачем это нужно?
Это объединяет лучшее из recsys и языкового моделирования. С одной стороны, у языковых моделей есть знание мира, и они умеют красноречиво рассуждать о товарах, но они не знают о нашем каталоге. К тому же их рекомендации шаблонны и страдают от popularity bias. С другой стороны, поисковые и рекомендательные модели обучаются на нашем каталоге и миллиардах пользовательских взаимодействий. Они отлично предсказывают, что пользователь кликнет или купит дальше, но ими нельзя управлять через естественный язык, и они не могут объяснять свои выборы. (Объединяет их то, что обе обучаются на последовательностях.)
Чтобы это сделать, мы сначала расширяем словарь языковой модели токенами семантических ID вида <|sid_0|>, <|sid_1|>, <|sid_2|> и т. д. Этими токенами представляется каталог. Затем мы применяем continued pretraining, чтобы научить модель связям между семантическими ID и каталогом, а потом дополнительно дообучаем её на последовательностях пользовательского поведения. В совокупности это учит модель давать рекомендации — из нашего каталога — на основе предпочтений пользователя.
Хотя этот гибрид LLM-рекомендера может не дотягивать до точности специализированного многоступенчатого recsys, он даёт новую возможность: управляемость и рассуждения над рекомендациями. В итоге получается единый опыт поиска, рекомендаций и чата, позволяющий пользователям находить то, что им нужно в нашем каталоге, просто запросив это словами.
User: Мне нравятся игры про животных и милые. <|rec|> Модель интерпретирует запрос на естественном языке («про животных и милые») и генерирует семантические ID релевантных игр. <|sid_start|><|sid_173|><|sid_324|><|sid_764|><|sid_768|><|sid_end|>, <|sid_start|><|sid_201|><|sid_397|><|sid_701|><|sid_768|><|sid_end|>, <|sid_start|><|sid_173|><|sid_305|><|sid_670|><|sid_768|><|sid_end|> Assistant: «Animal Crossing: New Leaf», «DISNEY INFINITY Starter Pack 3DS», «Nintendogs + Cats: Golden Retriever and New Friends»
Данные и модели
Поиск обучающих данных с метаданными товаров и пользовательскими последовательностями
Мы будем использовать данные из категории Video Games Amazon Reviews 2023 (Hou et al., 2024). В данных есть богатые метаданные товаров, и из них можно построить пользовательские последовательности взаимодействий.
Датасет содержит 137 тыс. товаров, у каждого есть поля для названия, описания, характеристик, категории, информации о магазине, рейтингов, цен и т. д. Оставив только товары с названиями длиннее 20 символов и описаниями длиннее 100 символов, мы получили 66 тыс. товаров. Поведенческие данные содержат 737 тыс. записей. Из них я построил 79 тыс. последовательностей покупок пользователей, в каждой минимум по три валидных товара. Средняя длина таких последовательностей — 6,5 элементов.
Я также рассматривал данные Amazon KDD Cup 2023 (Amazon, 2023). Там 500 тыс. товаров и упор на последовательные поведенческие данные. Однако многоязычность добавляла сложности, а отсутствие поля категории товара затрудняло работу. В итоге я остановился на Amazon Reviews, чтобы начать просто и экономно расходовать compute-бюджет.
Семантические ID из RQ-VAE
Semantic IDs (Rajput et al., 2023; Singh et al., 2023) — это иерархические представления, которые кодируют товары в последовательность токенов, заменяя эмбеддинги или хеш-ID. В отличие от обычного ID товара (B0040JHNQG), у которого нет собственного смысла, семантический ID (<|sid_0|><|sid_256|><|sid_512|><|sid_768|>) кодирует информацию о товаре. В результате обучения похожие товары естественным образом получают общие префиксы, образуя древовидную структуру, где каждый уровень ID представляет всё более детальную информацию о товаре.
Иерархическая структура семантических ID для видео о еде (источник)
Residual Quantized Variational Autoencoders (RQ-VAE; Zeghidour et al., 2021, Lee et al., 2022) — это то, что мы используем для преобразования непрерывных эмбеддингов в дискретные семантические ID. Мы начинаем с того, что кодируем метаданные товара (например, название, описание) в эмбеддинг. Затем RQ-VAE с помощью иерархической квантизации преобразует этот эмбеддинг в последовательность дискретных токенов.
Как RQ-VAE превращают эмбеддинги в семантические ID (источник)
Это итеративный процесс. На первом уровне модель находит вектор в первом codebook, ближайший к входному эмбеддингу; этот вектор становится первым токеном семантического ID. Затем модель вычисляет ошибку квантизации, или остаток, вычитая выбранный вектор codebook из входного эмбеддинга. На втором уровне она находит вектор во втором codebook, ближайший к этому остатку, и так получает второй токен. Процесс повторяется на каждом уровне, и каждый следующий шаг фиксирует всё более тонкие детали, которые упустили предыдущие уровни.
Функция потерь RQ-VAE заслуживает обсуждения, поскольку её понимание — ключ к генерации качественных семантических ID. Общая loss состоит из двух основных компонентов:
Первый компонент, reconstruction loss, гарантирует, что декодер сможет точно восстановить исходный эмбеддинг товара ($x$) из итогового квантованного представления ($\hat{x}$). Это стандартная среднеквадратичная ошибка:
Второй компонент, quantization loss, измеряет, насколько хорошо векторы codebook соответствуют остаткам, которые выдаёт энкодер. Он состоит из двух слагаемых:
Первое слагаемое ($|\text{sg}[r_i] - e_{c_i}|^2)$, codebook loss, отвечает за обучение эмбеддингов codebook. Оно измеряет расстояние между остатком энкодера ($r_i$) и выбранным вектором codebook ($e_{c_i}$). Stop-gradient применяется к выходу энкодера ($\text{sg}[r_i]$), чтобы трактовать остатки как фиксированную цель. Таким образом, градиенты текут только в вектор codebook, подтягивая его ближе к выходу энкодера.
Второе слагаемое ($\beta|r_i - \text{sg}[e_{c_i}]|^2$), commitment loss, отвечает за обучение энкодера. Оно измеряет то же расстояние, но stop-gradient теперь применяется к вектору codebook ($\text{sg}[e_{c_i}]$). Это останавливает обновления codebook и заставляет энкодер выдавать выходы — то есть «коммититься» — к векторам, уже присутствующим в codebook. Гиперпараметр $\beta$ управляет силой этого штрафа за commitment.
# Pytorch-код функции потерь (без рекурсивного цикла) reconstruction_loss = F.mse_loss(x, x_reconstructed) codebook_loss = F.mse_loss(residual.detach(), codebook_vector) commitment_loss = F.mse_loss(residual, codebook_vector.detach()) quantization_loss = codebook_loss + commitment_weight * commitment_loss total_loss = recon_loss + quantization_loss
В этом процессе RQ-VAE производит семантический ID как последовательность токенов — по одному с каждого уровня квантизации. Поскольку похожие товары делят общие префиксы, языковые модели лучше понимают связи между товарами, что также полезно для tree-based retrieval.
# Как иерархически кодировать эмбеддинги в семантические ID def encode_to_semantic_ids(self, x: Tensor) -> Tensor: with torch.no_grad(): residual = self.encode(x) indices_list = [] for vq_layer in self.vq_layers: vq_output = vq_layer(residual) indices_list.append(vq_output.indices) residual = residual - vq_output.quantized return torch.stack(indices_list, dim=-1)
Тем не менее, одна практическая сложность в том, что это не гарантирует уникального ID для каждого товара. В моих экспериментах с трёхуровневым codebook (по 256 кодов на уровне) мы получили коллизии примерно у 10% из 66 тыс. товаров. Чтобы это решить, я добавил четвёртый уровень, где я приписывал к каждому ID последовательно увеличивающийся токен, гарантируя уникальную идентификацию каждого товара.
SASRec, Qwen3-Embedding-0.6B и Qwen3-8b
Помимо RQ-VAE, мы используем ещё три модели. Сначала мы обучаем SASRec на семантических ID, чтобы проверить их качество и сравнить с baseline SASRec на обычных item ID. Затем мы используем Qwen3-Embedding-0.6B для кодирования метаданных товаров в эмбеддинги. Наконец, мы дообучаем Qwen3-8B, чтобы она понимала и рекомендовала товары через семантические ID.
SASRec (Kang & McAuley, 2018) — это последовательный рекомендер, вдохновлённый архитектурой Transformer. Он кодирует историю взаимодействий пользователя и через self-attention взвешивает наиболее релевантные прошлые элементы, чтобы предсказать следующий. Это позволяет модели выучивать долгосрочные зависимости в поведении пользователя и тем самым превосходить более старые рекуррентные модели вроде RNN и GRU, оставаясь эффективнее благодаря возможности параллелизации.
Qwen3-Embedding-0.6B (Zhang et al., 2025) — часть серии эмбеддинг-моделей, доступных в размерах 0,6B, 4B и 8B. Они обучаются в несколько этапов: предобучение на синтетических данных, затем supervised-файнтюнинг и model merging для устойчивости. Модель 8B показывает SOTA на бенчмарке MTEB Multilingual.
Qwen3-8B (Yang et al., 2025) — плотная языковая модель из семейства Qwen3. Несмотря на то, что это одна из меньших моделей, её post-training оптимизирован через strong-to-weak distillation от Qwen3-235B-A22B и Qwen3-32B. Это делает Qwen3-8B относительно мощной для своего размера: она обходит более крупные модели предыдущего поколения, такие как Qwen2.5-14B, более чем на половине оцененных бенчмарков, особенно в STEM и коде. Как и другие модели серии Qwen3, Qwen3-8B имеет два режима: thinking и non-thinking.
Очистка данных и построение пользовательских последовательностей
Сначала мы готовим метаданные товаров, чтобы обеспечить качественный вход для модели семантических ID. Мы исключаем товары с названиями короче 20 символов или описаниями короче 100 символов. Это сократило количество товаров вдвое — со 137 тыс. до 66 тыс. уникальных позиций.
Затем мы чистим описания товаров с помощью Gemini 2.5 Flash Lite (Comanici et al., 2025): чиним обрезанные предложения, удаляем HTML и сокращаем многословие. Это уменьшило среднюю длину описания вдвое — с 1 038 до 538 символов. Аналогично мы убираем рекламный текст и стандартизируем форматирование названий, превращая многословные листинги вроде «NEW! LIMITED! Sega Saturn RGB SCART LEAD CABLE…» в чистое «Sega Saturn RGB SCART Cable».
Затем мы обогатили данные, извлекая структурированные метаданные: тип товара (Game, Hardware, Accessory), платформа (PS4, Xbox, Wii), жанр (Roguelike, Soulslike, Metroidvania), тип железа, бренд, режимы мультиплеера и т. д. Покрытие получилось 98% по платформе, 78% по бренду и 51% по жанру.
Наконец, для построения пользовательских последовательностей мы дедуплицировали пользователей и построили истории взаимодействий — получилось 91,5 тыс. последовательностей. Из них мы исключили товары без метаданных, а затем отфильтровали последовательности короче трёх элементов. Также мы обрезали последовательности по максимальной длине 100 элементов (обрезаны были только 28 последовательностей). В итоге датасет составил 78,6 тыс. последовательностей с медианной длиной 5 элементов и средней 6,5.
Обучение RQ-VAE для выдачи семантических ID
Для эмбеддингов товаров мы используем Qwen3-Embedding-0.6B. Она поддерживает настройку входной инструкции под разные задачи, и мы добавляем префикс «Given a product description, generate a semantic embedding that captures its key features and characteristics». На выходе получаем 1024-мерные эмбеддинги через last token pooling, нормализуем по L2 и сохраняем.
RQ-VAE состоит из энкодера, трёх уровней квантизации по 256 кодов и симметричного декодера. Для стабильности обучения мы применяем rotation trick (Fifty et al., 2025) вместо Straight-Through Estimator (для вычисления градиента $L_\text{recon}$). К другим оптимизациям относятся инициализация codebook через k-means кластеризацию, сброс неиспользуемых кодов и большой batch size. Я пробовал и несколько техник, которые не помогли: обновление codebook через EMA и остановка градиентов в декодере.
Обученный RQ-VAE дал 89% уникальных семантических ID по 66 тыс. товарам на трёх уровнях квантизации. Чтобы разрешить оставшиеся коллизии, я приписал четвёртый токен, присваивающий уникальный последовательный ID (0, 1, 2, …) всем товарам, у которых совпадают первые три кода. Это гарантирует, что у каждого товара уникальный четырёхчастный семантический ID.
Я провёл несколько десятков экспериментов, чтобы лучше понять RQ-VAE и их выходные семантические ID, а также подобрать оптимальную конфигурацию модели. Вот ключевые наблюдения.
Сначала я экспериментировал с весом commitment $\beta$, который балансирует точность реконструкции и commitment к codebook. Я пробовал значения 0,25 (жёлтый), 0,5 (оранжевый) и 1,0 (красный) и обнаружил, что более высокое значение $\beta$ = 1,0 даёт больше уникальных ID, но и самую высокую валидационную loss. Хотя более низкое $\beta$ = 0,25 даёт чуть больше уникальных ID, $\beta$ = 0,5 имеет самую низкую валидационную loss. Поэтому на этом датасете я обучал последующие RQ-VAE с $\beta$ = 0,5. (Замечание: это отличается от статей о Semantic ID, в которых использовали $\beta$ = 0,25.)
Кривые при beta = 0,25 (жёлтый), 0,5 (оранжевый) и 1,0 (красный)
Отступление: краткий разбор того, что означает каждая из этих метрик:
loss/reconstruction: насколько хорошо RQ-VAE восстанавливает исходные эмбеддинги товаров после сжатия в семантические ID и обратного разжатия. loss/vq: суммарная codebook и commitment loss по всем уровням. Обеспечивает близость выходов энкодера к векторам codebook и адаптацию векторов codebook к выходам энкодера. Важна для сжатия эмбеддингов в осмысленные семантические ID. loss/total: сумма reconstruction loss и VQ loss для отслеживания общего прогресса. loss/validation: общая loss на отложенной валидационной выборке для контроля обобщающей способности. metrics/avg_residual_norm: размер «остаточного» residual после всех уровней квантизации. Меньше residual = codebook лучше захватывает входной эмбеддинг. metrics/unique_id_proportion: % товаров с уникальными ID в батче. Контролирует codebook collapse. Выше = лучше способность различать товары.
Я также экспериментировал с более мелким энкодером и эффектом от очистки метаданных. Более мелкий энкодер (зелёный) дал результат хуже — выросла валидационная loss и снизилось число уникальных ID. Зато вложения в очистку данных окупились (синий): получилась модель с самыми низкими reconstruction и валидационной loss и самой высокой долей уникальных ID. Именно этот RQ-VAE я и взял.
Кривые при beta = 0,5 (оранжевый), более мелком энкодере (зелёный) и чистых данных (синий)
Ещё один способ оценить RQ-VAE — посмотреть на использование codebook. Относительно равномерное использование всех кодов говорит о том, что модель пользуется всей своей выразительной способностью. Финальный RQ-VAE это хорошо демонстрирует: по всем трём уровням квантизации использование распределено равномерно и с низкой дисперсией, как видно на гистограмме ниже.
Пример RQ-VAE с хорошо распределённым использованием codebook
Напротив, плохо сошедшийся RQ-VAE будет иметь разреженное и сильно сконцентрированное использование кодов. Гистограмма ниже показывает этот режим отказа: несколько кодов используются чрезмерно, а большая часть codebook игнорируется.
Пример RQ-VAE с плохо распределённым использованием codebook
С обученным RQ-VAE мы кодируем все эмбеддинги товаров в формат семантических ID, например <|sid_start|><|sid_191|><|sid_260|><|sid_716|><|sid_768|><|sid_end|>. Затем мы переводим 78,6 тыс. пользовательских последовательностей покупок из обычных ID в последовательности семантических ID. Эти последовательности — обучающие данные, которыми мы и проверяем качество ID на baseline SASRec, и дообучаем Qwen3-8B.
Обучение SASRec на обычных item ID vs. на семантических ID
Чтобы убедиться, что наши семантические ID действительно захватывают осмысленные связи между товарами, мы обучаем два варианта SASRec — baseline на обычных item ID и вариант на семантических ID — и сравниваем их.
Baseline SASRec следует стандартной архитектуре. Он воспринимает каждый товар как отдельную атомарную единицу, обучая для него эмбеддинг с нуля. Это чисто на поведенческих паттернах. Модель использует 2 каузальных self-attention блока, 64-мерное скрытое состояние и обучается на задаче отличить следующий элемент в последовательности от случайно семплированных негативных элементов через binary cross-entropy (BCE) loss.
# Predict-функция baseline SASRec def predict(self, input_ids: torch.Tensor, candidate_ids: torch.Tensor) -> torch.Tensor: """Predict scores for candidate items. Args: input_ids: Item sequences [batch_size, seq_length] candidate_ids: Candidate items to score [batch_size, num_candidates] Returns: Scores for each candidate [batch_size, num_candidates] """ # Получаем представления последовательности hidden_states = self.forward(input_ids) # [B, T, H] # Берём только последнее hidden-состояние для предсказания final_hidden = hidden_states[:, -1, :] # [B, H] # Получаем эмбеддинги кандидатов candidate_embs = self.item_emb(candidate_ids) # [B, C, H] # Считаем скоры через скалярное произведение scores = torch.bmm(candidate_embs, final_hidden.unsqueeze(-1)).squeeze(-1) # [B, C] return scores
В отличие от него, Semantic ID SASRec переформулирует рекомендацию как условную генеративную задачу. Вместо скоринга товаров, его цель — сгенерировать четырёхчастный семантический ID следующего элемента, токен за токеном. Это требует более крупной архитектуры: 4 transformer-блока и скрытое состояние 384-dim. В отличие от статьи TIGER, где используется T5 encoder-decoder, этот вариант SASRec — decoder-only, что делает сравнение с baseline более прямым и честным. Поскольку мы используем семантические ID, у нас вместо эмбеддинга на каждый из 66 тыс. товаров — всего 1 024 токен-уровневых эмбеддинга, по 256 токенов на уровень семантического ID.
# Predict-функция Semantic ID SASRec def predict_next_item(self, input_ids: torch.Tensor, teacher_forcing: bool = True, target_tokens: Optional[torch.Tensor] = None) -> Dict[str, torch.Tensor]: """Predict the next item's semantic ID tokens sequentially. Args: input_ids: Token sequences [batch_size, seq_length * num_levels] teacher_forcing: Use ground truth for conditioning during training target_tokens: Ground truth tokens for next item [batch_size, num_levels] Returns: Dictionary with logits for each level """ hidden_states = self.forward(input_ids) # [B, T*L, H] # Берём представление в последней позиции как контекст для всех предыдущих элементов last_hidden = hidden_states[:, -1, :] # [B, H] predictions = {} # Последовательная генерация: предсказываем каждый уровень с учётом предыдущих for level in range(self.num_levels): if level == 0: # Уровень 0: предсказываем напрямую из представления последовательности context = last_hidden else: # Уровни 1-3: обуславливаем на ранее предсказанных/истинных токенах if teacher_forcing and target_tokens is not None: # Используем ground truth для предыдущих уровней при обучении prev_tokens = target_tokens[:, :level] # [B, level] else: # Используем предсказанные токены при инференсе prev_tokens = self._sample_from_predictions(predictions, level) prev_embeds = self.token_emb(prev_tokens) # [B, level, input_dim] prev_embeds_projected = self.input_projection(prev_embeds) # [B, level, H] prev_context = prev_embeds_projected.mean(dim=1) # [B, H] # Объединяем с контекстом последовательности combined = torch.cat([last_hidden, prev_context], dim=-1) # [B, 2*H] context = self.context_combiners[level - 1](combined) # [B, H] # Предсказываем текущий уровень logits = self.level_heads[level](context) # [B, codebook_size] predictions[f"logits_l{level}"] = logits return predictions
Этот генеративный подход меняет и обучение, и оценку. Функция потерь — уже не простая BCE, а сумма cross-entropy по каждому уровню семантического ID, что заставляет модель правильно предсказать всю последовательность. Оценка тоже сложнее: вместо скалярного произведения скор товара — это совместный log-probability, считаемый как сумма log-вероятностей генерации каждого токена. Для стабильности обучения мы применяем teacher forcing — ground-truth токен с предыдущего уровня помогает направить предсказание следующего.
Для оценки обеих моделей мы используем валидационный набор, к которому добавили 500 негативных сэмплов на каждый положительный следующий элемент. Хотя baseline SASRec превзошёл вариант с семантическими ID, результат семантической модели — достойный, учитывая сложность генеративной задачи предсказать четыре правильных токена. Более того, у варианта с семантическими ID есть способность работать с cold-start товарами за счёт общих префиксов с похожими продуктами — возможности, которой нет у baseline. Это раскрывает основной trade-off: получение такой обобщающей способности требует в 4 раза больше предсказаний на товар и больше compute и при обучении, и при инференсе.
Файнтюнинг Qwen3-8B для рекомендаций через семантические ID
Дальше учим языковую модель разговаривать на семантических ID. Для этого мы дообучаем Qwen3-8B, чтобы она стала «двуязычной» — свободно говорила и на естественном языке, и на семантических ID.
Сначала мы строим обучающий датасет из 4,2 миллиона диалоговых примеров, чтобы научить модель семантическим ID и рекомендациям. Данные покрывают несколько типов задач, включая сопоставление семантических ID с их текстовыми описаниями (и наоборот), предсказание следующего элемента в последовательности пользователя, понимание связей между категориями товаров и multi-hop reasoning. Каждый такой пример оформлен как диалог с системным промптом, инструкцией пользователя и ответом ассистента.
Затем мы дообучаем модель в две фазы. Первая фаза посвящена расширению словаря: мы добавляем 1 027 новых токенов для семантических ID (то есть <|sid_start|>, <|sid_end|>, от <|sid_0|> до <|sid_1023|> и <|rec|>) в токенайзер Qwen3-8B и расширяем под них матрицу эмбеддингов модели. На этой фазе мы замораживаем все параметры модели, кроме входного и выходного слоёв эмбеддингов, обучая 1,23B параметров (15,3% от общего числа) в течение 1 000 шагов с относительно высоким learning rate.
С инициализированными эмбеддингами токенов семантических ID мы переходим к следующей фазе — полному файнтюнингу всех 8,3B параметров. Здесь мы используем более низкий learning rate, 3% warmup и 8-битный AdamW для экономии памяти, обучаясь три эпохи на полном датасете. Для мониторинга прогресса мы использовали несколько кастомных колбэков, которые отслеживали обучающие метрики вроде gradient norms, измеряли иерархическую точность на задачах рекомендации и семплировали выходы модели на фиксированном наборе диалоговых входов.
Общение с гибридом LLM-рекомендера
Дообученную модель можно использовать для рекомендаций товаров — она владеет и семантическими ID, и естественным языком. Поскольку семантические ID — родная часть её словаря, модель выдаёт рекомендации, просто предсказывая следующий ID в последовательности, токен за токеном. В интерактивных сессиях чат-интерфейс переводит сырой выход модели (содержащий семантические ID) обратно в названия товаров, создавая читабельный для человека диалог.
User: «Halo 3 Limited Edition -Xbox 360», «Lost Planet: Extreme Condition - Xbox 360», «Lost Planet: Extreme Condition Collector’s Edition -Xbox 360» <|rec|> Модель берёт историю взаимодействий пользователя (серию sci-fi шутеров на Xbox 360) и предсказывает следующий товар. <|sid_start|><|sid_64|><|sid_351|><|sid_733|><|sid_768|><|sid_end|> Assistant: «Mass Effect - Xbox 360»
Базовые тесты показывают, что модель может выдавать стандартные next-item рекомендации на основе истории пользователя и уточнять их по запросу на естественном языке — например, ограничениям по платформе («Xbox-игры, похожие на…») или жанру («Мне нравятся игры про животных и милые.»).
User: Xbox-игры, похожие на «The Legend of Zelda: Ocarina of Time 3D»: <|rec|> Модель берёт seed-товар («The Legend of Zelda») и ограничение на естественном языке («Xbox»), чтобы переориентировать рекомендации на другую платформу. <|sid_start|><|sid_64|><|sid_271|><|sid_576|><|sid_768|><|sid_end|>, <|sid_start|><|sid_64|><|sid_435|><|sid_615|><|sid_768|><|sid_end|>, <|sid_start|><|sid_167|><|sid_271|><|sid_578|><|sid_768|><|sid_end|> Assistant: «Fallout: New Vegas - Xbox 360 Ultimate Edition», «Halo 4 - Xbox 360 (Standard Game)», «Halo Reach - Legendary Edition»
Модель также умеет объяснять свои рекомендации, описывать связь между двумя товарами и даже придумывать креативные названия и описания для бандлов товаров.
User: > Я только что прошёл «Dragon Quest Heroes II Explorer’s Edition - PlayStation 4». Посоветуй ещё <|rec|> и объясни почему: Модель выполняет гибридную задачу: генерирует релевантный семантический ID, а затем даёт объяснение к рекомендации. <|sid_start|><|sid_139|><|sid_289|><|sid_534|><|sid_768|><|sid_end|> Если тебе понравилась Dragon Quest Heroes II, тебе может понравиться Nights of Azure, потому что обе — action RPG для PlayStation 4 с упором на бой и развитие персонажа. Обе игры предлагают нарративный опыт с сильным акцентом на боевую механику, что указывает на общий интерес игрока к этому жанру. Assistant: «Nights of Azure - PlayStation 4». Если тебе понравилась Dragon Quest Heroes II, тебе может понравиться Nights of Azure, потому что обе — action RPG для PlayStation 4 с упором на бой и развитие персонажа. Обе игры предлагают нарративный опыт с сильным акцентом на боевую механику, что указывает на общий интерес игрока к этому жанру.
Наконец, хотя по сырой точности это может уступать специализированному рекомендеру, такой единый интерфейс позволяет пользователям находить товары из нашего каталога через многоходовый диалог, ведя к слиянию поиска, recsys и чата.
И всё это делается самой моделью — без классификаторов намерений, роутеров, retrieval и тому подобного.
[TURN 1] User: Ищу игры, похожие на Mario Kart. <|rec|> Модель генерирует семантические ID для трёх картинг-гонок.: <|sid_start|><|sid_131|><|sid_503|><|sid_738|><|sid_768|><|sid_end|>, <|sid_start|><|sid_131|><|sid_260|><|sid_577|><|sid_768|><|sid_end|>, <|sid_start|><|sid_145|><|sid_416|><|sid_671|><|sid_768|><|sid_end|> Assistant: «Need for Speed Most Wanted - Sony PSP», «Need for Speed Hot Pursuit 2 - PlayStation 2», «Burnout Legends - Nintendo DS» [TURN 2] User: А что-нибудь похожее, но для Xbox? <|rec|> Модель использует историю диалога и новое ограничение «Xbox», чтобы сгенерировать семантические ID для подходящих аркадных гонок на Xbox. <|sid_start|><|sid_183|><|sid_461|><|sid_517|><|sid_768|><|sid_end|>, <|sid_start|><|sid_183|><|sid_461|><|sid_562|><|sid_768|><|sid_end|>, <|sid_start|><|sid_183|><|sid_461|><|sid_616|><|sid_768|><|sid_end|> Assistant: «Need for Speed Carbon - Xbox 360», «Project Gotham Racing 3 - Xbox 360», «Forza Motorsport 4 - Xbox 360» [TURN 3] User: Придумай название и описание для бандла. Модель понимает, что контекст сменился с рекомендации на креативную задачу генерации текста. Assistant:: Xbox Racing Legends: Speed & Style Pack
• • •
На этом всё! Надеюсь, вам это так же интересно, как и мне! Будь у меня больше compute, я бы поэкспериментировал с sentence piece токенизацией поверх семантических ID, которую Deepmind и Youtube посчитали полезной, и глубже изучил влияние семантических ID на cold-start. Также я бы обучал RQ-VAE и языковые модели на датасетах с миллионами товаров и более широким набором задач и диалогов, чтобы проверить, как этот подход масштабируется и обобщается.
Вы обучали модели или строили UX с семантическими ID? Был бы рад пообщаться — пишите!
Спасибо Devansh и Shrikar за обсуждения, а Luke и Runpod — за compute для этих экспериментов. (Пользуйтесь, пожалуйста, моим аффилиатным кодом, чтобы мне начислили больше кредитов на эксперименты!) Спасибо Vibhu, Shawn, Cheng и всем, кому пришлось терпеть мои бесконечные разговоры об этом.
Ссылки
Hou, Yupeng, Jiacheng Li, Zhankui He, An Yan, Xiusi Chen, and Julian McAuley. «Bridging Language and Items for Retrieval and Recommendation.» arXiv:2403.03952. Preprint, arXiv, March 6, 2024. https://doi.org/10.48550/arXiv.2403.03952.
Amazon. «Amazon KDD Cup ‘23 - Multilingual Recommendation Challenge Dataset.» AIcrowd, 2023. https://www.aicrowd.com/challenges/amazon-kdd-cup-23-multilingual-recommendation-challenge.
Rajput, Shashank, Nikhil Mehta, Anima Singh, et al. «Recommender Systems with Generative Retrieval.» arXiv:2305.05065. Preprint, arXiv, November 3, 2023. https://doi.org/10.48550/arXiv.2305.05065.
Singh, Anima, Trung Vu, Nikhil Mehta, et al. «Better Generalization with Semantic IDs: A Case Study in Ranking for Recommendations.» arXiv:2306.08121. Preprint, arXiv, May 30, 2024. https://doi.org/10.48550/arXiv.2306.08121.
Zeghidour, Neil, Alejandro Luebs, Ahmed Omran, Jan Skoglund, and Marco Tagliasacchi. «SoundStream: An End-to-End Neural Audio Codec.» arXiv:2107.03312. Preprint, arXiv, July 7, 2021. https://doi.org/10.48550/arXiv.2107.03312.
Lee, Doyup, Chiheon Kim, Saehoon Kim, Minsu Cho, and Wook-Shin Han. «Autoregressive Image Generation Using Residual Quantization.» arXiv:2203.01941. Preprint, arXiv, March 9, 2022. https://doi.org/10.48550/arXiv.2203.01941.
Kang, Wang-Cheng, and Julian McAuley. «Self-Attentive Sequential Recommendation.» arXiv:1808.09781. Preprint, arXiv, August 20, 2018. https://doi.org/10.48550/arXiv.1808.09781.
Vaswani, Ashish, Noam Shazeer, Niki Parmar, et al. «Attention Is All You Need.» arXiv:1706.03762. Preprint, arXiv, August 2, 2023. https://doi.org/10.48550/arXiv.1706.03762.
Zhang, Yanzhao, Mingxin Li, Dingkun Long, et al. «Qwen3 Embedding: Advancing Text Embedding and Reranking Through Foundation Models.» arXiv:2506.05176. Preprint, arXiv, June 11, 2025. https://doi.org/10.48550/arXiv.2506.05176.
Yang, An, Anfeng Li, Baosong Yang, et al. «Qwen3 Technical Report.» arXiv:2505.09388. Preprint, arXiv, May 14, 2025. https://doi.org/10.48550/arXiv.2505.09388.
Comanici, Gheorghe, Eric Bieber, Mike Schaekermann, et al. «Gemini 2.5: Pushing the Frontier with Advanced Reasoning, Multimodality, Long Context, and Next Generation Agentic Capabilities.» arXiv:2507.06261. Preprint, arXiv, July 22, 2025. https://doi.org/10.48550/arXiv.2507.06261.
Если этот материал оказался полезным, пожалуйста, цитируйте его так:
Yan, Ziyou. (Sep 2025). Training an LLM-RecSys Hybrid for Steerable Recs with Semantic IDs. eugeneyan.com. https://eugeneyan.com/writing/semantic-ids/.
или
@article{yan2025semantic-ids, title = {Training an LLM-RecSys Hybrid for Steerable Recs with Semantic IDs}, author = {Yan, Ziyou}, journal = {eugeneyan.com}, year = {2025}, month = {Sep}, url = {https://eugeneyan.com/writing/semantic-ids/} }
Присоединяйтесь к более чем 11 800 читателям, получающим обновления о machine learning, RecSys, LLM и инженерии.