Scikit Learn 回归:设计矩阵 X 太大而无法回归。我该怎么办?
Posted
技术标签:
【中文标题】Scikit Learn 回归:设计矩阵 X 太大而无法回归。我该怎么办?【英文标题】:ScikitLearn regression: Design matrix X too big for regression. What do I do? 【发布时间】:2018-03-06 03:09:04 【问题描述】:我有一个矩阵 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。我的意思是,我将 X 划分为两个具有相同列数(从而保留所有特征)但行数更少的矩阵。然后我每次都为每个子矩阵拟合模型?但是恐怕这真的不等于拟合整个矩阵..
有什么想法吗?
【问题讨论】:
我建议使用具有partial_fit
参数的回归算法,例如SGDRegressor
。然后你可以按块喂你的矩阵。不过,您必须手动完成简历。
那么对于带有 CV 的 Ridge 回归,没有实现“部分拟合”吗?
【参考方案1】:
这是一个众所周知的问题,可以使用核外学习来解决。通过谷歌搜索该术语,您会发现几种解决问题的方法。
对于您的具体问题,您必须首先创建一个生成器,该生成器将生成矩阵的一行(或几行),而不是使用算法的 partial_fit
方法。
scikit-learn 的标准算法实际上使用解决方案的精确计算,例如 sklearn.linear_model.LinearRegression
或 sklearn.linear_model.LinearRegression.RidgeCV
。其他方法基于批量学习,并有一个partial_fit
方法,如sklearn.linear_model.SGDRegressor
,只允许适合一个小批量。这就是你要找的东西。
过程是:使用生成器生成一个小批量,应用partial_fit
方法,从内存中删除小批量并获取一个新的。
但是,由于此方法是随机的,并且取决于数据的顺序和权重的初始化,这与标准回归方法给出的解决方案相反,该方法可以拟合内存中的所有数据。我不会详细介绍,而是看一下梯度下降优化以了解它是如何工作的 (http://ruder.io/optimizing-gradient-descent/)
【讨论】:
【参考方案2】:在我看来,这并不是非常大规模,你可能不需要在这里使用核外学习(虽然我不知道你有多少内存)。
在不需要时使用核外方法,您将付出代价(如果不进行大量调整,则不健壮)!
知道你的特征是稀疏还是密集也会很有趣,这会产生巨大的差异(因为大多数求解器都可以利用稀疏数据!)。
这里要说几句:
RidgeCV 使用了一些问题调整的交叉验证方法,可能正因为如此,没有用于控制底层求解器的参数 当有效的热启动有助于流程(性能)时,使用问题调整的 CV 方法并不少见 手动做CV的时候,sklearn里面所有工具都可以,可以选择不同的solvers 它们在方法和特征方面非常不同求解器:‘auto’、‘svd’、‘cholesky’、‘lsqr’、‘sparse_cg’、‘sag’、‘saga’
在计算例程中使用的求解器:
‘auto’根据数据类型自动选择求解器。
‘svd’使用 X 的奇异值分解来计算岭系数。奇异矩阵比“cholesky”更稳定。
‘cholesky’使用标准的 scipy.linalg.solve 函数来获得一个封闭形式的解。
‘sparse_cg’使用 scipy.sparse.linalg.cg 中的共轭梯度求解器。作为一种迭代算法,这个求解器比“cholesky”更适合大规模数据(可以设置 tol 和 max_iter)。
‘lsqr’使用专用的正则化最小二乘例程 scipy.sparse.linalg.lsqr。它是最快的,但在旧的 scipy 版本中可能不可用。它还使用迭代过程。
“sag”使用随机平均梯度下降,“saga”使用其改进的、无偏的版本,名为 SAGA。这两种方法也使用迭代过程,并且当 n_samples 和 n_features 都很大时,通常比其他求解器更快。请注意,“sag”和“saga”的快速收敛仅在具有大致相同规模的特征上得到保证。您可以使用 sklearn.preprocessing 中的缩放器对数据进行预处理。
最后五个求解器都支持密集和稀疏数据。但是,当 fit_intercept 为 True 时,只有 ‘sag’ 和 ‘saga’ 支持稀疏输入。
所以我强烈建议尝试: sparse_cg
或 lsqr
连同手动简历。预计,这种方法更加稳定/健壮(与使用 SGD 的核外方法相比),您不需要过多调整它们的参数,这是一个巨大的优势。
当然,总是可以使用sag
和sgd
,但收敛理论是基于一些关于参数调整的强假设。在非常大规模的环境中,这两个是可行的候选者(因为其他人不会工作),但在这里我看不到太多优点(再次:我不确定你有多少内存)。如果上述方法不起作用,请在sgd
之前尝试saga
并遵守规则(标准化+参数调整)。 (edit: sag 对我的测试数据非常不利!)
例子:
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。对此持保留态度(也许不评估绝对分数;但方差取决于参数):
备注:这是一个较小的例子!使用您的原始示例,如果不手动设置学习率eta_0
,任何 SGD 方法都不会获得收敛(因为内部启发式无法为您做到这一点!)
部分代码:
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 -> 一次一个样本)!再说一遍:这并不容易!
【讨论】:
IMO 这应该是公认的答案。这些是一些重要的细节,可能会大大改善您的结果。以上是关于Scikit Learn 回归:设计矩阵 X 太大而无法回归。我该怎么办?的主要内容,如果未能解决你的问题,请参考以下文章
SciPy NumPy 和 SciKit-learn ,创建一个稀疏矩阵