python中高效的在线线性回归算法

Posted

技术标签:

【中文标题】python中高效的在线线性回归算法【英文标题】:Efficient online linear regression algorithm in python 【发布时间】:2019-02-03 19:41:42 【问题描述】:

我得到了一个包含两列 xy 的二维数据集。我想在新数据输入时动态获取线性回归系数和截距。使用 scikit-learn 我可以计算所有当前可用的数据,如下所示:

from sklearn.linear_model import LinearRegression
regr = LinearRegression()
x = np.arange(100)
y = np.arange(100)+10*np.random.random_sample((100,))
regr.fit(x,y)
print(regr.coef_)
print(regr.intercept_)

但是,我得到了相当大的数据集(总共超过 10k 行),我想在有新行进入时尽快计算系数和截距。目前计算 10k 行大约需要 600 微秒,我想加速这个过程。

Scikit-learn 好像没有线性回归模块的在线更新功能。有没有更好的方法来做到这一点?

【问题讨论】:

在 sklearn 中,只有 estimators noted here 有在线学习的能力。 @VivekKumar 有没有其他公式或包可以解决这个问题? sklearn.linear_model.SGDRegressor 是线性回归,但不是使用最小二乘法,而是使用梯度体面。您应该尝试一下,看看您的输出是否保持足够接近(或者至少“损失”是相同的),而且 SGD(随机梯度体面)在具有大维度特征的大数据集上要快得多。 scikit-learn.org/stable/modules/generated/… 【参考方案1】:

我从这篇论文中找到了解决方案:updating simple linear regression。实现如下:

def lr(x_avg,y_avg,Sxy,Sx,n,new_x,new_y):
    """
    x_avg: average of previous x, if no previous sample, set to 0
    y_avg: average of previous y, if no previous sample, set to 0
    Sxy: covariance of previous x and y, if no previous sample, set to 0
    Sx: variance of previous x, if no previous sample, set to 0
    n: number of previous samples
    new_x: new incoming 1-D numpy array x
    new_y: new incoming 1-D numpy array x
    """
    new_n = n + len(new_x)

    new_x_avg = (x_avg*n + np.sum(new_x))/new_n
    new_y_avg = (y_avg*n + np.sum(new_y))/new_n

    if n > 0:
        x_star = (x_avg*np.sqrt(n) + new_x_avg*np.sqrt(new_n))/(np.sqrt(n)+np.sqrt(new_n))
        y_star = (y_avg*np.sqrt(n) + new_y_avg*np.sqrt(new_n))/(np.sqrt(n)+np.sqrt(new_n))
    elif n == 0:
        x_star = new_x_avg
        y_star = new_y_avg
    else:
        raise ValueError

    new_Sx = Sx + np.sum((new_x-x_star)**2)
    new_Sxy = Sxy + np.sum((new_x-x_star).reshape(-1) * (new_y-y_star).reshape(-1))

    beta = new_Sxy/new_Sx
    alpha = new_y_avg - beta * new_x_avg
    return new_Sxy, new_Sx, new_n, alpha, beta, new_x_avg, new_y_avg

性能对比:

总共计算 10k 个样本的 Scikit learn 版本。

from sklearn.linear_model import LinearRegression
x = np.arange(10000).reshape(-1,1)
y = np.arange(10000)+100*np.random.random_sample((10000,))
regr = LinearRegression()
%timeit regr.fit(x,y)
# 419 µs ± 14.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

我的版本假设已经计算了 9k 样本:

Sxy, Sx, n, alpha, beta, new_x_avg, new_y_avg = lr(0, 0, 0, 0, 0, x.reshape(-1,1)[:9000], y[:9000])
new_x, new_y = x.reshape(-1,1)[9000:], y[9000:]
%timeit lr(new_x_avg, new_y_avg, Sxy,Sx,n,new_x, new_y)
# 38.7 µs ± 1.31 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

快 10 倍,这是意料之中的。

【讨论】:

与 sklearn 相比,您得到相似的预测/系数吗? @VivekKumar 他们的系数和截距是一样的 Sxy 真的是 n 倍协方差,而 Sx 真的是 n 倍 x 的方差,不是吗?【参考方案2】:

不错!感谢您分享您的发现:) 以下是使用点积编写的此解决方案的等效实现:

class SimpleLinearRegressor(object):
    def __init__(self):
        self.dots = np.zeros(5)
        self.intercept = None
        self.slope = None

    def update(self, x: np.ndarray, y: np.ndarray):
        self.dots += np.array(
            [
                x.shape[0],
                x.sum(),
                y.sum(),
                np.dot(x, x),
                np.dot(x, y),
            ]
        )
        size, sum_x, sum_y, sum_xx, sum_xy = self.dots
        det = size * sum_xx - sum_x ** 2
        if det > 1e-10:  # determinant may be zero initially
            self.intercept = (sum_xx * sum_y - sum_xy * sum_x) / det
            self.slope = (sum_xy * size - sum_x * sum_y) / det

在处理时间序列数据时,我们可以将这个想法扩展为使用软(EMA-like)窗口来做sliding window regression。

【讨论】:

【参考方案3】:

您可以使用实现更快算法的加速库 - 特别是 https://github.com/intel/scikit-learn-intelex

对于线性回归,您将获得更好的性能

第一个安装包

pip install scikit-learn-intelex

然后添加你的python脚本

from sklearnex import patch_sklearn
patch_sklearn()

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review

以上是关于python中高效的在线线性回归算法的主要内容,如果未能解决你的问题,请参考以下文章

详细推导线性回归

SVM用于线性回归

回归算法中特征线性相关会怎样

机器学习模型和算法

如何搞懂机器学习中的线性回归模型?机器学习系列之线性回归基础篇

Python数据挖掘—回归—逻辑回归