C'est un record de défi de 100 langues de traitement knock 2015. L'environnement est Ubuntu 16.04 LTS + Python 3.5.2 : : Anaconda 4.1.1 (64 bits). Cliquez ici pour une liste des coups passés (http://qiita.com/segavvy/items/fb50ba8097d59475f760).
Dans ce chapitre, la tâche de classer les phrases en positives (positives) ou négatives (négatives) à l'aide du jeu de données de polarité des phrases v1.0 de Movie Review Data publié par Bo Pang et Lillian Lee (analyse de polarité). Travailler sur.
Apprenez le modèle de régression logistique en utilisant les propriétés extraites en> 72.
main.py
# coding: utf-8
import codecs
import snowballstemmer
import numpy as np
fname_sentiment = 'sentiment.txt'
fname_features = 'features.txt'
fname_theta = 'theta.npy'
fencoding = 'cp1252' # Windows-1252 semble
learn_alpha = 6.0 #Taux d'apprentissage
learn_count = 1000 #Nombre d'itérations d'apprentissage
stemmer = snowballstemmer.stemmer('english')
#Liste des mots vides http://xpo6.com/list-of-english-stop-words/À partir du format CSV
stop_words = (
'a,able,about,across,after,all,almost,also,am,among,an,and,any,are,'
'as,at,be,because,been,but,by,can,cannot,could,dear,did,do,does,'
'either,else,ever,every,for,from,get,got,had,has,have,he,her,hers,'
'him,his,how,however,i,if,in,into,is,it,its,just,least,let,like,'
'likely,may,me,might,most,must,my,neither,no,nor,not,of,off,often,'
'on,only,or,other,our,own,rather,said,say,says,she,should,since,so,'
'some,than,that,the,their,them,then,there,these,they,this,tis,to,too,'
'twas,us,wants,was,we,were,what,when,where,which,while,who,whom,why,'
'will,with,would,yet,you,your').lower().split(',')
def is_stopword(str):
'''Renvoie si le caractère est un mot vide
Égaliser le cas
Valeur de retour:
Vrai pour les mots vides, Faux pour différents
'''
return str.lower() in stop_words
def hypothesis(data_x, theta):
'''Fonction hypothétique
data_Pour x, utilisez thêta pour les données_Prédire y
Valeur de retour:
Matrice de valeurs prédites
'''
return 1.0 / (1.0 + np.exp(-data_x.dot(theta)))
def cost(data_x, theta, data_y):
'''Fonction objective
data_Calculez la différence entre le résultat prédit et la bonne réponse pour x
Valeur de retour:
Différence entre prédiction et réponse correcte
'''
m = data_y.size #Nombre de données
h = hypothesis(data_x, theta) # data_Matrice de valeurs prédites de y
j = 1 / m * np.sum(-data_y * np.log(h) -
(np.ones(m) - data_y) * np.log(np.ones(m) - h))
return j
def gradient(data_x, theta, data_y):
'''Calcul de la pente à la descente la plus raide
Valeur de retour:
Matrice de dégradé pour thêta
'''
m = data_y.size #Nombre de données
h = hypothesis(data_x, theta) # data_Matrice de valeurs prédites de y
grad = 1 / m * (h - data_y).dot(data_x)
return grad
def extract_features(data, dict_features):
'''Extraire l'identité du texte
Dict du texte_Extraire les fonctionnalités incluses dans les fonctionnalités et
dict_features['(Identité)']Renvoie une matrice avec la position 1.
Le premier élément est fixé à 1. Pour des poids qui ne correspondent pas à la nature.
Valeur de retour:
Le premier élément et la position de l'élément correspondant+Matrice avec 1 comme 1
'''
data_one_x = np.zeros(len(dict_features) + 1, dtype=np.float64)
data_one_x[0] = 1 #Le premier élément est fixe et 1 pour les poids qui ne correspondent pas à la nature.
for word in data.split(' '):
#Supprimer les caractères vides avant et après
word = word.strip()
#Arrêter la suppression des mots
if is_stopword(word):
continue
#Tige
word = stemmer.stemWord(word)
#Obtenez l'indice d'identité, définissez la partie correspondante de la matrice sur 1
try:
data_one_x[dict_features[word]] = 1
except:
pass # dict_Ignorer les fonctionnalités introuvables dans les fonctionnalités
return data_one_x
def load_dict_features():
'''features.Lire txt et créer un dictionnaire pour convertir l'identité en index
La valeur de l'index est basée sur 1, caractéristiques.Correspond au numéro de ligne en txt.
Valeur de retour:
Un dictionnaire qui convertit les identités en index
'''
with codecs.open(fname_features, 'r', fencoding) as file_in:
return {line.strip(): i for i, line in enumerate(file_in, start=1)}
def create_training_set(sentiments, dict_features):
'''Créer une matrice à apprendre et une matrice avec des étiquettes polaires à partir des bons sentiments de données de réponse
La taille de l'exemple de ligne à apprendre est le nombre de révisions des bonnes réponses ×(Numéro élémentaire+1)。
La valeur de la colonne sera 1 s'il existe une identité pertinente pour chaque avis, et 0 dans le cas contraire.
L'index de l'identité de la colonne est dict_features['(Identité)']C'est décidé par.
La première colonne est toujours 1 pour l'apprentissage des poids qui ne correspondent pas à la nature.
dict_Ignorez les fonctionnalités qui n'existent pas dans les fonctionnalités.
La taille de la matrice des étiquettes polaires est le nombre d'avis x 1.
1 pour un contenu positif et 0 pour un contenu négatif.
Valeur de retour:
Matrice à apprendre,Matrice d'étiquettes polaires
'''
#Initialiser la matrice avec 0
data_x = np.zeros([len(sentiments), len(dict_features) + 1], dtype=np.float64)
data_y = np.zeros(len(sentiments), dtype=np.float64)
for i, line in enumerate(sentiments):
#Extraction d'identité
data_x[i] = extract_features(line[3:], dict_features)
#Ensemble de matrices d'étiquettes polaires
if line[0:2] == '+1':
data_y[i] = 1
return data_x, data_y
def learn(data_x, data_y, alpha, count):
'''Apprentissage de la régression logistique
Valeur de retour:
Thêta formé
'''
theta = np.zeros(data_x.shape[1])
c = cost(data_x, theta, data_y)
print('\t Commencez à apprendre\tcost:{}'.format(c))
for i in range(1, count + 1):
grad = gradient(data_x, theta, data_y)
theta -= alpha * grad
#Calculez le coût et le montant maximum d'ajustement de thêta et affichez la progression (une fois toutes les 100 fois)
if i % 100 == 0:
c = cost(data_x, theta, data_y)
e = np.max(np.absolute(alpha * grad))
print('\t apprendre(#{})\tcost:{}\tE:{}'.format(i, c, e))
c = cost(data_x, theta, data_y)
e = np.max(np.absolute(alpha * grad))
print('\t Apprentissage terminé(#{}) \tcost:{}\tE:{}'.format(i, c, e))
return theta
#Lire le dictionnaire d'identité
dict_features = load_dict_features()
#Créer une matrice à former et une matrice avec des étiquettes polaires
with codecs.open(fname_sentiment, 'r', fencoding) as file_in:
data_x, data_y = create_training_set(list(file_in), dict_features)
#Apprentissage
print('Taux d'apprentissage:{}\t Nombre de répétitions d'apprentissage:{}'.format(learn_alpha, learn_count))
theta = learn(data_x, data_y, alpha=learn_alpha, count=learn_count)
#Enregistrer les résultats
np.save(fname_theta, theta)
Résultat d'exécution
Taux d'apprentissage: 6.0 Nombre de répétitions d'apprentissage: 1000
Coût de l'apprentissage: 0.6931471805599453
Apprentissage(#100) cost:0.4809284917412944 E:0.006248170735186127
Apprentissage(#200) cost:0.43188679850114775 E:0.003599155234198481
Apprentissage(#300) cost:0.4043113376254009 E:0.002616675715766214
Apprentissage(#400) cost:0.38547454091328076 E:0.0020805226234380772
Apprentissage(#500) cost:0.37135664408713015 E:0.0017952496476012821
Apprentissage(#600) cost:0.36017505743644285 E:0.0015873326040173347
Apprentissage(#700) cost:0.35098616931062043 E:0.0014288227472357999
Apprentissage(#800) cost:0.343231725184532 E:0.0013037591670736948
Apprentissage(#900) cost:0.33655507220582787 E:0.0012023865948793643
Apprentissage(#1000) cost:0.33071511988186225 E:0.0011184180264631118
Apprentissage terminé(#1000) cost:0.33071511988186225 E:0.0011184180264631118
La matrice de résultat formé $ \ theta $ est sortie vers "theta.npy". Le fichier est téléchargé sur GitHub.
En guise de préparation, vous devez installer une bibliothèque appelée NumPy qui peut effectuer des opérations matricielles à grande vitesse. Heureusement, il semblait avoir été installé par Anaconda, et j'ai pu l'utiliser tel quel sans rien faire. Le site officiel est ici.
La raison pour laquelle le calcul matriciel à grande vitesse par NumPy est nécessaire est que la quantité de calcul nécessaire pour le travail d'apprentissage de l'apprentissage automatique est très grande, et si la logique n'est pas implémentée en remplaçant la logique par le calcul matriciel par la méthode de vectorisation, le temps d'apprentissage sera très long. Parce que ça finira.
Par exemple, la formule suivante utilisée pour l'apprentissage qui apparaît dans Problème 72 extrait 3227 identités, donc n devient 3227.
Dans ce cas, il faut 3227 fois de multiplication et 3227 fois d'addition pour calculer y une fois. Normalement, $ \ theta_0 $ ~ $ \ theta_ {3227} $ et $ x_1 $ ~ $ x_ {3227} $ sont listés, et en bouclant 3227 fois avec l'instruction for, multipliez chacun et ajoutez à $ y $. Ce serait bien d'y aller, mais cela prendra un temps considérable car cette formule sera calculée plusieurs fois au cours de l'apprentissage.
Par conséquent, au lieu de lister $ \ theta_0 $ ~ $ \ theta_ {3227} $ et $ x_1 $ ~ $ x_ {3227} $, créez une matrice et implémentez-la en prenant le produit interne de la matrice. Le produit interne de la matrice est calculé en multipliant séquentiellement les éléments de chaque matrice et en les additionnant, la réponse est donc la même. Comme il est plus rapide de trouver le produit interne de la matrice avec NumPy que de répéter la multiplication et l'ajout des listes avec l'instruction for, cela accélère l'apprentissage. Une matrice avec 1 ligne ou 1 colonne est appelée un vecteur, donc le processus de création d'une matrice est appelé "vectorisation".
Il y a une mise en garde lors de la vectorisation. Il y a un total de 3228 $ \ theta $ de $ \ theta_0 $ à $ \ theta_ {3227} $, tandis que $ x $ en manque un de $ x_1 $ à $ x_ {3227} $. C'est. Si vous ne correspondez pas aux chiffres, vous ne pouvez pas obtenir le produit intérieur. Par conséquent, nous préparons $ x_0 $, transformons l'expression comme suit et l'implémentons de sorte que la valeur de $ x_0 $ soit toujours 1. Notez que $ x $ est un de plus que le nombre d'identités.
Les matrices $ X $ et $ \ theta $ ressemblent à ceci: La valeur est appropriée.
Maintenant $ y $ peut être trouvé par $ X \ theta $ par opération de matrice.
La méthode de manipulation de la matrice avec NumPy est en anglais, mais vous pouvez la comprendre en gros en regardant le Tutoriel de démarrage rapide. Il existe de nombreux articles de commentaires sur le net, veuillez donc les consulter.
Pour plus d'informations sur la vectorisation, voir junichiro Making Machine Learning Practical Level in One Month # 6 (Octave Practical Edition) et ken5scal's [Pour les débutants] L'introduction à la vectorisation dans l'apprentissage automatique était facile à comprendre.
Même si vous le vectorisez et laissez NumPy le calculer comme une matrice, le calcul effectué à l'intérieur devrait être le même, alors pourquoi ne pas l'accélérer? Vous pourriez penser que NumPy a une disposition de mémoire conçue pour les éléments de la matrice afin qu'elle puisse être calculée à la vitesse de C / C ++, et cela semble être considérablement plus rapide que le calcul en boucle en Python. ..
De plus, étant donné que de nombreuses opérations matricielles sont calculées en interne indépendamment les unes des autres, elles peuvent être calculées en parallèle plutôt que séquentiellement. Par conséquent, en fonction de la bibliothèque utilisée, vous pouvez vous attendre à une accélération par GPU.
Au départ, le GPU est une puce graphique dédiée, et contrairement au cœur du processeur, qui tente d'effectuer des tâches avancées de manière séquentielle et à grande vitesse, le cœur du GPU exécute un grand nombre de tâches de calcul de données de pixels pour l'affichage à l'écran en parallèle. Était spécialisé dans. Les GPU actuels ne sont plus dédiés aux graphiques et peuvent être utilisés pour des tâches de calcul telles que le calcul matriciel. Il semble que les GPU modernes soient équipés de milliers de cœurs, donc si c'est une tâche telle que le calcul matriciel que le cœur du GPU peut gérer, il peut traiter beaucoup plus en parallèle qu'un CPU avec un nombre limité de cœurs. .. Par conséquent, on peut s'attendre à une réduction significative du temps de traitement.
Dans les implémentations liées à l'apprentissage automatique, la vectorisation semble être une technique très importante pour accélérer le traitement.
La fonction de prédiction $ h_ \ theta (x) $ utilisée jusqu'à présent dans les exemples est appelée une fonction hypothétique.
Valeur prédite de
Cette fois, je vais traiter un peu plus cette formule et l'utiliser. Si vous l'utilisez tel quel [Problème 72](http://qiita.com/segavvy/items/6695f94c28126607227b#4 Prédisons-le réellement) Comme je l'ai mentionné un peu, la valeur prédite de $ y $ est supérieure à 1, ce qui montre une affirmation. En effet, il devient difficile à gérer lorsqu'il devient grand ou inférieur à 0, ce qui indique un refus.
Dans Problème 70, j'ai écrit que l'apprentissage automatique loue si le résultat peut être prédit correctement et ajuste $ \ theta $ de manière grondante si la réponse est incorrecte. En fait, la différence par rapport à la bonne réponse est calculée par la fonction objectif décrite plus loin, et $ \ theta $ est ajustée pour que la différence devienne plus petite. Si vous prédisez correctement l'affirmation, l'étiquette correcte pour $ y $ est 1. Cependant, si cette formule est utilisée, la valeur prédite sera 1, 10 ou 100, et si elle est 10 ou 100, la bonne réponse est prédite correctement, mais la différence avec l'étiquette de réponse correcte 1 devient grande. Je vais finir. Par conséquent, nous utilisons une fonction pratique appelée fonction sigmoïde.
Cette fonction convertit toute valeur donnée en une valeur comprise entre 0 et 1, et la relation d'amplitude de la valeur donnée est conservée même après la conversion. Si vous mettez la fonction hypothétique $ h_ \ theta (x) $ dans la partie $ z $ de cette fonction, le résultat sera toujours de 0 à 1.
Cela ressemble à ceci quand je l'écris sur un graphique (j'ai essayé de l'écrire sur le standard Mac Grapher, mais c'est pratique. Cependant, je ne sais pas comment spécifier l'étiquette de l'axe, donc je ne traite que cela).
Lorsque $ z $ fluctue autour de 0, le résultat de la fonction sigmoïde fluctue considérablement autour de 0,5, tandis que lorsque $ z $ devient plus grand ou plus petit qu'un certain niveau, le résultat de la fonction sigmoïde fluctue autour de 1 ou autour de 0. Ne fait pratiquement aucune différence.
Grâce à cette propriété, même si le résultat de la fonction hypothétique est 1, 10 ou 100, si vous le mettez dans $ z $, il sera proche de 1, et la différence par rapport à l'étiquette correcte sera faible.
Cette fois, j'ai utilisé une fonction hypothétique qui utilise cette fonction sigmoïde. hypothesis ()
est sa mise en œuvre. Il prend une matrice de $ X $ et $ \ theta $ et calcule la valeur prédite.
Ensuite, j'expliquerai la fonction objectif pour déterminer avec quelle précision la prédiction est faite. En fonction du résultat de cette fonction objectif, nous ajusterons $ \ theta $ pour faire une prédiction plus précise.
Vous pouvez savoir si la prédiction est correcte en comparant la valeur prédite calculée par la fonction d'hypothèse avec l'étiquette correcte. Si la différence est faible, la précision de la prédiction est élevée, et si la différence est grande, la précision de la prédiction est faible. Par conséquent, il est basique de calculer et d'ajouter à tous les avis à apprendre, puis de diviser par le nombre d'avis pour obtenir la moyenne. C'est une forme.
Normalement, au lieu d'ajouter simplement la différence, ajoutez la valeur au carré de la différence. Il semble que si vous la mettez au carré, vous n'avez pas à vous soucier du signe de la différence, et la valeur de la différence devient grande, il devient donc plus facile d'ajuster $ \ theta $.
Cependant, dans la prédiction que le résultat est soit 1 (affirmatif) soit 0 (négatif) comme cette fois, la différence n'est que de 1 au maximum simplement en utilisant la différence quadratique, et cette fonction objectif n'est plus une fonction convexe. Il semble qu'il sera difficile d'ajuster $ \ theta $ pour ramener la fonction objectif à la valeur minimale.
Par conséquent, en utilisant la propriété de $ -log () $, la fonction objectif suivante est utilisée de sorte que 0 si la prédiction et le résultat correspondent, et $ \ infty $ s'ils ne correspondent pas. $ y $ est l'étiquette correcte (1 pour affirmatif, 0 pour négatif) et $ h $ est la valeur prédite par la fonction hypothétique.
Dans la première moitié, la valeur lorsque la réponse correcte est affirmative ($ y = 1
L'implémentation de la fonction objectif est également accélérée en utilisant des opérations matricielles. Pour calculer la fonction objectif lorsqu'elle est implémentée normalement, trouvez la valeur prédite $ h $ pour chaque revue et $ -y , log (h) - (1 --y) log (1 --h) $ La valeur de sera calculée et elle sera ajoutée à plusieurs reprises pour le nombre de tous les avis. C'est parce que l'écriture dans une boucle le ralentit.
Tout d'abord, créez une matrice $ X $ avec des fonctionnalités extraites de tous les avis. La longueur de la matrice est le nombre de critiques et la largeur est le nombre de nombres premiers + 1. La raison de +1 est que, comme je l'ai écrit dans la section sur la vectorisation, j'ai toujours besoin d'un élément de 1 qui correspond à $ x_0 $ afin d'utiliser des opérations matricielles dans des fonctions hypothétiques.
Cette fois, le nombre d'avis est de 10 662 et le nombre d'identités est de 3227, donc $ X $ ressemble à ce qui suit. Ce n'est qu'une image et la valeur est appropriée.
$ \ theta $ est la forme écrite dans la section vectorisation.
Cela vous donnera une valeur prédictive de $ H $ pour tous les avis en un seul $ X \ theta $.
De la même manière, faites de l'étiquette correcte une matrice.
Vous pouvez maintenant calculer $ -y , log (h) - (1 --y) log (1 --h) $ par opération de matrice, et vous pouvez calculer tous les avis à la fois. Je vais. «Cost ()» est implémenté sous cette forme.
Notez que les matrices $ X $ et $ Y $ sont créées avec create_training_set ()
.
Vient ensuite la partie à ajuster $ \ theta $. Prédisez avec la fonction d'hypothèse, calculez la différence par rapport à la bonne réponse avec la fonction objectif et répétez l'ajustement de $ \ theta $ pour que la valeur devienne plus petite. Cette fois, j'ai utilisé la méthode de descente la plus raide pour cet ajustement. La méthode de descente la plus raide est une méthode d'examen du gradient de l'individu courant $ \ theta $ dans la fonction objectif et d'ajustement de l'individu $ \ theta $ dans la direction dans laquelle la valeur de la fonction objectif diminue.
Ci-dessous, une image lorsqu'il n'y a qu'un seul $ \ theta $.
Commencez par vérifier le gradient de position ① dans le $ \ theta $ courant. Le gradient examiné est représenté par une ligne droite sur la figure (la longueur de cette ligne droite correspond au taux d'apprentissage décrit plus loin). Ensuite, en supposant que la valeur de la fonction objectif tombe le long de la ligne droite du gradient, changez $ \ theta $ pour qu'il soit dans la position avant la rectitude. Ceci termine le premier ajustement, $ \ theta $ devient une nouvelle valeur et la position actuelle passe à ②. La deuxième fois, vérifiez la pente de la position ②. Après cela, répétez ce processus et passez à ③ et ④, et ajustez-vous à la position où le gradient devient plat pour minimiser la fonction objectif.
Dans l'explication de la fonction objectif ci-dessus, j'ai écrit que c'est un problème si ce n'est pas une fonction convexe, mais si ce n'est pas une fonction convexe, il y aura une bosse qui n'est pas le point minimum au milieu de la courbe de ce graphique. Si vous le faites, vous serez accro à un endroit qui n'est pas le point minimum, et vous ne pourrez pas dériver $ \ theta $ à la valeur minimale.
Le gradient peut être calculé par différenciation partielle, mais je vais omettre la méthode pour l'obtenir (ou plutôt, je ne peux pas le trouver moi-même car je ne me souviens pas encore de la différenciation partielle ^^;) Enfin, vous pouvez ajuster $ \ theta $ avec la formule suivante.
$ m $ est le nombre d'avis, $ h_ \ theta (x ^ {(i)}) $ est la fonction $ i $ extraite de la troisième revue $ x ^ {(i)} $ a été utilisée pour trouver la fonction d'hypothèse $ i $ Valeur prédite du 3ème avis, $ y ^ {(i)} $ est le libellé de la réponse correcte du ième avis, $ x_j ^ {(i)} $ est la caractéristique extraite du $ i $ e avis La valeur de j $, $ \ alpha $, est un paramètre appelé taux d'apprentissage. Le taux d'apprentissage sera décrit plus loin.
Le calcul du nouveau $ \ theta $ par cette méthode de descente la plus raide est également accéléré par le fonctionnement de la matrice au lieu de calculer à plusieurs reprises la boucle pour 3228 $ \ theta $. Le calcul du gradient est implémenté par gradient ()
, et l'ajustement répété de $ \ theta $ est implémenté par learn ()
.
Le taux d'apprentissage $ \ alpha $ lors de l'ajustement de $ \ theta $ correspond à la longueur de la ligne droite de la pente dans le diagramme de descente le plus raide ci-dessus. Si vous l'augmentez, le montant d'ajustement de $ \ theta $ augmentera et la valeur de la fonction objectif diminuera également de manière significative. Cependant, si elle est rendue trop grande, elle passera le point minimum de la fonction objectif, et la fonction objectif deviendra grande, et elle ne convergera pas à plusieurs reprises. En revanche, si vous le rendez trop petit, le temps de traitement sera très long, bien qu'il progressera régulièrement vers le point minimum de la fonction objectif. Cette zone nécessite des essais et des erreurs.
De plus, combien de fois cet ajustement $ \ theta $ doit-il être répété pour terminer l'apprentissage? Parce que c'est une partie qui nécessite des essais et des erreurs. La fonction objectif devient de plus en plus petite au début, mais sa vitesse devient de plus en plus lente. En général, on a dit que le montant d'ajustement de $ \ theta $ devrait être aussi petit que 10 $ ^ {-3} $ comme ligne directrice pour la fin. Dans cette implémentation, le montant de l'ajustement est calculé dans learn ()
et affiché comme E.
Je l'ai essayé plusieurs fois, mais cette fois j'ai mis le taux d'apprentissage à 6,0 et l'ai répété 1000 fois. E au 1 000e ajustement était d'environ 0,001, et la valeur de la fonction objectif n'a guère changé, il semble donc bon de s'arrêter ici. Au fait, il a fallu environ 1 minute pour étudier sur mon ordinateur.
Je pensais que j'écrivais cet article, mais quand il s'agit d'un contenu aussi compliqué, il est difficile d'essayer de l'expliquer facilement sans connaître ma propre compréhension ^^; Comme je l'ai écrit dans [Recommandations pour l'étude de l'apprentissage automatique dans le problème 70](http://qiita.com/segavvy/items/0e91fe02088b875a386a#Recommendations pour l'étude de l'apprentissage automatique), veuillez d'abord lire les merveilleux matériels pédagogiques et articles dans le monde. Veuillez en profiter.
C'est tout pour le 74e coup. Si vous avez des erreurs, j'apprécierais que vous les signaliez.
Recommended Posts