Crédit: NewsWire, The TakeOut.

Comment créer un système de recommandation pour les données d'achat (étape par étape)

Une application de filtrage collaboratif basé sur les articles avec Turicreate et Python

Que vous soyez responsable de l'expérience utilisateur et de la stratégie produit dans une entreprise centrée sur le client, ou assis dans votre canapé à regarder des films avec vos proches, il est probable que vous sachiez déjà comment la technologie de recommandation est utilisée pour personnaliser votre contenu et vos offres.

Les systèmes de recommandation sont l’une des applications les plus courantes et les plus faciles à comprendre du Big Data et de l’apprentissage automatique. Parmi les applications les plus connues, citons le moteur de recommandation d'Amazon qui nous fournit une page Web personnalisée lorsque nous visitons le site et la liste de recommandations de Spotify lorsque nous écoutons à l'aide de leur application.

La dernière fois, nous avons créé Spotify dans Discover Weekly avec un volume de données audio utilisant Spark. Cette fois, nous allons construire un moteur de recommandation pour des éléments plus concrets.

Le défi

Si vous recherchez en ligne, il existe de nombreuses façons de créer des systèmes de recommandation pour les données basées sur le classement, telles que les films et les chansons. Le problème des modèles fondés sur les notations est qu’ils ne pourraient pas être facilement normalisés pour les données contenant des valeurs cibles non mises à l’échelle, telles que les données d’achat ou de fréquence. Par exemple, les notes vont généralement de 0 à 5 ou de 0 à 10 pour les chansons et les films. Cependant, les données d'achat sont continues et sans limite supérieure.

Malheureusement, de nombreuses ressources en ligne donnent des résultats sans évaluer leurs modèles. Pour la plupart des scientifiques et ingénieurs en informatique, c’est un domaine dangereux lorsque vous utilisez des millions de données! Pour les industries, les résultats à eux seuls n’auront vos outils nulle part sans évaluation.

Le but

Pour résoudre ces problèmes, nous allons créer des modèles de filtrage collaboratif permettant de recommander des produits aux clients à l'aide de données d'achat. En particulier, nous verrons en détail le processus étape par étape de la construction d’un système de recommandation avec Python et le module d’apprentissage automatique Turicreate. Ces étapes comprennent:

  • Transformer et normaliser les données
  • Modèles de formation
  • Évaluation de la performance du modèle
  • Sélection du modèle optimal

Présentation du produit

Imaginez qu'une chaîne d'épicerie lance une nouvelle application mobile permettant à ses clients de passer des commandes avant même d'avoir à entrer dans le magasin.

Il est également possible que l'application affiche des recommandations: lorsqu'un client appuie pour la première fois sur la page "Commande", nous pouvons recommander l'ajout des 10 principaux articles à leur panier, par exemple. ustensiles jetables, viande fraîche, frites, etc.

L'outil sera également capable de rechercher une liste de recommandations basée sur un utilisateur spécifié, telle que:

  • Entrée: identifiant client
  • Retours: liste classée d'éléments (ID de produit), que l'utilisateur est le plus susceptible de vouloir mettre dans son "panier" (vide).

la mise en oeuvre

1. Modules d'importation

  • pandas et numpy pour la manipulation de données
  • turicreate pour effectuer la sélection et l'évaluation du modèle
  • sklearn pour fractionner les données en train et en ensemble de test
% load_ext autoreload
% auto-chargement 2

importer des pandas en tant que pd
importer numpy en tant que np
temps d'importation
importer turicreate en tant que tc
from sklearn.cross_validation import train_test_split

système d'importation
sys.path.append ("..")

2. Charger des données

Deux jeux de données au format .csv sont utilisés ci-dessous. Ils se trouvent dans le dossier data:

  • recommend_1.csv consistant en une liste de 1000 identifiants client à recommander en sortie
  • trx_data.csv constitué de transactions utilisateur
clients = pd.read_csv ('../ data / recommend_1.csv')
transactions = pd.read_csv ('../ data / trx_data.csv')

3. Préparation des données

Notre objectif ici est de décomposer chaque liste d'éléments de la colonne de produits en lignes et de compter le nombre de produits achetés par un utilisateur.

3.1. Créer des données avec un utilisateur, un élément et un champ cible

  • Ce tableau sera une entrée pour notre modélisation plus tard
  • Dans ce cas, notre utilisateur est customerId, productId et Purchase_count.
data = pd.melt (transactions.set_index ('customerId') ['produits']]. apply (pd.Series) .reset_index (),
             id_vars = ['customerId'],
             valeur_nom = 'produits') \
    .dropna (). drop (['variable'], axis = 1) \
    .groupby (['customerId', 'products']) \
    .agg ({'products': 'count'}) \
    .rename (columns = {'products': 'purchase_count'}) \
    .reset_index () \
    .rename (columns = {'products': 'productId'})
data ['productId'] = data ['productId']. astype (np.int64)

3.2. Créer un mannequin

  • Mannequin pour marquer si un client a acheté cet article ou non.
  • Si on achète un article, buy_dummy est marqué comme 1
  • Pourquoi créer un mannequin au lieu de le normaliser, demandez-vous? Normaliser le nombre d’achats, disons par chaque utilisateur, ne fonctionnerait pas car les clients pouvaient avoir une fréquence d’achat différente et n’avaient pas le même goût. Cependant, nous pouvons normaliser les articles en fonction de la fréquence d'achat de tous les utilisateurs, comme indiqué à la section 3.3. au dessous de.
def create_data_dummy (données):
    data_dummy = data.copy ()
    data_dummy ['purchase_dummy'] = 1
    retourne data_dummy
data_dummy = create_data_dummy (data)

3.3. Normaliser les valeurs des articles entre les utilisateurs

  • Pour ce faire, nous normalisons la fréquence d'achat de chaque article parmi les utilisateurs en créant d'abord une matrice utilisateur-article comme suit
df_matrix = pd.pivot_table (data, values ​​= 'nombre_achat', index = 'customerId', colonnes = 'productId')
df_matrix_norm = (df_matrix-df_matrix.min ()) / (df_matrix.max () - df_matrix.min ())
# créer une table pour la modélisation
d = df_matrix_norm.reset_index ()
d.index.names = ['scaled_purchase_freq']
data_norm = pd.melt (d, id_vars = ['customerId'], nom_valeur = 'scaled_purchase_freq'). dropna ()
print (data_norm.shape)
data_norm.head ()

Les étapes ci-dessus peuvent être combinées à une fonction définie ci-dessous:

def normalize_data (data):
    df_matrix = pd.pivot_table (data, values ​​= 'nombre_achat', index = 'customerId', colonnes = 'productId')
    df_matrix_norm = (df_matrix-df_matrix.min ()) / (df_matrix.max () - df_matrix.min ())
    d = df_matrix_norm.reset_index ()
    d.index.names = ['scaled_purchase_freq']
    return pd.melt (d, id_vars = ['customerId'], nom_valeur = 'scaled_purchase_freq'). dropna ()

Au cours de cette étape, nous avons normalisé l’historique de leurs achats, de 0 à 1 (1 représentant le plus grand nombre d’achats pour un article et 0, le nombre d’achats correspondant à 0 pour cet article).

4. Train divisé et ensemble d'essai

  • La division des données en ensembles de formation et de test est une partie importante de l'évaluation de la modélisation prédictive, dans ce cas un modèle de filtrage collaboratif. Généralement, nous utilisons une plus grande partie des données pour l’entraînement et une plus petite pour les tests.
  • Nous utilisons le rapport 80:20 pour la taille de notre ensemble de test de train.
  • Notre partie formation servira à développer un modèle prédictif, tandis que l’autre permettra d’évaluer les performances du modèle.

Définissons une fonction de division ci-dessous.

def split_data (data):
    '' '
    Divise le jeu de données en formation et test.
    
    Args:
        données (pandas.DataFrame)
        
    Résultats
        train_data (tc.SFrame)
        test_data (tc.SFrame)
    '' '
    train, test = train_test_split (data, test_size = .2)
    train_data = tc.SFrame (train)
    test_data = tc.SFrame (test)
    retour train_data, test_data

Maintenant que nous disposons de trois jeux de données avec des comptes d’achat, des achats factices et des comptes d’achat échelonnés, nous souhaitons les diviser pour la modélisation.

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. Définir les modèles en utilisant la bibliothèque Turicreate

Avant de recourir à une approche plus complexe, telle que le filtrage collaboratif, il convient d’exécuter un modèle de base pour comparer et évaluer les modèles. Étant donné que la base de référence utilise généralement une approche très simple, les techniques utilisées au-delà de cette approche doivent être choisies si elles présentent une précision et une complexité relativement meilleures. Dans ce cas, nous utiliserons le modèle de popularité.

Le filtrage collaboratif est une approche plus complexe mais courante pour prévoir les articles d'achat. J'aborderai plus en détail le modèle de popularité et le filtrage collaboratif dans la section suivante. Pour le moment, définissons d’abord nos variables à utiliser dans les modèles:

Nombre de variables constantes pour définir les noms de champs:
user_id = 'customerId'
item_id = 'productId'
users_to_recommend = list (clients [id_utilisateur])
n_rec = 10 # nombre d'éléments à recommander
n_display = 30 # pour afficher les premières lignes d'un jeu de données en sortie

Turicreate nous a permis d’appeler très facilement une technique de modélisation. Définissons donc notre fonction pour tous les modèles comme suit:

def model (train_data, name, user_id, item_id, target, users_to_recommend, n_rec, n_display):
    si nom == 'popularité':
        model = tc.popularity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    cible = cible)
    nom elif == 'cosinus':
        model = tc.item_similarity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    cible = cible,
                                                    similarity_type = 'cosinus')
nom elif == 'pearson':
        model = tc.item_similarity_recommender.create (train_data,
                                                    user_id = user_id,
                                                    item_id = item_id,
                                                    cible = cible,
                                                    similarity_type = 'pearson')
        
    recom = model.recommend (users = users_to_recommend, k = n_rec)
    recom.print_rows (n_display)
    modèle de retour

Bien que j'aie écrit des scripts python pour tous les processus ci-dessus, y compris la recherche de similarité à l'aide de scripts python (que l'on peut trouver ici, nous utilisons la bibliothèque turicreate pour capturer différentes mesures plus rapidement et évaluer les modèles.

6. Modèle de popularité comme référence

  • Le modèle de popularité prend les articles les plus populaires à des fins de recommandation. Ces articles sont les produits avec le plus grand nombre de ventes chez tous les clients.
  • Les données d'apprentissage sont utilisées pour la sélection du modèle

je. Utilisation du nombre d'achats

nom = 'popularité'
target = 'nombre_achat'
popularité = model (train_data, name, user_id, item_id, target, users_to_recommend, n_rec, n_display)

ii. Utiliser le mannequin d'achat

nom = 'popularité'
target = 'Purchase_dummy'
pop_dummy = model (train_data_dummy, nom, id_utilisateur, id_item, cible, users_to_recommend, n_rec, n_display)

iii. Utilisation du nombre d'achats mis à l'échelle

nom = 'popularité'
target = 'scaled_purchase_freq'
pop_norm = model (train_data_norm, nom, user_id, item_id, target, users_to_recommend, n_rec, n_display)

6.1. Résumé de base

  • Une fois le modèle créé, nous avons prédit les éléments de recommandation en utilisant les scores par popularité. Comme vous pouvez le constater pour les résultats du modèle ci-dessus, les lignes affichent les 30 premiers enregistrements de 1 000 utilisateurs avec 10 recommandations. Ces 30 enregistrements incluent 3 utilisateurs et leurs éléments recommandés, ainsi que les scores et les rangs descendants.
  • Dans le résultat, bien que différents modèles aient une liste de recommandations différente, il est recommandé à chaque utilisateur d'utiliser la même liste de 10 éléments. En effet, la popularité est calculée en prenant les articles les plus populaires parmi tous les utilisateurs.
  • Si vous souhaitez un exemple de regroupement ci-dessous, les produits 132, 248, 37 et 34 sont les plus populaires (best-seller) de tous les clients. En utilisant leur nombre d’achats divisé par le nombre de clients, nous constatons que ces produits sont achetés au moins 3 fois en moyenne dans l’ensemble de transactions de formation (identique à la première mesure de popularité de la variable purchase_count).

7. Modèle de filtrage collaboratif

Dans le filtrage collaboratif, nous recommandons des éléments en fonction de la manière dont des utilisateurs similaires achètent des éléments. Par exemple, si les clients 1 et 2 ont acheté des articles similaires, par ex. 1 acheté X, Y, Z et 2 acheté X, Y, nous recommandons un article Z au client 2.

7.1. Méthodologie

Pour définir la similarité entre les utilisateurs, nous utilisons les étapes suivantes:

1. Créez une matrice d'éléments utilisateur, où les valeurs d'index représentent des ID client uniques et les valeurs de colonne représentent des ID produits uniques.

2. Créez une matrice de similarité entre les articles. L'idée est de calculer la similitude d'un produit avec un autre produit. Il y a plusieurs façons de calculer cela. Aux étapes 7.2 et 7.3, nous utilisons respectivement une mesure de similarité cosinus ou pearson.

  • Pour calculer la similarité entre les produits X et Y, examinez tous les clients qui ont évalué ces deux éléments. Par exemple, X et Y ont été notés par les clients 1 et 2.
  • Nous créons ensuite deux vecteurs d'items, v1 pour l'item X et v2 pour l'item Y, dans l'espace utilisateur de (1, 2), puis nous trouvons le cosinus ou l'angle / distance pearson entre ces vecteurs. Un angle zéro ou des vecteurs superposés avec une valeur de cosinus de 1 signifient une similarité totale (ou par utilisateur, tous les éléments sont identiques) et un angle de 90 degrés signifierait un cosinus de 0, voire aucune similitude.

3. Pour chaque client, nous prédisons ensuite sa probabilité d'acheter un produit (ou son achat compte) pour des produits qu'il n'a pas achetés.

  • Pour notre exemple, nous allons calculer la note de l'utilisateur 2 dans le cas de l'article Z (article cible). Pour calculer cela, nous pesons la mesure de similarité qui vient d'être calculée entre l'article cible et les autres articles que le client a déjà achetés. Le facteur de pondération est le nombre d’achats donné par l’utilisateur aux articles qu’il a déjà achetés.
  • Nous mettons ensuite à l'échelle cette somme pondérée avec la somme des mesures de similarité, de sorte que la note calculée reste dans les limites prédéfinies. Ainsi, l’évaluation prévue pour le poste Z pour l’utilisateur 2 serait calculée à l’aide de mesures de similarité.

7.2. Similitude cosinus

  • La similarité est le cosinus de l'angle entre les 2 vecteurs des éléments vecteurs de A et B
  • Il est défini par la formule suivante
  • Plus les vecteurs sont proches, plus l'angle est petit et plus le cosinus est grand

je. Utilisation du nombre d'achats

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

ii. Utiliser le mannequin d'achat

nom = 'cosinus'
target = 'Purchase_dummy'
cos_dummy = model (train_data_dummy, nom, id_utilisateur, id_item, cible, users_to_recommend, n_rec, n_ affichage)

iii. Utilisation du nombre d'achats mis à l'échelle

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

7.3. Similitude de Pearson

  • La similarité est le coefficient de Pearson entre les deux vecteurs.
  • Il est défini par la formule suivante

je. Utilisation du nombre d'achats

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

ii. Utiliser le mannequin d'achat

name = 'pearson'
target = 'Purchase_dummy'
pear_dummy = model (train_data_dummy, nom, id_utilisateur, id_article, cible, users_to_recommend, n_rec, n_ affichage)

iii. Utilisation du nombre d'achats mis à l'échelle

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

8. Évaluation du modèle

Pour évaluer les moteurs de recommandation, nous pouvons utiliser le concept de RMSE et de rappel de précision.

je. RMSE (erreurs moyennes quadratiques)

  • Mesure l'erreur des valeurs prédites
  • Moins la valeur RMSE est élevée, meilleures sont les recommandations

ii. Rappel

  • Quel pourcentage de produits achetés par un utilisateur est réellement recommandé?
  • Si un client achète 5 produits et que la recommandation a décidé d’en montrer 3, le rappel est de 0,6

iii. Précision

  • Parmi tous les éléments recommandés, combien l'utilisateur a-t-il réellement aimé?
  • Si 5 produits ont été recommandés au client et qu'il en a acheté 4, la précision est de 0.8

Pourquoi le rappel et la précision sont-ils importants?

  • Prenons un cas où nous recommandons tous les produits afin que nos clients couvrent sûrement les articles qu’ils ont aimé et achetés. Dans ce cas, nous avons 100% de rappel! Est-ce que cela signifie que notre modèle est bon?
  • Nous devons considérer la précision. Si nous recommandons 300 articles mais que l'utilisateur aime et n'en achète que 3, alors la précision est de 0.1%! Cette précision très faible indique que le modèle n'est pas génial, malgré leur excellent rappel.
  • Notre objectif doit donc être d'optimiser à la fois le rappel et la précision (pour être aussi proche de 1 que possible).

Commençons par créer les variables appelables initiales pour l’évaluation du modèle:

models_w_counts = [popularité_modèle, cos, poire]
models_w_dummy = [pop_dummy, cos_dummy, pear_dummy]
models_w_norm = [pop_norm, cos_norm, pear_norm]
names_w_counts = ['Modèle de popularité sur les comptes d'achats', 'Similarité de Cosinus sur les comptes d'achats', 'Similarité de Pearson sur les comptes d'achats']
names_w_dummy = ['Modèle de popularité sur le mannequin d'achat', 'Similarité de Cosinus sur le mannequin d'achat', 'Similarité de Pearson sur le mannequin d'achat']
names_w_norm = ['Modèle de popularité pour le nombre d'achats pondérés', 'Similarité de cosinus pour le nombre d'achats pondérés', 'Similarité de Pearson pour le nombre d'achats pondérés']

Permet de comparer tous les modèles que nous avons construits sur la base des caractéristiques RMSE et de rappel de précision:

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. Résultat d'évaluation

  • Basé sur RMSE
  • Basé sur la précision et le rappel

8.2. Résumé de l'évaluation

  • Popularité v. Filtrage collaboratif: nous pouvons constater que les algorithmes de filtrage collaboratif fonctionnent mieux que le modèle de popularité pour le nombre d'achats. En effet, le modèle de popularité ne donne aucune personnalisation car il ne donne que la même liste d'éléments recommandés à chaque utilisateur.
  • Précision et rappel: En examinant le résumé ci-dessus, nous constatons que la précision et le rappel sont valables pour Comptage des achats> Factice d'achat> Comptages des achats normalisés. Toutefois, comme les scores de recommandation pour les données d’achat normalisées sont nuls et constants, nous choisissons le mannequin. En fait, le RMSE n’est pas très différent entre les modèles du mannequin et ceux des données normalisées.
  • RMSE: Comme le RMSE est plus élevé en utilisant pearson distance que thancosine, nous choisirions de modéliser les erreurs quadratiques moyennes les plus petites, qui dans ce cas seraient en cosinus.
Par conséquent, nous avons sélectionné l’approche de similarité Cosinus sur achat factice comme modèle final.

9. Sortie finale

Enfin, nous aimerions manipuler le format de sortie de la recommandation dans un format pouvant être exporté au format csv, ainsi qu’une fonction qui renverra la liste de recommandations à partir d’un ID client.

Nous devons d’abord réexécuter le modèle en utilisant l’ensemble du jeu de données, puis nous sommes arrivés à un modèle final utilisant des données de train et évalué avec un ensemble de tests.

final_model = tc.item_similarity_recommender.create (tc.SFrame (data_norm),
                                            user_id = user_id,
                                            item_id = item_id,
                                            target = 'purchase_dummy', similarity_type = 'cosinus')
recom = final_model.recommend (users = users_to_recommend, k = n_rec)
recom.print_rows (n_display)

9.1. Fichier de sortie CSV

Ici, nous voulons manipuler notre résultat vers une sortie csv. Voyons ce que nous avons:

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

Définissons une fonction pour créer la sortie souhaitée:

def create_output (modèle, users_to_recommend, n_rec, print_csv = True):
    recomendation = model.recommend (users = users_to_recommend, 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')
    si print_csv:
        df_output.to_csv ('../ output / option1_recommendation.csv')
        print ("Un fichier de sortie se trouve dans le dossier 'output' avec le nom 'option1_recommendation.csv'")
    retourne df_output

Permet d’imprimer la sortie ci-dessous et d'affecter la valeur true à printprint_csv. Nous pourrons ainsi imprimer littéralement notre fichier de sortie au format CSV, que vous pouvez également trouver ici.

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

9.2. Fonction de recommandation client

Définissons une fonction qui renverra la liste de recommandations à partir d’un identifiant client:

def customer_recomendation (customer_id):
    Si customer_id ne figure pas dans df_output.index:
        print ('Client non trouvé.')
        return customer_id
    return df_output.loc [id_client]

Bingo!

Sommaire

Dans cet article, nous avons pu suivre un processus étape par étape pour formuler des recommandations aux clients. Nous avons utilisé des approches de filtrage collaboratif avec Cosine et Pearson pour mesurer et comparer les modèles à notre modèle de popularité de base.

Nous avons également préparé trois ensembles de données comprenant le nombre d’achats réguliers, le modèle d’achat factice et une fréquence d’achat normalisée en tant que variable cible. À l'aide de RMSE, de précision et de rappel, nous avons évalué nos modèles et observé l'impact de la personnalisation. Enfin, nous avons sélectionné l’approche Cosine en utilisant des données factices comme meilleur modèle de système de recommandation.

J'espère que vous apprécierez lire cet article et que vous êtes maintenant prêt à créer votre propre bouton «Ajouter au panier». S'il vous plaît donner 50 claps et commenter ci-dessous si vous voulez plus de lectures comme ça :) Profitez de piratage!

Moorissa est une scientifique spécialisée dans les données et un enthousiaste pour les entreprises sociales. En décembre 2017, elle a obtenu un diplôme en science des données et apprentissage automatique à l'Université Columbia. Elle espère pouvoir toujours mettre ses compétences à profit pour rendre le monde meilleur, un jour à la fois.