sklearn浅析(二)——Generalized Linear Models之一

来源:互联网 发布:窗户漏风 知乎 编辑:程序博客网 时间:2024/06/05 08:45

        所有的线性模型都位于sklearn.linear_model下,包含线性回归、岭回归、lasso、弹性网、最小角回归,及各自的多任务版本和带交叉验证的版本,还包括一些其他的模型,如感知机(Perceptron)

Ordinary Least Squares

        线性回归通过最小化均方误差来拟合一个线性模型,属于监督学习,对于给定的数据集X和类标签y,最小化均方误差:

minXwy22

通过最小二乘法求得模型参数为:

w=(XTX)1XTy

推导详见:最小二乘求解线性回归

LinearRegression的使用

from sklearn.linear_model import LinearRegressionlr = LinearRegression()

LinearRegression类的定义

class LinearRegression(LinearModel, RegressorMixin):    def __init__(self, fit_intercept=True, normalize=False, copy_X=True,                 n_jobs=1):        self.fit_intercept = fit_intercept        self.normalize = normalize        self.copy_X = copy_X        self.n_jobs = n_jobs
  • fit_intercept:初始化时的一个参数,默认为True,是否计算b值
  • normalize:是否需要将样本归一化
  • n_jobs:并行时的CPU的线程数,如果为-1,则调用所有可用的CPU线程
            如之前所述,线性回归实现了回归器的基类RegressorMixin,以及一个抽象类LinearModel:
class LinearModel(six.with_metaclass(ABCMeta, BaseEstimator)):    @abstractmethod    def fit(self, X, y):        pass    def predict(self, X):        return self._decision_function(X)    def _decision_function(self, X):        check_is_fitted(self, "coef_")        X = check_array(X, accept_sparse=['csr', 'csc', 'coo'])        return safe_sparse_dot(X, self.coef_.T,                               dense_output=True) + self.intercept_

        LinearModel继承自ABCMeta(用来实现抽象类)和评估器的基类BaseEstimator,它也是其他线性回归器的基类(岭回归,lasso等)。实现了两个关键方法,fit()和predict(),其中前者是一个抽象方法,用来让子类自定义拟合方法:

        predict方法接受待预测的样本x,并调用safe_sparse_dot求解x与模型参数(self.coef_)的点积。


LinearRegression类的fit()方法

    def fit(self, X, y, sample_weight=None):        类型检查和预处理        ...        if sp.issparse(X):            if y.ndim<2:                调用sparse_lsqr函数            else:                并行调用sparse_lsqr函数        else:            调用numpy的lstsq函数

相关优化处理:

  1. 如果是稀疏矩阵,则采用最小平方QR分解求解;否则采用最小二乘。
  2. numpy的lstsq函数,它除了X,y,还接受另外一个参数rcond,用来舍入较小的元素值,比如X中最大值为5,rcond设置为0.1,那么小于0.1*5的值会被设置为0。
  3. 在调用lstsq时,对X中的元素是否是complexfloat做不同的处理。

fit()函数返回值:

  • self:LinearRegression的实例对象

属性:

  • coef_:模型参数
  • residues_:残差
  • intercept_:b值,即截距

        sparse_lsqr()即scipy中的lsqr()函数,即最小平方QR分解法,函数参数如下,lsqr常用来求解大规模稀疏矩阵的最小二乘问题

def lsqr(A, b, damp=0.0, atol=1e-8, btol=1e-8, conlim=1e8,         iter_lim=None, show=False, calc_var=False):
  • A:训练样本矩阵
  • b:标签
  • damp:阻尼系数,当不为0时,变为求解阻尼最小二乘问题
  • atolbtol:a和b停止迭代的误差阈值
  • iter_lim:迭代次数限制
  • show:是否打印迭代信息
  • calc_var:是否评价对角矩阵(XTX+damp^2*I)-1

关于阻尼最小二乘和LSQR,详见:LSQR求解最小二乘问题

Ridge Regression

        岭回归即我们所说的L2正则线性回归,在一般的线性回归最小化均方误差的基础上增加了一个参数w的L2范数的罚项,从而最小化罚项残差平方和:

minXwy22+λw22

其最小二乘解法如下:

w=(XTX+λI)1XTy

        岭回归有利于约束参数向着较小的方向收敛,同时可以解决XTX不满秩导致的无法求导问题。

RidgeRegression的使用

from sklearn.linear_model import Ridgelr = Ridge()

Ridge类的定义:

class Ridge(_BaseRidge, RegressorMixin):       def __init__(self, alpha=1.0, fit_intercept=True, normalize=False,                 copy_X=True, max_iter=None, tol=1e-3, solver="auto",                 random_state=None):        super(Ridge, self).__init__(**args)#这里传入的是上述所有参数,为了简写
  • fit_intercept:是否求解b值
  • tol:判断迭代是否收敛的阈值,当误差小于tol时,停止迭代
  • random_state:可以传入相同的整数值来使得运算结果可以重现,也可以是RandomState对象
  • alpha:即正则化系数,必须为一个正数。
  • solver:求解算法
    取值可以为:
    1. auto:视数据类型选定相应的算法,主要根据矩阵X是否为稀疏矩阵进行选择
    2. svd:通过奇异值分解计算岭回归的系数,针对奇异矩阵有较好的稳定性
    3. cholesky:调用scipy.linalg.solve,得到的是一个闭式解
    4. sparse_cg:使用scipy.sparse.linalg.cg(),即共轭梯度法,是一个迭代算法,比cholesky在大规模数据上表现的更好
    5. lsqr:最小平方QR分解,调用scipy.sparse.linalg.lsqr()
    6. sag:随机平均梯度下降法,注意,如果矩阵是稀疏矩阵并且return_intercept=True那么solver只能为sag,如果不是会给出警告信息并转换成sag

其中_BaseRidge也是LinearModel的子类:

class _BaseRidge(six.with_metaclass(ABCMeta, LinearModel)):

Ridge的init方法直接调用了super(Ridge, self).init,即_BaseRidge的初始化方法,并且直接返回了一个实例,该实例是_BaseRidge调用其fit()返回的_BaseRidge对象。

_BaseRidge类的fit()方法:

def fit(self, X, y, sample_weight=None):    类型检查和预处理    if sparse.issparse(X) and self.fit_intercept:        self.coef_, self.n_iter_, self.intercept_ = ridge_regression(**args)        其中return_intercept=True        self.intercept_ += y_offset    else:        self.coef_, self.n_iter_ = ridge_regression(**args)        其中return_intercept=False        self._set_intercept(X_offset, y_offset, X_scale)
  • fit_intercept:初始化时的一个参数,默认为True,是否计算偏置b
  • y_offset:预处理中返回的一个值

fit()函数返回值:

  • self:_BaseRidge的实例对象

属性:

  • coef_:模型参数
  • n_iters_:每个参数对应的迭代次数
  • intercept_:b值,即截距

ridge_regression方法:

def ridge_regression(X, y, alpha, sample_weight=None, solver='auto',                     max_iter=None, tol=1e-3, verbose=0, random_state=None,                     return_n_iter=False, return_intercept=False):
  • verbose:整数,当大于0时会根据不同的solver打印出额外的信息
    其余的参数同上。

solver对应的方法位于sklearn.linear_model.ridge.py:

  • svd:_solve_svd
  • cholesky:solve_cholesky_kernel
  • sparse_cg:_solve_sparse_cg
  • lsqr:_solve_lsqr
  • sag:sag_solver

  其中,除了sag是用c实现的(sag_fast.pyd),其他的均为python实现。
在sparse_cg中,对于特征数量大于和小于样本数量的情况分别采用了不同的求解方式。
  附,_solve_svd的实现:svd求解最小二乘问题

ElasticNet

  弹性网络优化的残差项位于L1和L2之间,它的罚项由L1和L2先验混合而成,并且将L2作为其正则化项,因此,优化目标变为最小化:

min12nsamplesXwy22+αρw1+α(1ρ)2w22

ElasticNet的使用

from sklearn.linear_model import ElasticNetlr = ElasticNet()

ElasticNet类的定义:

class ElasticNet(LinearModel, RegressorMixin):      def __init__(self, alpha=1.0, l1_ratio=0.5, fit_intercept=True,                 normalize=False, precompute=False, max_iter=1000,                 copy_X=True, tol=1e-4, warm_start=False, positive=False,                 random_state=None, selection='cyclic'):                 ...
  • alpha:式子中的α
  • l1_ratio:式子中的ρ
  • fit_intercept:是否计算b值
  • precompute:是否使用Gram矩阵进行与计算从而提高速度,一般对于稀疏矩阵,设置为True
  • copy_X:是否对样本进行拷贝,如果不拷贝,计算操作可能会导致X发生改变
  • tol:迭代终止的阈值
  • warm_start:是否使用前一次的计算结果作为参数的初始化值
  • positive:是否强制模型参数为正数
  • random_state:随机状态,设置相同的值可以使得计算结果重现
  • selection:“cyclic”或“random”,前者异步(一轮迭代完后进行参数跟新)更新参数,后者同步(每个样本计算完就更新)更新,一般地,当tol>1e-4时,后者会更快的收敛。

ElasticNet类的fit()方法:

def fit(self, X, y, check_input=True):    类型检查和预处理    n_targets = y.shape[1]     for k in xrange(n_targets):#用于多任务         ...          _, this_coef, this_dual_gap, this_iter = \                self.path(**args)
  • check_input:是否对输入进行类型检查
      这里的self.path()调用的是一个被封装静态方法的方法enet_path():
def enet_path(X, y, l1_ratio=0.5, eps=1e-3, n_alphas=100, alphas=None,              precompute='auto', Xy=None, copy_X=True, coef_init=None,              verbose=False, return_n_iter=False, positive=False,              check_input=True, **params):

  该算法采用的坐标下降法进行求解,用c语言(cd_fast.pyd)实现,根据不同的设置调用不同的方法:

  • 非多任务非稀疏矩阵 :cd_fast.enet_coordinate_descent()
  • 非多任务稀疏矩阵 :cd_fast.sparse_enet_coordinate_descent()
  • 多任务非稀疏矩阵 :cd_fast.enet_coordinate_descent_multi_task()
    注意,多任务的弹性网络不支持稀疏矩阵
  • precompute=True :cd_fast.enet_coordinate_descent_gram()

fit()函数返回值:

  • alphas:输入的alpha
  • coefs:解得的模型参数
  • dual_gaps:用来判定当前模型是否收敛
  • n_iters:得到当前参数进行的迭代次数,可选

属性:

  • sparse_coef_:模型参数对应的稀疏矩阵
  • coef_:模型参数
  • intercept_:b值
  • n_iter_:模型的迭代次数

PS:fit()函数的返回值是在调用了fit函数后可以直接接收到的值,属性是指在fit()调用后,用obj.attr获取的值,返回值和属性可能有重叠,可能有不同.

原创粉丝点击