Kredit: NewsWire, The TakeOut.

Sådan oprettes et anbefalingssystem til køb af data (trin for trin)

En anvendelse af varebaseret samarbejdsfiltrering med Turicreate og Python

Uanset om du er ansvarlig for brugeroplevelse og produktstrategi i en kundecentrisk virksomhed, eller sidder i din sofa og ser film med kære, er chancerne for, at du allerede er opmærksom på nogle måder, som anbefalingsteknologi bruges til at tilpasse dit indhold og tilbud.

Anbefalingssystemer er en af ​​de mest almindelige, let forståelige applikationer af big data og maskinlæring. Blandt de mest kendte applikationer er Amazons anbefalingsmotor, der giver os en personlig webside, når vi besøger webstedet, og Spotifys anbefalingsliste over sange, når vi lytter ved hjælp af deres app.

Sidste gang blev vi nødt til at opbygge en Spotify's Discover Weekly med en hovedpart af lyddata ved hjælp af Spark. Denne gang bygger vi en anbefalingsmotor til mere håndgribelige genstande.

Udfordringen

Hvis du kigger online, er der mange måder at opbygge anbefalingssystemer til vurderingsbaserede data, såsom film og sange. Problemet med vurderingsbaserede modeller er, at de ikke let kunne standardiseres til data med ikke-skalerede målværdier, f.eks. Køb eller frekvensdata. For eksempel er klassificeringer normalt fra 0–5 eller 0–10 på tværs af sange og film. Købsdata er dog kontinuerligt og uden en øvre grænse.

En masse online-ressourcer giver desværre resultater uden at evaluere deres modeller. For de fleste dataforskere og ingeniører er dette et farligt område, når du involverer millioner af data! For brancher får resultaterne ikke dine værktøjer nogen steder uden nogen evaluering.

Målet

Når vi løser disse problemer, bygger vi samarbejdsfiltreringsmodeller til anbefaling af produkter til kunder, der bruger købsdata. Vi dækker især detaljerede trin for trin i konstruktion af et anbefalingssystem med Python og maskinlæringsmodul Turicreate. Disse trin inkluderer:

  • Transformering og normalisering af data
  • Træningsmodeller
  • Evaluering af modelydelse
  • Valg af den optimale model

Produkt oversigt

Forestil dig, at en købmandskæde frigiver en ny mobilapp, der giver sine kunder mulighed for at afgive ordrer, før de endda skal gå ind i butikken.

Der er en mulighed for appen til at vise anbefalinger: Når en kunde først banker på “ordre” -siden, kan vi anbefale top 10-elementer, der tilføjes deres kurv, f.eks. engangsredskaber, frisk kød, chips og så videre.

Værktøjet kan også søge efter en anbefalingsliste baseret på en specificeret bruger, således at:

  • Input: kunde-id
  • Returnerer: rangeret liste over varer (produkt-id'er), som brugeren mest sandsynligt ønsker at lægge i sin (tomme) “kurv”

Implementering

1. Importer moduler

  • pandaer og numpy til datamanipulation
  • turicreate til udførelse af modelvalg og evaluering
  • sklearn til opdeling af data i tog- og testsæt
% load_ext autoreload
% automatisk indlæsning 2

importer pandaer som pd
import numpy som np
importtid
import turicreate som tc
fra sklearn.cross_validation import train_test_split

importer sys
sys.path.append ( "..")

2. Indlæs data

To datasæt i .csv-format bruges nedenfor, som kan findes i datamappen her:

  • recommend_1.csv bestående af en liste med 1000 kunde-id'er, der kan anbefales som output
  • trx_data.csv bestående af brugertransaktioner
kunder = pd.read_csv ('../ data / anbefal_1.csv')
transaktioner = pd.read_csv ('../ data / trx_data.csv')

3. Forberedelse af data

Vores mål her er at fordele hver liste over varer i produktkolonnen i rækker og tælle antallet af produkter, der er købt af en bruger

3.1. Opret data med bruger-, element- og målfelt

  • Denne tabel vil være et input til vores modellering senere
  • I dette tilfælde er vores bruger kundeIde, produktId og købskonto
data = pd.melt (transaktioner.set_index ('customerId') ['produkter']. gælder (pd.Series) .reset_index (),
             id_vars = [ 'CustomerID'],
             value_name = 'produkter') \
    .dropna (). drop (['variabel'], akse = 1) \
    .groupby (['kundeId', 'produkter']) \
    .agg ({'products': 'count'}) \
    .rename (columns = {'products': 'purchase_count'}) \
    .reset_index () \
    .rename (columns = {'products': 'productId'})
data ['productId'] = data ['productId']. astype (np.int64)

3.2. Opret dummy

  • Dummy til mærkning af om en kunde købte den vare eller ej.
  • Hvis man køber en vare, markeres køb_dummy som 1
  • Hvorfor oprette en dummy i stedet for at normalisere den, spørger du? Normalisering af købstælling, f.eks. For hver bruger, ville ikke fungere, fordi kunder kan have forskellige købsfrekvenser, der ikke har den samme smag. Vi kan dog normalisere varer efter købsfrekvens på tværs af alle brugere, hvilket gøres i afsnit 3.3. under.
def create_data_dummy (data):
    data_dummy = data.copy ()
    data_dummy ['purchase_dummy'] = 1
    return data_dummy
data_dummy = create_data_dummy (data)

3.3. Normaliser elementværdier på tværs af brugere

  • For at gøre dette normaliserer vi købsfrekvensen for hver vare på tværs af brugere ved først at oprette en bruger-vare-matrix som følger
df_matrix = pd.pivot_table (data, værdier = 'køb_antal', indeks = 'kundeId', kolonner = 'produktId')
df_matrix_norm = (df_matrix-df_matrix.min ()) / (df_matrix.max () - df_matrix.min ())
# Opret en tabel til input til modelleringen
d = df_matrix_norm.reset_index ()
d.index.names = ['skaleret_køb_freq']
data_norm = pd.melt (d, id_vars = ['customerId'], value_name = 'scaled_purchase_freq'). dropna ()
print (data_norm.shape)
data_norm.head ()

Ovenstående trin kan kombineres til en funktion defineret nedenfor:

def normalize_data (data):
    df_matrix = pd.pivot_table (data, værdier = 'køb_antal', indeks = 'kundeId', kolonner = 'produktId')
    df_matrix_norm = (df_matrix-df_matrix.min ()) / (df_matrix.max () - df_matrix.min ())
    d = df_matrix_norm.reset_index ()
    d.index.names = ['skaleret_køb_freq']
    return pd.melt (d, id_vars = ['customerId'], value_name = 'scaled_purchase_freq'). dropna ()

I dette trin har vi normaliseret deres købshistorik fra 0–1 (hvor 1 er det største antal køb for en vare og 0 er 0 købstælling for den vare).

4. Opdel tog og testsæt

  • Opdeling af data i trænings- og testsæt er en vigtig del af evalueringen af ​​forudsigelig modellering, i dette tilfælde en samarbejdsfiltreringsmodel. Vi bruger typisk en større del af dataene til træning og en mindre del til test.
  • Vi bruger 80:20-forholdet til vores størrelsestogstest.
  • Vores træningsdel vil blive brugt til at udvikle en forudsigelig model, mens den anden til at evaluere modellens præstation.

Lad os definere en opdelingsfunktion nedenfor.

def split_data (data):
    '''
    Opdeler datasæt i trænings- og testsæt.
    
    args:
        data (pandas.DataFrame)
        
    Vender tilbage
        train_data (tc.SFrame)
        test_data (tc.SFrame)
    '''
    tog, test = tog_test_split (data, test_størrelse = .2)
    train_data = tc.SFrame (train)
    test_data = tc.SFrame (test)
    return train_data, test_data

Nu hvor vi har tre datasæt med købstællinger, købdummy og skaleret købstælling, vil vi gerne dele hver til modellering.

train_data, test_data = split_data (data)
train_data_dummy, test_data_dummy = split_data (data_dummy)
train_data_norm, test_data_norm = split_data (data_norm)

5. Definer modeller ved hjælp af Turicreate-bibliotek

Inden vi kører en mere kompliceret tilgang, såsom samarbejdsfiltrering, skal vi køre en basismodel for at sammenligne og evaluere modeller. Da baseline typisk bruger en meget enkel tilgang, bør teknikker, der anvendes ud over denne tilgang, vælges, hvis de viser relativt bedre nøjagtighed og kompleksitet. I dette tilfælde bruger vi popularitetsmodel.

En mere kompliceret, men almindelig tilgang til at forudsige købsprodukter er samarbejdsfiltrering. Jeg vil diskutere mere om popularitetsmodellen og samarbejdsfiltrering i det senere afsnit. Lad os nu definere vores variabler, der skal bruges i modellerne:

# konstante variabler til at definere feltnavne inkluderer:
user_id = 'customerId'
item_id = 'productId'
users_to_recommend = liste (kunder [user_id])
n_rec = 10 # antal varer at anbefale
n_display = 30 # for at få vist de første par rækker i et outputdatasæt

Turicreate har gjort det super let for os at kalde en modelleringsteknik, så lad os definere vores funktion for alle modeller som følger:

def-model (train_data, name, user_id, item_id, target, users_to_recommend, n_rec, n_display):
    hvis navn == 'popularitet':
        model = tc.popularity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    target = mål)
    elif name == 'cosinus':
        model = tc.item_similarity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    target = target,
                                                    similarity_type = 'cosinus')
elif name == 'pearson':
        model = tc.item_similarity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    target = target,
                                                    similarity_type = 'Pearson')
        
    recom = model.recommend (brugere = users_to_recommend, k = n_rec)
    recom.print_rows (n_display)
    retur model

Mens jeg skrev python-scripts til alle ovennævnte processer, herunder at finde lighed ved hjælp af python-scripts (som kan findes her, bruger vi turicreate-bibliotek til nu for at fange forskellige mål hurtigere og evaluere modeller.

6. Popularitetsmodel som baseline

  • Popularitetsmodellen tager de mest populære varer til anbefaling. Disse varer er produkter med det største antal sælger på tværs af kunder.
  • Træningsdata bruges til modelvalg

jeg. Brug af købstælling

name = 'popularitet'
target = 'purchase_count'
popularitet = model (train_data, navn, user_id, item_id, target, users_to_recommend, n_rec, n_display)

ii. Brug af køb dummy

name = 'popularitet'
target = 'purchase_dummy'
pop_dummy = model (train_data_dummy, navn, user_id, item_id, target, users_to_recommend, n_rec, n_display)

iii. Brug af skaleret købstælling

name = 'popularitet'
target = 'scaled_purchase_freq'
pop_norm = model (train_data_norm, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

6.1. Basisoversigt

  • Når vi oprettede modellen, forudsagde vi anbefalingsemnerne ved hjælp af score efter popularitet. Som du kan se for hver modelresultat ovenfor, viser rækkerne de første 30 poster fra 1000 brugere med 10 anbefalinger. Disse 30 poster inkluderer 3 brugere og deres anbefalede varer sammen med score og faldende rækker.
  • I resultatet, selvom forskellige modeller har forskellig anbefalingsliste, anbefales hver bruger den samme liste med 10 elementer. Dette skyldes, at popularitet beregnes ved at tage de mest populære varer på tværs af alle brugere.
  • Hvis et grupperingsexempel nedenfor, er produkter 132, 248, 37 og 34 de mest populære (bedst sælgende) på tværs af kunder. Ved hjælp af deres indkøbstælling divideret med antallet af kunder ser vi, at disse produkter i det mindste købes 3 gange i gennemsnit i træningssættet med transaktioner (det samme som den første popularitetsmåling på variablen køb_antal)

7. Samarbejdsfiltreringsmodel

I samarbejdsfiltrering vil vi anbefale emner baseret på, hvordan lignende brugere køber varer. For eksempel, hvis kunde 1 og kunde 2 købte lignende varer, f.eks. 1 købte X, Y, Z og 2 købte X, Y, vi vil anbefale en artikel Z til kunde 2.

7.1. Metode

For at definere lighed på tværs af brugere bruger vi følgende trin:

1. Opret en brugerelementmatrix, hvor indeksværdier repræsenterer unikke kunde-id'er og kolonneværdier repræsenterer unikke produkt-id'er

2. Opret en matrix til lighed mellem elementer og ting. Tanken er at beregne, hvor længe et produkt ligner et andet produkt. Der er en række måder at beregne dette på. I trin 7.2 og 7.3 bruger vi henholdsvis cosinus- eller pearsons-lighedstiltag.

  • For at beregne ligheden mellem produkter X og Y skal du se på alle kunder, der har bedømt begge disse varer. For eksempel er både X og Y bedømt af kunder 1 og 2.
  • Derefter opretter vi to elementvektorer, v1 for punkt X og v2 for punkt Y, i brugerrummet (1, 2) og finder derefter cosinus- eller pærsonvinklen / afstanden mellem disse vektorer. En vinkel på nul eller overlappende vektorer med en cosinusværdi på 1 betyder total lighed (eller pr. Bruger, på tværs af alle emner, der er samme bedømmelse), og en vinkel på 90 grader ville betyde cosinus på 0 eller ingen lighed.

3. For hver kunde forudsiger vi derefter hans sandsynlighed for at købe et produkt (eller hans indkøbstælling) for produkter, som han ikke havde købt.

  • For vores eksempel beregner vi bedømmelse for bruger 2 i tilfælde af Z (målpost). For at beregne dette vejer vi den lige beregnede lighedsmåling mellem målelementet og andre varer, som kunden allerede har købt. Vejefaktoren er de indkøbstællinger, som brugeren har givet til varer, der allerede er købt af ham.
  • Derefter skalerer vi denne vægtede sum med summen af ​​lighedstiltag, så den beregnede bedømmelse forbliver inden for et foruddefinerede grænser. Således beregnes den forudsagte vurdering for punkt Z for bruger 2 ved anvendelse af lighedstiltag.

7.2. Kosinelighed

  • Lighed er kosinus i vinklen mellem de 2 vektorer i elementvektorerne i A og B
  • Det er defineret ved følgende formel
  • Tættere vektorerne, mindre vil vinklen være og større kosinus

jeg. Brug af købstælling

name = 'cosinus'
target = 'purchase_count'
cos = model (train_data, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

ii. Brug af køb dummy

name = 'cosinus'
target = 'purchase_dummy'
cos_dummy = model (train_data_dummy, navn, user_id, item_id, target, users_to_recommend, n_rec, n_display)

iii. Brug af skaleret købstælling

name = 'cosinus'
target = 'scaled_purchase_freq'
cos_norm = model (train_data_norm, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

7.3. Pearson lighed

  • Lighed er perleskoefficienten mellem de to vektorer.
  • Det er defineret ved følgende formel

jeg. Brug af købstælling

name = 'pearson'
target = 'purchase_count'
pear = model (train_data, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

ii. Brug af køb dummy

name = 'pearson'
target = 'purchase_dummy'
pear_dummy = model (train_data_dummy, navn, user_id, item_id, target, users_to_recommend, n_rec, n_display)

iii. Brug af skaleret købstælling

name = 'pearson'
target = 'scaled_purchase_freq'
pear_norm = model (train_data_norm, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

8. Modelevaluering

Til evaluering af anbefalingsmotorer kan vi bruge begrebet RMSE og præcisionsgenkaldelse.

jeg. RMSE (rod gennemsnit kvadratiske fejl)

  • Måler fejlen ved forudsagte værdier
  • Mindre RMSE-værdi, bedre anbefalingerne

ii. Minde om

  • Hvilken procentdel af produkter, som en bruger køber, anbefales faktisk?
  • Hvis en kunde køber 5 produkter, og anbefalingen besluttede at vise 3 af dem, er tilbagekaldelsen 0,6

iii. Præcision

  • Hvor mange af brugerne kunne lide det ud af alle de anbefalede varer?
  • Hvis der blev anbefalet 5 produkter til kunden, hvoraf han køber 4 af dem, er præcisionen 0,8

Hvorfor er både tilbagekaldelse og præcision vigtig?

  • Overvej et tilfælde, hvor vi anbefaler alle produkter, så vores kunder helt sikkert vil dække de varer, de kunne lide og købt. I dette tilfælde har vi 100% tilbagekaldelse! Betyder det, at vores model er god?
  • Vi skal overveje præcision. Hvis vi anbefaler 300 varer, men brugeren kan lide og køber kun 3 af dem, er præcisionen 0,1%! Denne meget lave præcision indikerer, at modellen ikke er stor på trods af deres fremragende tilbagekaldelse.
  • Så vores mål skal være at optimere både tilbagekaldelse og præcision (for at være tæt på 1 som muligt).

Lad os først oprette indledende konverterbare variabler til modelevaluering:

models_w_counts = [popularitet_model, cos, pære]
models_w_dummy = [pop_dummy, cos_dummy, pear_dummy]
models_w_norm = [pop_norm, cos_norm, pear_norm]
names_w_counts = ['Popularitetsmodel ved købstællinger', 'Cosine-lighed ved købstællinger', 'Pearson lighed ved købstællinger']
names_w_dummy = ['Popularitetsmodel på indkøbsdummi', 'Cosine-lighed på køb-dummy', 'Pearson-lighed på køb-dummy']
names_w_norm = ['Popularitetsmodel for skalerede indkøbstællinger', 'Cosine-lighed på skalerede indkøbstællinger', 'Pearson lighed med skalerede indkøbstællinger']

Lad os sammenligne alle de modeller, vi har bygget, baseret på RMSE og præcisionsgenkaldelsesegenskaber:

eval_counts = tc.recommender.util.compare_models (test_data, models_w_counts, model_names = names_w_counts)
eval_dummy = tc.recommender.util.compare_models (test_data_dummy, models_w_dummy, model_names = names_w_dummy)
eval_norm = tc.recommender.util.compare_models (test_data_norm, models_w_norm, model_names = names_w_norm)

8.1. Evaluering Output

  • Baseret på RMSE
  • Baseret på præcision og tilbagekaldelse

8.2. Evalueringsoversigt

  • Popularitet v. Samarbejdsfiltrering: Vi kan se, at de samarbejdsfiltreringsalgoritmer fungerer bedre end popularitetsmodel til købstællinger. Faktisk giver popularitetsmodel ingen personaliseringer, da den kun giver den samme liste over anbefalede varer til enhver bruger.
  • Præcision og tilbagekaldelse: Når vi ser på resuméet ovenfor, ser vi, at præcisionen og tilbagekaldelsen for indkøbstællinger> Indkøbsdummy> Normaliserede indkøbstællinger. Fordi anbefalingens score for de normaliserede købsdata er nul og konstant, vælger vi dummy. Faktisk er RMSE ikke meget forskellig mellem modeller på dummy og modeller på normaliserede data.
  • RMSE: Da RMSE er højere ved hjælp af pearson-afstand thancosin, ville vi vælge model for de mindre gennemsnitlige kvadratiske fejl, som i dette tilfælde ville være kosinus.
Derfor vælger vi Cosine-ligheden på køb Dummy-tilgangen som vores endelige model.

9. Endelig output

Til sidst vil vi gerne manipulere format til anbefalingsudgang til et, vi kan eksportere til csv, og også en funktion, der returnerer anbefalingsliste, givet et kunde-id.

Vi skal først køre modellen vha. Hele datasættet, da vi kom til en endelig model ved hjælp af togdata og evalueres med testsæt.

final_model = tc.item_similarity_recommender.create (tc.SFrame (data_norm),
                                            user_id = user_id,
                                            item_id = item_id,
                                            target = 'køb_dummy', lighed_type = 'kosinus')
recom = final_model.recommend (brugere = users_to_recommend, k = n_rec)
recom.print_rows (n_display)

9.1. CSV-outputfil

Her ønsker vi at manipulere vores resultat til en csv-output. Lad os se, hvad vi har:

df_rec = recom.to_dataframe ()
print (df_rec.shape)
df_rec.head ()

Lad os definere en funktion til at oprette en ønsket output:

def create_output (model, users_to_recommend, n_rec, print_csv = True):
    recomendation = model.recommend (brugere = brugere_til_anbefaling, k = n_rec)
    df_rec = recomendation.to_dataframe ()
    df_rec ['recommendedProducts'] = df_rec.groupby ([user_id]) [item_id] \
        .transform (lambda x: '|' .join (x.astype (str)))
    df_output = df_rec [['customerId', 'recommendedProducts']]. drop_duplicates () \
        .sort_values ​​( 'CustomerID'). set_index ( 'CustomerID')
    hvis print_csv:
        df_output.to_csv ( '../ output / option1_recommendation.csv')
        print ("En outputfil kan findes i 'output' mappe med navn 'option1_recommendation.csv'")
    returner df_output

Gør det muligt at udskrive output nedenfor og sætteprint_csv til sandt, på denne måde kan vi bogstaveligt talt udskrive vores outputfil i csv, som du også kan finde den her.

df_output = create_output (pear_norm, users_to_recommend, n_rec, print_csv = True)
print (df_output.shape)
df_output.head ()

9.2. Kundeanbefalingsfunktion

Lad os definere en funktion, der returnerer anbefalingsliste med en kunde-ID:

def customer_recomendation (customer_id):
    hvis customer_id ikke i df_output.index:
        print ('Kunden blev ikke fundet.')
        returner kunde_id
    returner df_output.loc [customer_id]

Bingo!

Resumé

I denne artikel var vi i stand til at krydse en trin-for-trin-proces til at fremsætte anbefalinger til kunderne. Vi brugte samarbejdsfiltreringsmetoder med Cosine og Pearson-mål og sammenlignede modellerne med vores baseline popularitetsmodel.

Vi udarbejdede også tre datasæt, der inkluderer regelmæssigt købstælling, køb af dummy samt normaliseret købsfrekvens som vores målvariabel. Ved hjælp af RMSE, præcision og tilbagekaldelse evaluerede vi vores modeller og observerede indvirkningen af ​​personalisering. Endelig valgte vi Cosine-metoden ved hjælp af dummy-data som vores bedste anbefalingssystemmodel.

Håber du kan lide at læse denne artikel og nu er klar til at oprette din egen "tilføjelse til indkøbskurv" -knap. Giv 50 klapper og kommenter nedenunder, hvis du vil have flere læsninger som dette :) Nyd hacking!

Moorissa er en mission-drevet dataforsker og social virksomhedsentusiast. I december 2017 uddannede hun sig fra Columbia University med en undersøgelse i datavidenskab og maskinlæring. Hun håber altid at kunne udnytte sine evner til at gøre verden til et bedre sted, en dag ad gangen.