Chapitre 4 Analyse morphologique, Chapitre 5 Analyse des dépendances et Traitement du langage 100 J'ai résolu ce problème avec Python3. Pour le moment, je n'ai pas besoin du contenu du «Chapitre 6: Traitement du texte anglais» et du «Chapitre 7: Base de données», je vais donc les ignorer et passer au «Chapitre 8: Machine Learning». ..
Quant à mon niveau de connaissances, je n'ai jamais utilisé Python au travail, j'ai pris une leçon d'introduction à Coursera, je suis un amateur complet en traitement du langage naturel / apprentissage automatique, mais je prévois de l'utiliser au travail à partir de maintenant. C'est un sentiment.
À partir de ce chapitre, je ne peux plus comprendre ce que dit l'énoncé du problème, alors j'écrirai l'explication des termes.
Dans ce chapitre, [jeu de données de polarité des phrases] de Movie Review Data publié par Bo Pang et Lillian Lee. v1.0](http://www.cs.cornell.edu/people/pabo/movie-review-data/rt-polaritydata.README.1.0.txt) est utilisé pour rendre la phrase positive ou négative. Travaillez sur la tâche (analyse de polarité) à classer comme (négative).
Créez des données de réponse correctes (sentiment.txt) comme suit en utilisant les données de réponse correctes de l'analyse de polarité pour les phrases.
Ajoutez la chaîne "+1" au début de chaque ligne dans rt-polarity.pos (étiquette de polarité "+1" suivie d'un espace suivi du contenu de l'instruction positive) Ajoutez la chaîne "-1" au début de chaque ligne dans rt-polarity.neg (étiquette de polarité "-1" suivie d'un espace suivi d'une déclaration négative) Concaténer le contenu de 1 et 2 ci-dessus et trier les lignes au hasard Après avoir créé sentiment.txt, vérifiez le nombre d'exemples positifs (phrases positives) et le nombre d'exemples négatifs (phrases négatives).
--_ Polarité __: Lorsqu'une expression linguistique telle qu'une phrase, une phrase ou un mot a une signification positive ou négative, «affirmative» ou «négative» est appelée la polarité de l'expression linguistique. La détermination automatique de la polarité est une technique de base pour le traitement des informations de réputation.
ml/Main.py
from ml.Sentiment import Sentiments, Util
from ml.Model import LogisticRegression
import matplotlib.pyplot as plt
sentiments = Sentiments()
sentiments.restore_from_pos_neg_file('../8_ML/rt-polarity.pos', '../8_ML/rt-polarity.neg')
sentiments.shuffle()
sentiments.save('../8_ML/70_sentiment.txt')
ml/Sentiments.py
import random
import re
from itertools import chain
from nltk.stem.porter import PorterStemmer
from stop_words import get_stop_words
from ml.Model import LogisticRegression
class Sentiments:
"""
Classe qui gère la liste des avis
"""
def __init__(self) -> None:
self.all = []
def restore_from_pos_neg_file(self, pos_file: str, neg_file: str) -> None:
"""
Conservez les phrases positives et négatives sous forme de liste avec des étiquettes polaires.
Comme étiquette polaire pour les phrases positives'1'Comme étiquette polaire pour les phrases négatives'-1'Mettez.
:param pos_fichier Fichier qui enregistre le texte positif(latin_1)
:param neg_fichier Un fichier qui stocke des phrases négatives(latin_1)
"""
with open(pos_file, encoding='latin_1') as input_file:
self.all += [{'label': 1, 'sentence': line.replace('\n', '')} for line in input_file]
with open(neg_file, encoding='latin_1') as input_file:
self.all += [{'label': -1, 'sentence': line.replace('\n', '')} for line in input_file]
def shuffle(self):
random.shuffle(self.all)
- Créez une liste de mots vides anglais (liste d'arrêt) de manière appropriée. De plus, implémentez une fonction qui renvoie true si le mot (chaîne de caractères) donné en argument est inclus dans la liste d'arrêt, et false dans le cas contraire. De plus, écrivez un test pour cette fonction.
- Concevez vos propres traits qui peuvent être utiles pour l'analyse de polarité et extrayez les traits des données d'entraînement. En ce qui concerne la nature, la ligne de base minimale serait celle avec les mots vides supprimés de la revue et chaque mot dérivé.
--__ mots vides __: Une liste de mots qui doivent être supprimés des mots d'index dans la recherche d'informations. Il est composé de mots qui ne sont pas considérés comme efficaces pour la recherche d'informations, tels que des mots auxiliaires et des mots qui ont des significations générales telles que être et ont. Le nombre de mots vides étant limité, ils sont souvent créés manuellement à l'avance. Je suis un peu confus sur "le créer correctement", mais quand j'ai jeté un coup d'œil à la façon dont les autres le font, "faites-le vraiment texto", "faites-le par analyse morphologique + analyse de fréquence", "général sur le net" Il semble que certaines personnes utilisent des méthodes telles que «prendre une liste et l'écrire solidement». Je vais utiliser un package python appelé mots vides ici.
--__ Fonctionnalité : Fait référence aux informations qui peuvent être utilisées comme indice pour classer les données pendant l'apprentissage automatique. Aussi connu sous le nom de fonctionnalité pour l'apprentissage automatique. Par exemple, lors de l'apprentissage automatique d'un modèle d'estimation de la partie d'un mot, les mots et les parties qui apparaissent avant et après le mot sont utilisés comme éléments d'apprentissage. En d'autres termes, apprenez un modèle qui estime la partie du mot cible en utilisant les mots et les parties qui apparaissent avant et après celui-ci comme indices. Ce qui est utilisé comme capacité d'apprentissage est un facteur important qui détermine le succès ou l'échec du traitement du langage naturel basé sur l'apprentissage automatique. -- Données d'apprentissage _: Données utilisées pour former automatiquement un modèle de classification en machine learning. Données d'entraînement. Ce sont les données de révision (liste de sentiments) enregistrées dans sentiment.txt. -- Processus de tige (tige) : Tige = tige. Le mot radical est également appelé le mot base et est la partie qui ne change pas lorsque la forme du mot change. Par exemple, dans le cas du verbe "throw", "throw" est la racine. La tige est la conversion du mot «lancer» en «lancer». En Python, il peut être exécuté avec la méthode stem de la classe PorterStemmer dans le package nltk.stem.porter. (Il est repris dans "52. Stemming".) -- Concevez votre identité __: Concevez vos propres fonctionnalités qui peuvent être utiles pour l'analyse de polarité. .. .. Que devrais-je faire? En plus du processus de suppression de mot d'arrêt + dérivation, qui est la ligne de base minimale dans l'énoncé du problème, le nombre d'occurrences est trop élevé (10000 fois ou plus) ou trop peu (3 fois ou moins), et il semble que la polarité ne sera pas affectée. J'ai essayé de supprimer les mots de leur arrière-plan, mais je n'ai pas obtenu de bons résultats à la fin, alors je vais continuer avec la ligne de base minimale ici.
Nous ajouterons une liste d'identité pour chaque avis que les sentiments ont.
ml/Main.py
sentiments.add_features()
sentiments.save('../8_ML/72_sentiment.txt')
ml/Sentiments.py
class Sentiments:
#Méthode introduite
def __init__(self) -> None: ...
def restore_from_pos_neg_file(self, pos_file: str, neg_file: str) -> None: ...
#Ci-dessous, 1.Conception d'identité(71.Mot d'arrêt/ 72.Extraction d'identité)Méthode à ajouter avec
def add_features(self):
stemmer = PorterStemmer()
#Nous ajouterons des informations d'identité aux données d'entraînement
for sentiment in self.all:
words = re.compile(r'[,.:;\s]').split(sentiment['sentence'])
features = []
for word in words:
stem = stemmer.stem(word)
# if not (stop_words.is_stop_word(stem) or is_freq_word(stem) or stem == ''):
if not (Util.is_stop_word(stem) or stem == ''):
features.append(stem)
sentiment['features'] = list(set(features))
class Util:
stop_words = get_stop_words('english')
@staticmethod
def is_stop_word(_word: str) -> bool:
"""
Renvoie true si le mot (chaîne de caractères) donné dans l'argument est inclus dans la liste d'arrêt, false dans le cas contraire.
:param _mot mot
:Vrai si le mot (chaîne de caractères) donné dans l'argument booléen de retour est inclus dans la liste d'arrêt, faux sinon
"""
return _word in Util.stop_words
- Apprenons le modèle de régression logistique en utilisant les propriétés extraites en 73. 72. --Vérifiez les 10 principaux caractères de poids élevé et les 10 principaux caractères de faible poids dans le modèle de régression logistique appris en 75.73.
--__ Apprendre le modèle de régression logistique __ Je ne comprends pas assez pour expliquer la définition exacte, mais il a dit: "À partir de la correspondance entre" label "et" features "créée en 72, calculez le" vecteur de poids "en utilisant la fonction d'identification (fonction sigmoïde)." Je vais le comprendre provisoirement.
--__ vecteur de poids __
Une collection de valeurs qui indiquent dans quelle mesure chaque propriété affecte le résultat (exprimée sous forme de type dict en Python).
Par exemple, si vous exécutez le programme suivant, vous verrez dictweights
tels que {..., 'perfect': 1.829376624682014, 'remarque': 1.8879018752394459, 'dull': -2.8891666516371806, 'bore': -3.153237996082115, ... Je peux l'obtenir. Ce commentaire sera probablement positif si le mot parfait ou remarque (racine) est inclus dans le texte de la critique, et il sera probablement négatif si le mot terne ou ennuyeux est inclus. Cela peut être interprété comme tel. C'est un nombre qui semble raisonnable intuitivement.
--____ Fonction d'identification (fonction sigmoïde) __ Une fonction qui prédit la possibilité d'un examen positif en utilisant un vecteur de poids et une liste d'identité comme entrées. En interne, nous ne nous soucions pas de la logique de calcul ici.
--__ Taux d'apprentissage __ Ajustez le déplacement du paramètre avec une seule mise à jour avec une valeur positive appropriée. Plus le taux d'apprentissage est élevé, plus l'apprentissage est rapide, mais la probabilité de prédiction n'est pas stable. Il semble qu'il soit courant de fixer la valeur initiale à environ 0,1 et de la diminuer progressivement à mesure que l'apprentissage progresse, mais ici, après essais et erreurs, elle est fixée à 0,6.
ml/Main.py
model = LogisticRegression()
model.calc_weights(sentiments=sentiments.all)
ml/Model.py
import math
from collections import defaultdict
class LogisticRegression:
def __init__(self):
self.weights = defaultdict(float) #Vecteur de poids
def predict(self, _features: list) -> float:
"""
Fonction de discrimination:Prédire la possibilité d'un avis positif en utilisant le vecteur de poids et la liste d'identité comme entrées
:param _fonctionnalités Une liste d'arrière-plans organisée par texte de révision
:retour Probabilité d'un avis positif
"""
#Produit intérieur du vecteur de poids et de la liste d'identité
x = sum([self.weights[feature] for feature in _features])
#Fonction Sigmaid
return 1.0 / (1.0 + math.exp(-x))
def update(self, _features: list, _label: int, _eta: float) -> None:
"""
Mettre à jour le vecteur de poids.
:param _fonctionnalités Une liste d'arrière-plans organisée par texte de révision
:param _étiquette Étiquette attachée au texte de l'avis(Avis positif:+1 /Avis négatif:-1)
:param _taux d'apprentissage eta
"""
#Réponse prédite par la fonction discriminante(Probabilité d'être un avis positif)
predict_answer = self.predict(_features)
#S'il s'agit en fait d'un avis positif(Convertir les étiquettes en probabilités(-1 => 0.0000, 1 => 1.0000))
actual_answer = (float(_label) + 1) / 2
#Mettre à jour le vecteur de poids
for _feature in _features:
_dif = _eta * (predict_answer - actual_answer)
#Ne pas mettre à jour si la différence est trop proche de 0
if 0.0002 > abs(self.weights[_feature] - _dif):
continue
self.weights[_feature] -= _dif
def calc_weights(self, eta0: float = 0.6, etan: float = 0.9999, sentiments: list = None) -> None:
"""
Calculer le vecteur de poids
:param eta0 Taux d'apprentissage initial
:param etan Taux de réduction du taux d'apprentissage
:param sentiments Liste de dictionnaires contenant des étiquettes de révision, des phrases et une liste d'identités
"""
for idx, sentiment in enumerate(sentiments):
self.update(sentiment['features'], sentiment['label'], eta0 * (etan ** idx))
def save_weights(self, file_name: str) -> None:
"""
Ecrire le vecteur de poids dans un fichier(Trier)
:param file_nom Nom du fichier
"""
with open(file_name, mode='w', encoding='utf-8') as output:
for k, v in sorted(self.weights.items(), key=lambda x: x[1]):
output.write('{}\t{}\n'.format(k, v))
def restore_weights(self, file_name: str) -> dict:
"""
Restaurer le vecteur de poids à partir du fichier
:param file_nom Nom du fichier où le vecteur de poids est stocké
:vecteur de poids de retour
"""
weights = {}
with open(file_name, encoding='utf-8') as input_file:
for line in input_file:
key, value = line.split()
weights[key] = float(value)
self.weights = weights
- En utilisant le modèle de régression logistique appris en 74.73, implémentez un programme qui calcule l'étiquette de polarité ("+1" pour un exemple positif, "-1" pour un exemple négatif) d'une phrase donnée et sa probabilité de prédiction. .. --76. Appliquez le modèle de régression logistique aux données d'entraînement et affichez le bon libellé, le libellé prévu et la probabilité prédite dans un format délimité par des tabulations.
- Créez un programme qui reçoit la sortie de 76 et obtient le taux de réponse correct de la prédiction, le taux de réponse correct pour l'exemple correct, le taux de rappel et le score F1.
ml/Mian.py
sentiments.add_predict(model.predict)
score = sentiments.calc_score()
Util.print_score(score, '77.Mesure du taux de réponse correcte')
ml/Sentiments.py
class Sentiments:
#Méthode introduite
def __init__(self) -> None: ...
def restore_from_pos_neg_file(self, pos_file: str, neg_file: str) -> None: ...
def add_features(self) -> None: ...
#Ci-dessous, la méthode à ajouter ici
def add_predict(self, predict_func: classmethod, threshold: float = 0.0):
for sentiment in self.all:
probability = predict_func(sentiment['features'])
sentiment['probability'] = probability
if probability > 0.5 + threshold:
sentiment['predict_label'] = 1
elif probability < 0.5 - threshold:
sentiment['predict_label'] = -1
else:
sentiment['predict_label'] = 0
def calc_score(self) -> dict:
count = 0
correct_count = 0
actual_positive_count = 0
predict_positive_count = 0
correct_positive_count = 0
for sentiment in self.all:
count += 1
correct = int(sentiment['label']) == int(sentiment['predict_label'])
positive = int(sentiment['label']) == 1
predict_positive = int(sentiment['predict_label']) == 1
if correct:
correct_count += 1
if positive:
actual_positive_count += 1
if predict_positive:
predict_positive_count += 1
if correct and predict_positive:
correct_positive_count += 1
precision_rate = correct_positive_count / predict_positive_count
recall_rate = correct_positive_count / actual_positive_count
f_value = (2 * precision_rate * recall_rate) / (precision_rate + recall_rate)
return {
'correct_rate': correct_count / count,
'precision_rate': precision_rate,
'recall_rate': recall_rate,
'f_value': f_value
}
class Util:
#Méthode introduite
def is_stop_word(_word: str) -> bool: ...
#Ci-dessous, la méthode à ajouter ici
@staticmethod
def print_score(score: dict, title: str = '') -> None:
print('\n{}\n\t Taux de précision de la prédiction: {}\n\t Taux de conformité pour les exemples positifs: {}\n\t rappeler: {}\n\Score tF1: {}'.format(
title, score['correct_rate'], score['precision_rate'], score['recall_rate'], score['f_value']))
Il semble qu'il ne devrait pas être supprimé considérablement.
77.Mesure du taux de réponse correcte
Taux d'exactitude des prévisions: 0.8743200150065654
Taux de conformité pour les cas positifs: 0.8564029290944811
Rappel: 0.8994560120052523
Score F1: 0.8774016468435498
Dans l'expérience 76-77, le cas utilisé pour l'apprentissage a également été utilisé pour l'évaluation, il ne peut donc pas être considéré comme une évaluation valide. Autrement dit, le classificateur évalue les performances lors de la mémorisation du cas de formation et ne mesure pas les performances de généralisation du modèle. Par conséquent, trouvez le taux de réponse, le taux de précision, le taux de rappel et le score F1 corrects de la classification de polarité par le test croisé à 5 divisions.
--__ Test croisé en 5 divisions __: Une méthode de division des données d'entraînement en 5 parties, en utilisant 4 pour la formation et en utilisant 1 pour tester pour construire et évaluer un modèle 5 fois avec différentes combinaisons.
ml/Main.py
sentiments.restore('../8_ML/72_sentiment.txt') #Restaurer les sentiments désappris.
score = sentiments.cross_validation(model=LogisticRegression())
Util.print_score(score, '78.Test croisé en 5 divisions')
ml/Sentiments.py
class Sentiments:
#Méthode introduite
def __init__(self) -> None: ...
def restore_from_pos_neg_file(self, pos_file: str, neg_file: str) -> None: ...
def add_features(self) -> None: ...
def add_predict(self, predict_func: classmethod, threshold: float = 0.0) -> None: ...
def calc_score(self) -> dict: ...
#Ci-dessous, la méthode à ajouter ici
def restore(self, file: str):
_sentiments = []
with open(file, encoding='utf-8') as input_file:
for line in input_file:
_label, _sentence, _features_str, _probability, _predict_label = line.split('\t')
_sentiments.append({
'label': int(_label),
'sentence': _sentence,
'features': _features_str.split(' '),
'probability': 0 if _probability == '' else float(_probability),
'predict_label': 0 if _predict_label.rstrip() == '' else float(_predict_label)
})
self.all = _sentiments
def cross_validation(self, _divide_count: int = 5, model: LogisticRegression = None, threshold: float = 0.0) -> dict:
divided_list = Util.divide_list(self.all, _divide_count)
_scores = []
for i in range(_divide_count):
#Apprentissage
learning_data = list(chain.from_iterable([divided_list[x] for x in [_i for _i in range(_divide_count) if _i != i]]))
model.calc_weights(sentiments=learning_data)
#tester
self.all = divided_list[i]
self.add_predict(model.predict, threshold)
_scores.append(self.calc_score())
return {
'correct_rate': sum([_score['correct_rate'] for _score in _scores]) / _divide_count,
'precision_rate': sum([_score['precision_rate'] for _score in _scores]) / _divide_count,
'recall_rate': sum([_score['recall_rate'] for _score in _scores]) / _divide_count,
'f_value': sum([_score['f_value'] for _score in _scores]) / _divide_count
}
class Util:
#Méthode introduite
def is_stop_word(_word: str) -> bool: ...
def print_score(score: dict, title: str = '') -> None: ...
#Ci-dessous, la méthode à ajouter ici
@staticmethod
def divide_list(lst: list, count: int) -> list:
"""
Diviser la liste par le nombre spécifié
:param lst Liste à diviser
:param count Combien diviser
:liste de retour Split list
"""
divided_list = []
list_len = len(lst) / count
for _i in range(count):
begin_index = int(_i * list_len)
end_index = int((_i + 1) * list_len if _i + 1 < count else len(lst))
divided_list.append(lst[begin_index:end_index])
return divided_list
Bien sûr, il est inférieur à 77, mais ce n'est pas une grave détérioration.
78.Test croisé en 5 divisions
Taux d'exactitude des prévisions: 0.848988423671968
Taux de conformité pour les cas positifs: 0.8481575029900081
Rappel: 0.852642297684391
Score F1: 0.8503632552717463
Dessinez un graphique de rappel de précision en modifiant le seuil de classification du modèle de régression logistique.
--Seuil: Jusqu'à présent, nous avons déterminé que l'étiquette prédite est positive si la probabilité est de 0,5 ou plus, et négative dans le cas contraire. Il semble que ce critère s'appelle le seuil.
Modifions le seuil de 0,0 à 0,45 par incréments de 0,05 et observons la transition du taux de précision et du taux de rappel.
ml/Main.py
precision_rates = []
recall_rates = []
thresholds = [t / 20 for t in range(10)]
for threshold in thresholds:
sentiments.restore('../8_ML/72_sentiment.txt') #Restaurer les sentiments désappris.
score = sentiments.cross_validation(model=LogisticRegression(), threshold=threshold)
precision_rates.append(score['precision_rate'])
recall_rates.append(score['recall_rate'])
print(thresholds)
print(precision_rates)
print(recall_rates)
plt.plot(thresholds, precision_rates, label="precision", color="red")
plt.plot(thresholds, recall_rates, label="recall", color="blue")
plt.xlabel("threshold")
plt.ylabel("rate")
plt.xlim(-0.05, 0.5)
plt.ylim(0, 1)
plt.title("Logistic Regression")
plt.legend(loc=3)
plt.show()
Augmenter le seuil signifie ne prédire que lorsqu'il est absolument certain, donc plus le seuil est élevé, plus le taux de précision est élevé. Au contraire, le taux de rappel diminuera car il ne prédit que lorsqu'il est absolument certain.
Recommended Posts