sklearn中LinearRegression关键源码解读

问题的引入

我们知道,线性回归方程的参数,可以用梯度下降法求解,或者用正规方程求解。
sklearn.linear_model.LinearRegression中,是不是可以指定求解方式呢?能不能从中获取梯度相关信息呢?
下面是线性回归最简单的用法。

from sklearn import  linear_model

# Create linear regression object
regr = linear_model.LinearRegression()

# Train the model using the training sets
regr.fit(x_train, y_train)

# Predict
regr.predict(x_test)

参考LinearRegression文档,发现并不能指定用梯度下降法求解,或者用正规方程求解该线性回归,更不能获取该线性回归梯度相关的信息。

这是为什么呢?sklearn.linear_model.LinearRegression是用什么方法求解线性回归方程(一般用Y=WX+b来表示)参数的呢?

为了回答这些问题,我们需要看一下sklearn.linear_model.LinearRegression的代码,来了解它的实现方式。

sklearn.linear_model.LinearRegression源码解读

在github可以找到sklearn.linear_model.LinearRegression实现代码。通过寻找LinearRegression类的fit()方法,我们定位到线性回归方程训练阶段的代码

if sp.issparse(X):#如果是稀疏矩阵
    if y.ndim < 2:
        out = sparse_lsqr(X, y)
        self.coef_ = out[0]
        self._residues = out[3]
    else:
        # sparse_lstsq cannot handle y with shape (M, K)
        outs = Parallel(n_jobs=n_jobs_)(
            delayed(sparse_lsqr)(X, y[:, j].ravel())
            for j in range(y.shape[1]))
        self.coef_ = np.vstack(out[0] for out in outs)
        self._residues = np.vstack(out[3] for out in outs)
else:
    self.coef_, self._residues, self.rank_, self.singular_ = \
        linalg.lstsq(X, y)
    self.coef_ = self.coef_.T

其中,线性回归方程的参数获取,主要靠这两个方法:sparse_lsqr()linalg.lstsq()。若训练集X是稀疏矩阵,则用sparse_lsqr(),否则用linalg.lstsq()

这两个方法是怎么求解线性回归方程参数的呢?

linalg.lstsq()方法源码解读

LinearRegression类的源码注释中,可以发现如下说明。

"""
Notes
-----
From the implementation point of view, this is just plain Ordinary
Least Squares (scipy.linalg.lstsq) wrapped as a predictor object.
"""

也就是说,这里是用scipy.linalg.lstsq()方法来计算线性回归方程参数的,sklearn.linear_model.LinearRegression是用普通的最小二乘(OLS)法求解线性回归方程参数的。
我们跟进去看看scipy.linalg.lstsq()是怎么实现的。

scipy.linalg.lstsq()方法源码解读

从github中找到scipy.linalg.lstsq()方法的代码实现。从它的源码注释中,我们发现,可以通过参数lapack_driver来选择求解线性回归方程参数的方法。

"""
lapack_driver: str, optional
    Which LAPACK driver is used to solve the least-squares problem.
    Options are ``'gelsd'``, ``'gelsy'``, ``'gelss'``. Default
    (``'gelsd'``) is a good choice.  However, ``'gelsy'`` can be slightly
    faster on many problems.  ``'gelss'`` was used historically.  It is
    generally slow but uses less memory.
"""

可见,这里一共提供了三种方法来求解least-squares problem(最小均方问题,这也是线性回归模型的优化目标):gelsd, gelsy, gelss,它们是什么意思呢,看了这页代码,也看了文档,都没找到解释!不过能看到,这个参数是传给函数get_lapack_funcs()的(如下),那就跟进去看看函数get_lapack_funcs()的代码吧。

driver = lapack_driver
if driver is None:
    driver = lstsq.default_lapack_driver
if driver not in ('gelsd', 'gelsy', 'gelss'):
    raise ValueError('LAPACK driver "%s" is not found' % driver)

lapack_func, lapack_lwork = get_lapack_funcs((driver,
                                             '%s_lwork' % driver),
                                             (a1, b1))

get_lapack_funcs()方法源码解读

找到get_lapack_funcs()方法源码,发现它是调用_get_funcs()实现的(如下)。

"""
In LAPACK, the naming convention is that all functions start with a
type prefix, which depends on the type of the principal
matrix. These can be one of {'s', 'd', 'c', 'z'} for the numpy
types {float32, float64, complex64, complex128} respectevely, and
are stored in attribute `typecode` of the returned functions.
"""
return _get_funcs(names, arrays, dtype,
                  "LAPACK", _flapack, _clapack,
                  "flapack", "clapack", _lapack_alias)

原来这些gelsd, gelsy, gelss,是C函数名,函数是从LAPACK—Linear Algebra PACKage中拿到的。

gelsd:它是用singular value decomposition of A and a divide and conquer method方法来求解线性回归方程参数的。
gelsy:computes the minimum-norm solution to a real/complex linear least squares problem
gelss:Computes the minimum-norm solution to a linear least squares problem using the singular value decomposition of A.

scipy.linalg.lstsq()方法默认选择gelsd。由于sklearn.linear_model.LinearRegression并没有提供参数选择所用解法,所以我们也只能用默认的gelsd

sparse_lsqr()方法源码解读

当训练集X是稀疏矩阵时,用sparse_lsqr()方法求解线性回归方程的参数。
sparse_lsqr()方法是在这里被导入的。

if sp_version < (0, 15):
    # Backport fix for scikit-learn/scikit-learn#2986 / scipy/scipy#4142
    from ._scipy_sparse_lsqr_backport import lsqr as sparse_lsqr
else:
    from scipy.sparse.linalg import lsqr as sparse_lsqr

原来sparse_lsqr()就是不同版本的scipy.sparse.linalg.lsqr()。它在源码注释中指出,是参考论文C. C. Paige and M. A. Saunders (1982a). "LSQR: An algorithm for sparse linear equations and sparse least squares", ACM TOMS 8(1), 43-71.中的Golub & Kahan双对角线化过程实现的。

结论

sklearn.linear_model.LinearRegression求解线性回归方程参数时,首先判断训练集X是不是稀疏矩阵,如是,就用Golub & Kahan双对角线化过程方法来求解;否则就调用C库LAPACK中的用基于分治法的奇异值分解来求解。

这些解法都跟梯度下降没有半毛钱的关系,可见常见的求解线性回归方程参数理论与实际用代码实现的数值解法还是有很大区别。

读源码时,要特别注意看注释,否则有些数学解法没有解读很难看懂。

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Sklearn是一个流行的Python机器学习库,它提供了各种机器学习算法和工具,以帮助开发者构建和部署机器学习模型。如果你想深入了解Sklearn源代码,下面是一些可能有助于你理解它的步骤和资源: 1. 官方文档:首先,我建议你查阅Sklearn的官方文档。官方文档详细介绍了每个模块、每个函数以及它们的参数和用法。你可以通过阅读文档来了解Sklearn的工作原理和设计思路。 2. GitHub仓库:Sklearn源代码托管在GitHub上,你可以访问https://github.com/scikit-learn/scikit-learn 来查看源代码。你可以浏览不同的模块和子模块,了解它们是如何实现的,并且参考相关的注释和文档字符串。 3. 源码结构:Sklearn源代码结构非常清晰,不同的算法和功能被组织在不同的模块。你可以从顶层的sklearn目录开始,逐步深入到具体的模块和类。阅读源码时,要注意查看各个类的继承关系和方法的调用关系,这有助于你理解整个库的结构和运行流程。 4. 调试和打印:如果你想更深入地了解Sklearn的内部运行机制,你可以使用调试工具来跟踪代码的执行过程。另外,你还可以在关键的位置插入打印语句,输出一些变量的值,以便观察代码的执行情况。 5. 论文和博客:Sklearn源代码通常是基于机器学习算法的论文和研究成果实现的。如果你对某个具体的算法感兴趣,你可以查阅该算法的相关论文和博客文章,了解其原理和实现细节。这有助于你更好地理解Sklearn相应模块的代码。 总之,阅读Sklearn源代码是一个深入理解机器学习库实现细节的好方法。通过仔细阅读文档、查看源码、调试代码以及参考相关论文,你可以更好地了解Sklearn的工作原理和设计思想。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值