y y ¿Y si tu tienda en línea supiera lo que un cliente quería antes de que lo hicieran? ¿Y si tu tienda en línea supiera lo que un cliente quería antes de que lo hicieran? sugieren artículos “populares” o “similares” basados en datos limitados y desactualizados. cuando los usuarios son nuevos (el problema de inicio frío), y raramente se adaptan lo suficiente rápidamente cuando las preferencias de un usuario cambian en tiempo real. Most recommendation engines are like helpful but slightly clueless assistants: struggle Pero, ¿y si el sistema puede como un merchandiser —combinando datos estáticos de producto y comportamiento de usuario en tiempo real a la superficie ¿En el momento adecuado? En realidad piensan Los elementos correctos En realidad piensan Uso , uno que supera estas deficiencias tradicionales al convertir sus datos en perfiles de usuario actuables y evolucionando utilizando una infraestructura vectorial nativa. This guide walks you through building a modern recommendation engine Superlinked (Quieres saltar directamente al código? Compruebe el código de código abierto en GitHub aquí. Listo para probar sistemas de recomendar para su propio caso de uso? Obtenga una demostración aquí.) Aquí Aquí Aquí Aquí También puedes seguir el tutorial en el navegador con nuestro El colab. TD y DR: La mayoría de los recomendadores de comercio electrónico son demasiado estáticos (basados en reglas) o demasiado en la caja negra (modelos ML opacos). Superlinked ofrece un camino medio: recomendaciones flexibles en tiempo real que pueden adaptarse a los usuarios de inicio frío combinando metadatos con el comportamiento en vivo - todo sin retraer los modelos ML. Lograr la personalización a pesar de los retos de incorporación vectorial de RecSys Mientras que las incorporaciones vectoriales pueden mejorar enormemente los sistemas de recomendación, implementarlos de manera efectiva requiere abordar varios desafíos, incluyendo: y y y y Calidad y relevancia: El proceso de generación de embudo, la arquitectura y los datos deben considerarse cuidadosamente. Datos escasos y ruidosos: las incorporaciones son menos eficaces cuando tienen entradas incompletas o ruidosas. Escalabilidad: Se necesitan métodos eficientes para grandes conjuntos de datos; de lo contrario, la latencia será un problema. Superlinked le permite abordar estos desafíos combinando todos los datos disponibles sobre usuarios y productos en ricos vectores multimodales.En nuestro ejemplo de comercio electrónico RecSys a continuación, lo hacemos utilizando los siguientes elementos de la biblioteca Superlinked: y y y y y Spaces de número min_max: para comprender las opiniones de los clientes y la información de precios texto-similaridad Espacio: para la comprensión semántica de la información del producto Esquema de eventos y efectos para modificar vectores pesos de tiempo de consulta: para definir cómo desea que los datos se traten cuando ejecuta la consulta, lo que le permite optimizar y escalar sin reincorporar todo el conjunto de datos (latencia) Al incorporar nuestros inicialmente escasos datos específicos del usuario (preferencia inicial del producto del usuario), podemos manejar el problema de inicio frío. -Personalizar las recomendaciones mediante la incorporación de estos datos de evento, creando un ciclo de retroalimentación que le permite actualizar vectores con preferencias de usuario en tiempo real.Además, los pesos de tiempo de consulta de Superlinked le permiten ajustar sus resultados de búsqueda, biasándolos para coincidir con preferencias de usuario específicas. Hiper Let's get started! Construir un motor de recomendación de comercio electrónico con Superlinked Al comienzo del desarrollo, tenemos lo siguiente: : de product data y y y y y y Número de revisores Clases de productos Descripción textual Nombre del producto (normalmente contiene el nombre de marca) Categorías También tenemos el siguiente : de data about users and products y y y cada usuario elige uno de los tres productos ofrecidos al registrarse (es decir, datos de preferencias de productos) comportamiento del usuario (después del registro) proporciona datos adicionales de eventos - preferencias para características textuales de los productos (descripción, nombre, categoría) Además, la economía clásica nos dice que, en promedio, todos los usuarios ceteris paribus prefieren productos que: y y y y Cuesta menos Tiene muchas reseñas Tienen calificaciones más altas Podemos configurar nuestros espacios para tener en cuenta estos datos, de modo que nuestro RecSys funcione en escenarios de inicio frío - recomendando artículos para usuarios de los que sabemos muy poco.Una vez que nuestro RecSys esté en funcionamiento, también tendremos datos de comportamiento: los usuarios van a hacer clic en determinados productos, comprar determinados productos, etc. Podemos capturar y usar estos datos de eventos para crear flujos de retroalimentación, actualizar nuestros vectores para reflejar las preferencias de los usuarios y mejorar la calidad de la recomendación. Creación de Superlink Primero, necesitamos instalar la biblioteca Superlinked y importar las 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) También definimos nuestros conjuntos de datos, y creamos una constante para almacenar los 10 primeros elementos - véase en el cuaderno. Célula 3 Ahora que se han identificado las ubicaciones de la biblioteca, las clases importadas y los conjuntos de datos, podemos echar un vistazo a nuestro conjunto de datos para informar de la forma en que configuramos nuestros espacios. Inicialmente, tenemos datos de registro de usuarios, es decir, cuál de los tres productos eligió user_1 y user_2. # 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 También podemos establecer un examen detallado de los datos de distribución de nuestros productos - véase Esto le da una idea de cuántos productos están en diferentes puntos de precio, tienen diferentes cuentas de comentarios y tienen diferentes calificaciones (incluyendo donde la mayoría de los productos se encuentran en estos rangos). Célula 5 Los precios de los productos están principalmente por debajo del punto de precio de $1000. Es posible que deseemos establecer el rango de espacio a 25-1000 para que sea representativo, no distorsionado por los valores externos. Las cuentas de revisión de productos se distribuyen uniformemente, y las calificaciones de revisión se distribuyen relativamente uniformemente, por lo que no se requiere tratamiento adicional. . el Células 7-9 Desarrollar el índice para la búsqueda vectorial La biblioteca de Superlinked contiene un conjunto de bloques de construcción de núcleo que utilizamos para construir el índice y gestionar la recuperación. . el Aquí Pongamos los bloques de construcción de esta biblioteca para usar en nuestro EComm RecSys. Para informar al sistema sobre sus datos. 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() A continuación, utiliza espacios para decir cómo desea tratar cada parte de los datos cuando se incorpora. En definiciones de espacio, describimos cómo incorporar las entradas para que reflejen las relaciones semánticas en nuestros datos. Cada espacio está optimizado para incorporar los datos para devolver la más alta calidad 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]) Ahora que tienes tus datos definidos en espacios, estás listo para jugar con tus datos y optimizar los resultados. - Nuestra solución de inicio frío. Lo que podemos hacer sin eventos Solucionar el problema de inicio frío de RecSys Aquí, definimos una consulta de usuario que busca con sólo el vector de preferencia del usuario.Tenemos control de configuración sobre la importancia (pesos) de cada tipo de entrada (Espacio). 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() Los resultados de esta consulta reflejan el hecho de que user_1 eligió una bolsa cuando se registró por primera vez en nuestro sitio de ecomm. También es posible recomendar productos a user_1 que son atractivo - es decir, basado en su precio siendo bajo, y teniendo un montón de buenas críticas.Nuestros resultados ahora reflejarán tanto la elección de producto de user_1 al registrarse la popularidad general de los productos. (También podemos jugar con estos pesos para desviar los resultados en la dirección de un espacio u otro.) En general y 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() La búsqueda de un nuevo usuario introduce el texto de la consulta como una entrada para nuestros resultados de recomendación - véase . el Célula 20 En nuestro caso de ejemplo, user_1 buscó "camisetas de mujer".Podemos optimizar nuestros resultados dando (en el ), para recomendar más productos de "camisetas de ropa femenina". 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() Nuestro peso de categoría adicional produce más resultados de ropa de mujer. También podemos bias nuestras recomendaciones a los productos de primera clasificación ( Los resultados ahora reflejan la preferencia inicial del usuario_1 por bolsas y artículos que son generalmente populares, mientras que los productos con calificaciones bajas se eliminan por completo. . el review_rating_weight=5 Célula 22 Usar datos de eventos para crear experiencias personalizadas Nuestros usuarios han interactuado con nuestra plataforma - user_1 más, user_2 menos así. (ver más abajo), representados como eventos: behavioral data y y y un usuario interesado en productos caseros y de ocio (user_2) un usuario interesado en productos elegantes para salir y ocasiones formales de trabajo (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 Vamos a ponderar acciones específicas para registrar el nivel de interés del usuario en un producto en particular, y ajustar la configuración para tener en cuenta los eventos al realizar la 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() Ahora creamos un nuevo índice para tener en cuenta los eventos de los usuarios, y luego personalizamos las recomendaciones 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 el impacto de incorporar eventos en nuestros RecSys ponderando la personalización o Primero, veamos el efecto (en comparación con la base) de ponderar los espacios que están influenciados por estos eventos (datos de comportamiento). Sólo ligeramente pesado # 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 muy poco peso puesto en los espacios afectados por los eventos, observamos un cambio pero principalmente sólo en la segunda mitad de nuestro top 10, en comparación con los resultados anteriores ("id_base", a la derecha). Pero si pesamos más los espacios afectados por el evento, superponemos elementos completamente nuevos en nuestra lista de recomendaciones. # 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"]] También podemos, por supuesto, utilizar pesos para personalizar nuestras recomendaciones basadas en el comportamiento de un usuario en particular (datos de eventos) y Por ejemplo, el precio (ver ) de priorizar simultáneamente otros atributos del producto Célula 31 Conclusión La implementación de eComm RecSys de la biblioteca Superlinked (abajo) le muestra cómo realizar el poder de las incorporaciones vectoriales incorporando el significado semántico de las consultas de los usuarios y los datos de comportamiento. Usando nuestros espacios de número min_max y similitud de texto, esquema de eventos y efectos, y pesos de tiempo de consulta, puede abordar los desafíos de inicio frío, calidad y relevancia y escalabilidad de RecSys y proporcionar recomendaciones altamente precisas y personalizadas para el usuario en la producción. ¡Ahora es tu turno! . el Trate de implementar la biblioteca Superlinked usted mismo usando nuestro cuaderno Try It Yourself – Get the Code & Demo! ¡Pruébalo tú mismo - ¡Get the Code & Demo! y y y Grab the Code: Compruebe la implementación completa en nuestro repo de GitHub aquí.Fork, ajuste y hazlo tuyo! Verlo en Acción: ¿Quieres ver esto funcionando en un mundo real? Reserva una demostración rápida y explora cómo Superlinked puede sobrecargar tus recomendaciones. Aquí Aquí . el Los motores de recomendación están moldeando la forma en que descubrimos el contenido, ya sean pantalones populares, música u otros productos. —y ahora tienes las herramientas para construir tu propia. vector search is the future