Scikit Learn 回归:设计矩阵 X 对于回归来说太大了。我该怎么办?

ScikitLearn regression: Design matrix X too big for regression. What do I do?

我有一个矩阵 X,大约有 7000 列和 38000 行。因此它是一个 numpy array 形状 (38000, 7000).

我实例化了模型

model = RidgeCV(alphas = (0.001,0.01, 0.1, 1)

然后装上

model.fit(X, y)

其中 y 是响应向量,它是一个形状为 (38000,).

的 numpy 数组

通过 运行 这个我得到一个 Memory Error.

我该如何解决这个问题?

我的想法

我的第一个想法是对矩阵X进行除法"horizontally"。我的意思是说,我将 X 分成两个具有相同列数(因此保留所有特征)但行数较少的矩阵。然后我每次都为每个子矩阵拟合模型?但是这恐怕真的不等于拟合整个矩阵..

有什么想法吗?

这是一个众所周知的问题,可以使用核外学习来解决。通过谷歌搜索该术语,您会找到几种解决问题的方法。

对于您的特定问题,您必须首先创建一个生成器来生成矩阵的一行(或几行),而不是使用算法的 partial_fit 方法。

scikit-learn 的标准算法实际上使用解的精确计算,例如 sklearn.linear_model.LinearRegressionsklearn.linear_model.LinearRegression.RidgeCV。其他方法基于批量学习,有 partial_fit 方法,如 sklearn.linear_model.SGDRegressor,只允许适合小批量。这就是您要找的。

过程是:使用生成器产生一个mini-batch,应用partial_fit方法,从内存中删除mini-batch并得到一个新的。

但是,由于此方法是随机的,并且取决于您的数据顺序和您对权重的初始化,因此与可以适合内存中所有数据的标准回归方法给出的解决方案相反。我不会进入细节,但会查看梯度下降优化以了解其工作原理 (http://ruder.io/optimizing-gradient-descent/)

在我看来这并不是非常大规模,你可能不需要在这里使用核外学习(虽然我不知道有多少内存你得到了)。

在不需要时使用核外方法,你会付出代价(没有大量调整就不够健壮)!

了解您的特征是稀疏还是密集也会很有趣,这会产生巨大的差异(因为大多数求解器都可以利用稀疏数据!)。

这里有几点要说:

  • RidgeCV 使用一些问题调整的交叉验证方法,可能由于这个原因,没有用于控制底层求解器的参数
    • 当有效的热启动可以帮助过程(性能)时,使用问题调整的 CV 方法并不少见
  • 手动做CV的时候,sklearn里面的工具都有,你可以选择不同的solvers
    • 它们在方法和特点上有很大不同

solver : {‘auto’, ‘svd’, ‘cholesky’, ‘lsqr’, ‘sparse_cg’, ‘sag’, ‘saga’}

Solver to use in the computational routines:

‘auto’ chooses the solver automatically based on the type of data.

‘svd’ uses a Singular Value Decomposition of X to compute the Ridge coefficients. More stable for singular matrices than ‘cholesky’.

‘cholesky’ uses the standard scipy.linalg.solve function to obtain a closed-form solution.

‘sparse_cg’ uses the conjugate gradient solver as found in scipy.sparse.linalg.cg. As an iterative algorithm, this solver is more appropriate than ‘cholesky’ for large-scale data (possibility to set tol and max_iter).

‘lsqr’ uses the dedicated regularized least-squares routine scipy.sparse.linalg.lsqr. It is the fastest but may not be available in old scipy versions. It also uses an iterative procedure.

‘sag’ uses a Stochastic Average Gradient descent, and ‘saga’ uses its improved, unbiased version named SAGA. Both methods also use an iterative procedure, and are often faster than other solvers when both n_samples and n_features are large. Note that ‘sag’ and ‘saga’ fast convergence is only guaranteed on features with approximately the same scale. You can preprocess the data with a scaler from sklearn.preprocessing.

All last five solvers support both dense and sparse data. However, only ‘sag’ and ‘saga’ supports sparse input when fit_intercept is True.

所以我强烈建议尝试: sparse_cg 或者 lsqr 连同手动简历。 如果这很有效(对我来说很完美),我希望这种方法更加稳定/健壮(与使用 SGD 的核外方法相比)并且您不需要太多调整它们的参数,这是一个巨大的优势。

当然,我们总是可以使用 sagsgd,但是收敛理论是基于一些关于参数调整的强有力的假设。在非常大规模的设置中,这两个是可行的候选者(因为其他人将无法工作),但在这里我看不到太多优点(再次:我不确定你有多少内存)。如果上述方法不起作用,请在 sgd 之前尝试 saga 并遵守规则(标准化 + 参数调整)。 (编辑: 下垂对我的测试数据来说非常糟糕!)

示例:

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)

输出:

LSQR: used secs:  8.489622474064486

因此,即使在密集情况下,也不难优化(并使用 ~6-8 GB 内存)。

虽然我会小心暗示 Ridge 模型和以下基于 SGD 的 Ridge 模型之间的等价性(注意哪个变量是正则化的一部分;懒得检查),这里只是一个关于有多难的演示就是调SGD。对此持保留态度(也许不评估绝对分数;但方差取决于参数):

备注:这是一个小例子!使用您的原始示例,none 的 SGD 方法将在不手动设置学习率的情况下获得收敛 eta_0(因为内部启发式无法为您做到这一点!)

部分代码:

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))

输出:

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

此外:当采用 partial_fit / out-of-memory 方法时,还需要调整 mini_batches 的大小(在上面的演示中忽略了这一点 -> 纯 SGD -> 一个样本时间)!再说一遍:这并不容易!