[PYTHON] 100 traitement du langage knock-73 (en utilisant scikit-learn): apprentissage

Il s'agit du bilan du 73ème «apprentissage» de 100 langues de traitement knock 2015. Il a fallu beaucoup de temps pour faire des recherches et des essais et erreurs.

Jusqu'à présent, je ne l'ai pas posté sur le blog car c'était fondamentalement la même chose que "Traitement du langage amateur 100 coups". , "Chapitre 8: Machine Learning" a été pris au sérieux et modifié dans une certaine mesure. Je posterai. J'utilise principalement scikit-learn.

Lien de référence

Lien Remarques
073_1.Apprentissage(Prétraitement).ipynb Programmederéponse(Prétraitement編)Lien GitHub
073_2.Apprentissage(Entraînement).ipynb Programmederéponse(Entraînement編)Lien GitHub
100 coups de traitement du langage amateur:73 Je vous suis toujours redevable de 100 coups de traitement linguistique
Introduction à Python avec 100 coups de traitement du langage#73 -Apprentissage automatique, scikit-Retour logistique avec apprentissage scikit-Résultat Knock en utilisant Learn

environnement

type version Contenu
OS Ubuntu18.04.01 LTS Il fonctionne virtuellement
pyenv 1.2.15 J'utilise pyenv car j'utilise parfois plusieurs environnements Python
Python 3.6.9 python3 sur pyenv.6.J'utilise 9
3.7 ou 3.Il n'y a aucune raison profonde de ne pas utiliser la série 8
Les packages sont gérés à l'aide de venv

Dans l'environnement ci-dessus, j'utilise les packages Python supplémentaires suivants. Installez simplement avec pip ordinaire.

type version
nltk 3.4.5
stanfordnlp 0.2.0
pandas 0.25.3
scikit-learn 0.21.3

Tâche

Chapitre 8: Machine Learning

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).

73. Apprentissage

Apprenez le modèle de régression logistique en utilisant les propriétés extraites en> 72.

Répondre

Réponse prémisse

Hypothèse 1: diviser en prétraitement et apprentissage

J'ai utilisé Stanford NLP pour la suppression des mots vides et le traitement de la racine des lemmes, et cela a pris beaucoup de temps, donc je l'ai divisé en prétraitement et apprentissage.

Hypothèse 2: vectorisation de mots

J'utilise tf-idf pour vectoriser les mots. tf-idf calcule l'importance sur la base de deux indicateurs, tf (fréquence du terme) et idf (fréquence inverse du document). Diminuez l'importance des mots (mots généraux) qui apparaissent dans de nombreux documents et augmentez l'importance des mots qui n'apparaissent que dans des documents spécifiques.

Hypothèse 3: Explorer les hyperparamètres

Pour être honnête, je ne pouvais pas juger si tf-idf était valide même avec le traitement des mots vides, donc [CountVectorizer](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer. Comparaison de la vectorisation et de la précision avec la fréquence des mots simples en utilisant html). Nous comparons également les hyperparamètres de régression logistique avec une recherche de grille.

Programme de réponse (extraction) [073_1. Apprentissage (pré-traitement) .ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/08.%E6%A9%9F%E6%A2%B0%E5 % AD% A6% E7% BF% 92 / 073_1.% E5% AD% A6% E7% BF% 92 (% E5% 89% 8D% E5% 87% A6% E7% 90% 86) .ipynb)

Le premier est le prétraitement. Cependant, ce que nous faisons est [Précédent "Programme de réponse (analyse) 072_2. Extraction d'identité (analyse) .ipynb"](https://qiita.com/FukuharaYohei/items/f1a12d8e63fc576a456f#%E5%9B % 9E% E7% AD% 94% E3% 83% 97% E3% 83% AD% E3% 82% B0% E3% 83% A9% E3% 83% A0% E5% 88% 86% E6% 9E% 90 % E7% B7% A8-072_2% E7% B4% A0% E6% 80% A7% E6% 8A% BD% E5% 87% BA% E5% 88% 86% E6% 9E% 90ipynb) Il n'y a rien de spécial à mentionner. L'inconvénient est que le traitement prend environ une heure.

import warnings
import re
import csv

from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer as PS
import stanfordnlp

#Défini comme un taple pour la vitesse
STOP_WORDS = set(stopwords.words('english'))

# Stemmer
ps = PS()

#Semble être conforme aux balises Universal POS
# https://universaldependencies.org/u/pos/
EXC_POS = {'PUNCT',   #Ponctuation
           'X',       #Autre
           'SYM',     #symbole
           'PART',    #Particule('s etc.)
           'CCONJ',   #conjonction(et etc.)
           'AUX',     #Verbe auxiliaire(serait etc.)
           'PRON',    #Synonyme
           'SCONJ',   #Connectif subordonné(si etc.)
           'ADP',     #Conjoint(dans etc.)
           'NUM'}     #nombre
		   
#Il était lent de spécifier tous les processeurs par défaut, donc réduisez au minimum
# https://stanfordnlp.github.io/stanfordnlp/processors.html
nlp = stanfordnlp.Pipeline(processors='tokenize,pos,lemma')

reg_sym = re.compile(r'^[!-/:-@[-`{-~]|[!-/:-@[-`{-~]$')
reg_dit = re.compile('[0-9]')


#Suppression des symboles de début et de fin
def remove_symbols(lemma):
    return reg_sym.sub('', lemma)
	

#Arrêter le jugement d'authenticité des mots
def is_stopword(word):
    lemma = remove_symbols(word.lemma)
    return True if lemma in STOP_WORDS \
                  or lemma == '' \
                  or word.upos in EXC_POS \
                  or len(lemma) == 1 \
                  or reg_dit.search(lemma)\
                else False

#Masquer l'avertissement
warnings.simplefilter('ignore', UserWarning)

with open('./sentiment.txt') as file_in:
    with open('./sentiment_stem.txt', 'w') as file_out:
        writer = csv.writer(file_out, delimiter='\t')
        writer.writerow(['Lable', 'Lemmas'])

        for i, line in enumerate(file_in):
            print("\r{0}".format(i), end="")
        
            lemma = []
        
            #Les 3 premiers caractères indiquent uniquement négatif / positif, donc n'effectuez pas de traitement nlp(Faites-le aussi vite que possible)
            doc = nlp(line[3:])
            for sentence in doc.sentences:
                lemma.extend([ps.stem(remove_symbols(word.lemma)) for word in sentence.words if is_stopword(word) is False])
            writer.writerow([1 if line[0] == '+' else 0, ' '.join(lemma)])

Answer Program (Training) [073_2. Learning (Training) .ipynb](https://github.com/YoheiFukuhara/nlp100/blob/master/08.%E6%A9%9F%E6%A2%B0%E5% AD% A6% E7% BF% 92 / 073_2.% E5% AD% A6% E7% BF% 92 (% E8% A8% 93% E7% B7% B4) .ipynb)

C'est la partie formation du sujet principal.

import csv

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.base import BaseEstimator, TransformerMixin

#Classe d'utilisation de la vectorisation de mots dans GridSearchCV
class myVectorizer(BaseEstimator, TransformerMixin):
    def __init__(self, method='tfidf', min_df=0.0005, max_df=0.10):
        self.method = method
        self.min_df = min_df
        self.max_df = max_df

    def fit(self, x, y=None):
        if self.method == 'tfidf':
            self.vectorizer = TfidfVectorizer(min_df=self.min_df, max_df=self.max_df)
        else:
            self.vectorizer = CountVectorizer(min_df=self.min_df, max_df=self.max_df)
        self.vectorizer.fit(x)
        return self

    def transform(self, x, y=None):
        return self.vectorizer.transform(x)
		
#Paramètres de GridSearchCV
PARAMETERS = [
    {
        'vectorizer__method':['tfidf', 'count'], 
        'vectorizer__min_df': [0.0004, 0.0005], 
        'vectorizer__max_df': [0.07, 0.10], 
        'classifier__C': [1, 3],    #J'ai aussi essayé 10 mais le SCORE est faible juste parce qu'il est lent
        'classifier__solver': ['newton-cg', 'liblinear']},
    ]

#Lire le fichier
def read_csv_column(col):
    with open('./sentiment_stem.txt') as file:
        reader = csv.reader(file, delimiter='\t')
        header = next(reader)
        return [row[col] for row in reader]

x_all = read_csv_column(1)
y_all = read_csv_column(0)

def train(x_train, y_train, file):
    pipline = Pipeline([('vectorizer', myVectorizer()), ('classifier', LogisticRegression())])
    
    #clf signifie classification
    clf = GridSearchCV(
            pipline, # 
            PARAMETERS,           #Jeu de paramètres que vous souhaitez optimiser
            cv = 5)               #Nombre de tests croisés
    
    clf.fit(x_train, y_train)
    pd.DataFrame.from_dict(clf.cv_results_).to_csv(file)

    print('Grid Search Best parameters:', clf.best_params_)
    print('Grid Search Best validation score:', clf.best_score_)
    print('Grid Search Best training score:', clf.best_estimator_.score(x_train, y_train))    

train(x_all, y_all, 'gs_result.csv')

Réponse aux commentaires (formation)

Vectorisation de mots

TfidfVectorizer ou [CountVectorizer](https://scikit-learn.org/stable/modules/generated /sklearn.feature_extraction.text.CountVectorizer.html) est utilisé pour la vectorisation de mots. C'est un peu déroutant car il est classé de manière à pouvoir être utilisé avec la fonction GridSearchCV, mais les points importants sont les suivants.

def fit(self, x, y=None):
    if self.method == 'tfidf':
        self.vectorizer = TfidfVectorizer(min_df=self.min_df, max_df=self.max_df)
    else:
        self.vectorizer = CountVectorizer(min_df=self.min_df, max_df=self.max_df)
    self.vectorizer.fit(x)
    return self

def transform(self, x, y=None):
    return self.vectorizer.transform(x)

Utilisez fit pour apprendre de tous les mots et transformer pour transformer la chaîne de mots. Les paramètres sont TfidfVectorizer et CountVectorizer. /generated/sklearn.feature_extraction.text.CountVectorizer.html) Les deux utilisent les deux suivants.

--min_df: Si la fréquence d'occurrence est inférieure au rapport spécifié, elle sera exclue de la vectorisation. Il est précisé car on considère que "l'apprentissage n'est pas possible si la fréquence d'apparition est trop faible". --max_df: Exclut de la vectorisation si elle se produit plus fréquemment que le pourcentage spécifié. Cette fois, j'ai pensé que "des mots comme" film "n'ont aucun sens" et je l'ai précisé.

Retour logistique

Je m'entraîne avec LogisticRegression en utilisant LogisticRegression. L'explication de la régression logistique est écrite dans l'article "Coursera Machine Learning Introduction Course (3ème semaine-Régression logistique, régression)". ・ ・). Grâce au Cours d'introduction à l'apprentissage automatique de Coursera, j'ai pu aborder avec une compréhension de la régularisation.

def train(x_train, y_train, file):
    pipline = Pipeline([('vectorizer', myVectorizer()), ('classifier', LogisticRegression())])

Dans la définition de paramètre suivante, le terme de régularisation est classifier__C et l'optimiseur est classifier__solver. Je ne comprends pas la différence entre les optimiseurs, mais je ne l'ai pas étudié avec le sentiment qu '"il devrait être optimisé par une recherche de grille".

PARAMETERS = [
    {
        'vectorizer__method':['tfidf', 'count'], 
        'vectorizer__min_df': [0.0004, 0.0005], 
        'vectorizer__max_df': [0.07, 0.10], 
        'classifier__C': [1, 3],    #J'ai aussi essayé 10 mais le SCORE est faible juste parce qu'il est lent
        'classifier__solver': ['newton-cg', 'liblinear']},
    ]

Pipeline

J'utilise Pipeline pour canaliser la partie formation avec vectorisation de mots et régression logistique. En conséquence, deux processus peuvent être exécutés en même temps, et la recherche d'hyper paramètre dans la recherche de grille décrite plus loin peut également être traitée en même temps.

def train(x_train, y_train, file):
    pipline = Pipeline([('vectorizer', myVectorizer()), ('classifier', LogisticRegression())])

Recherche de grille hyper-paramètres

Je recherche des hyper paramètres en utilisant GridSearchCV. Puisque le pipeline est implémenté, le mot vectorisation et la partie formation par régression logistique peuvent être recherchées en même temps. La cible de recherche est définie par «PARAMETERS», et le «nom de traitement cible» et le «nom de paramètre» sont combinés par «__». En fait, il existe davantage de paramètres de recherche, mais ils sont omis car le traitement prend beaucoup de temps. Ce paramètre prend environ 2 minutes.

#Paramètres de GridSearchCV
PARAMETERS = [
    {
        'vectorizer__method':['tfidf', 'count'], 
        'vectorizer__min_df': [0.0004, 0.0005], 
        'vectorizer__max_df': [0.07, 0.10], 
        'classifier__C': [1, 3],    #J'ai aussi essayé 10 mais le SCORE est faible juste parce qu'il est lent
        'classifier__solver': ['newton-cg', 'liblinear']},
    ]

#clf signifie classification
clf = GridSearchCV(
        pipline,
        PARAMETERS,           #Jeu de paramètres que vous souhaitez optimiser
        cv = 5)               #Nombre de tests croisés

Recherche de méthodes de vectorisation de mots avec recherche de grille

TfidfVectorizer et [CountVectorizer](https://scikit-learn.org/stable/modules/generated /sklearn.feature_extraction.text.CountVectorizer.html) définit la classe myVectorizer pour savoir laquelle est la meilleure. Le Vectorizer qui reçoit le paramètre «method» et le traite dans la branche conditionnelle «if» est modifié. J'ai fait référence à l'article suivant.

class myVectorizer(BaseEstimator, TransformerMixin):
    def __init__(self, method='tfidf', min_df=0.0005, max_df=0.10):
        self.method = method
        self.min_df = min_df
        self.max_df = max_df

    def fit(self, x, y=None):
        if self.method == 'tfidf':
            self.vectorizer = TfidfVectorizer(min_df=self.min_df, max_df=self.max_df)
        else:
            self.vectorizer = CountVectorizer(min_df=self.min_df, max_df=self.max_df)
        self.vectorizer.fit(x)
        return self

    def transform(self, x, y=None):
        return self.vectorizer.transform(x)

Résultats de la recherche dans la grille

Le résultat de la recherche de grille est généré dans un fichier CSV. Comparons chaque critère avec les scores moyens et maximum (en utilisant Excel).

pd.DataFrame.from_dict(clf.cv_results_).to_csv(file)

J'ai un peu augmenté les paramètres. Par conséquent, la formation a duré environ 11 minutes.

#Paramètres de GridSearchCV
PARAMETERS = [
    {
        'vectorizer__method':['tfidf', 'count'], 
        'vectorizer__min_df': [0.0003, 0.0004, 0.0005, 0.0006], 
        'vectorizer__max_df': [0.07, 0.10], 
        'classifier__C': [1, 3],    #J'ai aussi essayé 10 mais le SCORE est faible juste parce qu'il est lent
        'classifier__solver': ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']}
    ]

Hyper paramètre du score le plus élevé

L'hyper paramètre le plus élevé était un taux moyen de réponses correctes de 75,6% dans 5 vérifications croisées.

Maintenant, comparons chaque paramètre ci-dessous.

Paramètre de vectorisation

TfidfVectorizer/CountVectorizer tf-idf a clairement un meilleur score. image.png

min_df Plus le min_df est petit, meilleur est le score. image.png

max_df Pour td-idf, max_df est un meilleur score s'il est inférieur. image.png

Paramètres de régression logistique

Par optimiseur

Il n'y a pas beaucoup de différence. image.png

Terme de régularisation

De toute évidence, 1 a un meilleur score. image.png

Recommended Posts

100 traitement du langage knock-73 (en utilisant scikit-learn): apprentissage
100 traitement du langage knock-76 (en utilisant scicit-learn): étiquetage
100 traitement du langage knock-74 (en utilisant scicit-learn): prédiction
100 traitement du langage knock-90 (en utilisant Gensim): apprendre avec word2vec
100 langage de traitement knock-79 (en utilisant scikit-learn): dessin de graphe avec rappel de précision
100 coups de traitement linguistique (2020): 28
100 traitement du langage knock-31 (en utilisant des pandas): verbe
100 traitement du langage knock-38 (en utilisant des pandas): histogramme
100 traitements linguistiques knock-77 (en utilisant scicit-learn): mesure du taux de réponse
100 Language Processing Knock-33 (en utilisant des pandas): nom sahen
100 Language Processing Knock-39 (en utilisant des pandas): la loi de Zipf
100 traitement de langage knock-34 (utilisant des pandas): "B of A"
100 traitements linguistiques Knock 2020 [00 ~ 39 réponse]
100 langues de traitement knock 2020 [00-79 réponse]
100 traitements linguistiques Knock 2020 [00 ~ 69 réponse]
100 Language Processing Knock 2020 Chapitre 1
100 coups de traitement du langage amateur: 17
100 Traitement du langage Knock-52: Stemming
100 Traitement du langage Knock Chapitre 1
100 coups de langue amateur: 07
100 Language Processing Knock 2020 Chapitre 3
100 Language Processing Knock 2020 Chapitre 2
100 coups de traitement du langage amateur: 09
Traitement 100 langues knock-53: Tokenisation
100 coups de traitement du langage amateur: 97
100 traitements linguistiques Knock 2020 [00 ~ 59 réponse]
100 coups de traitement du langage amateur: 67
100 langage de traitement knock-20 (à l'aide de pandas): lecture de données JSON
100 Language Processing Knock-32 (utilisant des pandas): Prototype de verbe
Traitement de 100 langues knock-98 (en utilisant des pandas): Clustering par méthode Ward
100 traitement du langage knock-99 (à l'aide de pandas): visualisation par t-SNE
100 traitement du langage knock-95 (en utilisant des pandas): Note avec WordSimilarity-353
100 traitement du langage knock-72 (en utilisant Stanford NLP): Extraction d'identité
100 coups de traitement du langage avec Python 2015
100 traitement du langage Knock-51: découpage de mots
100 Language Processing Knock-57: Analyse des dépendances
100 traitement linguistique knock-50: coupure de phrase
100 Language Processing Knock Chapitre 1 (Python)
100 Language Processing Knock Chapitre 2 (Python)
100 Language Processing Knock-25: Extraction de modèles
Traitement du langage 100 Knock-87: similitude des mots
J'ai essayé 100 traitements linguistiques Knock 2020
100 Language Processing Knock-56: analyse de co-référence
Résolution de 100 traitements linguistiques Knock 2020 (01. "Patatokukashi")
100 coups de traitement du langage amateur: Résumé
100 langage traitement knock-92 (utilisant Gensim): application aux données d'analogie
100 traitement du langage knock-36 (en utilisant des pandas): fréquence d'occurrence des mots
100 Language Processing Knock: Chapitre 2 Principes de base des commandes UNIX (à l'aide de pandas)
100 Language Processing Knock-83 (en utilisant des pandas): Mesure de la fréquence des mots / contextes
100 Language Processing Knock-30 (en utilisant des pandas): lecture des résultats de l'analyse morphologique
100 traitement de langage knock-94 (en utilisant Gensim): calcul de similarité avec WordSimilarity-353
Apprenez facilement 100 traitements linguistiques Knock 2020 avec "Google Colaboratory"
100 Language Processing Knock 2020 Chapitre 2: Commandes UNIX
100 Language Processing Knock 2015 Chapitre 5 Analyse des dépendances (40-49)
[Traitement du langage 100 coups 2020] Chapitre 6: Machine learning
100 traitements de langage avec Python
100 Language Processing Knock Chapitre 1 en Python
100 Language Processing Knock-84 (en utilisant des pandas): Création d'une matrice de contexte de mots
100 Language Processing Knock 2020 Chapitre 4: Analyse morphologique
100 traitements de langage avec Python (chapitre 3)
100 Language Processing Knock: Chapitre 1 Mouvement préparatoire