[PYTHON] Modèle de sujet par LDA avec gensim ~ Penser au goût de l'utilisateur à partir de la balise Qiita ~

introduction

Dans cet article, je vais essayer de réfléchir aux préférences de l'utilisateur par rapport aux balises Qiita suivies en utilisant le modèle LDA dans le gensim de la bibliothèque python.

Le but est de voir à quoi il ressemble en utilisant réellement le modèle de sujet et le gensim avec les données. Nous espérons que ce sera l'occasion pour vous d'utiliser réellement le modèle de sujet et de commencer à étudier en détail.

Je ne touche pas vraiment à la façon dont l'intérieur du modèle LDA l'implémente. Concentrez-vous sur "ce que vous pouvez faire". Il touche également à l'acquisition de données (grattage, etc.).

Il y avait aussi un article qui expliquait en détail, donc si vous vous sentez insatisfaisant après avoir lu cet article, vous devriez le lire.

En outre, voici deux livres qui ont été introduits dans les livres sur les modèles de sujets et qui sont souvent présentés même si vous les recherchez.

J'étudie moi-même au moment de la rédaction de cet article, donc si vous avez des erreurs ou d'autres conseils, je vous serais reconnaissant si vous pouviez me le faire savoir dans les commentaires.

Qu'est-ce qu'un modèle de sujet?

En termes simples, il s'agit d'un modèle qui estime et classe les phrases par sujet (catégorie) à partir du contenu. Le modèle thématique peut classer les phrases en estimant la probabilité d'apparition des mots de la phrase. Si des mots similaires apparaissent, ils sont considérés comme le même sujet.

Pour expliquer ce que vous pouvez faire, par exemple, il existe plusieurs phrases et elles peuvent être classées dans un nombre illimité de catégories. Par exemple, préparez 500 articles de presse. Et si vous demandez à ce modèle de "le diviser en 10 catégories", il jugera 500 articles à partir des mots de la phrase et les divisera en 10 groupes liés. Il est important de noter ici que nous ne savons pas toujours si chaque groupe peut être nommé une catégorie «sports» ou une catégorie «divertissement». Il peut s'agir des catégories «baseball» et «soccer» au lieu de la catégorie «sports». Puisqu'il apprend sans enseignant, il n'a pas d'étiquette spécifique.

Ce qui est intéressant, c'est que le modèle décide comment diviser les choses que vous n'auriez jamais cru possibles. L'examen des mots fortement pondérés pour chaque sujet peut vous donner de nouvelles suggestions. De plus, si vous modifiez le nombre de catégories à diviser, le groupe de mots changera et ce sera intéressant.

Comment faire le modèle de sujet

À propos, si vous résumez la procédure du contenu apparu dans les exemples jusqu'à présent, il semble que vous puissiez classer le sujet des phrases par la procédure suivante.

  1. Préparez une phrase
  2. Divisez les phrases en mots (analyse morphologique)
  3. Ajustez les mots (suppression des mots vides, extraction)
  4. Vectorisation (sac de mots)
  5. Convertissez au format requis et placez-le dans le modèle LDA

L'article suivant qui fait quelque chose de similaire à cela sera utile. Création d'une application à l'aide d'un modèle de rubrique

De plus, l'article suivant fait quelque chose de similaire, mais à la fin, j'apprends avec un enseignant dans Random Forest au lieu de LDA. Je pense qu'il est intéressant de voir la différence en comparant. Classer les articles de presse par scikit-learn et gensim

Que faire cette fois

À propos, LDA semble s'appliquer à diverses choses (c'est ainsi que cela est arrivé). J'aimerais donc faire quelque chose d'un peu différent. À partir de là, nous ferons enfin ce que nous avons publié dans le titre, «Pensez aux préférences des utilisateurs à partir des balises Qiita».

Dans les exemples jusqu'à présent, les phrases sont catégorisées à partir des mots de la phrase. Il

--Sentence-> Utilisateur --Mots-> Tags que vous suivez

Je voudrais essayer LDA comme si c'était le cas.

la mise en oeuvre

procédure

  1. Acquisition de données
  2. Données utilisateur
  3. Suivez les données de balise associées aux données utilisateur
  4. Mise en forme des données
  5. Appliquer au modèle

Obtenez des données

Tout d'abord, récupérez les données de l'utilisateur. Cette fois, nous amènerons d'abord les 1000 personnes les plus riches en nombre de contributions. J'ai utilisé les données du Classement des utilisateurs Qiita. Les utilisateurs de Qiita sont répertoriés dans l'ordre de contribution, alors obtenez-le tel quel. Puisqu'il y a 20 utilisateurs par page, nous explorerons 50 pages.

import requests
from bs4 import BeautifulSoup
import csv
import time

base_url = 'https://qiita-user-ranking.herokuapp.com/'
max_page = 50 #20 utilisateurs par page

qiita_users = []


for i in range(max_page):
    target_url = base_url + "?page=" + str(i + 1)
    target_html = requests.get(target_url).text
    soup = BeautifulSoup(target_html, 'html.parser')
    users = soup.select('main > p > a') #Emplacement du nom d'utilisateur

    for k, user in enumerate(users):
        qiita_users.append([(i*20 + k + 1), user.get_text()]) #ID utilisateur (rang) et nom d'utilisateur

    time.sleep(1) #Intervalle de 1 seconde pour ne pas surcharger le serveur
    print('scraping page: ' + str(i + 1))

#Cracher des données au format CSV
f = open('qiita_users.csv', 'w') 
writer = csv.writer(f, lineterminator='\n')
writer.writerow(['user_id', 'name'])
for user in qiita_users:
    print(user)
    writer.writerow(user)

f.close()

Vous obtiendrez le CSV suivant.

user_id,name
1,hirokidaichi
2,jnchito
3,suin
4,icoxfog417
5,shu223
...

Ensuite, récupérez les données de balise. Utilisez l'API fournie par Qiita pour obtenir les données de balise suivies par chaque utilisateur. Vous pouvez obtenir des données de balise sous la forme de https://qiita.com/api/v1/users/ (user_name) / following_tags. Cependant, comme l'API de Qiita n'accepte que 150 requêtes par heure, il n'a pas été possible d'acquérir les données de 1000 balises à la fois. Cette fois, si vous l'exécutez à nouveau après 1 heure, il mettra à jour le CSV (7 fois, il atteindra 1000 personnes). Même si les données sont acquises pour 150 personnes pour le moment, cela se poursuivra sans problème par la suite. (Le nombre 1 000 est approprié en premier lieu. Si vous le modifiez, vous obtiendrez peut-être des résultats intéressants. Si vous le souhaitez, je souhaite obtenir les données Qiita directement avec SQL plutôt que via l'API.)

import csv, requests, os.path, time

#Utilisez les données utilisateur antérieures.
f = open('qiita_users.csv', 'r')
reader = csv.reader(f)
next(reader)

qiita_tags = []
qiita_user_tags = []

#Obtient le nombre de données utilisateur CSV. (La première fois n'est pas pertinente)
if os.path.isfile('qiita_user_tags.csv'):
    user_tag_num = sum(1 for line in open('qiita_user_tags.csv'))
else:
    user_tag_num = 0

#Si vous avez déjà des données de balise CSV, récupérez ces données de balise (la première fois n'est pas pertinente)
if os.path.isfile('qiita_tags.csv'):
    f_tag = open('qiita_tags.csv', 'r')
    reader_tag = csv.reader(f_tag)
    qiita_tags = [tag[0] for tag in reader_tag]

#Ouvrez le fichier CSV
f_tag = open('qiita_tags.csv', 'w')
writer_tag = csv.writer(f_tag, lineterminator='\n')
f_user_tag = open('qiita_user_tags.csv', 'a')
writer_user_tag = csv.writer(f_user_tag, lineterminator='\n')

#Frappez l'API pour chaque utilisateur
for user in reader:
    if user_tag_num < int(user[0]):
        target_url = 'https://qiita.com/api/v1/users/' + user[1] + '/following_tags'
        print('scraping: ' + user[0])

        #Vérification des erreurs(Il y a deux points: le nombre de requêtes est dépassé et l'utilisateur n'existe pas)
        try:
            result = requests.get(target_url)
        except requests.exceptions.HTTPError as e:
            print(e)
            break
        target = result.json()

    #Abandonner lorsque le nombre de demandes est dépassé
        if 'error' in target:
            print(target['error'])
            if target['error'] == 'Rate limit exceeded.':
                break
            continue

        # user_id, tag_1, tag_2, ...Mettez des données comme
        qiita_user_tag = [int(user[0])]
        for tag in target:
            if tag['name'] in qiita_tags:
                qiita_user_tag.append(qiita_tags.index(tag['name']) + 1)
            else:
                qiita_tags.append(tag['name'])
                tag_num = len(qiita_tags)
                qiita_user_tag.append(tag_num)
        qiita_user_tags.append(qiita_user_tag)
        time.sleep(1) #Laissez un intervalle de 1 seconde pour éviter de surcharger le serveur

#Expirez les données au format CSV
for tag in qiita_tags:
    writer_tag.writerow([tag])
writer_user_tag.writerows(qiita_user_tags)

f_tag.close()
f_user_tag.close()
f.close()

Voici un exemple de sortie. Il existe deux fichiers CSV, un fichier de balise et un fichier de données de relation utilisateur-balise. Le numéro de ligne de la balise est l'identifiant de la balise.

qiita_tags.csv


GoogleAppsScript
ActionScript
JavaScript
CSS
docker
...

qiita_user_tags.csv


1,1,2,3,4
2,5,6,7,3,8,9,10,11,12,13,14,15,16,17
3,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37
4,38,39,40,41,42,43,44,45,46,47,48,3,49
5,50,51,52,53,54,55,56,57,58,59,60,41,61,62,63,64,65,66,67,68
...

Former des données et les appliquer aux modèles

Nous façonnerons les données et le modèle. Il peut être exécuté avec le fichier suivant, mais en fait je travaille avec le notebook iPython. En outre, il semble que son utilisation puisse être facilitée en tant que classe, mais cela suffit si vous connaissez le flux que vous avez créé, le flux de travail est donc le même.

import csv
import gensim
from pandas import DataFrame

#ID de tag(key)Et nom(value)Créer un dictionnaire qui se connecte
tag_name_dict = {}
with open('qiita_tags.csv', 'r') as f_tags:
    tag_reader = csv.reader(f_tags)

    for i, row in enumerate(tag_reader):
        tag_name_dict[(i+1)] = row[0]


#Quelle balise un utilisateur (clé) suit
user_tags_dict = {}
with open('qiita_user_tags.csv', 'r') as f_user_tags:
    user_tags_reader = csv.reader(f_user_tags)

    for i, row in enumerate(user_tags_reader):
        user_tags_dict[int(row[0])] = row[1:-1]

# tags_list Liste des balises suivies(Il y a duplication s'il est suivi par plusieurs personnes)
tags_list = []
for k, v in user_tags_dict.items():
    tags_list.extend(v)

#Balises suivies par une seule personne
once_tags = [tag for tag in tags_list if tags_list.count(tag) == 1]

#Utilisateur d'un tag qui n'est suivi que par une seule personne_Également supprimé des balises
user_tags_dict_multi = { k: [tag for tag in user_tags if not tag in once_tags] for k, user_tags in user_tags_dict.items()}

#Omettre les utilisateurs qui ne suivent pas les balises(Notez que nous avons supprimé des balises qu'une seule personne suit)
user_tags_dict_multi = {k: v for k, v in user_tags_dict_multi.items() if not len(v) == 0}

#Convertir pour l'entrée en gemsim
corpus = [[(int(tag), 1) for tag in user_tags]for k, user_tags in user_tags_dict_multi.items()]

#Appeler et apprendre des modèles LDA Voici le nombre de sujets(Nombre de groupes d'utilisateurs) peut être défini
lda = gensim.models.ldamodel.LdaModel(corpus=corpus, num_topics=15)

#Obtenez le top 10 des fréquences d'apparition de chaque sujet
topic_top10_tags = []
for topic in lda.show_topics(-1, formatted=False):
    topic_top10_tags.append([tag_name_dict[int(tag[0])] for tag in topic[1]])

#Afficher les 10 sujets les plus fréquemment abordés
topic_data = DataFrame(topic_top10_tags)
print(topic_data)
print("------------------")

#Affichage des préférences utilisateur
c = [(1, 1), (2, 1)] #Utilisateurs suivant les balises 1 et 2
for (tpc, prob) in lda.get_document_topics(c):
    print(str(tpc) + ': '+str(prob))

résultat

Vous trouverez ci-dessous un groupe de mots pour chaque sujet. (J'ai utilisé l'affichage du notebook iPython) Chaque ligne représente chaque sujet. Il a été divisé en 15 groupes d'utilisateurs, tels que le groupe d'utilisateurs de la ligne 0, la couche d'utilisateurs de la ligne 1, etc.

スクリーンショット 2016-09-07 22.44.17.png

À quel sujet appartient l'utilisateur suivant les balises 1 et 2? Il ressemble à "7". Je saisis correctement cette fois, mais si vous utilisez les balises que vous suivez comme données d'entrée, vous pourrez peut-être voir quel type de groupe d'utilisateurs vous êtes. De plus, si vous l'utilisez, il semble que vous puissiez également recommander des balises.

0: 0.0222222589596
1: 0.0222222222222
2: 0.0222222417019
3: 0.0222222755597
4: 0.022222240412
5: 0.0222222222222
6: 0.0222222374859
7: 0.688888678865
8: 0.02222225339
9: 0.0222222557189
10: 0.0222222222222
11: 0.0222222222222
12: 0.0222222222222
13: 0.0222222245736
14: 0.0222222222222

Bonus: Ce qui suit est une version à 20 sujets. Je pense que 20 pièces semblent plus sujettes. スクリーンショット 2016-09-07 22.45.19.png

Il semble que diverses choses peuvent être vues en augmentant le nombre de données et en modifiant le nombre de sujets.

À la fin

J'espère que c'est le catalyseur pour quiconque s'intéresse au modèle thématique. J'étudierai plus moi-même.

Recommended Posts

Modèle de sujet par LDA avec gensim ~ Penser au goût de l'utilisateur à partir de la balise Qiita ~
Classement des numéros de stock par balise Qiita avec python