Dans cet article, j'expliquerai scikit-learn en utilisant 100 Language Processing Knock Chapitre 6.
Tout d'abord, faisons pip install scikit-learn
.
Téléchargez l'ensemble de données News Aggregator et créez les données d'entraînement (train.txt), les données de vérification (valid.txt) et les données d'évaluation (test.txt) comme suit.
Décompressez le fichier zip téléchargé et lisez l'explication de readme.txt. Seuls les cas (articles) où la source d'information (éditeur) est «Reuters», «Huffington Post», «Businessweek», «Contactmusic.com», «Daily Mail» sont extraits. Triez au hasard les cas extraits. Divisez 80% des observations extraites en données d'entraînement et les 10% restants en données de vérification et en données d'évaluation, et enregistrez-les respectivement sous les noms de fichier train.txt, valid.txt et test.txt. Écrivez un cas par ligne dans le fichier et utilisez le format délimité par des tabulations du nom de la catégorie et de l'en-tête de l'article (ce fichier sera réutilisé plus tard dans le problème 70).
Après avoir créé les données d'entraînement et les données d'évaluation, vérifiez le nombre de cas dans chaque catégorie.
Ce problème n'a rien à voir avec scicit-learn, vous pouvez donc le résoudre comme vous le souhaitez. Tout d'abord, téléchargez le fichier et lisez readme.txt.
!wget https://archive.ics.uci.edu/ml/machine-learning-databases/00359/NewsAggregatorDataset.zip
!unzip -c NewsAggregatorDataset.zip readme.txt
Readme est très bien, mais je veux gérer les fichiers compressés sans les décompresser autant que possible. Le fichier zip du corps de données doit être géré par le module zipfile. N'importe quelle méthode peut être utilisée pour la lecture, mais dans ce cas, Use pandas est facile. Laissez sklearn.model_selection.train_test_split () faire le fractionnement. Il mélange également.
En guise d'histoire rudimentaire, le nom de la bibliothèque est scikit-learn, mais le nom du module lors de l'importation est sklearn.
import csv
import zipfile
import pandas as pd
from sklearn.model_selection import train_test_split
with zipfile.ZipFile("NewsAggregatorDataset.zip") as z:
with z.open("newsCorpora.csv") as f:
names = ('ID','TITLE','URL','PUBLISHER','CATEGORY','STORY','HOSTNAME','TIMESTAMP')
df = pd.read_table(f, names=names, quoting=csv.QUOTE_NONE)
publisher_set = {"Reuters", "Huffington Post", "Businessweek", "Contactmusic.com", "Daily Mail"}
df = df[df['PUBLISHER'].isin(publisher_set)]
df, valid_test_df = train_test_split(df, train_size=0.8, random_state=0)
df.to_csv('train.txt', columns=('CATEGORY','TITLE'), sep='\t', header=False, index=False)
valid_df, test_df = train_test_split(valid_test_df, test_size=0.5, random_state=0)
valid_df.to_csv('valid.txt', columns=('CATEGORY','TITLE'), sep='\t', header=False, index=False)
test_df.to_csv('test.txt', columns=('CATEGORY','TITLE'), sep='\t', header=False, index=False)
pandas.read_table ()
lit un fichier TSV et crée un objet de type DataFrame
. names définit le nom de la colonne. quoting = csv.QUOTE_NONE
est un paramètre permettant de traiter le guillemet comme une chaîne de caractères. csv.QUOTE_NONE
est le même même si vous écrivez 3
.
(J'ai entendu dire que read_table ()
était obsolète, vous pouvez donc utiliser read_csv (sep = '\ t')
, mais il semble qu'il ne soit plus obsolète car il n'y a pas d'avertissement.)
La partie df ['PUBLISHER']
est une opération d'extraction de colonnes, et la valeur de retour sera de type Series
. Le type de pandas «DataFrame» représentait la structure de la table entière, et chaque colonne était représentée par le type «Série». Sa méthode ʻisin () retourne la
Series de la valeur de vérité de l'opération ʻin
pour chaque élément. Et si vous le passez comme s'il s'agissait d'une clé df, il retournera un DataFrame
qui extrait uniquement les lignes True.
names = ('CATEGORY','TITLE')
df = pd.read_table('train.txt', names=names, quoting=csv.QUOTE_NONE)
df['CATEGORY'].value_counts()
b 4503
e 4254
t 1210
m 717
Name: CATEGORY, dtype: int64
df = pd.read_table('test.txt', names=names, quoting=csv.QUOTE_NONE)
df['CATEGORY'].value_counts()
b 565
e 518
t 163
m 90
Name: CATEGORY, dtype: int64
Extrayez les fonctionnalités des données d'entraînement, des données de vérification et des données d'évaluation, et enregistrez-les respectivement sous les noms de fichier
train.feature.txt
,valid.feature.txt
ettest.feature.txt
. N'hésitez pas à concevoir les fonctionnalités susceptibles d'être utiles pour la catégorisation. La ligne de base minimale serait un titre d'article converti en une chaîne de mots.
Dans ce problème, il n'est pas dit que les caractéristiques extraites doivent être converties en un vecteur (matrice). Il semble qu'il soit nécessaire d'enregistrer le montant de la fonction dans un format lisible par l'homme afin de l'utiliser ultérieurement pour l'analyse des erreurs.
(Si vous utilisez Count vectorizer
de scikit-learn
, l'extraction de caractéristiques et la vectorisation seront effectuées comme un ensemble, ce qui n'est pas familier avec ce problème.)
Par conséquent, extrayez les fonctionnalités par vous-même, créez un objet dictionnaire, enregistrez-le et utilisez Dictvectorizer dans le problème suivant. Nous allons le résoudre avec la politique de vectorisation en utilisant. La clé du dictionnaire est le nom de la quantité de caractéristiques et la valeur est 1,0. C'est une nature binaire. Création d'un dictionnaire à partir de quantités d'entités Ce processus est également requis pour l'inférence, alors faites-en une fonction.
Le format de stockage de la quantité de fonctionnalités n'est pas spécifié, mais je pense que le format jsonl est meilleur du point de vue de la lisibilité.
Je veux séparer les virgules et les guillemets des mots. Peu importe comment vous le faites. SpaCy est célèbre en tant que tokenizer, mais je pense que le tokenizer de Count vectorizer
est également efficace dans ce problème.
q51.py
import argparse
import json
from sklearn.feature_extraction.text import CountVectorizer
def ngram_gen(seq, n):
return zip(*(seq[i:] for i in range(n)))
nlp = CountVectorizer().build_tokenizer()
def make_feats_dict(title):
words = nlp(title)
feats = {}
for token in words:
feats[token] = 1.0
for bigram in ngram_gen(words, 2):
feats[' '.join(bigram)] = 1.0
for trigram in ngram_gen(words, 3):
feats[' '.join(trigram)] = 1.0
return feats
def dump_features(input_file, output_file):
with open(input_file) as fi, open(output_file, 'w') as fo:
for line in fi:
vals = line.rstrip().split('\t')
label, title = vals
feats = {'**LABEL**': label}
feats.update(make_feats_dict(title))
print(json.dumps(feats), file=fo)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('input_file')
parser.add_argument('output_file')
args = vars(parser.parse_args())
dump_features(**args)
if __name__ == '__main__':
main()
Overwriting q51.py
!python q51.py test.txt test.feature.txt
!python q51.py valid.txt valid.feature.txt
!python q51.py train.txt train.feature.txt
ngram_gen ()
fait ngram. Pour le bigramme, transposez [[I, am, an, NLPer], [am, an, NLPer]]
(selon le plus court)! Je le fais de manière élégante.
(L'étiquette n'est pas une quantité de caractéristiques, mais je l'écris car elle atténuera le problème suivant)
Je fais quelque chose d'étrange avec la fonction principale, mais cela est également utilisé dans le chapitre 4 [Déballer la liste des arguments](https://docs.python.org/ja/3/tutorial/controlflow.html#unpacking- J'essaie de passer des arguments de mots clés dans un dictionnaire par des listes d'arguments) Puisque la valeur de retour de parse_args ()
est un objet d'espace de noms, elle est convertie en objet dictionnaire par vars ()
(comme mentionné au chapitre 5).
Apprenez le modèle de régression logistique à l'aide des données d'entraînement construites en> 51.
Commencez par créer une liste «X» constituée d'un dictionnaire représentant la quantité de caractéristiques du fichier créé en 51. Pour l'entrer dans le modèle d'apprentissage automatique, nous avons besoin d'un vecteur qui répertorie les valeurs de toutes les fonctionnalités. Utilisez donc DictVectorizer (). La méthode fit (X)
du DictVectorizer
récupère le mappage du nom de la fonction-index du X
et le stocke dans une variable à l'intérieur de l'instance. Ensuite, utilisez transform (X)
pour transformer X
en une matrice numpy
. Fit_transform (X)
fait tout cela en même temps.
Utilisez ensuite LogisticRegression (). Instanciez simplement et appelez la méthode fit (X, y)
pour apprendre le vecteur de poids à l'intérieur de l'instance. Hypara est défini lors de l'instanciation. «X» est comme une matrice, «y» est comme une liste, et ce n'est pas grave si les longueurs correspondent.
Enregistrez le modèle entraîné en vous référant à Persistance du modèle. Lors de l'utilisation de joblib.dump (), un grand nombre de fichiers sera généré à moins que l'argument optionnel «compress» ne soit spécifié. Donc sois prudent.
À ce stade, si vous n'enregistrez pas le mappage entre le nom de la quantité d'entités et l'index, vous serez en difficulté au moment de l'inférence. Jetons chaque instance de DictVectorizer
.
q52.py
import argparse
import json
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.linear_model import LogisticRegression
import joblib
def argparse_imf():
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input')
parser.add_argument('-m', '--model')
parser.add_argument('-f', '--feats')
args = parser.parse_args()
return args
def load_xy(filename):
X = []
y = []
with open(filename) as f:
for line in f:
dic = json.loads(line)
y.append(dic.pop('**LABEL**'))
X.append(dic)
return X, y
def main():
args = argparse_imf()
X_train, y_train = load_xy(args.input)
vectorizer = DictVectorizer()
X_train = vectorizer.fit_transform(X_train)
y_train = np.array(y_train)
clf = LogisticRegression(random_state=0, max_iter=1000, verbose=1)
clf.fit(X_train, y_train)
joblib.dump(clf, args.model, compress=3)
joblib.dump(vectorizer, args.feats, compress=3)
if __name__ == '__main__':
main()
Overwriting q52.py
!python q52.py -i train.feature.txt -m train.logistic.model -f train.feature.joblib
Utilisez le modèle de régression logistique appris en> 52 et implémentez un programme qui calcule la catégorie et sa probabilité de prédiction à partir de l'en-tête d'article donné.
Je pense que le "titre d'article donné" dans cette question ne fait pas référence aux données de test créées ci-dessus, mais plutôt à faire des prédictions à partir de n'importe quel titre d'article.
Si vous chargez le modèle sauvegardé et appelez predict (X)
, l'étiquette sortira, et si vous appelezpredict_proba (X)
, la probabilité de prédiction sortira. Ce X
peut être obtenu en créant un dictionnaire de caractéristiques à partir de l'entrée et en le convertissant avec le Dictvectorizer ()
enregistré dans 52.
Si vous entrez deux titres et appliquez predict_proba ()
, vous obtiendrez un numpy.ndarray
comme celui-ci.
>>> y_proba
array([[0.24339871, 0.54111814, 0.10059608, 0.11488707],
[0.19745579, 0.69644375, 0.04204659, 0.06405386]])
Les probabilités de prédiction pour toutes les étiquettes sortent, mais je pense que vous ne voulez que la valeur maximale. que devrais-je faire? Il semble que ndarray
ait une méthode max () ...
>>> y_proba.max()
0.6964437549683299
>>> y_proba.max(axis=0)
array([0.24339871, 0.69644375, 0.10059608, 0.11488707])
Faisons de notre mieux. Voici un exemple de réponse.
q53.py
import argparse
import json
import sys
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.linear_model import LogisticRegression
import joblib
from q51 import make_feats_dict
from q52 import argparse_imf, load_xy
def predict_label_proba(X, vectorizer, clf):
X = vectorizer.transform(X)
y_proba = clf.predict_proba(X)
y_pred = clf.classes_[y_proba.argmax(axis=1)]
y_proba_max = y_proba.max(axis=1)
return y_pred, y_proba_max
def main():
args = argparse_imf()
vectorizer = joblib.load(args.feats)
clf = joblib.load(args.model)
X = list(map(make_feats_dict, sys.stdin))
y_pred, y_proba = predict_label_proba(X, vectorizer, clf)
for label, proba in zip(y_pred, y_proba):
print('%s\t%.4f' % (label, proba))
if __name__ == '__main__':
main()
Overwriting q53.py
!echo 'I have a dog.' | python q53.py -m train.logistic.model -f train.feature.joblib
e 0.5441
Mesurez le taux de réponse correct du modèle de régression logistique appris en> 52 sur les données d'entraînement et les données d'évaluation.
Vous pouvez l'implémenter à la main, mais je vais le laisser à sklearn.metrics.accuracy_score ().
La chose la plus importante dans l'apprentissage de «scikit-learn» est le flux jusqu'à présent.
Dictvectorizer.fit_transform ()
LogisticRegression
fit (X_train, y_train)
prédire (X_test)
Tenons cela fermement.
q54.py
import argparse
import json
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
import joblib
from q52 import argparse_imf, load_xy
def predict(args):
X_test, y_true = load_xy(args.input)
vectorizer = joblib.load(args.feats)
X_test = vectorizer.transform(X_test)
y_true = np.array(y_true)
clf = joblib.load(args.model)
y_pred = clf.predict(X_test)
return y_true, y_pred
def main():
args = argparse_imf()
y_true, y_pred = predict(args)
accuracy = accuracy_score(y_true, y_pred) * 100
print('Accuracy: %.3f' % accuracy)
if __name__ == '__main__':
main()
Overwriting q54.py
!python q54.py -i train.feature.txt -m train.logistic.model -f train.feature.joblib
Accuracy: 99.897
!python q54.py -i test.feature.txt -m train.logistic.model -f train.feature.joblib
Accuracy: 87.275
Créer une matrice de confusion du modèle de régression logistique appris en> 52 sur les données d'entraînement et les données d'évaluation.
Laissez-le à sklearn.metrics.confusion_matrix ().
q55.py
from sklearn.metrics import confusion_matrix
from q52 import argparse_imf
from q54 import predict
def main():
args = argparse_imf()
y_true, y_pred = predict(args)
labels = ('b', 'e', 't', 'm')
matrix = confusion_matrix(y_true, y_pred, labels=labels)
print(labels)
print(matrix)
if __name__ == '__main__':
main()
Overwriting q55.py
!python q55.py -i train.feature.txt -m train.logistic.model -f train.feature.joblib
('b', 'e', 't', 'm')
[[4499 1 3 0]
[ 2 4252 0 0]
[ 3 1 1206 0]
[ 0 1 0 716]]
!python q55.py -i test.feature.txt -m train.logistic.model -f train.feature.joblib
('b', 'e', 't', 'm')
[[529 26 10 0]
[ 13 503 2 0]
[ 37 36 89 1]
[ 19 26 0 45]]
Mesurer la précision, le rappel et le score F1 du modèle de régression logistique appris en> 52 sur les données d'évaluation. Obtenez le taux de précision, le taux de rappel et le score F1 pour chaque catégorie, et intégrez les performances de chaque catégorie avec une micro-moyenne et une macro-moyenne.
Laissez-le à sklearn.metrics.classification_report (). Dans la classification multi-classes (étiquette unique), la micro-moyenne de toutes les classes correspond au taux de réponse correct (Référence).
q56.py
from sklearn.metrics import classification_report
from q52 import argparse_imf
from q54 import predict
def main():
args = argparse_imf()
y_true, y_pred = predict(args)
print(classification_report(y_true, y_pred, digits=4))
if __name__ == '__main__':
main()
Overwriting q56.py
!python q56.py -i test.feature.txt -m train.logistic.model -f train.feature.joblib
precision recall f1-score support
b 0.8846 0.9363 0.9097 565
e 0.8511 0.9710 0.9071 518
m 0.9783 0.5000 0.6618 90
t 0.8812 0.5460 0.6742 163
accuracy 0.8728 1336
macro avg 0.8988 0.7383 0.7882 1336
weighted avg 0.8775 0.8728 0.8633 1336
Vérifiez les 10 principales fonctionnalités avec des poids élevés et les 10 principales caractéristiques avec des poids faibles dans le modèle de régression logistique appris en> 52.
L'attribut coef_
a un poids, mais comme il s'agit d'une classification multi-classes, le poids est égal au nombre de classes x le nombre d'étiquettes d'entités. Les 4 classes seront-elles générées?
q57.py
import joblib
import numpy as np
from q52 import argparse_imf
def get_topk_indices(array, k=10):
unsorted_max_indices = np.argpartition(-array, k)[:k]
max_weights = array[unsorted_max_indices]
max_indices = np.argsort(-max_weights)
return unsorted_max_indices[max_indices]
def show_weights(args):
vectorizer = joblib.load(args.feats)
feature_nemes = np.array(vectorizer.get_feature_names())
clf = joblib.load(args.model)
coefs = clf.coef_
y_labels = clf.classes_
for coef, y_label in zip(coefs, y_labels):
max_k_indices = get_topk_indices(coef)
print(y_label)
for name, weight in zip(feature_nemes[max_k_indices], coef[max_k_indices]):
print(name, weight, sep='\t')
print('...')
min_k_indices = get_topk_indices(-coef)
for name, weight in zip(feature_nemes[min_k_indices], coef[min_k_indices]):
print(name, weight, sep='\t')
print()
def main():
args = argparse_imf()
show_weights(args)
if __name__ == '__main__':
main()
Overwriting q57.py
!python q57.py -i test.feature.txt -m train.logistic.model -f train.feature.joblib
C'est une façon détournée comme celle-ci, car je ne veux que les niveaux supérieur et inférieur au lieu de trier le
coef_entier. C'est parce qu'il n'y a pas de fonction de type
topk ()dans numpy, et il n'y a pas d'autre choix que d'obtenir l'index supérieur qui n'est pas trié par ʻargpartition ()
.
Lors de l'entraînement d'un modèle de régression logistique, le degré de surajustement pendant l'entraînement peut être contrôlé en ajustant les paramètres de régularisation. Apprenez le modèle de régression logistique avec différents paramètres de régularisation et trouvez le taux de réponse correct sur les données d'entraînement, les données de validation et les données d'évaluation. Résumez les résultats de l'expérience dans un graphique avec les paramètres de régularisation sur l'axe horizontal et le taux de précision sur l'axe vertical.
import argparse
import json
import numpy as np
from sklearn.feature_extraction import DictVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
import joblib
import matplotlib.pyplot as plt
from tqdm import tqdm
from q52 import load_xy
def get_accuracy(clf, X, y_true):
y_pred = clf.predict(X)
return accuracy_score(y_true, y_pred)
X_train, y_train = load_xy('train.feature.txt')
X_valid, y_valid = load_xy('valid.feature.txt')
X_test, y_test = load_xy('test.feature.txt')
vectorizer = DictVectorizer()
X_train = vectorizer.fit_transform(X_train)
X_valid = vectorizer.transform(X_valid)
X_test = vectorizer.transform(X_test)
train_accuracies = []
valid_accuracies = []
test_accuracies = []
for exp in tqdm(range(10)):
clf = LogisticRegression(random_state=0, max_iter=1000, C=2**exp)
clf.fit(X_train, y_train)
train_accuracies.append(get_accuracy(clf, X_train, y_train))
valid_accuracies.append(get_accuracy(clf, X_valid, y_valid))
test_accuracies.append(get_accuracy(clf, X_test, y_test))
cs = [2**c for c in range(10)]
plt.plot(cs, train_accuracies, label='train')
plt.plot(cs, valid_accuracies, label='valid')
plt.plot(cs, test_accuracies, label='test')
plt.legend()
plt.show()
Apprenez le modèle de catégorisation tout en modifiant l'algorithme d'apprentissage et les paramètres d'apprentissage. Trouvez le paramètre d'algorithme d'apprentissage qui donne le taux de précision le plus élevé sur les données de vérification. Trouvez également le taux de réponse correct sur les données d'évaluation lorsque l'algorithme d'apprentissage et les paramètres sont utilisés.
La sélection de l'algorithme high-para doit être effectuée sur les données de vérification et non sur le réglage de l'ensemble de test. Mais cette fois, je n'ai pas fait grand chose et j'ai fini par utiliser sklearn.ensemble.GradientBoostingClassifier
comme il convenait ... J'avais l'intention d'y mettre fin.
from sklearn.ensemble import GradientBoostingClassifier
clf = GradientBoostingClassifier(random_state=0, min_samples_split=0.01,
min_samples_leaf=5, max_depth=10,
max_features='sqrt', n_estimators=500,
subsample=0.8)
clf.fit(X_train, y_train)
valid_acc = get_accuracy(clf, X_valid, y_valid) * 100
print('Validation Accuracy: %.3f' % valid_acc)
test_acc = get_accuracy(clf, X_test, y_test) * 100
print('Test Accuracy: %.3f' % test_acc)
Validation Accuracy: 88.997
Test Accuracy: 88.548
C'est un GBDT à la mode! Je l'ai essayé, mais c'était difficile car les performances changeaient considérablement avec un para élevé. Si j'avais le temps, je ferais sérieusement une recherche dans la grille ...
Quoi qu'il en soit, en ce qui concerne l'apprentissage automatique en Python, je pense que c'est un chapitre où vous pouvez apprendre scikit-learn.
Recommended Posts