queQue pasaría se a túa tenda en liña soubese o que un cliente quería antes de que o fixesen?
que
Que pasaría se a túa tenda en liña soubese o que un cliente quería antes de que o fixesen?
Most recommendation engines are like helpful but slightly clueless assistants:suxiren elementos "populares" ou "similares" baseados en datos limitados e desactualizados.strugglecando os usuarios son novos (o problema de inicio frío), e raramente se adaptan rápido o suficiente cando as preferencias dun usuario cambian en tempo real.
E se o sistema puideseEn realidade pensamoscomo un merchandiser - combinando datos estáticos de produtos e comportamento do usuario en tempo real á superficieOs elementos correctosNo momento axeitado?
En realidade pensamos
This guide walks you through building a modern recommendation engineUtilizaciónSuperlinked, un que supera estas deficiencias tradicionais ao converter os seus datos en perfís de usuario actuables e en evolución utilizando infraestruturas vector-nativas.
(Queres ir directamente ao código? comproba o código de código aberto en GitHub aquí. listo para probar sistemas de recomendación para o seu propio caso de uso?
aquíaquíaquíaquí
Tamén podes seguir o tutorial no navegador co nosoUnha colab.
TD e DR:
A maioría das recomendacións de comercio electrónico son demasiado estáticas (baseadas en regras) ou demasiado negras (modelos ML opacos). Superlinked ofrece un camiño medio: recomendacións flexibles en tempo real que se poden adaptar aos usuarios de inicio frío combinando metadatos con comportamento en directo - todo sen retraer modelos ML.
Conseguir a personalización a pesar dos retos da incorporación vectorial de RecSys
Aínda que as incorporacións vectoriales poden mellorar enormemente os sistemas de recomendación, a súa implementación efectiva require abordar varios retos, incluíndo:
- que
- Calidade e relevancia: O proceso de xeración de embudo, a arquitectura e os datos deben considerarse coidadosamente. que
- Datos escasos e ruidosos: As incorporacións son menos eficaces cando teñen entradas incompletas ou ruidosas. que
- Escalabilidade: son necesarios métodos eficientes para grandes conxuntos de datos; doutro xeito, a latencia será un problema. que
Superlinked permite abordar estes retos combinando todos os datos dispoñibles sobre usuarios e produtos en vectores multimodales ricos. No noso exemplo de comercio electrónico RecSys a continuación, facémolo usando os seguintes elementos da biblioteca Superlinked:
- que
- espazos de número min_max: para comprender as opinións dos clientes e a información de prezos que
- texto-similaridade Espazo: para a comprensión semántica da información do produto que
- Esquema de eventos e efectos para modificar vectores que
- pesos de tempo de consulta - para definir como desexa que os datos sexan tratados cando execute a consulta, permitíndolle optimizar e escalar sen reencher o conxunto de datos enteiro (latencia) que
Ao incorporar os nosos inicialmente escasos datos específicos do usuario (preferencia inicial do produto do usuario), podemos xestionar o problema de inicio frío.Hipérbole-Personalizar recomendacións mediante a incorporación destes datos de eventos, creando un loop de retroceso que lle permite actualizar vectores con preferencias de usuario en tempo real.Ademais, os pesos de tempo de consulta de Superlinked permítenlle axustar os seus resultados de búsqueda, biasándoos para coincidir con preferencias de usuario específicas.
Let's get started!
Construír un motor de recomendación de comercio electrónico con Superlinked
Ao comezo do desenvolvemento, temos o seguinteproduct data: de
- que
- Número de revisores que
- Avaliacións de produtos que
- Descrición textual que
- Nome do produto (xeralmente contén o nome da marca) que
- Categoría que
Tamén temos o seguintedata about users and products: de
- que
- cada usuario elixe un dos tres produtos ofrecidos ao rexistrarse (é dicir, datos de preferencias de produtos) que
- comportamento do usuario (despois do rexistro) proporciona datos adicionais de evento - preferencias para características textuais dos produtos (descrición, nome, categoría) que
Ademais, a economía clásica dinos que, en media, todos os usuarios ceteris paribus prefiren produtos que:
- que
- Custa menos que
- Teñen moitas revisións que
- Teñen ratings máis altos que
Podemos configurar os nosos espazos para ter en conta estes datos, de xeito que o noso RecSys funcione en escenarios de inicio frío - recomendando elementos para usuarios que coñecemos moi pouco. Unha vez que o noso RecSys estea en funcionamento, tamén teremos datos de comportamento: os usuarios farán clic en certos produtos, mercar determinados produtos, etc. Podemos capturar e usar estes datos de eventos para crear fluxos de feedback, actualizar os nosos vectores para reflectir as preferencias do usuario e mellorar a calidade da recomendación.
Introdución ao Superlink
Primeiro, necesitamos instalar a biblioteca Superlinked e importar as clases.
%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)
Tamén definimos os nosos conxuntos de datos e creamos unha constante para almacenar os 10 principais elementos - verCélula 3no seu caderno.
Agora que se identificaron as localizacións instaladas da biblioteca, as clases importadas e os conxuntos de datos, podemos botar unha ollada ao noso conxunto de datos para informar de como configuramos os nosos espazos. Inicialmente, temos datos do rexistro do usuario - é dicir, cal dos tres produtos usuaria_1 e usuaria_2 elixiu.
# 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
Tamén podemos establecer un exame detallado dos datos de distribución dos nosos produtos - verCélula 5Isto dálle unha idea de cantos produtos están en diferentes puntos de prezo, teñen diferentes contas de revisións e teñen diferentes clasificacións (incluíndo onde a maioría dos produtos atópanse nestes rangos).
Os prezos dos produtos están xeralmente por baixo do punto de prezo de $1.000. Pode que queiramos poñer o rango de espazo a 25-1000 para facelo representativo, sen distorsionar os valores exteriores. As contas de revisión dos produtos distribúense uniformemente e as clasificacións de revisión distribúense relativamente uniformemente, polo que non se require ningún tratamento adicional.Células 7-9. .
Construír o índice para a busca de vectores
A biblioteca de Superlinked contén un conxunto de bloques de construción de núcleo que usamos para construír o índice e xestionar a recuperación.aquí. .
Imos poñer os bloques de construción desta biblioteca para usar no noso EComm RecSys.define your SchemaPara informar ao sistema sobre os seus datos.
# 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()
A continuación, usa Espazos para dicir como quere tratar cada parte dos datos ao incorporar. En Definicións de Espazos, describimos como incorporar entradas para que reflictan as relacións semánticas nos nosos datos. Cada Espazos está optimizado para incorporar os datos para devolver a maior calidade posible de resultados de recuperación.
# 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])
Agora que tes os teus datos definidos en Espazos, estás listo para xogar cos teus datos e optimizar os resultados.O que podemos facer sen eventosA nosa solución de inicio frío.
Solución ao problema de inicio frío de RecSys
Aquí definimos unha consulta de usuario que busca só co vector de preferencia do usuario.Temos control de configuración sobre a importancia (pesos) de cada tipo de entrada (Espazo).
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()
Os resultados desta consulta reflicten o feito de que user_1 elixiu unha bolsa cando se rexistrou por primeira vez no noso sitio de ecomm.
Tamén é posible recomendar produtos para o usuario_1 que sonEn xeralatractivo - é dicir, baseado no seu prezo sendo baixo, e ter unha morea de boas opinións.Os nosos resultados agora reflectirán tanto a elección de produto de user_1 no momento do rexistroea popularidade xeral dos produtos. (Tamén podemos xogar con estes pesos para distorsionar os resultados na dirección dun espazo ou outro.)
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()
A busca dun novo usuario introduce texto de consulta como unha entrada para os nosos resultados de recomendación - verCélula 20. .
No noso exemplo, user_1 buscou "camisetas de roupa feminina". Podemos optimizar os nosos resultados dandoadditional weight to the category space(por exemplocategory_weight = 10
), para recomendar máis produtos de "xatos de roupa feminina".
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()
O noso peso de categoría adicional produce máis resultados de roupa feminina.
Tamén podemos bias as nosas recomendacións para produtos de primeira clasificación (review_rating_weight=5
Os resultados reflicten agora a preferencia inicial do usuario_1 por bolsas e elementos que son xeralmente populares, mentres que os produtos con clasificacións baixas son eliminados por completo.Páxina 22. .
Usar datos de eventos para crear experiencias personalizadas
Os nosos usuarios interactúan coa nosa plataforma - user_1 máis, user_2 menos.behavioral data(ver abaixo), representados como eventos:
- que
- un usuario interesado en produtos casual e de lecer (user_2) que
- un usuario interesado en produtos elegantes para saír e ocasións de traballo formais (user_1) que
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
Imos pesar accións específicas para rexistrar o nivel de interese do usuario nun determinado produto, e axustar a configuración para ter en conta os eventos ao realizar a recuperación.
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()
Agora creamos un novo índice para ter en conta os eventos do usuario e, a continuación, personalizamos as recomendacións a cada usuario en consecuencia.
# 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"))
)
Podemos observar o impacto da incorporación de eventos nos nosos RecSys ponderando a personalizaciónsó un poucooupesadoPrimeiro, imos ver o efecto (en comparación coa base) de pesar os espazos que son influenciados por estes eventos (datos de comportamento).
# 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"]]
Con moi pouco peso colocado nos espazos afectados por eventos, observamos un cambio pero principalmente só na segunda metade do noso top 10, en comparación cos resultados anteriores ("id_base", á dereita).
Pero se pesamos máis os espazos afectados polo evento, superamos elementos completamente novos na nosa lista de recomendacións.
# 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"]]
Tamén podemos, por suposto, usar pesos para personalizar as nosas recomendacións baseadas no comportamento dun usuario en particular (datos de eventos) ePrioriza simultaneamente outros atributos do produto- Por exemplo, o prezo (verPáxina 31) da
Conclusión
A implementación de eComm RecSys da biblioteca Superlinked (abaixo) móstralle como realizar o poder das incorporacións de vectores incorporando o significado semántico de consultas de usuarios e datos de comportamento. Usando os nosos espazos de número min_max e semellanza de texto, esquema de eventos e efectos e pesos de tempo de consulta, pode abordar os retos de inicio frío, calidade e relevancia e escalabilidade de RecSys e proporcionar recomendacións altamente precisas e personalizadas para o usuario na produción.
Agora é a túa vez!Probe implementar a biblioteca Superlinked vostede mesmo usando o noso notebook. .
Try It Yourself – Get the Code & Demo!
Probe-o vostede mesmo - Obter o código & Demo!- que
- Grab the Code: Consulte a implementación completa no noso repo de GitHub aquí.Fork, axusta-lo e fai-lo seu! que
- Ver en acción: Queres ver isto funcionando nunha configuración do mundo real? Reserva unha demostración rápida e explora como Superlinked pode superchargar as túas recomendacións. que
Os motores de recomendación están a modelar a forma en que descubrimos o contido, xa sexan pantalóns populares, música ou outros produtos,vector search is the future- e agora tes as ferramentas para construír a túa propia.