[PYTHON] Apprenez le filtrage collaboratif avec les supports Coursera Machine Learning

introduction

Le filtrage coopératif utilisé dans le système de recommandation est une application de l'apprentissage automatique, mais il diffère légèrement du modèle simple «d'apprentissage supervisé». J'ai essayé un peu de littérature et d'informations sur Internet, mais il est difficile de comprendre immédiatement le concept et la configuration du système. Cependant, je me suis souvenu qu'il avait été traité dans Coursera Machine Learning (semaine 9) par le Dr Andrew Ng, et en passant en revue le matériel pédagogique, j'ai pu approfondir ma compréhension du filtrage coopératif.

Comme vous le savez peut-être, Matlab / Octave est utilisé dans les exercices de ce cours. Cette fois, j'ai implémenté le co-filtrage (prototype) en Python. Tout d'abord, nous avons confirmé l'opération avec Numpy + Scipy (Optimize), puis nous avons confirmé l'implémentation à l'aide de TensorFlow.

Petit tour d'horizon du co-filtrage

Extrait du lecteur de formation Data Scientist, Introduction à l'apprentissage automatique.

Le filtrage coopératif est une méthode pour réaliser les présentations suivantes sur les sites EC.

  • "Les personnes qui ont acheté ce produit ont également acheté ce produit."
  • "Les personnes qui achètent les mêmes produits que vous achètent également ces produits."

Je l'implémenterai lorsque j'aurai compris le concept, mais je confirmerai l'explication dans Coursera Machine Learning. Ici, l'explication est avancée à l'aide d'un exemple de classement de film.

Fig.1 Movie Rating Data MovieRatingData.png

Le tableau montre les évaluations des films par chaque utilisateur, mais les évaluations sont divisées en fonction des préférences de l'utilisateur. Les données [X], qui sont un paramètre d'échelles différentes (appelées majeures dans la classe Coursera) telles que «forte tendance à la romance amoureuse» et «haute action», sont préparées en fonction du contenu du film. D'autre part, un paramètre [Theta] pour "préférence utilisateur" est également requis. Comme le montre la figure, la taille des données d'évaluation est une matrice bidimensionnelle ([Y]) du nombre de films x le nombre d'utilisateurs, mais elle est souvent indéfinie car les utilisateurs n'évaluent pas tous les films. .. Cette indéfinie? Le point principal du problème est de compléter la marque par régression.

L'explication est que si la matrice d'évaluation complémentaire peut être calculée avec précision, le traitement dans le processus suivant de recommandation de films similaires peut être bien fait.

Essayez de mettre en œuvre avec Numpy + Scipy.

Comme pour les autres exercices du cours Cousera, nous commençons par trouver la fonction de coût. Puisqu'il y a deux paramètres, c'est une expression assez "dure".

 \begin{eqnarray}
J(x^{(1)},...,x^{n_m},\theta^{1},...,\theta^{n_u}) = &\ & \frac{1}{2} \sum_{(i,j):r(i,j)=1} 
(({\theta}^{(j)})^T \ x^{(i)} - y^{(i,j)})^2 \ + \\
&\ & (\frac{\lambda}{2} \sum_{j=1}^{n_u} \sum_{k=1}^{n} ({\theta}_k^{(j)}) ^2)
 +  (\frac{\lambda}{2} \sum_{i=1}^{n_m} \sum_{k=1}^{n} ({x}_k^{(i)}) ^2)    
 \end{eqnarray}

La fonction de coût avec les paramètres [X] et [Theta] consiste en l'erreur quadratique entre la valeur prédite nominale déterminée par le produit matriciel des paramètres et les données d'évaluation réelles (étiquette), et le terme de régularisation du paramètre. Si cela est converti en code Python, ce sera comme suit.

def cofi_costfunc(Y, R, shapes, lambda_c, params):
    #  [J, grad] = COFICOSTFUNC(params, Y, R, num_users, num_movies, ...
    #   num_features, lambda) returns the cost and gradient for the
    #   collaborative filtering problem.
    num_users = shapes[0]
    num_movies = shapes[1]
    num_features = shapes[2]
    boundary = num_movies * num_features
    params_c1 = np.copy(params[:boundary])
    params_c2 = np.copy(params[boundary:])
    X = params_c1.reshape((num_movies, num_features))
    Theta = params_c2.reshape((num_users, num_features))
           
    # You need to return the following values correctly
    Jcost = 0.5 * (((np.dot(X, Theta.T) - Y) * R) ** 2).sum() \
        + 0.5 * lambda_c * ((X.ravel() **2).sum() + (Theta.ravel() **2).sum())

    return Jcost

Les paramètres «X» (paramètres du contenu du film) et «Theta» (paramètres orientés utilisateur) sont saisis dans un bloc «params». «Y» correspond aux données de classement sous la forme de nombre de films x nombre d'utilisateurs. «R» est une donnée binaire de la même taille que «Y», définie sur 1 lorsqu'il est évalué et 0 lorsqu'il n'y a pas encore d'évaluation. (Le type de variable du programme est un type entier.) Après le prétraitement, la déclaration finale est de trouver le coût correspondant à la formule de fonction de coût ci-dessus.

Vient ensuite l'équation du coefficient différentiel partiel (gradient) pour les paramètres (x, thêta) de cette fonction de coût.

\frac{\partial J}{\partial x_k^{(i)}} = \sum_{j:r(i,j)=1} (( {\theta}^{(j)})^T x^{(i)} 
- y^{(i,j)} ) {\theta}_k^{(j)} + \lambda x_k^{(i)}\\

\frac{\partial J}{\partial {\theta}_k^{(j)}} = \sum_{i:r(i,j)=1} (( {\theta}^{(j)})^T x^{(i)} 
- y^{(i,j)} ) {x}_k^{(i)} + \lambda {\theta}_k^{(j)}

C'est aussi une formule "dure" telle quelle. Cependant, il est plus facile de comprendre s'il est converti en code.

def cofi_costfunc_grad(Y, R, shapes, lambda_c, params):
    #  [J, grad] = COFICOSTFUNC(params, Y, R, num_users, num_movies, ...
    #   num_features, lambda) returns the cost and gradient for the
    #   collaborative filtering problem.
    num_users = shapes[0]
    num_movies = shapes[1]
    num_features = shapes[2]
    boundary = num_movies * num_features
    params_c1 = np.copy(params[:boundary])
    params_c2 = np.copy(params[boundary:])
    X = params_c1.reshape((num_movies, num_features))
    Theta = params_c2.reshape((num_users, num_features))
           
    # You need to return the following values correctly  
    X_grad = np.dot(((np.dot(X, Theta.T) - Y) * R), Theta) + lambda_c * X
    Tgsub = (np.dot(X, Theta.T) - Y) * R
    Theta_grad = np.dot(Tgsub.T, X) + lambda_c * Theta
    Jgrad = np.concatenate((X_grad.ravel(), Theta_grad.ravel()))

    return Jgrad

Le gradient de coût est calculé dans les quatre dernières lignes.

Dans l'exercice de Coursera, le processus d'optimisation de la minimisation des coûts est effectué en utilisant la fonction de fmincg.m fournie à l'avance. Lors de la conversion en Python, le même traitement peut être effectué avec scipy.optimize.minimize ().

# 
import scipy.optimize as spopt

#Définir la valeur initiale du paramètre
theta_ini = np.concatenate((X.flatten(), Theta.flatten()))

#Préparer la fonction wrapper
def compute_cost_sp(theta):
    global Y, R, shapes, lambda_c
    j =  cofi_costfunc(Y, R, shapes, lambda_c, params=theta)

    return j

def compute_grad_sp(theta):
    global Y, R, shapes, lambda_c
    j_grad = cofi_costfunc_grad(Y, R, shapes, lambda_c, params=theta)

    return j_grad

# scipy.optimize.Définir les options de réduction
options = {'gtol': 1.e-6, 'disp': True}
#Coefficient de régularisation
lambda_c = 1.5

res1 = spopt.minimize(compute_cost_sp, theta_ini, method='CG', \
         jac=compute_grad_sp, options=options)
theta =  res1.x

#Décomposer un ensemble de paramètres en X et Thêta
X = theta[:num_movies*num_features].reshape((num_movies, num_features))
Theta = theta[num_movies*num_features:].reshape((num_users, num_features))

Pour plus de détails sur scipy.optimize.minimize, veuillez vous référer au document, mais vous pouvez sélectionner différentes méthodes d'optimisation. Cette fois, nous définissons method = 'CG' (méthode du gradient conjugué) en fonction du matériau d'origine (fmincg.m).

Informations nécessaires à partir des paramètres [X] et [Thêta] obtenus en minimisant le coût de cette manière (ex. "Les personnes qui ont évalué ce film ont également donné une note élevée à ce film.") Est le traitement du système de recommandation. (Il semble y avoir différentes façons de mesurer la similitude, mais je vais omettre l'explication ici.)

Utiliser TensorFlow

Cela fait environ un an que TensorFlow a été publié l'année dernière (2015). Je me souviens avoir écrit le code avec enthousiasme et avoir écrit le message Qiita à la hâte. Cette fois, nous avons décidé d'utiliser TensorFlow pour exécuter l'apprentissage du filtrage coopératif à une vitesse plus élevée.

Dans le cas d'un "apprentissage supervisé" normal, la relation entrée-sortie est claire, il n'y a donc aucun doute sur la représentation graphique de TensorFlow, mais cette fois c'était un peu difficile.

--Un problème normal qui correspond au poids w et au biais b ... Cette fois, le paramètre x du contenu du film et le paramètre préféré de l'utilisateur Theta. --Un problème normal qui correspond aux données de l'enseignant ... Cette fois, les informations de notation données par l'utilisateur à l'avance. Plus précisément, les données Y (matrice du nombre de films comprenant la note donnée par l'utilisateur au nombre d'utilisateurs x nombre d'utilisateurs) et R (données binaires indiquant si l'utilisateur a évalué ou non).

Par conséquent, le code pour la préparation des variables est le suivant.

# Parameter definition
    if WARM_START:
        data_param = load_data(fn='./data/ex8_movieParams.mat')
        X_np = data_param['X'].astype(np.float32)
        Theta_np = data_param['Theta'].astype(np.float32)
        x = tf.Variable(X_np)
        theta = tf.Variable(Theta_np)
    else:   # COLD START
        x = tf.Variable(tf.random_normal([num_movies, num_features],
            mean=0.0, stddev=0.05))
        theta = tf.Variable(tf.random_normal([num_users, num_features],
            mean=0.0, stddev=0.05))
# Placeholders
    Y_ph = tf.placeholder(tf.float32, shape=[None, num_users])
    R_ph = tf.placeholder(tf.float32, shape=[None, num_users])

Étant donné que les données de paramètres qui avaient été pré-apprises dans une certaine mesure étaient fournies dans les exercices du programme Coursera, nous avons préparé une option «WARM_START» pour les utiliser et une option pour repartir de zéro. Dans'WARM_START ', les données de numpy.array sont passées à tf.Variable () telles quelles, et dans le cas d'un démarrage à zéro, un nombre aléatoire avec une petite valeur est généré et défini dans x et thêta. En outre, l'espace réservé des données de l'enseignant a été préparé avec la forme requise. (Shape = [None, num_users] est OK avecshape = [num_movies, num_users], mais il est défini sur None selon la convention de TensorFlow.)

Ensuite, nous définissons le calcul de la fonction de coût.

def Ypred(X, Theta):
    '''
      calculate rating with parameter X and Theta
      args.:
        X:      movie contents parameter
        Theta:  user characteristic parameter
    '''
    feat_dim1 = tf.shape(X)[1]
    feat_dim2 = tf.shape(Theta)[1]

    tf.assert_equal(feat_dim1, feat_dim2)
    rating = tf.matmul(X, tf.transpose(Theta))

    return rating


# Cost Function, etc.
    cost = tf.reduce_sum(((Ypred(x, theta) - Y_ph) * R_ph) ** 2)
    L2_sqr = tf.nn.l2_loss(x) + tf.nn.l2_loss(theta)
    lambda_c = 1.0      # L2 norm coefficient      
    loss = cost + lambda_c * L2_sqr

Non seulement TensorFlow, mais la chose utile à propos du framework Deep Learning est qu'il calcule automatiquement Gradient. Je pense qu'il est "génial" de définir uniquement la fonction de coût comme décrit ci-dessus. Après cela, comme d'habitude, l'optimiseur est configuré pour former une boucle d'apprentissage.

    lr = 1.e-5
    train_step = tf.train.GradientDescentOptimizer(lr).minimize(loss)
    init_op = tf.initialize_all_variables()

    # Train
    with tf.Session() as sess:
        sess.run(init_op)

        for i in range(10001):
            sess.run(train_step, feed_dict={Y_ph: Y_np, R_ph: Y_np})
        
            if i % 1000 == 0:
                loss_mon = loss.eval({Y_ph: Y_np, R_ph: Y_np})
                print(' step, loss = {:6d}: {:10.1f}'.format(i, loss_mon))

        # evaluate ranking with final parameters
        ymat = Ypred(x, theta).eval()
        sio.savemat('./ymat_tf.mat', {'Y': ymat})
        print('ymat is saved to "ymat_tf.mat".')

Je pense que vous pouvez utiliser votre optimiseur préféré au lieu de tf.train.GradientDescentOptimizer (), mais cette fois j'ai essayé d'utiliser une fonction de base car c'est un problème de Cousera. Il faut un certain temps pour ajuster le taux d'apprentissage, mais j'ai pu effectuer le calcul en toute sécurité (à grande vitesse avec le fonctionnement du GPU).

Comparaison des résultats

Puisque la solution est le même algorithme, le résultat de scipy.optimize.minimize et le résultat de TensorFlow doivent être similaires. Cette fois, nous avons calculé la matrice de notation Y à partir des paramètres obtenus (X, Thêta) et l'avons faite comme indiqué dans le diagramme Heatmap. (Bleu: taux bas ~ rouge: taux élevé)

By scipy.optimize.minimize heatmap_sp.png

By TensorFLow heatmap_tf.png

Des différences telles que des nuances de couleur peuvent être vues dans les détails, mais le même motif peut être considéré comme un grand motif. Il ne semble y avoir aucun problème dans le processus de calcul.

Si vous souhaitez compenser la différence entre les deux, vous devez ajuster les détails tels que la régularisation, le taux d'apprentissage et l'état de convergence. Je pense que le contenu ci-dessus est correct pour «apprendre le filtrage coopératif», mais pour se battre dans des compétitions comme Kaggle, divers détails devront être examinés.

(L'environnement de programmation utilisé cette fois-ci est le suivant. Python 3.5.2, Numpy 1.11.2, Scipy 0.18.1, TensorFlow 0.11.0) (Le code a été téléchargé sur Gist: https://gist.github.com/tomokishii/2e9f83d391df82f25942c1310b41e0b5)

Références, site web

Recommended Posts

Apprenez le filtrage collaboratif avec les supports Coursera Machine Learning
Profitez deux fois du matériel Coursera / Machine Learning
Apprenez en quelque sorte le machine learning
Co-filtrage avec PySpark
Résumé du site pour apprendre l'apprentissage automatique avec une vidéo en anglais
L'apprentissage automatique appris avec Pokemon
Apprentissage automatique avec Python! Préparation
Démineur d'apprentissage automatique avec PyTorch
Commencer avec l'apprentissage automatique Python
Essayez le machine learning à la légère avec Kaggle
Ne pas apprendre avec la séquence TensorFlow ~ Fibonacci de la bibliothèque d'apprentissage automatique
Apprentissage automatique pour apprendre avec Nogisaka 46 et Keyakizaka 46 Partie 1 Introduction
J'ai essayé l'apprentissage automatique avec liblinear
Apprentissage automatique par python (1) Classification générale
SVM essayant l'apprentissage automatique avec scikit-learn
Machine learning d'inspiration quantique avec des réseaux de tenseurs
Démarrez avec l'apprentissage automatique avec SageMaker
Mémo d'apprentissage "Scraping & Machine Learning avec Python"
Coursera Machine Learning Challenge en Python: ex7-1 (Compression d'image avec clustering K-means)
Prédire la demande de puissance avec l'apprentissage automatique, partie 2
Amplifiez les images pour l'apprentissage automatique avec Python
Sklearn de données déséquilibrées avec apprentissage automatique k-NN
Apprentissage automatique avec python (2) Analyse de régression simple
Une histoire sur l'apprentissage automatique avec Kyasuket
[Shakyo] Rencontre avec Python pour l'apprentissage automatique
Apprentissage automatique avec Pytorch sur Google Colab
Comment profiter de Coursera / Machine Learning (semaine 10)
Construction d'environnement AI / Machine Learning avec Python
[Apprentissage automatique] Analyse de régression à l'aide de scicit learn
Défis d'apprentissage automatique de Coursera en Python: ex3 (reconnaissance de nombres manuscrits avec récursivité logistique)
Apprentissage automatique
Chapitre 6 Apprentissage supervisé: Classification pg212 ~ [Apprenez en vous déplaçant avec Python! Nouveau manuel d'apprentissage automatique]
[Python] Introduction facile à l'apprentissage automatique avec python (SVM)
Début de l'apprentissage automatique (matériel didactique / informations recommandés)
Apprentissage automatique à partir de Python Personal Memorandum Part1
[Python] Collectez des images avec Icrawler pour l'apprentissage automatique [1000 feuilles]
Apprentissage automatique à partir de zéro (apprentissage automatique appris avec Kaggle)
[Super introduction à l'apprentissage automatique] Découvrez les didacticiels Pytorch
J'ai commencé l'apprentissage automatique avec le prétraitement des données Python
Créer un environnement d'apprentissage automatique Python avec des conteneurs
Apprenez en exécutant avec le nouveau Python! Manuel d'apprentissage automatique par Makoto Ito numpy / keras Attention!