Los trabajos paralelos no terminan en GridSearchCV de scikit-learn

En la siguiente secuencia de comandos, encuentro que los trabajos iniciados por GridSearchCV parecen bloquearse.

import json import pandas as pd import numpy as np import unicodedata import re from sklearn.pipeline import Pipeline from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer from sklearn.decomposition import TruncatedSVD from sklearn.linear_model import SGDClassifier import sklearn.cross_validation as CV from sklearn.grid_search import GridSearchCV from nltk.stem import WordNetLemmatizer # Seed for randomization. Set to some definite integer for debugging and set to None for production seed = None ### Text processing functions ### def normalize(string):#Remove diacritics and whatevs return "".join(ch.lower() for ch in unicodedata.normalize('NFD', string) if not unicodedata.combining(ch)) wnl = WordNetLemmatizer() def tokenize(string):#Ignores special characters and punct return [wnl.lemmatize(token) for token in re.compile('\w\w+').findall(string)] def ngrammer(tokens):#Gets all grams in each ingredient max_n = 2 return [":".join(tokens[idx:idx+n]) for n in np.arange(1,1 + min(max_n,len(tokens))) for idx in range(len(tokens) + 1 - n)] print("Importing training data...") with open('/Users/josh/dev/kaggle/whats-cooking/data/train.json','rt') as file: recipes_train_json = json.load(file) # Build the grams for the training data print('\nBuilding n-grams from input data...') for recipe in recipes_train_json: recipe['grams'] = [term for ingredient in recipe['ingredients'] for term in ngrammer(tokenize(normalize(ingredient)))] # Build vocabulary from training data grams. vocabulary = list({gram for recipe in recipes_train_json for gram in recipe['grams']}) # Stuff everything into a dataframe. ids_index = pd.Index([recipe['id'] for recipe in recipes_train_json],name='id') recipes_train = pd.DataFrame([{'cuisine': recipe['cuisine'], 'ingredients': " ".join(recipe['grams'])} for recipe in recipes_train_json],columns=['cuisine','ingredients'], index=ids_index) # Extract data for fitting fit_data = recipes_train['ingredients'].values fit_target = recipes_train['cuisine'].values # extracting numerical features from the ingredient text feature_ext = Pipeline([('vect', CountVectorizer(vocabulary=vocabulary)), ('tfidf', TfidfTransformer(use_idf=True)), ('svd', TruncatedSVD(n_components=1000)) ]) lsa_fit_data = feature_ext.fit_transform(fit_data) # Build SGD Classifier clf = SGDClassifier(random_state=seed) # Hyperparameter grid for GRidSearchCV. parameters = { 'alpha': np.logspace(-6,-2,5), } # Init GridSearchCV with k-fold CV object cv = CV.KFold(lsa_fit_data.shape[0], n_folds=3, shuffle=True, random_state=seed) gs_clf = GridSearchCV( estimator=clf, param_grid=parameters, n_jobs=-1, cv=cv, scoring='accuracy', verbose=2 ) # Fit on training data print("\nPerforming grid search over hyperparameters...") gs_clf.fit(lsa_fit_data, fit_target) 

La salida de la consola es:

 Importing training data... Building n-grams from input data... Performing grid search over hyperparameters... Fitting 3 folds for each of 5 candidates, totalling 15 fits [CV] alpha=1e-06 ..................................................... [CV] alpha=1e-06 ..................................................... [CV] alpha=1e-06 ..................................................... [CV] alpha=1e-05 ..................................................... [CV] alpha=1e-05 ..................................................... [CV] alpha=1e-05 ..................................................... [CV] alpha=0.0001 .................................................... [CV] alpha=0.0001 .................................................... 

Y luego simplemente cuelga. Si configuro n_jobs=1 en GridSearchCV , entonces el script se completa como se esperaba con el resultado:

 Importing training data... Building n-grams from input data... Performing grid search over hyperparameters... Fitting 3 folds for each of 5 candidates, totalling 15 fits [CV] alpha=1e-06 ..................................................... [CV] ............................................ alpha=1e-06 - 6.5s [Parallel(n_jobs=1)]: Done 1 jobs | elapsed: 6.6s [CV] alpha=1e-06 ..................................................... [CV] ............................................ alpha=1e-06 - 6.6s [CV] alpha=1e-06 ..................................................... [CV] ............................................ alpha=1e-06 - 6.7s [CV] alpha=1e-05 ..................................................... [CV] ............................................ alpha=1e-05 - 6.7s [CV] alpha=1e-05 ..................................................... [CV] ............................................ alpha=1e-05 - 6.7s [CV] alpha=1e-05 ..................................................... [CV] ............................................ alpha=1e-05 - 6.6s [CV] alpha=0.0001 .................................................... [CV] ........................................... alpha=0.0001 - 6.6s [CV] alpha=0.0001 .................................................... [CV] ........................................... alpha=0.0001 - 6.7s [CV] alpha=0.0001 .................................................... [CV] ........................................... alpha=0.0001 - 6.7s [CV] alpha=0.001 ..................................................... [CV] ............................................ alpha=0.001 - 7.0s [CV] alpha=0.001 ..................................................... [CV] ............................................ alpha=0.001 - 6.8s [CV] alpha=0.001 ..................................................... [CV] ............................................ alpha=0.001 - 6.6s [CV] alpha=0.01 ...................................................... [CV] ............................................. alpha=0.01 - 6.7s [CV] alpha=0.01 ...................................................... [CV] ............................................. alpha=0.01 - 7.3s [CV] alpha=0.01 ...................................................... [CV] ............................................. alpha=0.01 - 7.1s [Parallel(n_jobs=1)]: Done 15 out of 15 | elapsed: 1.7min finished 

La ejecución de un solo subproceso finaliza bastante rápido, así que estoy seguro de que le estoy dando al caso de trabajo paralelo el tiempo suficiente para realizar el cálculo por sí mismo.

Especificaciones del entorno: MacBook Pro (15 pulgadas, mediados de 2010), 2.4 GHz Intel Core i5, 8 GB 1067 MHz DDR3, OSX 10.10.5, python 3.4.3, ipython 3.2.0, numpy v1.9.3, scipy 0.16.0 , scikit-learn v0.16.1 (python y paquetes todos de la distribución de anaconda)

Algunos comentarios adicionales:

Uso n_jobs=-1 con GridSearchCV todo el tiempo en esta máquina sin problemas, por lo que mi plataforma admite la funcionalidad. Por lo general, tiene 4 trabajos a la vez, ya que tengo 4 núcleos en esta máquina (2 físicos, pero 4 “núcleos virtuales” debido al hyperthreading de Mac). Pero a menos que entienda mal la salida de la consola, en este caso tiene 8 trabajos sin ninguna devolución. Observando el uso de la CPU en el Monitor de actividad en tiempo real, inicie 4 trabajos, trabaje un poco, luego termine (¿o muera?) Seguido de 4 más que inicie, trabaje un poco, y luego quede completamente inactivo pero permanezca inactivo.

En ningún momento veo una presión significativa en la memoria. El proceso principal es superior a aproximadamente 1 GB de memoria real, el niño procesa a unos 600 MB. Para cuando cuelgan, la memoria real es insignificante.

El script funciona bien con varios trabajos si uno elimina el paso TruncatedSVD de la tubería de extracción de características. Tenga en cuenta, sin embargo, que esta canalización actúa antes de la búsqueda de cuadrícula y no es parte de los trabajos de GridSearchCV .

Este script es para la competencia de Kaggle What’s Cooking? así que si quieres intentar ejecutarlo en los mismos datos que estoy usando, puedes capturarlo desde allí. Los datos vienen como una matriz JSON de objetos. Cada objeto representa una receta y contiene una lista de fragmentos de texto que son los ingredientes. Como cada muestra es una colección de documentos en lugar de un solo documento, terminé teniendo que escribir algunos de mis propios n-gramming y lógica de tokenización, ya que no podía averiguar cómo obtener los transformadores incorporados de scikit-learn a hacer exactamente lo que quiero Dudo que nada de eso importa, pero sólo un FYI.

Normalmente ejecuto scripts dentro de la CLI de iPython con% run, pero obtengo el mismo comportamiento al ejecutarlo desde el terminal bash de OSX con python (3.4.3) directamente.

Esto podría ser un problema con el multiprocesamiento utilizado por GridSearchCV si njob> 1. Entonces, en lugar de usar multiprocesamiento, puedes intentar multiproceso para ver si funciona bien.

 from sklearn.externals.joblib import parallel_backend clf = GridSearchCV(...) with parallel_backend('threading'): clf.fit(x_train, y_train) 

Tenía el mismo problema con mi estimador usando GSV con njob> 1 y esto funciona muy bien en todos los valores de njob.

PD: No estoy seguro de que el “subprocesamiento” tenga las mismas ventajas que el “multiprocesamiento” para todos los estimadores. Pero teóricamente, el “subprocesamiento” no sería una buena opción si su estimador está limitado por GIL, pero si el estimador se basa en un cython / numpy sería mejor que el “multiprocesamiento”

Sistema probado en:

 MAC OS: 10.12.6 Python: 3.6 numpy==1.13.3 pandas==0.21.0 scikit-learn==0.19.1 

Creo que tuve un problema similar y el culpable fue un aumento repentino en el uso de la memoria. El proceso intentaría asignar memoria y morir inmediatamente porque no hay suficiente disponible

Si tiene acceso a una máquina con mucha más memoria disponible (como 128-256 GB), vale la pena verificar con el mismo número de trabajos (n_jobs = 4) o menor. Así es como resolví eso de todos modos: simplemente moví mi script a un servidor masivo.

Pude resolver un problema similar estableciendo explícitamente la semilla aleatoria:

np.random.seed(0) .

Mi problema se debió a la ejecución de GSCV varias veces, por lo que es posible que esto no se aplique directamente a su caso de uso.