所有的线性模型都位于sklearn.linear_model下,包含线性回归、岭回归、lasso、弹性网、最小角回归,及各自的多任务版本和带交叉验证的版本,还包括一些其他的模型,如感知机(Perceptron)
线性回归通过最小化均方误差来拟合一个线性模型,属于监督学习,对于给定的数据集X和类标签y,最小化均方误差:
通过最小二乘法求得模型参数为:
推导详见:最小二乘求解线性回归
from sklearn.linear_model import LinearRegression
lr = 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线程 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_)的点积。
def fit(self, X, y, sample_weight=None):
类型检查和预处理
...
if sp.issparse(X):
if y.ndim<2:
调用sparse_lsqr函数
else:
并行调用sparse_lsqr函数
else:
调用numpy的lstsq函数
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时,变为求解阻尼最小二乘问题atol
和btol
:a和b停止迭代的误差阈值iter_lim
:迭代次数限制show
:是否打印迭代信息calc_var
:是否评价对角矩阵(XTX+damp^2*I)-1关于阻尼最小二乘和LSQR,详见:LSQR求解最小二乘问题
岭回归即我们所说的L2正则线性回归,在一般的线性回归最小化均方误差的基础上增加了一个参数w的L2范数的罚项,从而最小化罚项残差平方和:
其最小二乘解法如下:
岭回归有利于约束参数向着较小的方向收敛,同时可以解决XTX不满秩导致的无法求导问题。
from sklearn.linear_model import Ridge
lr = 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
:求解算法 auto
:视数据类型选定相应的算法,主要根据矩阵X是否为稀疏矩阵进行选择svd
:通过奇异值分解计算岭回归的系数,针对奇异矩阵有较好的稳定性cholesky
:调用scipy.linalg.solve,得到的是一个闭式解sparse_cg
:使用scipy.sparse.linalg.cg(),即共轭梯度法,是一个迭代算法,比cholesky在大规模数据上表现的更好lsqr
:最小平方QR分解,调用scipy.sparse.linalg.lsqr()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对象。
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,是否计算偏置by_offset
:预处理中返回的一个值self
:_BaseRidge的实例对象coef_
:模型参数n_iters_
:每个参数对应的迭代次数intercept_
:b值,即截距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_svdcholesky
:solve_cholesky_kernelsparse_cg
:_solve_sparse_cglsqr
:_solve_lsqrsag
:sag_solver 其中,除了sag是用c实现的(sag_fast.pyd),其他的均为python实现。
在sparse_cg中,对于特征数量大于和小于样本数量的情况分别采用了不同的求解方式。
附,_solve_svd的实现:svd求解最小二乘问题
弹性网络优化的残差项位于L1和L2之间,它的罚项由L1和L2先验混合而成,并且将L2作为其正则化项,因此,优化目标变为最小化:
from sklearn.linear_model import ElasticNet
lr = 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矩阵进行与计算从而提高速度,一般对于稀疏矩阵,设置为Truecopy_X
:是否对样本进行拷贝,如果不拷贝,计算操作可能会导致 X 发生改变tol
:迭代终止的阈值warm_start
:是否使用前一次的计算结果作为参数的初始化值positive
:是否强制模型参数为正数random_state
:随机状态,设置相同的值可以使得计算结果重现selection
:“cyclic”或“random”,前者异步(一轮迭代完后进行参数跟新)更新参数,后者同步(每个样本计算完就更新)更新,一般地,当tol>1e-4时,后者会更快的收敛。 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
:是否对输入进行类型检查 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)实现,根据不同的设置调用不同的方法:
alphas
:输入的alphacoefs
:解得的模型参数dual_gaps
:用来判定当前模型是否收敛n_iters
:得到当前参数进行的迭代次数,可选sparse_coef_
:模型参数对应的稀疏矩阵coef_
:模型参数intercept_
:b值n_iter_
:模型的迭代次数PS:fit()函数的返回值是在调用了fit函数后可以直接接收到的值,属性是指在fit()调用后,用obj.attr获取的值,返回值和属性可能有重叠,可能有不同.