paint-brush
Sluta rulla, börja bygga: Skapa din egen AI-filmrekommenderareförbi@superlinked
Ny historia

Sluta rulla, börja bygga: Skapa din egen AI-filmrekommenderare

förbi Superlinked11m2025/03/06
Read on Terminal Reader

För länge; Att läsa

Lär dig hur du bygger ett anpassat, AI-drivet rekommendationssystem som förutsäger din nästa favoritfilm med precision. I den här handledningen guidar vi dig genom processen att skapa ett filmrekommendationssystem med hjälp av vektordatabaser. Du kommer att lära dig hur moderna AI-rekommendationsmotorer fungerar och får praktisk erfarenhet av att bygga ditt eget system med Superlinked.
featured image - Sluta rulla, börja bygga: Skapa din egen AI-filmrekommenderare
Superlinked HackerNoon profile picture
0-item
1-item

"Jag sa att jag ville ha en B-film för fan!"

Ett slut på ändlös rullning (och argument om vad man ska titta på...)

Trött på att oändligt rulla igenom Netflix, osäker på vad du ska se härnäst? Tänk om du kunde bygga ditt eget anpassade, AI-drivna rekommendationssystem som förutsäger din nästa favoritfilm med precision?


I den här handledningen guidar vi dig genom processen att skapa ett filmrekommendationssystem med hjälp av vektordatabaser (VectorDBs) . Du lär dig hur moderna AI-rekommendationsmotorer fungerar och får praktisk erfarenhet av att bygga ditt eget system med Superlinked .


(Vill du hoppa direkt till koden? Kolla in vår repo på GitHub här . Är du redo att prova rekommendatorsystem för ditt eget bruk? Skaffa en demo här .)

Låt oss rekommendera!

Vi kommer att följa den här anteckningsboken genom hela artikeln. Du kan också köra koden direkt från din webbläsare med hjälp av Colab.


Netflix rekommendationsalgoritm gör ett ganska bra jobb med att föreslå relevant innehåll - med tanke på den stora mängden alternativ (~16 000 filmer och TV-program 2023) och hur snabbt den måste föreslå program till användare. Hur gör Netflix det? Med ett ord, semantisk sökning .


Semantisk sökning förstår innebörden och sammanhanget (både attribut och konsumtionsmönster) bakom användarfrågor och beskrivningar av filmer/TV-program, och kan därför ge bättre personalisering i sina frågor och rekommendationer än traditionella sökordsbaserade tillvägagångssätt.


Men semantisk sökning innebär vissa utmaningar - främst bland dem: 1) säkerställa korrekta sökresultat, 2) tolkningsbarhet och 3) skalbarhet - utmaningar som alla framgångsrika innehållsrekommendationer måste hantera. Genom att använda Superlinkeds bibliotek kan du övervinna dessa svårigheter.


I den här artikeln visar vi dig hur du använder Superlinked-biblioteket för att ställa in din egen semantiska sökning och generera en lista över relevanta filmer baserat på dina preferenser.

Semantisk sökning - utmaningar

Semantisk sökning förmedlar mycket värde i vektorsökning men innebär tre betydande utmaningar för inbäddning av vektorer för utvecklare:

  • Kvalitet och relevans : För att säkerställa att dina inbäddningar korrekt fångar den semantiska innebörden av dina data krävs noggrant urval av inbäddningstekniker, träningsdata och hyperparametrar. Inbäddningar av dålig kvalitet kan leda till felaktiga sökresultat och irrelevanta rekommendationer.


  • Tolkbarhet : Högdimensionella vektorrum är för komplicerade för att vara lätta att förstå. För att få insikter i relationerna och likheterna som kodas inom dem måste datavetare utveckla metoder för att visualisera och analysera dem.


  • Skalbarhet : Hantering och bearbetning av högdimensionella inbäddningar, särskilt i stora datamängder, kan anstränga beräkningsresurser och öka latensen. Effektiva metoder för indexering, hämtning och likhetsberäkning är avgörande för att säkerställa skalbarhet och realtidsprestanda i produktionsmiljöer.


Superlinked-biblioteket gör att du kan hantera dessa utmaningar. Nedan bygger vi en innehållsrekommendator (specifikt för filmer), börjar med information vi har om en given film, bäddar in denna information som en multimodal vektor, bygger ut ett sökbart vektorindex för alla våra filmer och använder sedan frågevikter för att justera våra resultat och komma fram till bra filmrekommendationer. Låt oss gå in i det.

Skapa en snabb och pålitlig sökupplevelse med Superlinked

Nedan utför du en semantisk sökning på Netflix-filmdataset med hjälp av följande element i Superlinked-biblioteket:

  • Nyhetsutrymme - för att förstå färskheten (valuta och relevans) för dina data, identifiera nyare filmer.
  • TextSimilarity space - för att tolka de olika bitarna av metadata du har om filmen, såsom beskrivning, titel och genre.
  • Frågetidsvikter – låter dig välja vad som är viktigast i din data när du kör frågan, och optimerar därigenom utan att behöva bädda in hela datamängden igen, göra efterbearbetning eller använda en anpassad omrankningsmodell (dvs. minska latensen).

Netflix-datauppsättningen och vad vi ska göra med den

Att framgångsrikt rekommendera filmer är svårt, främst för att det finns så många alternativ (>9000 titlar 2023), och användare vill ha rekommendationer på begäran, omedelbart. Låt oss ta ett datadrivet tillvägagångssätt för att hitta något vi vill titta på. I vår datauppsättning av filmer känner vi till:

  • beskrivning
  • genre
  • titel
  • release_year


Vi kan bädda in dessa ingångar och sätta ihop ett vektorindex ovanpå våra inbäddningar, vilket skapar ett utrymme som vi kan söka semantiskt.


När vi har vårt indexerade vektorutrymme kommer vi:

  • först, bläddra bland filmerna, filtrerade av en idé (hjärtlig romantisk komedi)
  • justera sedan resultaten och ge mer vikt åt matchningar i vissa inmatningsfält (dvs viktning)
  • sök sedan i beskrivning, genre och titel med olika söktermer för var och en
  • och, efter att ha hittat en film som är en nära men inte exakt matchning, kan du också söka runt och använda den filmen som referens

Installation och förberedelse av datamängder

Ditt första steg är att installera biblioteket och importera de erforderliga klasserna.


(Obs: Nedan ändrar du alt.renderers.enable(“mimetype”) till alt.renderers.enable('colab') om du kör detta i google colab . Behåll "mimetype" om du kör i github .)


 %pip install superlinked==5.3.0 from datetime import timedelta, datetime import altair as alt import os import pandas as pd from superlinked.evaluation.charts.recency_plotter import RecencyPlotter from superlinked.framework.common.dag.context import CONTEXT_COMMON, CONTEXT_COMMON_NOW from superlinked.framework.common.dag.period_time import PeriodTime from superlinked.framework.common.schema.schema import schema from superlinked.framework.common.schema.schema_object import String, Timestamp 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.query.param import Param from superlinked.framework.dsl.query.query import Query from superlinked.framework.dsl.query.result import Result 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.recency_space import RecencySpace alt.renderers.enable("mimetype") # NOTE: to render altair plots in colab, change 'mimetype' to 'colab' alt.data_transformers.disable_max_rows() pd.set_option("display.max_colwidth", 190)


Vi behöver också förbereda datamängden - definiera tidskonstanter, ställa in URL-platsen för datan, skapa en datalagringsordbok, läsa in CSV:en i en pandas DataFrame, rensa dataramen och data så att den kan sökas ordentligt, och gör en snabb verifiering och översikt. (Se cell 3 och 4 för detaljer.)


Nu när datasetet är förberett kan du optimera din hämtning med hjälp av Superlinked-biblioteket.

Bygger ut indexet för vektorsökning

Superlinkeds bibliotek innehåller en uppsättning kärnbyggstenar som vi använder för att konstruera ett index och hantera hämtning. Du kan läsa mer om dessa byggstenar här .


Först måste du definiera ditt Schema för att berätta för systemet om dina data.

 # accommodate our inputs in a typed schema @schema class MovieSchema: description: String title: String release_timestamp: Timestamp genres: String id: IdField movie = MovieSchema()


Därefter använder du Spaces för att säga hur du vill behandla varje del av data när du bäddar in. Vilka utrymmen som används beror på din datatyp. Varje utrymme är optimerat för att bädda in data för att ge högsta möjliga kvalitet på hämtningsresultaten.


I rymddefinitioner beskriver vi hur indata ska vara inbäddade för att spegla de semantiska sambanden i vår data.


 # textual fields are embedded using a sentence-transformers model description_space = TextSimilaritySpace( text=movie.description, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) title_space = TextSimilaritySpace( text=movie.title, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) genre_space = TextSimilaritySpace( text=movie.genres, model="sentence-transformers/paraphrase-MiniLM-L3-v2" ) # release date are encoded using our recency space # periodtimes aim to reflect notable breaks in our scores recency_space = RecencySpace( timestamp=movie.release_timestamp, period_time_list=[ PeriodTime(timedelta(days=4 * YEAR_IN_DAYS)), PeriodTime(timedelta(days=10 * YEAR_IN_DAYS)), PeriodTime(timedelta(days=40 * YEAR_IN_DAYS)), ], negative_filter=-0.25, ) movie_index = Index(spaces=[description_space, title_space, genre_space, recency_space])


När du har ställt in dina utrymmen och skapat ditt index använder du käll- och exekveringsdelen av biblioteket för att ställa in dina frågor. Se cellerna 10-13 i anteckningsboken .


Nu när frågorna är förberedda, låt oss gå vidare till att köra frågor och optimera hämtning genom att justera vikter.

Förstå senaste nytt och hur man använder det i Superlinked

Nyhetsutrymmet låter dig ändra resultaten av din fråga genom att i första hand dra in äldre eller nyare versioner från din datauppsättning. Vi använder 4, 10 och 40 år som våra periodtider så att vi kan ge år med fler titlar mer fokus - se cell 5 ).


Lägg märke till pauserna i poängen vid 4, 10 och 40 år. Titlar äldre än 40 år får en negative_filter -poäng.

Nyhetspoäng per period

Granska och optimera sökresultat med hjälp av olika frågetidsvikter

Låt oss definiera en snabbfunktion för att presentera våra resultat i anteckningsboken.


 def present_result( result: Result, cols_to_keep: list[str] = ["description", "title", "genres", "release_year", "id"], ) -> pd.DataFrame: # parse result to dataframe df: pd.DataFrame = result.to_pandas() # transform timestamp back to release year df["release_year"] = [ datetime.fromtimestamp(timestamp).year for timestamp in df["release_timestamp"] ] return df[cols_to_keep]


Enkla och avancerade frågor

Superlinked-biblioteket låter dig utföra olika typer av frågor; här definierar vi två. Båda våra frågetyper av frågeställningar (enkla och avancerade) låter mig väga individuella utrymmen (beskrivning, titel, genre och naturligtvis nycitet) enligt mina preferenser. Skillnaden mellan dem är att med en enkel fråga ställer jag in en frågetext och visar sedan liknande resultat i beskrivningen, titeln och genren.


Med en avancerad fråga har jag mer finkornig kontroll. Om jag vill kan jag ange olika frågetexter i var och en av beskrivnings-, titel- och genreutrymmen. Här är frågekoden:


 query_text_param = Param("query_text") simple_query = ( Query( movie_index, weights={ description_space: Param("description_weight"), title_space: Param("title_weight"), genre_space: Param("genre_weight"), recency_space: Param("recency_weight"), }, ) .find(movie) .similar(description_space.text, query_text_param) .similar(title_space.text, query_text_param) .similar(genre_space.text, query_text_param) .limit(Param("limit")) ) advanced_query = ( Query( movie_index, weights={ description_space: Param("description_weight"), title_space: Param("title_weight"), genre_space: Param("genre_weight"), recency_space: Param("recency_weight"), }, ) .find(movie) .similar(description_space.text, Param("description_query_text")) .similar(title_space.text, Param("title_query_text")) .similar(genre_space.text, Param("genre_query_text")) .limit(Param("limit")) )


Enkel fråga

I enkla frågor ställer jag in min frågetext och lägger olika vikter beroende på deras betydelse för mig.


 result: Result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=1, genre_weight=1, recency_weight=0, limit=TOP_N, ) present_result(result) 


Enkla sökresultat 1

Våra resultat innehåller några titlar som jag redan har sett. Jag kan hantera detta genom att vikta nycitet för att påverka mina resultat mot de senaste titlarna. Vikter är normaliserade till att ha en enhetssumma (dvs alla vikter justeras så att de alltid summeras till totalt 1), så du behöver inte oroa dig för hur du ställer in dem.


 result: Result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=1, genre_weight=1, recency_weight=3, limit=TOP_N, ) present_result(result) 


Enkla sökresultat 1

Mina resultat (ovan) är nu alla efter 2021.


Med den enkla frågan kan jag vikta vilket specifikt utrymme som helst (beskrivning, titel, genre eller senaste nytt) för att få det att räknas mer när jag returnerar resultat. Låt oss experimentera med detta. Nedan kommer vi att ge mer tyngd åt genren och nedviktstiteln - min frågetext är i princip bara en genre med lite extra sammanhang. Jag behåller min uppdatering som den är eftersom jag fortfarande vill att mina resultat ska vara partiska mot de senaste filmerna.


 result = app.query( simple_query, query_text="Heartfelt romantic comedy", description_weight=1, title_weight=0.1, genre_weight=2, recency_weight=1, limit=TOP_N, ) present_result(result)


Den här frågan skjuter tillbaka releaseåret lite för att ge mig mer genreviktade resultat (nedan).


Enkla frågeresultat 3

Avancerad fråga

Den avancerade frågan ger mig ännu mer finkornig kontroll. Jag behåller kontrollen över senaste tiden, men kan också ange söktext för beskrivning, titel och genre, och tilldela var och en en specifik vikt enligt mina preferenser, enligt nedan (och celler 19-21 ),

 result = app.query( advanced_query, description_query_text="Heartfelt lovely romantic comedy for a cold autumn evening.", title_query_text="love", genre_query_text="drama comedy romantic", description_weight=0.2, title_weight=3, genre_weight=1, recency_weight=5, limit=TOP_N, ) present_result(result)


Sök med en specifik film

Säg i mina senaste filmresultat, jag hittade en film som jag redan har sett och skulle vilja se något liknande. Låt oss anta att jag gillar White Christmas, en romantisk komedi från 1954 (id = tm16479) om sångare-dansare som samlas för en scenshow för att locka gäster till ett kämpande värdshus i Vermont. Genom att lägga till en extra with_vector -sats (med en movie_id parameter) till advanced_query, låter with_movie_query mig söka med den här filmen (eller vilken film jag gillar), och ger mig all den finkorniga kontrollen av separat undersökningsfrågetext och viktning.


Först lägger vi till vår movie_id-parameter:

 with_movie_query = advanced_query.with_vector(movie, Param("movie_id"))


Och sedan kan jag ställa in mina andra undersökningar till antingen tomma eller vad som är mest relevant, tillsammans med alla viktningar som är vettiga. Låt oss säga att min första fråga ger resultat som återspeglar scenframträdandet/bandaspekten av White Christmas (se cell 24 ), men jag vill se en film som är mer familjeorienterad. Jag kan ange en description_query_text för att snedställa mina resultat i önskad riktning.

 result = app.query( with_movie_query, description_query_text="family", title_query_text="", genre_query_text="", description_weight=1, title_weight=0, genre_weight=0, recency_weight=0, description_query_weight=1, movie_id="tm16479", limit=TOP_N, ) present_result(result) 


Avancerade frågeresultat 1

Men nu när jag ser mina resultat inser jag att jag faktiskt är mer på humör för något lättsamt och roligt. Låt oss justera min fråga därefter:


 Result = app.query( with_movie_query, description_query_text="", title_query_text="", genre_query_text="comedy", description_weight=1, title_weight=0, genre_weight=2, recency_weight=0, description_query_weight=1, movie_id="tm16479", limit=TOP_N, ) present_result(result) 


Avancerade frågeresultat 2

Okej, de här resultaten är bättre. Jag väljer en av dessa. Sätt på popcornen!

Slutsats

Superlinked gör det enkelt att testa, iterera och förbättra din hämtningskvalitet. Ovan har vi gått igenom hur du använder Superlinked-biblioteket för att göra en semantisk sökning på ett vektorutrymme, som Netflix gör, och returnera korrekta, relevanta filmresultat. Vi har också sett hur vi finjusterar våra resultat, justerar vikter och söktermer tills vi kommer till precis rätt resultat.


Prova nu anteckningsboken själv och se vad du kan uppnå!

Prova själv – Skaffa koden och demo!

  • 💾 Ta tag i koden : Kolla in den fullständiga implementeringen i vår GitHub-repo här . . Dela den, finjustera den och gör den till din egen!


  • 🚀 Se det i aktion : Vill du se detta fungera i en verklig installation? Boka en snabb demo och utforska hur Superlinked kan förstärka dina rekommendationer. Skaffa en demo nu !


Rekommendationsmotorer formar hur vi upptäcker innehåll. Oavsett om det är filmer, musik eller produkter är vektorsökning framtiden – och nu har du verktygen för att bygga din egen.


Författare: Mór Kapronczay