2017-03-14 2 views
2

J'essaie d'utiliser scikit-learn pour classer un grand nombre de documents texte, bien que j'utilise la fonctionnalité extra-core (avec SGDClassifier et HashingVectorizer) le programme semble consommer beaucoup de RAM (> 10GB). J'ai effectué la lemmatisation et supprimé les mots vides des données de texte avant cela. J'ai l'impression de manquer quelque chose d'important ici. Pouvez-vous repérer une erreur dans mon code?Scikit-apprendre la consommation de mémoire de classification de texte hors-core

Merci beaucoup pour votre suggestion!

Ceci est mon code python:

import time 
import numpy as np 
import os 
import re 
import pyprind 
from sklearn.feature_extraction.text import HashingVectorizer 
from sklearn.linear_model import SGDClassifier 
from sklearn.naive_bayes import MultinomialNB 
from sklearn import metrics 

directory = "mydirectory" 
batch_size = 1000 
n_batches = 44 
pbar = pyprind.ProgBar(n_batches) 

class Doc_Iterable: 
    def __init__(self, file): 
     self.file = file 
    def __iter__(self): 
     for line in self.file: 
      line = re.sub('[^\w\s]|(.\d{1,4}[\./]\d{1,2}[\./]\d{1,4})|(\s\d{1,})', '', line) 
      yield line 


def stream_docs(path, texts_file, labels_file): 
    with open(path + texts_file, 'r') as fX, open(path + labels_file, 'r') as fy: 
     for text in fX: 
      label = next(fy) 
      text = re.sub('[^\w\s]|(.\d{1,4}[\./]\d{1,2}[\./]\d{1,4})|(\s\d{1,})', '', text) 
      yield text, label 

def get_minibatch(doc_stream, size): 
    X, y = [], [] 
    for _ in range(size): 
     text, label = next(doc_stream) 
     X.append(text) 
     y.append(label) 
    return X, y 


classes = set() 
for label in open(directory + 'y_train', 'r'): 
    classes.add(label) 
for label in open(directory + 'y_test', 'r'): 
    classes.add(label) 
classes = list(classes) 

validation_scores = [] 
training_set_size = [] 

h_vectorizer = HashingVectorizer(lowercase=True, ngram_range=(1,1)) 
clf = SGDClassifier(loss='hinge', n_iter=5, alpha=1e-4, shuffle=True) 

doc_stream = stream_docs(path=directory, texts_file='X_train', labels_file='y_train') 
n_samples = 0 
iteration = 0 

for _ in range(n_batches): 
    print("Training with batch nr.", iteration) 
    iteration += 1 

    X_train, y_train = get_minibatch(doc_stream, size=batch_size) 

    n_samples += len(X_train) 

    X_train = h_vectorizer.transform(X_train) 

    clf.partial_fit(X_train, y_train, classes=classes) 

    pbar.update() 


del X_train 
del y_train 
print("Training complete. Classifier trained with " + str(n_samples) + " samples.") 
print() 
print("Testing...") 
print() 
X_test = h_vectorizer.transform(Doc_Iterable(open(directory + 'X_test'))) 
y_test = np.genfromtxt(directory + 'y_test', dtype=None, delimiter='|').astype(str) 
prediction = clf.predict(X_test) 
score = metrics.accuracy_score(y_test, prediction) 
print("Accuracy: ", score) 
print() 
+0

Quelle est la taille totale de votre ensemble de données: 44000 documents? Avez-vous essayé d'appliquer un HashingVectorizer à l'ensemble de données complet? D'après mon expérience, l'extraction de fonctions à partir de 700 000 courriels textuels sans approche hors noyau nécessite moins de 16 Go de RAM, de sorte que vos chiffres sont assez importants. À moins que les documents ne soient très longs. Réduire le nombre de fonctionnalités ne changera pas de manière significative de toute façon car ce sont des tableaux clairsemés avec peu de collisions de hachage (je ne suis pas d'accord avec la réponse ci-dessous). – rth

+1

Merci. J'ai 3 millions de documents, je vais probablement devoir en faire plus en termes de pré-traitement pour réduire le nombre de fonctionnalités/taille de vocabulaire. L'utilisation de Tf-idf semble fonctionner correctement pour un sous-ensemble de 1 Go de fichiers texte en entrée. Je vais également essayer d'utiliser le 'HashingVectorizer' sans apprentissage hors-core ... – cookiedealer

+0

3 millions de documents est assez grand. Vous devriez faire un profilage ligne par ligne avec 'memory_profiler' pour comprendre où la mémoire est allouée (ou non libérée). S'il s'agit d'un script, l'exécuter avec 'python -m memory_profiler' devrait suffire ... – rth

Répondre

2

Essayez d'ajuster n_features dans le HashingVectorizer, par exemple:

h_vectorizer = HashingVectorizer(n_features=10000, lowercase=True, ngram_range=(1,1)) 

Avec les paramètres par défaut (n_features=1048576) vous pouvez vous attendre votre matrice transformée pour avoir jusqu'à:

1048576(features) x 1000(mini batch size) x 8 bytes = 8.4 GB 

Il sera moins que cela en raison de la rareté, mais les coefficients du classificateur vont ajouter:

1048576(features) x len(classes) * 8 bytes 

ce qui pourrait expliquer votre utilisation actuelle de la mémoire.

+0

Merci! Je pense que le nombre de fonctionnalités est en effet mon problème, je vais devoir le limiter par une sorte de pré-traitement. Comment HashingVectorizer choisit-il les fonctions si le nombre réel dépasse n_features? – cookiedealer

+2

S'il y a plus de fonctionnalités que 'n_features', alors vous aurez des collisions et deux fonctions pourraient être regroupées en une seule. Mais ce n'est pas si mal dans la pratique car la plupart des mots ne sont jamais utilisés (loi de Zipf). Vous pouvez vous assurer que c'est le cas en mesurant votre score de CV avec différents «n_features» et en comparant à tf-idf. Vous pouvez jeter un coup d'oeil [ici] (https://en.wikipedia.org/wiki/Feature_hashing) à la façon dont le truc de hachage fonctionne afin que vous obteniez une meilleure image. – elyase

+1

Oui, [ici] (https: // github.com/scikit-learn/scikit-learn/issues/7513 # issuecomment-252322348) sont des exemples plus pratiques de la façon dont la réduction de la taille de la table de hachage diminue la précision de la catégorisation du texte. Je suppose que pour les fonctionnalités 1e4-1e5, il devrait y avoir presque pas d'impact .. – rth

0

Cela pourrait ne pas être une réponse (désolé si je ne pouvais pas commenter en raison de problèmes de réputation) mais je travaille sur un projet de classification d'image. D'après mon expérience, l'utilisation de scikit-learn était très lente (dans mon cas, j'ai utilisé environ 30 images, il m'a fallu près de 2 à 6 minutes pour former un classificateur). Quand je suis passé à OpenCV-python, il me faudrait environ une minute ou moins pour former le même classificateur en utilisant le même nombre de données d'entraînement.

+0

Ici le problème est assez différent, car les documents texte sont vectorisés en des tableaux clairsemés contrairement aux images représentées par des tableaux denses (où OpenCV pourrait en effet être plus optimisé). – rth