** 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
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.
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.
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.
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.
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».
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. "
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. "
C'est également un succès. Il était présumé appartenir à la catégorie «dent de ver».
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.
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