→Что, если ваш интернет-магазин знал, чего хочет клиент, прежде чем он это сделает?
→
Что, если ваш интернет-магазин знал, чего хочет клиент, прежде чем он это сделает?
Most recommendation engines are like helpful but slightly clueless assistants:они предлагают «популярные» или «подобные» элементы на основе ограниченных, устаревших данных.struggleкогда пользователи новые (проблема холодного старта), и они редко адаптируются достаточно быстро, когда предпочтения пользователя меняются в режиме реального времени.
Но что если ваша система можетНа самом деле думатькак маркетолог — сочетание статических данных о продуктах и поведения пользователей в режиме реального времениПравильные предметыВ нужное время?
На самом деле думать
This guide walks you through building a modern recommendation engineИспользованиеSuperlinked, который преодолевает эти традиционные недостатки, превращая ваши данные в действующие, эволюционирующие пользовательские профили с использованием векторной инфраструктуры.
(Хотите прыгнуть прямо к коду? Проверьте код с открытым исходным кодом на GitHub здесь. Готовы попробовать системы рекомендующих для вашего собственного использования? Получите демо здесь.)
здесьздесьздесьздесь
Вы также можете следовать вместе с инструкцией в браузере с нашимКолледж
Тл; Др :
Большинство рекомендаций для электронной коммерции либо слишком статичны (на основе правил), либо слишком черные ящики (ошибочные модели ML).Superlinked предлагает средний путь: гибкие рекомендации в режиме реального времени, которые могут адаптироваться к пользователям холодного старта, объединяя метаданные с живым поведением — все без переподготовки моделей ML.
Достижение персонализации, несмотря на проблемы встраивания векторов RecSys
В то время как встраивания векторов могут значительно улучшить системы рекомендаций, их эффективное внедрение требует решения нескольких задач, включая:
- →
- Качество и актуальность: Процесс генерации встраивания, архитектура и данные должны быть тщательно рассмотрены. →
- Слабые и шумные данные: встраивания менее эффективны, когда они имеют неполный или шумный ввод. →
- Эффективные методы для больших наборов данных необходимы; в противном случае латентность будет проблемой. →
Superlinked позволяет решить эти проблемы, объединив все имеющиеся данные о пользователях и продуктах в богатые мультимодальные векторы.В нашем примере электронной коммерции RecSys ниже мы делаем это с помощью следующих элементов библиотеки Superlinked:
- →
- min_max номера Пространства: для понимания отзывов клиентов и информации о ценах →
- текстовое сходство Пространство: для семантического понимания информации о продукте →
- Схема событий и эффекты для изменения векторов →
- Временные весы запроса — для определения того, как вы хотите, чтобы данные обрабатывались при выполнении запроса, что позволяет оптимизировать и масштабировать без повторного встраивания всего набора данных (задержка). →
Встраивая наши первоначально редкие пользовательские данные (первоначальные предпочтения продукта пользователя), мы можем справиться с проблемой холодного старта.гипер- персонализация рекомендаций, встраивая данные этого события, создавая цикл обратной связи, который позволяет вам обновлять векторы с пользовательскими предпочтениями в режиме реального времени.Кроме того, весы времени запроса Superlinked позволяют тонко настроить результаты поиска, упреждая их, чтобы соответствовать конкретным предпочтениям пользователя.
Let's get started!
Создание системы рекомендаций для электронной коммерции с помощью Superlinked
В начале разработки мы имеем следующееproduct data: →
- →
- Количество рецензентов →
- Рейтинги продукции →
- Текстовое описание →
- Название продукта (обычно содержит название бренда) →
- Категория →
У нас также есть следующееdata about users and products: →
- →
- каждый пользователь выбирает один из трех предлагаемых продуктов при регистрации (т.е. данные о предпочтениях продуктов) →
- поведение пользователя (после регистрации) предоставляет дополнительные данные о событиях – предпочтения по текстовым характеристикам продуктов (описание, название, категория) →
Также классическая экономика говорит нам, что в среднем все пользователи ceteris paribus предпочитают продукты, которые:
- →
- Стоит меньше →
- Есть много отзывов →
- Иметь более высокие рейтинги →
Мы можем настроить наши пространства, чтобы учитывать эти данные, так что наши RecSys работают в сценариях холодного старта - рекомендуя предметы для пользователей, о которых мы знаем очень мало.После того как наши RecSys запущены и запущены, у нас также будут данные о поведении: пользователи будут нажимать на определенные продукты, покупать определенные продукты и т. Д. Мы можем захватить и использовать эти данные событий для создания откликов, обновления наших векторов, чтобы отразить предпочтения пользователей и улучшить качество рекомендаций.
Создание Superlinked
Для начала необходимо установить библиотеку Superlinked и импортировать классы.
%pip install superlinked==6.0.0
import altair as alt
import os
import pandas as pd
import sys
from superlinked.framework.common.embedding.number_embedding import Mode
from superlinked.framework.common.schema.schema import schema
from superlinked.framework.common.schema.event_schema import event_schema
from superlinked.framework.common.schema.schema_object import String, Integer
from superlinked.framework.common.schema.schema_reference import SchemaReference
from superlinked.framework.common.schema.id_schema_object import IdField
from superlinked.framework.common.parser.dataframe_parser import DataFrameParser
from superlinked.framework.dsl.executor.in_memory.in_memory_executor import (
InMemoryExecutor,
InMemoryApp,
)
from superlinked.framework.dsl.index.index import Index
from superlinked.framework.dsl.index.effect import Effect
from superlinked.framework.dsl.query.param import Param
from superlinked.framework.dsl.query.query import Query
from superlinked.framework.dsl.source.in_memory_source import InMemorySource
from superlinked.framework.dsl.space.text_similarity_space import TextSimilaritySpace
from superlinked.framework.dsl.space.number_space import NumberSpace
alt.renderers.enable(get_altair_renderer())
pd.set_option("display.max_colwidth", 190)
Мы также определяем наши наборы данных и создаем константу для хранения топ-10 элементов - см.Клетка 3В ноутбуке
Теперь, когда установлены библиотеки, импортированы классы и определены места набора данных, мы можем взглянуть на наш набор данных, чтобы проинформировать вас о том, как мы настраиваем наши пространства.
# the user preferences come from the user being prompted to select a product out of 3 - those will be the initial preferences
# this is done in order to give somewhat personalised recommendations
user_df: pd.DataFrame = pd.read_json(USER_DATASET_URL)
user_df
Мы также можем установить тщательный осмотр данных о распределении наших продуктов - см.Клетка 5Это дает вам представление о том, сколько продуктов находятся в разных ценовых точках, имеют разные оценки и имеют разные рейтинги (включая то, где большинство продуктов находятся в этих диапазонах).
Цены на продукты в основном ниже 1000$. Мы можем настроить диапазон пространства на 25-1000, чтобы сделать его представительным, не искаженным внешними значениями. Количество отзывов о продуктах равномерно распределено, а рейтинги отзывов относительно равномерно распределены, поэтому дополнительная обработка не требуется.Клетки 7-9. .
Создание индекса для векторного поиска
Библиотека Superlinked содержит набор основных строительных блоков, которые мы используем для построения индекса и управления поиском.здесь. .
Давайте поставим строительные блоки этой библиотеки для использования в нашем EComm RecSys.define your Schemaчтобы сообщить системе о ваших данных.
# schema is the way to describe the input data flowing into our system - in a typed manner
@schema
class ProductSchema:
description: String
name: String
category: String
price: Integer
review_count: Integer
review_rating: Integer
id: IdField
@schema
class UserSchema:
preference_description: String
preference_name: String
preference_category: String
id: IdField
@event_schema
class EventSchema:
product: SchemaReference[ProductSchema]
user: SchemaReference[UserSchema]
event_type: String
id: IdField
# we instantiate schemas as follows
product = ProductSchema()
user = UserSchema()
event = EventSchema()
Далее вы используете пространства, чтобы сказать, как вы хотите обрабатывать каждую часть данных при встраивании. В определениях пространства мы описываем, как встраивать входы, чтобы они отражали семантические отношения в наших данных.Каждое пространство оптимизировано для встраивания данных, чтобы вернуть наивысшее возможное качество результатов поиска.
# textual inputs are embedded in a text similarity space powered by a sentence_transformers model
description_space = TextSimilaritySpace(
text=[user.preference_description, product.description],
model="sentence-transformers/all-distilroberta-v1",
)
name_space = TextSimilaritySpace(
text=[user.preference_name, product.name],
model="sentence-transformers/all-distilroberta-v1",
)
category_space = TextSimilaritySpace(
text=[user.preference_category, product.category],
model="sentence-transformers/all-distilroberta-v1",
)
# NumberSpaces encode numeric input in special ways to reflect a relationship
# here we express relationships to price (lower the better), or ratings and review counts (more/higher the better)
price_space = NumberSpace(
number=product.price, mode=Mode.MINIMUM, min_value=25, max_value=1000
)
review_count_space = NumberSpace(
number=product.review_count, mode=Mode.MAXIMUM, min_value=0, max_value=100
)
review_rating_space = NumberSpace(
number=product.review_rating, mode=Mode.MAXIMUM, min_value=0, max_value=4
)
# create the index using the defined spaces
product_index = Index(
spaces=[
description_space,
name_space,
category_space,
price_space,
review_count_space,
review_rating_space,
]
)
# parse our data into the schemas - not matching column names can be conformed to schemas using the mapping parameter
product_df_parser = DataFrameParser(schema=product)
user_df_parser = DataFrameParser(
schema=user, mapping={user.preference_description: "preference_desc"}
)
# setup our application
source_product: InMemorySource = InMemorySource(product, parser=product_df_parser)
source_user: InMemorySource = InMemorySource(user, parser=user_df_parser)
executor: InMemoryExecutor = InMemoryExecutor(
sources=[source_product, source_user], indices=[product_index]
)
app: InMemoryApp = executor.run()
# load the actual data into our system
source_product.put([products_df])
source_user.put([user_df])
Теперь, когда ваши данные определены в пространствах, вы готовы играть с вашими данными и оптимизировать результаты.Что можно сделать без событийНаше решение для холодного старта.
Решение проблемы холодного старта RecSys
Здесь мы определяем пользовательский запрос, который использует только вектор предпочтений пользователя.У нас есть конфигурационный контроль над значением (весами) каждого типа ввода (пространства).
user_query = (
Query(
product_index,
weights={
description_space: Param("description_weight"),
name_space: Param("name_weight"),
category_space: Param("category_weight"),
price_space: Param("price_weight"),
review_count_space: Param("review_count_weight"),
review_rating_space: Param("review_rating_weight"),
},
)
.find(product)
.with_vector(user, Param("user_id"))
.limit(Param("limit"))
)
# simple recommendations for our user_1
# these are based only on the initial product the user chose when first entering our site
simple_result = app.query(
user_query,
user_id="user_1",
description_weight=1,
name_weight=1,
category_weight=1,
price_weight=1,
review_count_weight=1,
review_rating_weight=1,
limit=TOP_N,
)
simple_result.to_pandas()
Результаты этого запроса отражают тот факт, что user_1 выбрал сумку, когда они впервые зарегистрировались на нашем сайте ecomm.
Также можно рекомендовать продукты пользователю_1, которыеВ целомпривлекательные - то есть, основываясь на их низкой цене, и имея много хороших отзывов. Наши результаты теперь будут отражать как выбор продукта user_1 при регистрациииобщая популярность продуктов. (Мы также можем играть с этими весами, чтобы исказить результаты в направлении одного пространства или другого.)
general_result = app.query(
user_query,
user_id="user_1",
description_weight=0,
name_weight=0,
category_weight=0,
price_weight=1,
review_count_weight=1,
review_rating_weight=1,
limit=TOP_N,
)
general_result.to_pandas()
Поиск нового пользователя вводит текст запроса в качестве ввода для наших результатов рекомендаций - см.Клетка 20. .
В нашем примере пользователь_1 искал "костюмы для женщин".Мы можем оптимизировать наши результаты, предоставляяadditional weight to the category space(наcategory_weight = 10
), чтобы порекомендовать больше продуктов «женская одежда куртки».
women_cat_result = app.query(
search_query,
user_id="user_1",
query_text="women clothing jackets",
description_weight=1,
name_weight=1,
category_weight=10,
price_weight=1,
review_count_weight=1,
review_rating_weight=1,
limit=TOP_N,
)
women_cat_result.to_pandas()
Наша дополнительная категория взвешивания производит больше результатов женской одежды.
Мы также можем отклонить наши рекомендации к высокооцененным продуктам (review_rating_weight=5
Результаты теперь отражают первоначальное предпочтение пользователя_1 для сумок и предметов, которые обычно популярны, в то время как продукты с низкими рейтингами удаляются полностью.Клетка 22. .
Использование данных о событиях для создания персонализированных опытов
Наши пользователи взаимодействовали с нашей платформой - user_1 больше, user_2 меньше.behavioral data(см. ниже), представленные как события:
- →
- пользователь, заинтересованный в случайных и досуговых продуктах (user_2) →
- пользователь, заинтересованный в элегантных продуктах для выхода на улицу и формальных рабочих мероприятий (user_1) →
events_df = (
pd.read_json(EVENT_DATASET_URL)
.reset_index()
.rename(columns={"index": "id"})
.head(NROWS)
)
events_df = events_df.merge(
products_df[["id"]], left_on="product", right_on="id", suffixes=("", "r")
).drop("idr", axis=1)
events_df = events_df.assign(created_at=1715439600)
events_df
Давайте взвесим конкретные действия, чтобы зарегистрировать уровень интереса пользователя к конкретному продукту, и скорректируем настройки, чтобы учитывать события при выполнении поиска.
event_weights = {
"clicked_on": 0.2,
"buy": 1,
"put_to_cart": 0.5,
"removed_from_cart": -0.5,
}
# adjust the setup to events
product_index_with_events = Index(
spaces=[
description_space,
category_space,
name_space,
price_space,
review_count_space,
review_rating_space,
],
effects=[
Effect(
description_space,
event.user,
event_weight * event.product,
event.event_type == event_type,
)
for event_type, event_weight in event_weights.items()
]
+ [
Effect(
category_space,
event.user,
event_weight * event.product,
event.event_type == event_type,
)
for event_type, event_weight in event_weights.items()
]
+ [
Effect(
name_space,
event.user,
event_weight * event.product,
event.event_type == event_type,
)
for event_type, event_weight in event_weights.items()
],
)
event_df_parser: DataFrameParser = DataFrameParser(schema=event)
source_event: InMemorySource = InMemorySource(schema=event, parser=event_df_parser)
executor_with_events: InMemoryExecutor = InMemoryExecutor(
sources=[source_product, source_user, source_event],
indices=[product_index_with_events],
)
app_with_events: InMemoryApp = executor_with_events.run()
Теперь мы создаем новый индекс для учета событий пользователей, а затем персонализируем рекомендации каждому пользователю соответственно.
# for a new index, all data has to be put into the source again
source_product.put([products_df])
source_user.put([user_df])
source_event.put([events_df])
# a query only searching with the user's vector the preferences are now much more personalised thanks to the events
personalised_query = (
Query(
product_index_with_events,
weights={
description_space: Param("description_weight"),
category_space: Param("category_weight"),
name_space: Param("name_weight"),
price_space: Param("price_weight"),
review_count_space: Param("review_count_weight"),
review_rating_space: Param("review_rating_weight"),
},
)
.find(product)
.with_vector(user, Param("user_id"))
.limit(Param("limit"))
)
Мы можем наблюдать влияние включения событий в наши RecSys, взвешивая персонализациюТолько слегкаилиТяжелоВо-первых, давайте рассмотрим эффект (по сравнению с исходной линией) взвешивания промежутков, на которые влияют эти (поведенческие данные) события.
# with small weight on event-affected spaces, we mainly just alter the results below position 4
general_event_result = app_with_events.query(
personalised_query,
user_id="user_1",
description_weight=1,
category_weight=1,
name_weight=1,
price_weight=1,
review_count_weight=1,
review_rating_weight=1,
limit=TOP_N,
)
general_event_result.to_pandas().join(
simple_result.to_pandas(), lsuffix="", rsuffix="_base"
)[["description", "id", "description_base", "id_base"]]
С очень малой нагрузкой на пространства, затронутые событиями, мы наблюдаем изменение, но в основном только в последней половине нашего топ-10 по сравнению с предыдущими результатами («id_base», справа).
Но если мы более серьезно взвесим пространства, затрагиваемые событиями, то в нашем списке рекомендаций мы увидим совершенно новые элементы.
# with larger weight on the the event-affected spaces, more totally new items appear in the TOP10
event_weighted_result = app_with_events.query(
personalised_query,
user_id="user_1",
query_text="",
description_weight=5,
category_weight=1,
name_weight=1,
price_weight=1,
review_count_weight=1,
review_rating_weight=1,
limit=TOP_N,
)
event_weighted_result.to_pandas().join(
simple_result.to_pandas(), lsuffix="", rsuffix="_base"
)[["description", "id", "description_base", "id_base"]]
Мы также можем, конечно, использовать весы для персонализации наших рекомендаций на основе поведения конкретного пользователя (данные о событиях) иодновременно отдавать приоритет другим атрибутам продуктаВ частности, цена (см.Клетка 31)
Заключение
Реализация eComm RecSys библиотеки Superlinked (выше) показывает вам, как реализовать мощь встраиваний векторов, включая семантический смысл запросов пользователей и поведенческих данных.Используя наши пространства числа и текстового сходства min_max, схему событий и эффекты, а также весы времени запросов, вы можете решить проблемы холодного старта, качества и актуальности и масштабируемости RecSys и предоставить высокоточные, пользовательские рекомендации в производстве.
Теперь ваша очередь!Попробуйте реализовать библиотеку Superlinked самостоятельно с помощью нашего ноутбука. .
Try It Yourself – Get the Code & Demo!
Попробуйте сами – получите код и демо!- →
- Загрузите код: ознакомьтесь с полной реализацией в нашем репо GitHub здесь.Форкируйте его, настройте и сделайте его своим! →
- See It in Action: Хотите увидеть, как это работает в реальном мире? Забронируйте быстрое демо, и исследуйте, как Superlinked может перезарядить ваши рекомендации. →
Рекомендационные двигатели формируют то, как мы находим контент, будь то популярные брюки, музыка или другие продукты.vector search is the future— и теперь у вас есть инструменты, чтобы создать свой собственный.