[PYTHON] Calculer tf-idf avec scikit-learn

** Cet article est l'article du 4ème jour de Real Escape Advent Calendar 2013. ** **

Article du jour 2 utilise le code HTML récupéré à l'aide de l'API Bing, donc Jour 2 Il est plus facile de comprendre si vous lisez d'abord / items / 62291ba328de9d12bd30).

Résumez cet article en 3 lignes

-J'ai vérifié une bibliothèque Python appelée scikit-learn -Calculé le tf-idf du mot dans le html enregistré au Jour 2

référence

scikit-learn officiel, document d'extraction d'identité textuelle

Calcul Tfidf des mots dans Tweet à l'aide de scikit-learn

Produit fini

https://github.com/katryo/tfidf_with_sklearn

Fork me!

théorie

Définition de tfidf

--tf-idf est la valeur de ** tf * idf **. Attaché à un mot d'un document dans un ensemble de documents. Les mots avec un tf-idf élevé peuvent être considérés comme importants. Il peut être utilisé pour pondérer des mots dans la recherche d'informations. --tf (Fréquence du terme) est ** le nombre d'occurrences du mot (Terme) dans le document / le nombre total de tous les mots apparus dans le document **. Il se développe lorsque le mot est utilisé plusieurs fois dans le document. --idf (Inverse Document Frequency) est l'inverse de df. Cependant, en réalité, le journal est utilisé pour faciliter le calcul. Cela devient donc ** log (1 / df) **. Le bas du journal est généralement 2, mais il peut être e ou 10. Devrait être. --df (Fréquence des documents) est ** le nombre de documents dans lesquels le mot apparaît / le nombre total de documents **. Il grandit lorsque le mot est utilisé dans un large éventail de sujets. «Ha» et «o», et en anglais, «is» et «that» sont très grands. La valeur attachée à un mot dans un ensemble de documents.

Extraction d'identité à partir de texte avec scikit-learn

En traduisant partiellement le contenu de scikit-learn officiel, document d'extraction d'identité textuelle, comprenez le vôtre De plus, il sera décrit.

Lors de l'extraction de l'identité du texte avec scikit-learn, trois processus sont nécessaires.

-tokenizing: Convertit le texte en sac de mots. Dans le cas de l'anglais, il est acceptable de diviser par un espace blanc, puis de supprimer le bruit comme les symboles, mais lorsque vous le faites en japonais, utilisez un analyseur morphologique tel que MeCab ou KyTea. Comme scikit-learn n'inclut pas d'analyseur morphologique japonais, ce processus est requis séparément. --counting: compte la fréquence d'occurrence de chaque mot pour chaque document.

Dans scikit-learn, les trois étapes ci-dessus sont appelées collectivement ** vectorisation **, c'est-à-dire "vectorisation". Le dernier Vectorizer Tfidf peut effectuer les trois étapes. Si vous avez déjà terminé la procédure à mi-chemin, vous pouvez calculer à partir du milieu ou de la moitié.

À propos, scikit-learn peut non seulement faire un sac de mots mais aussi un calcul tfidf avec n-gramme se concentrant sur la suite de deux mots ou plus, mais je ne le ferai pas cette fois.

CountVectorizer Le vecteur de décompte dans sklearn.feature_extraction.text peut tokenize et compter. Puisque le résultat du comptage est représenté par un vecteur, Vectorizer.

La documentation officielle l'explique ici.

TfidfTransformer Le transformateur Tfidf, également dans sklearn.feature_extraction.text, est responsable de la normalisation. La méthode fit_transform calcule tfidf en se basant uniquement sur la "fréquence d'occurrence de mot pour chaque document" et le normalise même. Ici dans la documentation officielle.

TfidfVectorizer Existence qui a les fonctions de Count Vectorizer et Tfidf Transformer. Forme Trinity, exactement la trinité. Ceci est pratique lors de l'extraction de l'identité du texte brut.

Je l'ai utilisé

Jetez un oeil aux mots élevés de tfidf

Le sujet principal est d'ici. 36934 types de mots dans 400 pages Web récupérés par 8 requêtes. À partir de ceux-ci, imprimez "les mots avec tfidf supérieur à 0,1 dans le document apparaissant".

Tout d'abord, le calcul de tfidf est assez cher, calculons donc le tfidf, puis décapons le résultat.

set_tfidf_with_sklearn_to_fetched_pages.py


import utils
import constants
import pickle
import os
from sklearn.feature_extraction.text import TfidfVectorizer


def is_bigger_than_min_tfidf(term, terms, tfidfs):
    '''
    [term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]Utiliser dans
Une fonction qui devine dans l'ordre à partir de la liste des valeurs tfidf des mots.
La valeur de tfidf est MIN_Renvoie True si supérieur à TFIDF
    '''
    if tfidfs[terms.index(term)] > constants.MIN_TFIDF:
        return True
    return False


def tfidf(pages):
    #analyseur est une fonction qui renvoie une liste de chaînes de caractères lorsqu'une chaîne de caractères est saisie.
    vectorizer = TfidfVectorizer(analyzer=utils.stems, min_df=1, max_df=50)
    corpus = [page.text for page in pages]

    x = vectorizer.fit_transform(corpus)

    #À partir de maintenant, cela n'a rien à voir avec la valeur renvoyée. Je voulais juste voir à quoi ressemblent les hauts mots de tfidf
    terms = vectorizer.get_feature_names()
    tfidfs = x.toarray()[constants.DOC_NUM]
    print([term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)])

    print('total%je sortes de mots%Trouvé à partir de la page i.' % (len(terms), len(pages)))

    return x, vectorizer  #x est tfidf_Recevoir en principal comme résultat

if __name__ == '__main__':
    utils.go_to_fetched_pages_dir()
    pages = utils.load_all_html_files()  #les pages récupèrent du HTML et le définissent sur du texte
    tfidf_result, vectorizer = tfidf(pages)  # tfidf_result est le x de la fonction tfidf

    pkl_tfidf_result_path = os.path.join('..', constants.TFIDF_RESULT_PKL_FILENAME)
    pkl_tfidf_vectorizer_path = os.path.join('..', constants.TFIDF_VECTORIZER_PKL_FILENAME)

    with open(pkl_tfidf_result_path, 'wb') as f:
        pickle.dump(tfidf_result, f)
    with open(pkl_tfidf_vectorizer_path, 'wb') as f:
        pickle.dump(vectorizer, f)

Dans la fonction tfidf

vectorizer = TfidfVectorizer(analyzer=utils.stems, min_df=1, max_df=50)

C'est dit. l'analyseur met une fonction qui renvoie une liste de chaînes lorsque vous mettez une chaîne. Par défaut, il est divisé par un espace blanc et un seul symbole de caractère est supprimé, mais lorsque vous le faites en japonais, il est nécessaire de créer et de définir une fonction à l'aide d'un analyseur morphologique par vous-même. La fonction utils.stems est une fonction qui effectue une analyse morphologique avec MeCab, la convertit en une racine et la renvoie sous forme de liste, écrite dans utils.py, qui sera décrite plus loin.

Ce qui est imprimé dans la fonction tfidf est un mot dont la valeur tfidf est de 0,1 ou plus parmi les mots que l'on peut trouver dans l'une des pages de résultats recherchées par "penchant l'estomac". Le résultat de ceci sera décrit plus loin.

Les utilitaires qui apparaissent dans le code sont les suivants et constituent une collection de fonctions utiles qui peuvent être utilisées dans diverses situations.

utils.py


import MeCab
import constants
import os
import pdb
from web_page import WebPage

def _split_to_words(text, to_stem=False):
    """
contribution: 'Tout à moi'
production: tuple(['tout', 'moi même', 'de', 'Comment', 'Quoi'])
    """
    tagger = MeCab.Tagger('mecabrc')  #Vous pouvez utiliser un autre Tagger
    mecab_result = tagger.parse(text)
    info_of_words = mecab_result.split('\n')
    words = []
    for info in info_of_words:
        #Si vous divisez par macab, "" est à la fin de la phrase, avant cela'EOS'Arrive
        if info == 'EOS' or info == '':
            break
            # info => 'Nana\t assistant,Aide finale,*,*,*,*,Nana,N / a,N / a'
        info_elems = info.split(',')
        #Sixièmement, il y a des mots qui ne sont pas utilisés. Si le sixième est'*'Si tel est le cas, entrez le 0e
        if info_elems[6] == '*':
            # info_elems[0] => 'Van Rossam\t substantif'
            words.append(info_elems[0][:-3])
            continue
        if to_stem:
            #Convertir en racine de mot
            words.append(info_elems[6])
            continue
        #La parole telle qu'elle est
        words.append(info_elems[0][:-3])
    return words


def words(text):
    words = _split_to_words(text=text, to_stem=False)
    return words


def stems(text):
    stems = _split_to_words(text=text, to_stem=True)
    return stems


def load_all_html_files():
    pages = []
    for query in constants.QUERIES:
        pages.extend(load_html_files_with_query(query))
    return pages


def load_html_files_with_query(query):
    pages = []
    for i in range(constants.NUM_OF_FETCHED_PAGES):
        with open('%s_%s.html' % (query, str(i)), 'r') as f:
            page = WebPage()
            page.html_body = f.read()
        page.remove_html_tags()
        pages.append(page)
    return pages

def load_html_files():
    """
À utiliser en supposant que le fichier HTML se trouve dans le répertoire
    """
    pages = load_html_files_with_query(constants.QUERY)
    return pages


def go_to_fetched_pages_dir():
    if not os.path.exists(constants.FETCHED_PAGES_DIR_NAME):
        os.mkdir(constants.FETCHED_PAGES_DIR_NAME)
    os.chdir(constants.FETCHED_PAGES_DIR_NAME)

Et les constantes sont les suivantes.

constants.py


FETCHED_PAGES_DIR_NAME = 'fetched_pages'
QUERIES = 'La pollinose des dents de ver penchée de l'estomac mesure la dépression Fracture mécanique Épaule raide Documents'.split(' ')
NUM_OF_FETCHED_PAGES = 50
NB_PKL_FILENAME = 'naive_bayes_classifier.pkl'
DOC_NUM = 0
MIN_TFIDF = 0.1
TFIDF_RESULT_PKL_FILENAME = 'tfidf_result.pkl'
TFIDF_VECTORIZER_PKL_FILENAME = 'tfidf_vectorizer.pkl'

Si vous regardez l'ordre des QUERIES, vous pouvez voir que la catégorie «estomac penché» vient en premier. La constante DOC_NUM a été créée pour cette expérience et a été utilisée pour spécifier le 0ème fichier dans la catégorie "estomac penché", c'est-à-dire le fichier nommé "estomac penché_0.html".

Maintenant. Exécutons ce code.

$ python set_tfidf_with_sklearn_to_fetched_pages.py

Même si vous utilisez scikit-learn, le calcul de tfidf prend du temps. Cela a pris 25,81 secondes dans mon environnement. résultat.

['gaJsHost', 'https', 'Égouttage', 'Brûler', 'Avaler de l'air', 'Hyperacidité', 'Sein', 'cuisine', 'Produit alimentaire', 'Hernie de la fissure œsophagienne']
Un total de 36934 mots ont été trouvés sur 400 pages.

C'est un mot qui ressemble à un mal de ventre. Il a été constaté que parmi les mots de l'estomac rest_0.html, les 10 types de mots ci-dessus ont un tfidf supérieur à 0,1.

gaJsHost et https semblent faire partie du code JavaScript. Hmmm. Je veux me débarrasser de tout ce bruit, mais je ne vois pas de bon moyen. Mieux encore, il peut être préférable d'éliminer les mots qui ne sont que alphabétiques.

À propos, des mots comme «hernie de la fissure œsophagienne» ne sont pas inclus dans l'IPADIC de MeCab (voir cet article pour l'origine de l'IPADIC), donc Wikipedia Il faut le renforcer en mettant les mots du mot-clé Hatena dans le dictionnaire. S'il vous plaît google comment faire.

Vérifiez le mappage entre les mots et les valeurs tfidf

J'ai lu la page officielle, mais le résultat du calcul de tfidf est sorti dans le type de csr_matrix de scipy. Il s'agit d'une matrice clairsemée (principalement 0) qui représente le mot tf-idf dans chaque document sous la forme d'une fraction de 0 à 1.

(Pdb) type(x)
<class 'scipy.sparse.csr.csr_matrix'>

Je ne savais pas ce que l'ensemble de valeurs tfidf était associé au mot (je l'ai découvert plus tard), j'ai donc fait une expérience simple en utilisant pdb.set_trace ().

TfidfVectorizer a la méthode à utiliser

Et scipy.sparse.csr_matrix a

Est.

Premièrement, quand j'ai vérifié la page Web avec le numéro de document 0, c'était une page appelée Stomach leaning.com. Découvrez comment les mots qui apparaissent sur cette page sont représentés.

Après avoir décapé le résultat du calcul de tfidf, le code suivant a été exécuté.

play_with_tfidf.py


# -*- coding: utf-8 -*-
import pickle
import constants
import pdb

def is_bigger_than_min_tfidf(term, terms, tfidfs):
    '''
    [term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)]Utiliser dans
Une fonction qui devine dans l'ordre à partir de la liste des valeurs tfidf des mots.
La valeur de tfidf est MIN_Renvoie True si supérieur à TFIDF
    '''
    if tfidfs[terms.index(term)] > constants.MIN_TFIDF:
        return True
    return False

if __name__ == '__main__':
    with open(constants.TFIDF_VECTORIZER_PKL_FILENAME, 'rb') as f:
        vectorizer = pickle.load(f)
    with open(constants.TFIDF_RESULT_PKL_FILENAME, 'rb') as f:
        x = pickle.load(f)

    pdb.set_trace()

    terms = vectorizer.get_feature_names()
    for i in range(3):
        tfidfs = x.toarray()[i]
        print([term for term in terms if is_bigger_than_min_tfidf(term, terms, tfidfs)])

Vous pouvez utiliser pdb.set_trace comme point d'arrêt, et à partir de là, vous pouvez générer des valeurs dans un environnement interactif, afin de pouvoir effectuer diverses tâches de confirmation.

(Pdb) vectorizer.inverse_transform(x)[0]
> array(['Hernie de la fissure œsophagienne', 'Produit alimentaire', 'remède diététique', 'Opération', 'Œsophagite par reflux', 'cuisine', 'Sein', 'Hyperacidité', 'Douleur d'estomac',
       'Ulcère gastrique', 'Ptose gastrique', 'Cancer de l'estomac', 'Avaler de l'air', 'Phytothérapie chinoise', 'Construction', 'Gastrite chronique', 'L'ulcère duodénal', 'assurance médicale',
       'Avertissement', 'Information d'entreprise', 'polype', 'pot', 'se soucier', 'Compagnie d'assurance-vie familiale américaine', 'Aflac', 'aller',
       'Brûler', 'En ce qui concerne', 'Égouttage', 'unescape', 'try', 'ssl', 'protocol',
       'javascript', 'inquiry', 'https', 'gaJsHost', 'ga', 'err',
       'comCopyright', 'analytics', 'Inc', 'Cscript', 'CROSSFINITY',
       '=\'"', "='", ':"', '.")', '."', ')\u3000', '(("', '("%', "'%",
       '"))'],
      dtype='<U26')

Le terme «hernie de la fissure œsophagienne» est rare et semble rarement apparaître sur les autres pages, j'ai donc décidé de l'utiliser comme marqueur.

(Pdb) vectorizer.get_feature_names().index('Hernie de la fissure œsophagienne')
36097

Il s'est avéré être le 36097e mot. Alors, quelle est la valeur de tfidf pour le 36097ème mot dans le 0ème document (c'est-à-dire, estomac upset.com)?

(Pdb) x.toarray()[0][36097]
0.10163697033184078

Assez cher. Dans le document numéro 0, le mot portant le numéro 36097 a un tfidf de 0,10163697033184078. Je ne pense pas qu'une valeur tfidf aussi élevée (d'abord et avant tout non nulle) apparaisse au mot numéro 36097. x.toarray () est une matrice très clairsemée, dont la plupart devrait être 0. Par conséquent, on peut considérer que l'ordre de la liste de mots qui peut être prise par vectorizer.get_feature_names () et l'ordre des mots qui ont tfidf qui peuvent être pris par x.toarray () sont les mêmes.

De cette manière, il a été confirmé que la liste de mots était conservée dans le même ordre. Je pense qu'il est dit quelque part dans la documentation officielle que «l'ordre des mots est conservé».

Après cela, j'ai supprimé pdb.set_trace () et relancé play_with_tfidf.py.

['gaJsHost', 'https', 'Égouttage', 'Brûler', 'Avaler de l'air', 'Hyperacidité', 'Sein', 'cuisine', 'Produit alimentaire', 'Hernie de la fissure œsophagienne']
['Égouttage', 'Répugnant', 'Brûler', 'Douleur d'estomac', 'Sein', 'Passer']
['TVCM', 'Gusuru', 'Égouttage', 'Nomu', 'もÉgouttage', 'Brûler', 'Brûlerる', 'Ri', 'action', 'Science', 'Sacron', 'Cerbère', 'tripler', 'Voile', 'gueule de bois', 'Faible', 'Organiser', 'mucus', 'Douleur d'estomac', 'Médecine de l'estomac', 'Sein', 'Plénitude']

Ces mots ont un tfidf élevé (qui semble être très élevé) et sont considérés comme utiles comme base de calcul de la similitude entre les documents et la catégorie de penchant d'estomac.

Résumé

scikit-learn Pratique.

J'ai posté le code sur Github.

https://github.com/katryo/tfidf_with_sklearn

Aperçu de la prochaine fois

Je veux implémenter la fonction de calcul de tfidf et la comparer avec scicit-learn.

Recommended Posts

Calculer tf-idf avec scikit-learn
DBSCAN avec scikit-learn
Clustering avec scikit-learn (1)
Clustering avec scikit-learn (2)
PCA avec Scikit-learn
kmeans ++ avec scikit-learn
Validation croisée avec scikit-learn
SVM multi-classes avec scikit-learn
Clustering avec scikit-learn + DBSCAN
Apprentissage Scikit-Learn avec la chimioinfomatique
DBSCAN (clustering) avec scikit-learn
Installez scikit.learn avec pip
Traitement parallèle avec Parallel de scikit-learn
[Python] Régression linéaire avec scicit-learn
[MCMC] Calculer WAIC avec pystan
Régression linéaire robuste avec scikit-learn
Calculer la distribution de l'échantillon avec Scipy (distribution discrète)
Recherche en grille d'hyper paramètres avec Scikit-learn
Calculer l'utilisation du processeur d'un domaine avec libvirt
Créer un arbre déterminé avec scikit-learn
Segmentation d'image avec scikit-image et scikit-learn
Identifiez les valeurs aberrantes avec le classificateur de forêt aléatoire de scikit-learn
Cartes propres laplaciennes avec Scikit-learn (notes personnelles)
Factorisation matricielle non négative (NMF) avec scikit-learn
SVM essayant l'apprentissage automatique avec scikit-learn
Scikit-learn DecisionTreeClassifier avec des valeurs de type datetime
Comment calculer la date avec python
Calculer et afficher le poids standard avec python
L'analyse de clustering la plus basique avec scikit-learn
Affinons les hyper paramètres du modèle avec scikit-learn!
[Scikit-learn] J'ai joué avec la courbe ROC
Essayez SVM avec scikit-learn sur Jupyter Notebook
Classification multi-étiquettes par forêt aléatoire avec scikit-learn
Regrouper les écoles représentatives à l'été 2016 avec scikit-learn
Implémentez un estimateur auto-créé minimal avec scikit-learn
Calculer la séquence de Fibonacci avec générateur et itérateur
Remplissez les valeurs manquantes avec Scikit-learn impute
Essayez de calculer la fonction β de Godel avec SymPy
[Python] J'ai essayé de calculer TF-IDF régulièrement
Visualisez l'arbre de décision scikit-learn avec Treemap de Plotly