Découvrez Naive Bayes implémenté en Python 3.3 sur une page Web obtenue avec l'API Bing. Que les phrases soient classées

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

Dernière mise en œuvre Naive Bayes. Depuis que je l'ai fait avec beaucoup d'efforts, je l'ai combiné avec le code que j'ai écrit jusqu'à présent pour en faire un système pratique.

J'ai téléchargé tout le code autre que la clé API sur Github, veuillez donc l'utiliser tel quel si vous le souhaitez. https://github.com/katryo/bing_search_naive_bayes

Aperçu de ce que j'ai fait

  1. Avec la requête de recherche donnée, utilisez l'API Bing pour rechercher sur le Web et obtenir 50 pages Web.
  2. Faites les phrases dans le fichier HTML acquis Bag-of-words et apprenez Naive Bayes avec la requête de recherche comme catégorie. Le classificateur appris est converti en pickle et enregistré.
  3. Utilisez le classificateur pour deviner à quelle catégorie appartient une phrase donnée

En combinant les trois fonctions ci-dessus, nous avons créé un système qui peut acquérir des données d'apprentissage, apprendre et classer.

1. Rechercher sur le Web avec l'API Bing

Basé sur http://o-tomox.hatenablog.com/entry/2013/09/25/191506 de @ o-tomox, j'ai écrit un wrapper amélioré qui fonctionne avec Python 3.3.

Écrivez à l'avance la clé d'API Bing dans un fichier appelé my_api_keys.py.

my_api_keys.py


BING_API_KEY = 'abcdefafdafsafdafasfaafdaf'

Veuillez l'ignorer avec .gitignore. Sinon, la clé API sera publiée.

Vous trouverez ci-dessous un wrapper pour l'API Bing.

bing.py


# -*- coding: utf-8 -*-
import urllib
import requests
import sys
import my_api_keys


class Bing(object):
    #Au même niveau mon_api_keys.Créez un fichier appelé py et BING là-bas_API_Écrivez la CLÉ.
    # my_api_keys.Gardez py gitignore.
    def __init__(self, api_key=my_api_keys.BING_API_KEY):
        self.api_key = api_key

    def web_search(self, query, num_of_results, keys=["Url"], skip=0):
        """
en clés'ID','Title','Description','DisplayUrl','Url'Peut entrer
        """
        #URL de base
        url = 'https://api.datamarket.azure.com/Bing/Search/Web?'
        #Nombre maximum renvoyé à la fois
        max_num = 50
        params = {
            "Query": "'{0}'".format(query),
            "Market": "'ja-JP'"
        }
        #Format reçu par json
        request_url = url + urllib.parse.urlencode(params) + "&$format=json"
        results = []

        #Nombre de répétitions de l'API avec un nombre maximum
        repeat = int((num_of_results - skip) / max_num)
        remainder = (num_of_results - skip) % max_num

        #Répétez en frappant l'API avec le nombre maximum
        for i in range(repeat):
            result = self._hit_api(request_url, max_num, max_num * i, keys)
            results.extend(result)
        #restant
        if remainder:
            result = self._hit_api(request_url, remainder, max_num * repeat, keys)
            results.extend(result)

        return results

    def related_queries(self, query, keys=["Title"]):
        """
en clés'ID','Title','BaseUrl'Peut entrer
        """
        #URL de base
        url = 'https://api.datamarket.azure.com/Bing/Search/RelatedSearch?'
        params = {
            "Query": "'{0}'".format(query),
            "Market": "'ja-JP'"
        }
        #Format reçu par json
        request_url = url + urllib.parse.urlencode(params) + "&$format=json"
        results = self._hit_api(request_url, 50, 0, keys)
        return results

    def _hit_api(self, request_url, top, skip, keys):
        #URL finale pour accéder à l'API
        final_url = "{0}&$top={1}&$skip={2}".format(request_url, top, skip)
        response = requests.get(final_url, 
                                auth=(self.api_key, self.api_key), 
                                headers={'User-Agent': 'My API Robot'}).json()
        results = []
        #Obtenir les informations spécifiées à partir des articles retournés
        for item in response["d"]["results"]:
            result = {}
            for key in keys:
                result[key] = item[key]
            results.append(result)
        return results


if __name__ == '__main__':
    # bing_api.Lorsque py est utilisé seul, il devient un outil pour rechercher 50 éléments avec le mot saisi et afficher les résultats.
    for query in sys.stdin:
        bing = Bing()
        results = bing.web_search(query=query, num_of_results=50, keys=["Title", "Url"])
        print(results)

En utilisant ce wrapper d'API Bing, j'ai écrit un script qui enregistre localement 50 pages de résultats de recherche.

fetch_web_pages.py


from bing_api import Bing
import os
import constants
from web_page import WebPage

if __name__ == '__main__':
    bing = Bing()
    if not os.path.exists(constants.FETCHED_PAGES_DIR_NAME):
        os.mkdir(constants.FETCHED_PAGES_DIR_NAME)
    os.chdir(constants.FETCHED_PAGES_DIR_NAME)
    results = bing.web_search(query=constants.QUERY, num_of_results=constants.NUM_OF_FETCHED_PAGES, keys=['Url'])
    for i, result in enumerate(results):
        page = WebPage(result['Url'])
        page.fetch_html()
        f = open('%s_%s.html' % (constants.QUERY, str(i)), 'w')
        f.write(page.html_body)
        f.close()

De plus, créez un fichier appelé constants.py et écrivez le nom du répertoire dans lequel le code HTML de la requête et des résultats de la recherche est stocké. Cette fois, je chercherai d'abord la requête "cassé".

constants.py


FETCHED_PAGES_DIR_NAME = 'fetched_pages'
QUERY = 'fracture'
NUM_OF_FETCHED_PAGES = 50
NB_PKL_FILENAME = 'naive_bayes_classifier.pkl'

J'ai créé une classe appelée WebPage pour faciliter la gestion de la page Web acquise. Sur la base de l'URL obtenue par l'API Bing, récupérez le HTML, vérifiez le code de caractère avec cChardet et supprimez la balise HTML dérangeante avec une expression régulière.

web_page.py


import requests
import cchardet
import re


class WebPage():
    def __init__(self, url=''):
        self.url = url

    def fetch_html(self):
        try:
            response = requests.get(self.url)
            self.set_html_body_with_cchardet(response)
        except requests.exceptions.ConnectionError:
            self.html_body = ''

    def set_html_body_with_cchardet(self, response):
        encoding_detected_by_cchardet = cchardet.detect(response.content)['encoding']
        response.encoding = encoding_detected_by_cchardet
        self.html_body = response.text

    def remove_html_tags(self):
        html_tag_pattern = re.compile('<.*?>')
        self.html_body = html_tag_pattern.sub('', self.html_body)

Maintenant, mettez le code ci-dessus dans le même répertoire,

$ python fetch_web_pages.py

Éxécuter. Il faut un moment pour accéder à l'API Bing pour obtenir 50 URL, mais il faut un peu de temps pour envoyer une requête HTTP à chacun des 50 et obtenir le HTML. Je pense que cela prendra environ 30 secondes.

Lorsque vous avez terminé, jetez un œil au répertoire fetched_pages. Vous devriez avoir un fichier HTML de fracture_0.html à fracture.49.html.

2. Apprenez à partir de 50 fichiers HTML

Maintenant, enfin, la dernière implémentation de Naive Bayes entre en jeu.

naive_bayes


#coding:utf-8
# http://gihyo.jp/dev/serial/01/machine-learning/Python3 avec l'implémentation du filtre basilien de 0003.Amélioré pour être lisible pendant 3
import math
import sys
import MeCab


class NaiveBayes:
    def __init__(self):
        self.vocabularies = set()
        self.word_count = {}  # {'Mesures contre la pollinose': {'Pollen Sugi': 4, 'médicament': 2,...} }
        self.category_count = {}  # {'Mesures contre la pollinose': 16, ...}

    def to_words(self, sentence):
        """
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(sentence)
        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
            words.append(info_elems[6])
        return tuple(words)

    def word_count_up(self, word, category):
        self.word_count.setdefault(category, {})
        self.word_count[category].setdefault(word, 0)
        self.word_count[category][word] += 1
        self.vocabularies.add(word)

    def category_count_up(self, category):
        self.category_count.setdefault(category, 0)
        self.category_count[category] += 1

    def train(self, doc, category):
        words = self.to_words(doc)
        for word in words:
            self.word_count_up(word, category)
        self.category_count_up(category)

    def prior_prob(self, category):
        num_of_categories = sum(self.category_count.values())
        num_of_docs_of_the_category = self.category_count[category]
        return num_of_docs_of_the_category / num_of_categories

    def num_of_appearance(self, word, category):
        if word in self.word_count[category]:
            return self.word_count[category][word]
        return 0

    def word_prob(self, word, category):
        #Calcul de la loi bayésienne
        numerator = self.num_of_appearance(word, category) + 1  # +1 est la méthode de Laplace de lissage additif
        denominator = sum(self.word_count[category].values()) + len(self.vocabularies)

        #Dans Python3, la division est automatiquement flottante
        prob = numerator / denominator
        return prob

    def score(self, words, category):
        score = math.log(self.prior_prob(category))
        for word in words:
            score += math.log(self.word_prob(word, category))
        return score

    def classify(self, doc):
        best_guessed_category = None
        max_prob_before = -sys.maxsize
        words = self.to_words(doc)

        for category in self.category_count.keys():
            prob = self.score(words, category)
            if prob > max_prob_before:
                max_prob_before = prob
                best_guessed_category = category
        return best_guessed_category

if __name__ == '__main__':
    nb = NaiveBayes()
    nb.train('''Python est un langage de programmation open source créé par le Néerlandais Guido Van Rossam.
C'est un type de langage de script orienté objet et est largement utilisé en Europe et aux États-Unis avec Perl. Nommé d'après la comédie "Flying Monty Python" produite par la chaîne de télévision britannique BBC.
Python signifie reptile Nishiki snake en anglais et est parfois utilisé comme mascotte ou icône dans le langage Python. Python est un langage de haut niveau à usage général. Conçu avec un accent sur la productivité du programmeur et la fiabilité du code, il dispose d'une grande bibliothèque standard pratique avec une syntaxe et une sémantique de base minimales.
Il prend en charge les opérations de chaîne en utilisant Unicode, et le traitement japonais est également possible en standard. Il prend en charge de nombreuses plates-formes (plates-formes de travail), et dispose de documents abondants et de bibliothèques abondantes, de sorte que son utilisation augmente dans l'industrie.
             ''',
             'Python')
    nb.train('''Ruby est un langage de script orienté objet développé par Yukihiro Matsumoto (communément appelé Matz).
Réalise la programmation orientée objet dans les domaines où les langages de script tels que Perl ont été traditionnellement utilisés.
Ruby est né le 24 février 1993 et a été annoncé le fj en décembre 1995.
Le nom Ruby est dû au fait que le langage de programmation Perl prononce la même chose que Pearl, la pierre de naissance de juin.
Il a été nommé d'après le rubis de la pierre de naissance du collègue de Matsumoto (juillet).
             ''',
             'Ruby')
    doc = 'Open source réalisé par Guido Van Rossam'
    print('%s =>Catégorie estimée: %s' % (doc, nb.classify(doc)))  #Catégorie estimée:Devrait être Python

    doc = 'Un pur langage orienté objet.'
    print('%s =>Catégorie estimée: %s' % (doc, nb.classify(doc)))  #Catégorie estimée:Devrait être Ruby

À l'aide de cette implémentation Naive Bayes et du fichier HTML téléchargé, entraînez le classificateur, qui est un objet Naive Bayes.

C'est un gaspillage de jeter à chaque fois l'objet Naive Bayes formé, alors enregistrez-le en utilisant la bibliothèque de cornichons.

Cliquez ici pour un script à apprendre et à enregistrer.

train_with_fetched_pages.py


import os
import pickle
import constants
from web_page import WebPage
from naive_bayes import NaiveBayes


def load_html_files():
    """
À utiliser en supposant que le fichier HTML se trouve dans le répertoire
    """
    pages = []
    for i in range(constants.NUM_OF_FETCHED_PAGES):
        with open('%s_%s.html' % (constants.QUERY, str(i)), 'r') as f:
            page = WebPage()
            page.html_body = f.read()
        page.remove_html_tags()
        pages.append(page)
    return pages

if __name__ == '__main__':
    #Si vous souhaitez l'utiliser à nouveau dans un autre endroit, faites-en une fonction
    if not os.path.exists(constants.FETCHED_PAGES_DIR_NAME):
        os.mkdir(constants.FETCHED_PAGES_DIR_NAME)
    os.chdir(constants.FETCHED_PAGES_DIR_NAME)
    pages = load_html_files()
    pkl_nb_path = os.path.join('..', constants.NB_PKL_FILENAME)

    #Si vous avez déjà enregistré un cornichon d'objets NaiveBayes, entraînez-le
    if os.path.exists(pkl_nb_path):
        with open(pkl_nb_path, 'rb') as f:
            nb = pickle.load(f)
    else:
        nb = NaiveBayes()
    for page in pages:
        nb.train(page.html_body, constants.QUERY)
    #J'ai tellement appris, alors sauvons-le
    with open(pkl_nb_path, 'wb') as f:
        pickle.dump(nb, f)

Mettez le code source ci-dessus dans le même répertoire et faites la même chose qu'avant

$ python train_with_fetched_web_pages.py

Effectuez l'apprentissage et enregistrez le classificateur à. Cette fois, cela ne prend pas beaucoup de temps car il ne communique pas avec l'extérieur par HTTP. Dans mon cas, cela a pris moins de 5 secondes.

3. Catégorie par classificateur enregistré

Avec la procédure ci-dessus, nous avons pu apprendre une requête = une catégorie appelée «fracture». Cependant, il ne peut pas être classé dans une seule catégorie. Par conséquent, je vais répéter la procédure ci-dessus plusieurs fois avec différentes requêtes.

répétition

Tout d'abord, réécrivez QUERY dans constants.py.

constants.py


QUERY =«Estomac penché»#Réécrit à partir de 'cassé'

Récupérez ensuite le HTML avec l'API Bing.

$ python fetch_web_pages.py

Entraînez le classificateur Naive Bayes enregistré sous le nom naive_bayes_classifier.pkl avec 50 fichiers HTML récupérés.

$ python train_with_fetched_web_pages.py

Répétez le travail ci-dessus plusieurs fois en réécrivant les constantes.QUERY en «contre-mesures contre la pollinose» et «dents de ver».

Catégorie

Eh bien, l'apprentissage est terminé. Enfin, la production commence à partir d'ici. Prenez une chaîne de l'entrée standard et laissez le classificateur classer la catégorie à laquelle la chaîne doit être affectée.

Le script à classer est simple, comme suit. Tout d'abord, chargez l'objet Naive Bayes mariné et retirez-le du sel. Ensuite, chaque fois que sys.stdin est entré, exécutez simplement la méthode classify () de l'objet NaiveBayes et affichez le résultat.

classify.py


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

if __name__ == '__main__':
    with open(constants.NB_PKL_FILENAME, 'rb') as f:
        nb = pickle.load(f)
    for query in sys.stdin:
        result = nb.classify(query)
        print('La catégorie inférée est%s' % result)

Faisons le.

$ python classify_inputs.py

Il fonctionne comme un outil de terminal, alors entrez la chaîne de caractères telle quelle. Pour commencer, [Allergies] de Wikipedia (http://en.wikipedia.org/wiki/%E3%82%A2%E3%83%AC%E3%83%AB%E3%82%AE%E3%83%BC ) Page «Allergie» signifie qu'une réaction immunitaire se produit de manière excessive contre un antigène spécifique La réaction immunitaire agit pour éliminer les substances étrangères étrangères (antigènes). , C'est une fonction physiologique indispensable pour le corps vivant. "

allergie.png

Succès! Il a été jugé dans la catégorie «contre-mesures contre la pollinose».

Ensuite, le texte sur la page de Lion's Clinica "Après avoir mangé un repas ou une collation, les bactéries présentes dans la plaque métabolisent le sucre. La surface des dents recouverte de plaque devient acide car elle produit de l'acide. "

tooth_brush.png

C'est également un succès. Il était présumé appartenir à la catégorie «dent de ver».

Résumé

Cette fois, j'ai utilisé Naive Bayes, que j'ai mis en œuvre moi-même, pour effectuer un apprentissage supervisé à l'aide des résultats de recherche Web. Je l'ai essayé plusieurs fois, mais il fonctionne assez bien.

Cette implémentation compte le nombre d'occurrences de tous les mots qui apparaissent, y compris les mots trop fréquents tels que «ha» et «o». À l'origine, nous devrions utiliser tf-idf pour réduire la valeur des mots qui apparaissent trop fréquemment, réduire leurs qualités et réduire le coût de calcul, mais cette fois nous ne l'avons pas fait. C'est parce que les données utilisées pour la formation étaient petites et que le calcul n'a pas pris longtemps. Dans le cadre d'une tâche future, j'aimerais utiliser des méthodes telles que tf-idf et LSA (analyse du sens latent) pour réduire le coût de calcul ou améliorer la précision.

Si vous utilisez une bibliothèque comme scikit-learn, il devrait être plus facile et plus pratique d'utiliser le lissage haute performance et tfidf, donc j'aimerais le faire la prochaine fois.

Poussé sur Github, alors jetez un œil si vous le souhaitez. Veuillez me donner une autre étoile.

Aperçu de la prochaine fois

Ajout d'une fonction pour calculer la similitude à partir de la fréquence de cooccurrence de mot une fois loin de Naive Bayes. La prochaine fois, vous verrez les larmes du temps. http://qiita.com/katryo/items/b6962facf744e93735bb

Recommended Posts

Découvrez Naive Bayes implémenté en Python 3.3 sur une page Web obtenue avec l'API Bing. Que les phrases soient classées
Exécutez le code de sortie sur le serveur Web local en tant que "A, faisant semblant d'être B" en python
Accédez à l'API Web en Python
Python VBA pour obtenir une capture de la page WEB entière avec Selenium
Hit une méthode d'une instance de classe avec l'API Web Python Bottle
Télécharger des fichiers sur le Web avec Python
À l'aide du classificateur Naive Bayes implémenté dans Python 3.3, calculez la similitude à partir de la fréquence de cooccurrence des mots dans les phrases et les chaînes.
Laisser Python mesurer le score moyen d'une page à l'aide de l'API PageSpeed Insights
[Python] Récupérez les fichiers dans le dossier avec Python
Créer une nouvelle page en confluence avec Python
Extraire des données d'une page Web avec Python
Utilisez tkinter pour déplacer le code de sortie en tant que "A et prétendant être B" en python
Automatisez la suppression de l'arrière-plan pour les derniers portraits dans un répertoire avec Python et API
[Python3] Prenez une capture d'écran d'une page Web sur le serveur et recadrez-la davantage
[Pour les débutants] Web scraping avec Python "Accédez à l'URL de la page pour obtenir le contenu"
Faire un point d'arrêt sur la couche c avec python
Ecrire un histogramme à l'échelle logarithmique sur l'axe des x en python
Test.py n'est pas reflété sur le serveur Web dans Python3.
Une note sur l'utilisation de l'API Facebook avec le SDK Python
Une note quand j'ai touché l'API de reconnaissance faciale de Microsoft avec Python
[Part.2] Exploration avec Python! Cliquez sur la page Web pour vous déplacer!
Enregistrer des images sur le Web sur un lecteur avec Python (Colab)
Apprentissage automatique Python sans avoir besoin d'un environnement de développement. Dans Azure notebook (jupyter notebook sur Azure), «Apprenons en programmant avec Ayaka Ikezawa! J'ai suivi un cours de mathématiques pour l'apprentissage automatique [théorème de Bayes]