regresion - maquinas de soporte vectorial en python
Regresión de ScikitLearn: la matriz de diseño X es demasiado grande para la regresión. ¿Qué debo hacer? (2)
Es un problema bien conocido que se puede abordar utilizando el aprendizaje fuera del núcleo. Al buscar en Google el término, encontrará varias formas de abordar el problema.
Para su problema específico, primero tiene que crear un generador que genere una fila (o varias de ellas) de su matriz y que use el método partial_fit
de su algoritmo.
Los algoritmos estándar de scikit-learn usan en realidad un cálculo exacto de la solución, como sklearn.linear_model.LinearRegression
o sklearn.linear_model.LinearRegression.RidgeCV
. Otros métodos se basan en el aprendizaje por lotes y tienen un método partial_fit
como sklearn.linear_model.SGDRegressor
, que permite sklearn.linear_model.SGDRegressor
solo un mini lote. Es lo que estás buscando.
El proceso es: use el generador para producir un mini lote, aplique el método partial_fit
, elimine el mini lote de la memoria y obtenga uno nuevo.
Sin embargo, como este método es estocástico y depende del orden de sus datos y su inicialización de los pesos, es lo opuesto a la solución dada por los métodos de regresión estándar que pueden ajustarse a todos los datos en la memoria. No entraré en los detalles, pero miro la optimización de la pendiente del degradado para entender cómo funciona ( http://ruder.io/optimizing-gradient-descent/ )
Tengo una matriz X
que tiene algo así como 7000 columnas y 38000 filas. Por lo tanto, es una numpy array
con forma (38000, 7000)
.
Yo instalé el modelo
model = RidgeCV(alphas = (0.001,0.01, 0.1, 1)
y luego se ajustó
model.fit(X, y)
donde y
es el vector de respuesta que es una matriz numpy con forma (38000,)
.
Al ejecutar esto obtengo un Memory Error
.
¿Como puedo resolver esto?
Mi idea
Mi primer pensamiento fue dividir la matriz X "horizontalmente". Con esto quiero decir que divido X en, digamos, dos matrices con el mismo número de columnas (manteniendo así todas las características) pero con menos filas. ¿Entonces ajusto el modelo cada vez para cada una de estas submatrices? Pero me temo que esto no es realmente equivalente a ajustar toda la matriz ...
¿Algunas ideas?
En mi opinión, no es realmente una gran escala y probablemente no necesites utilizar el aprendizaje fuera del núcleo (aunque no sé cuánta memoria tienes).
Si utiliza enfoques fuera del núcleo cuando no los necesite, tendrá que pagar un precio (¡no robusto sin una gran afinación)!
También sería interesante saber si sus características son dispersas o densas, lo que puede marcar una gran diferencia (¡ya que la mayoría de los solucionadores pueden explotar datos dispersos!).
Algunas cosas para decir aquí:
- RidgeCV utiliza un enfoque de validación cruzada sin problemas y, probablemente, por esta razón, no hay ningún parámetro para controlar el solucionador subyacente.
- No es raro usar un método de CV ajustado al problema cuando el arranque en caliente eficiente puede ayudar al proceso (rendimiento)
- Al hacer CV manualmente, todas las herramientas están disponibles en sklearn, puede elegir diferentes solucionadores
- Esos son muy diferentes en términos del enfoque y las características
solucionador: {''auto'', ''svd'', ''cholesky'', ''lsqr'', ''sparse_cg'', ''sag'', ''saga''}
Solver para usar en las rutinas computacionales:
''auto'' elige el solucionador automáticamente en función del tipo de datos.
''svd'' usa una Descomposición de Valor Singular de X para calcular los coeficientes de Ridge. Más estable para matrices singulares que ''cholesky''.
''cholesky'' usa la función estándar scipy.linalg.solve para obtener una solución cerrada.
''sparse_cg'' usa el solucionador de gradiente conjugado como se encuentra en scipy.sparse.linalg.cg. Como un algoritmo iterativo, este solucionador es más apropiado que ''cholesky'' para datos a gran escala (posibilidad de establecer tol y max_iter).
''lsqr'' usa la rutina dedicada de mínimos cuadrados regularizados scipy.sparse.linalg.lsqr. Es el más rápido, pero puede no estar disponible en versiones antiguas de scipy. También usa un procedimiento iterativo.
''sag'' usa un descenso de Gradiente promedio estocástico, y ''saga'' usa su versión mejorada e imparcial llamada SAGA. Ambos métodos también usan un procedimiento iterativo, y suelen ser más rápidos que otros solucionadores cuando tanto n_samples como n_features son grandes. Tenga en cuenta que la convergencia rápida ''sag'' y ''saga'' solo está garantizada en funciones con aproximadamente la misma escala. Puede preprocesar los datos con un escalador desde sklearn.preprocessing.
Los últimos cinco solucionadores admiten datos densos y escasos. Sin embargo, solo ''sag'' y ''saga'' admiten entradas dispersas cuando fit_intercept es True.
Así que recomiendo probar: sparse_cg
o quizás lsqr
junto con CV manual. Si eso funciona (lo hago a mi perfección), lo que esperaría, este enfoque es mucho más estable / robusto (en comparación con los enfoques fuera del núcleo que utilizan SGD) y no es necesario ajustar mucho sus parámetros, que es una gran ventaja.
Por supuesto, uno siempre puede usar sag
y sgd
, pero la teoría de la convergencia se basa en algunas suposiciones fuertes con respecto al ajuste de parámetros. En entornos de gran escala, estos dos son candidatos viables (ya que otros no funcionarán), pero aquí no veo mucho merrit (una vez más: no estoy seguro de la cantidad de memoria que tienes). Si el enfoque anterior no funciona, pruebe saga
before sgd
y respete las reglas (estandarización + param-tuning). ( editar: ¡la caída es bastante mala para mis datos de prueba!)
Ejemplo:
from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge
from time import perf_counter
X, y, = make_regression(n_samples=38000, n_features=7000, n_informative=500,
bias=-2.0, noise=0.1, random_state=0)
print(type(X)) # dense!!! if your data is sparse; use that fact!
clf = Ridge(alpha=1.0, solver="lsqr")
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''LSQR: used secs: '', end-start)
Salida:
LSQR: used secs: 8.489622474064486
Entonces, incluso en el caso denso, eso no es tan difícil de optimizar (y usa ~ 6-8 GB de memoria).
Y si bien tendría cuidado de implicar la equivalencia entre el modelo Ridge y el siguiente modelo Ridge basado en SGD (cuidado con qué variable es parte de la regularización, demasiado vago para verificarlo), aquí solo una demostración de lo difícil que es ajustar el SGD . Tome esto con un grano de sal (y tal vez no evalúe el puntaje absoluto, pero la varianza depende de los parámetros):
Observación: ¡ ese es un ejemplo más pequeño! Usando su ejemplo original, ninguno de los enfoques SGD obtendrá la convergencia sin establecer manualmente la tasa de aprendizaje eta_0
(¡ya que la heurística interna no puede hacer eso por usted!)
Código parcial:
X, y, = make_regression(n_samples=3800, n_features=700, n_informative=500,
noise=0.1, random_state=0)
print(type(X)) # dense!!! if your data is sparse; use that fact!
clf = Ridge(alpha=1.0, solver="lsqr", fit_intercept=False)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''LSQR: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
clf = Ridge(alpha=1.0, solver="sparse_cg", fit_intercept=False)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''sparse_cg: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
clf = SGDRegressor(loss=''squared_loss'', penalty=''l2'', alpha=1., fit_intercept=False,
random_state=0)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''SGD: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
clf = SGDRegressor(loss=''squared_loss'', penalty=''l2'', alpha=1., fit_intercept=False,
random_state=0, average=True)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''SGD: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
clf = SGDRegressor(loss=''squared_loss'', penalty=''l2'', alpha=1., fit_intercept=False,
random_state=0, learning_rate="constant", eta0=0.001)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''SGD: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
clf = SGDRegressor(loss=''squared_loss'', penalty=''l2'', alpha=1., fit_intercept=False,
random_state=0, n_iter=50, average=True)
start = perf_counter()
clf.fit(X, y)
end = perf_counter()
print(''SGD: used secs: '', end-start)
print(''train-score: '', clf.score(X, y))
Salida:
LSQR: used secs: 0.08252486090450709
train-score: 0.999999907282
sparse_cg: used secs: 0.13668818702548152
train-score: 0.999999181151
SGD: used secs: 0.04154542095705427
train-score: 0.743448766459
SGD: used secs: 0.05300238587407993
train-score: 0.774611911034
SGD: used secs: 0.038653031605587
train-score: 0.733585661919
SGD: used secs: 0.46313909066321507
train-score: 0.776444474871
Además: cuando se mini_batches
el enfoque partial_fit / out-of-memory
, también se necesita ajustar el tamaño de mini_batches
(que se ignora en la demostración anterior -> SGD puro -> ¡una muestra a la vez)! Y otra vez: ¡esto no es fácil!