Python MemoryError al realizar el ajuste con Scikit-learn

Estoy ejecutando Python 2.7 (64 bits) en un sistema Windows 8 de 64 bits con 24 GB de memoria. Al realizar el ajuste de los Sklearn.linear_models.Ridge habituales, el código funciona bien.

Problema: Sin embargo, cuando uso Sklearn.linear_models.RidgeCV(alphas=alphas) para el ajuste, me encuentro con el error MemoryError se muestra a continuación en la línea rr.fit(X_train, y_train) que ejecuta el procedimiento de ajuste.

¿Cómo puedo prevenir este error?

Fragmento de código

 def fit(X_train, y_train): alphas = [1e-3, 1e-2, 1e-1, 1e0, 1e1] rr = RidgeCV(alphas=alphas) rr.fit(X_train, y_train) return rr rr = fit(X_train, y_train) 

Error

 MemoryError Traceback (most recent call last)  in () 1 # Fit Training set ----> 2 rr = fit(X_train, y_train)  in fit(X_train, y_train) 3 4 rr = RidgeCV(alphas=alphas) ----> 5 rr.fit(X_train, y_train) 6 7 return rr C:\Python27\lib\site-packages\sklearn\linear_model\ridge.pyc in fit(self, X, y, sample_weight) 696 gcv_mode=self.gcv_mode, 697 store_cv_values=self.store_cv_values) --> 698 estimator.fit(X, y, sample_weight=sample_weight) 699 self.alpha_ = estimator.alpha_ 700 if self.store_cv_values: C:\Python27\lib\site-packages\sklearn\linear_model\ridge.pyc in fit(self, X, y, sample_weight) 608 raise ValueError('bad gcv_mode "%s"' % gcv_mode) 609 --> 610 v, Q, QT_y = _pre_compute(X, y) 611 n_y = 1 if len(y.shape) == 1 else y.shape[1] 612 cv_values = np.zeros((n_samples * n_y, len(self.alphas))) C:\Python27\lib\site-packages\sklearn\linear_model\ridge.pyc in _pre_compute_svd(self, X, y) 531 def _pre_compute_svd(self, X, y): 532 if sparse.issparse(X) and hasattr(X, 'toarray'): --> 533 X = X.toarray() 534 U, s, _ = np.linalg.svd(X, full_matrices=0) 535 v = s ** 2 C:\Python27\lib\site-packages\scipy\sparse\compressed.pyc in toarray(self, order, out) 559 def toarray(self, order=None, out=None): 560 """See the docstring for `spmatrix.toarray`.""" --> 561 return self.tocoo(copy=False).toarray(order=order, out=out) 562 563 ############################################################## C:\Python27\lib\site-packages\scipy\sparse\coo.pyc in toarray(self, order, out) 236 def toarray(self, order=None, out=None): 237 """See the docstring for `spmatrix.toarray`.""" --> 238 B = self._process_toarray_args(order, out) 239 fortran = int(B.flags.f_contiguous) 240 if not fortran and not B.flags.c_contiguous: C:\Python27\lib\site-packages\scipy\sparse\base.pyc in _process_toarray_args(self, order, out) 633 return out 634 else: --> 635 return np.zeros(self.shape, dtype=self.dtype, order=order) 636 637 MemoryError: 

Código

 print type(X_train) print X_train.shape 

Resultado

  (183576, 101507) 

Eche un vistazo a esta parte de la traza de su stack:

  531 def _pre_compute_svd(self, X, y): 532 if sparse.issparse(X) and hasattr(X, 'toarray'): --> 533 X = X.toarray() 534 U, s, _ = np.linalg.svd(X, full_matrices=0) 535 v = s ** 2 

El algoritmo que estás utilizando se basa en las rutinas de álgebra lineal de numpy para hacer SVD. Pero esos no pueden manejar matrices dispersas, por lo que el autor simplemente las convierte en matrices regulares no dispersas. Lo primero que tiene que suceder para esto es asignar una matriz todo-cero y luego rellenar los puntos apropiados con los valores escasamente almacenados en la matriz dispersa. Suena bastante fácil, pero vamos a las matemáticas. Un elemento float64 (el tipo de dty predeterminado, que probablemente estés usando si no sabes lo que estás usando) toma 8 bytes. Por lo tanto, en función de la forma de matriz que haya proporcionado, la nueva matriz con relleno cero será:

 183576 * 101507 * 8 = 149,073,992,256 ~= 150 gigabytes 

El administrador de memoria de su sistema probablemente echó un vistazo a esa solicitud de asignación y se suicidó. Pero, ¿qué puedes hacer al respecto?

En primer lugar, parece una cantidad bastante ridícula de características. No sé nada acerca de su dominio del problema o cuáles son sus características, pero mi reacción es que necesita hacer algo de reducción de dimensionalidad aquí.

En segundo lugar, puede intentar arreglar el mal manejo de las matrices dispersas del algoritmo. Se está ahogando en numpy.linalg.svd aquí, por lo que es posible que pueda usar scipy.sparse.linalg.svds en scipy.sparse.linalg.svds lugar. No conozco el algoritmo en cuestión, pero podría no ser susceptible de matrices dispersas. Incluso si utiliza las rutinas de álgebra lineal dispersas adecuadas, puede producir (o usar internamente) algunas matrices no dispersas con tamaños similares a sus datos. El uso de una representación de matriz dispersa para representar datos no dispersos solo resultará en el uso de más espacio del que tenía originalmente, por lo que este enfoque podría no funcionar. Proceda con precaución.

La opción relevante aquí es gcv_mode. Puede tomar 3 valores: “auto”, “svd” y “eigen”. De forma predeterminada, se establece en “auto”, que tiene el siguiente comportamiento: use el modo svd si n_samples> n_features, de lo contrario use el modo eigen.

Como en su caso n_samples> n_features, se elige el modo svd. Sin embargo, el modo svd actualmente no maneja los datos dispersos correctamente. scikit-learn debe ser arreglado para usar una SVD dispersa adecuada en lugar de la SVD densa.

Como solución alternativa, forzaría el modo eigen por gcv_mode = “eigen”, ya que este modo debería manejar adecuadamente los datos dispersos. Sin embargo, n_samples es bastante grande en su caso. Dado que el modo eigen construye una matriz de kernel (y por lo tanto tiene una complejidad de memoria n_samples ** 2), la matriz de kernel puede no encajar en la memoria. En ese caso, solo reduciría la cantidad de muestras (aunque el modo eigen puede manejar una gran cantidad de funciones sin problemas).

En cualquier caso, dado que tanto n_samples como n_features son bastante grandes, está llevando esta implementación a sus límites (incluso con una SVD dispersa adecuada).

También vea https://github.com/scikit-learn/scikit-learn/issues/1921