线性回归:普通最小二乘线性回归的实现
一个普通最小二乘法简单和多重线性回归的实现。
> 从 mlxtend.regressor 导入线性回归
概述
简单线性回归模型的示例:
在普通最小二乘(OLS)线性回归中,我们的目标是找到一条最小化垂直偏移的直线(或超平面)。换句话说,我们定义最佳拟合线为最小化目标变量(y)与我们在数据集中所有样本 $i$ 的预测输出之间的平方误差和(SSE)或均方误差(MSE)的那条线。
$$SSE = \sum_i \big(\text{目标}^{(i)} - \text{输出}^{(i)}\big)^2$$
$$MSE = \frac{1}{n} \times SSE$$
现在,LinearRegression
实现了一种线性回归模型,用于使用以下五种方法之一执行普通最小二乘回归:
- 正规方程
- QR 分解法
- SVD(奇异值分解)法
- 梯度下降
- 随机梯度下降
正常方程(闭式解)
对于"较小"的数据集,应该优先考虑闭式解,因为在这种情况下计算(“昂贵的”)矩阵逆并不是一个问题。对于非常大的数据集,或者逆矩阵 $[X^T X]$ 可能不存在的数据集(矩阵是不可逆或奇异的,例如在完美多重共线性的情况下),则应优先考虑 QR、SVD 或梯度下降方法。
线性函数(线性回归模型)定义为:
$$y = w_0x_0 + w_1x_1 + ... + w_mx_m = \sum_{i=0}^{m} = \mathbf{w}^T\mathbf{x}$$
其中 $y$ 是响应变量,$\mathbf{x}$ 是 $m$ 维样本向量,$\mathbf{w}$ 是权重向量(系数向量)。注意 $w_0$ 代表模型的 y 轴截距,因此 $x_0=1$。
使用闭式解(正常方程),我们计算模型的权重如下:
$$ \mathbf{w} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^Ty$$
通过QR分解实现稳定的OLS
QR分解方法提供了一种比基于“正规方程”的闭式解析解更数值稳定的替代方案,并且可以更高效地计算大型矩阵的逆。
QR分解方法将给定矩阵分解为两个易于获取其逆的矩阵。例如,给定矩阵 $X \in \mathbb{R}^{n \times m}$,其QR分解为两个矩阵为:
$$\mathbf{X = QR},$$
其中
$Q \in \mathbb{R}^{n \times m}$ 是一个正交标准矩阵,使得 $Q^\top Q = QQ^\top = I$。 第二个矩阵 $R \in \mathbf{R}^{m \times m}$ 是一个上三角矩阵。
普通最小二乘回归模型的权重参数可以如下计算 [1]:
$$\mathbf{w} = \mathbf{R}^{-1}\mathbf{Q}^\top y.$$
通过奇异值分解实现稳定的OLS
另一种以数值稳定的方式获得OLS模型权重的替代方法是通过奇异值分解(SVD),其定义为:
$$\mathbf{X}=\mathbf{U}\Sigma \mathbf{V}^\top,$$
对于给定的矩阵 $\mathbf{X}$。
然后,可以证明,矩阵 $X$ 的伪逆 $X^+$ 可以通过以下方式获得 [1]:
$$ X^+ = \mathbf{U} \mathbf{\Sigma}^+ V^{\top}.$$
请注意,虽然 $\Sigma$ 是由 $\mathbf{X}$ 的奇异值组成的对角矩阵,但 $\Sigma^{+}$ 是由奇异值的倒数组成的对角矩阵。
模型权重可以通过以下方式计算:
$$\mathbf{w} = \mathbf{X}^+ \mathbf{y}.$$
请注意,这种OLS方法在计算上效率最低。然而,当直接方法(正态方程)或QR分解无法应用,或者正态方程(通过 $\mathbf{X}^T \mathbf{X}$)是病态的 [3],这种方法是非常有用的。
梯度下降 (GD) 和随机梯度下降 (SGD)
请参见梯度下降和随机梯度下降以及推导线性回归和Adaline的梯度下降规则以获取详细信息。
随机洗牌的实现过程为:
- 一个或多个周期
- 随机洗牌训练集中的样本
- 对于训练样本 i
- 计算梯度并进行权重更新
- 对于训练样本 i
- 随机洗牌训练集中的样本
参考文献
- [1] 第3章,第55页,回归的线性方法。特雷沃·哈斯蒂;罗伯特·提布希拉尼;杰罗姆·弗里德曼(2009)。 统计学习的要素:数据挖掘、推断与预测 (第2版)。纽约:施普林格出版社。(ISBN 978–0–387–84858–7)
- [2] G. Strang,《线性代数及其应用》,第2版,佛罗里达州奥兰多,学术出版社,1980年,139-142页。
- [3] 道格拉斯·威尔海姆·哈德。《工程数值分析》。第4.8节,病态矩阵
示例 1 - 闭式解法
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
ne_lr = LinearRegression()
ne_lr.fit(X, y)
print('Intercept: %.2f' % ne_lr.b_)
print('Slope: %.2f' % ne_lr.w_[0])
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, ne_lr)
plt.show()
Intercept: 0.25
Slope: 0.81
示例 2 - QR 分解方法
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
qr_lr = LinearRegression(method='qr')
qr_lr.fit(X, y)
print('Intercept: %.2f' % qr_lr.b_)
print('Slope: %.2f' % qr_lr.w_[0])
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, qr_lr)
plt.show()
Intercept: 0.25
Slope: 0.81
示例 3 - SVD 方法
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
svd_lr = LinearRegression(method='svd')
svd_lr.fit(X, y)
print('Intercept: %.2f' %svd_lr.b_)
print('Slope: %.2f' % svd_lr.w_[0])
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, svd_lr)
plt.show()
Intercept: 0.25
Slope: 0.81
示例 4 - 梯度下降
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
gd_lr = LinearRegression(method='sgd',
eta=0.005,
epochs=100,
minibatches=1,
random_seed=123,
print_progress=3)
gd_lr.fit(X, y)
print('Intercept: %.2f' % gd_lr.b_)
print('Slope: %.2f' % gd_lr.w_)
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, gd_lr)
plt.show()
Iteration: 100/100 | Cost 0.08 | Elapsed: 0:00:00 | ETA: 0:00:00
Intercept: 0.22
Slope: 0.82
# 可视化检查收敛性的成本并绘制线性模型:
plt.plot(range(1, gd_lr.epochs+1), gd_lr.cost_)
plt.xlabel('Epochs')
plt.ylabel('Cost')
plt.ylim([0, 0.2])
plt.tight_layout()
plt.show()
示例 5 - 随机梯度下降
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
sgd_lr = LinearRegression(method='sgd',
eta=0.01,
epochs=100,
random_seed=0,
minibatches=len(y))
sgd_lr.fit(X, y)
print('Intercept: %.2f' % sgd_lr.w_)
print('Slope: %.2f' % sgd_lr.b_)
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, sgd_lr)
plt.show()
Intercept: 0.82
Slope: 0.24
plt.plot(range(1, sgd_lr.epochs+1), sgd_lr.cost_)
plt.xlabel('Epochs')
plt.ylabel('Cost')
plt.ylim([0, 0.2])
plt.tight_layout()
plt.show()
示例 6 - 使用小批量的随机梯度下降
import numpy as np
import matplotlib.pyplot as plt
from mlxtend.regressor import LinearRegression
X = np.array([ 1.0, 2.1, 3.6, 4.2, 6])[:, np.newaxis]
y = np.array([ 1.0, 2.0, 3.0, 4.0, 5.0])
sgd_lr = LinearRegression(method='sgd',
eta=0.01,
epochs=100,
random_seed=0,
minibatches=3)
sgd_lr.fit(X, y)
print('Intercept: %.2f' % sgd_lr.b_)
print('Slope: %.2f' % sgd_lr.w_)
def lin_regplot(X, y, model):
plt.scatter(X, y, c='blue')
plt.plot(X, model.predict(X), color='red')
return
lin_regplot(X, y, sgd_lr)
plt.show()
Intercept: 0.24
Slope: 0.82
plt.plot(range(1, sgd_lr.epochs+1), sgd_lr.cost_)
plt.xlabel('Epochs')
plt.ylabel('Cost')
plt.ylim([0, 0.2])
plt.tight_layout()
plt.show()
API
LinearRegression(method='direct', eta=0.01, epochs=50, minibatches=None, random_seed=None, print_progress=0)
Ordinary least squares linear regression.
Parameters
-
method
: string (default: 'direct')For gradient descent-based optimization, use
sgd
(seeminibatch
parameter for further options). Otherwise, ifdirect
(default), the analytical method is used. For alternative, numerically more stable solutions, use eitherqr
(QR decomopisition) orsvd
(Singular Value Decomposition). -
eta
: float (default: 0.01)solver learning rate (between 0.0 and 1.0). Used with
method =
'sgd'
. (Seemethods
parameter for details) -
epochs
: int (default: 50)Passes over the training dataset. Prior to each epoch, the dataset is shuffled if
minibatches > 1
to prevent cycles in stochastic gradient descent. Used withmethod = 'sgd'
. (Seemethods
parameter for details) -
minibatches
: int (default: None)The number of minibatches for gradient-based optimization. If None: Direct method, QR, or SVD method (see
method
parameter for details) If 1: Gradient Descent learning If len(y): Stochastic Gradient Descent learning If 1 < minibatches < len(y): Minibatch learning -
random_seed
: int (default: None)Set random state for shuffling and initializing the weights. Used in
method = 'sgd'
. (Seemethods
parameter for details) -
print_progress
: int (default: 0)Prints progress in fitting to stderr if
method = 'sgd'
. 0: No output 1: Epochs elapsed and cost 2: 1 plus time elapsed 3: 2 plus estimated time until completion
Attributes
-
w_
: 2d-array, shape={n_features, 1}Model weights after fitting.
-
b_
: 1d-array, shape={1,}Bias unit after fitting.
-
cost_
: listSum of squared errors after each epoch; ignored if solver='normal equation'
Examples
For usage examples, please see https://rasbt.github.io/mlxtend/user_guide/regressor/LinearRegression/
Methods
fit(X, y, init_params=True)
Learn model from training data.
Parameters
-
X
: {array-like, sparse matrix}, shape = [n_samples, n_features]Training vectors, where n_samples is the number of samples and n_features is the number of features.
-
y
: array-like, shape = [n_samples]Target values.
-
init_params
: bool (default: True)Re-initializes model parameters prior to fitting. Set False to continue training with weights from a previous model fitting.
Returns
self
: object
get_params(deep=True)
Get parameters for this estimator.
Parameters
-
deep
: boolean, optionalIf True, will return the parameters for this estimator and contained subobjects that are estimators.
Returns
-
params
: mapping of string to anyParameter names mapped to their values.'
adapted from https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/base.py Author: Gael Varoquaux gael.varoquaux@normalesup.org License: BSD 3 clause
predict(X)
Predict targets from X.
Parameters
-
X
: {array-like, sparse matrix}, shape = [n_samples, n_features]Training vectors, where n_samples is the number of samples and n_features is the number of features.
Returns
-
target_values
: array-like, shape = [n_samples]Predicted target values.
set_params(params)
Set the parameters of this estimator.
The method works on simple estimators as well as on nested objects
(such as pipelines). The latter have parameters of the form
<component>__<parameter>
so that it's possible to update each
component of a nested object.
Returns
self
adapted from https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/base.py Author: Gael Varoquaux gael.varoquaux@normalesup.org License: BSD 3 clause
ython