- 优化基础模型
3.1 训练均方误差和测试均方误差
在回归中,我们最常使用的评价指标为均方误差(MSE):
M
S
E
=
1
N
∑
i
=
1
N
(
y
i
−
f
^
(
x
i
)
)
2
MSE=\frac {1}{N}\sum_{i=1}^{N}(y_i-\hat f(x_i))^2
MSE=N1∑i=1N(yi−f^(xi))2。
左:通过不同函数f拟合产生的数据。
右:灰色曲线->训练均方误差,红色曲线->测试均方误差。方块分别对应左图通过不同模型模拟数据的均方误差。
可以看到:当我们的模型的训练均方误差达到很小时,测试均方误差反而很大,但是我们寻找的最优的模型是测试均方误差达到最小时对应的模型。正如上右图所示:模型在训练误差很小,但是测试均方误差很大时,我们称这种情况叫模型的过拟合。
3.2 偏差-方差的均衡
从上图的测试均方误差曲线可以看到:测试均方误差曲线呈现U型曲线,这表明了在测试误差曲线中有两种力量在互相博弈。
E
(
y
0
−
f
^
(
x
0
)
)
2
=
v
a
r
(
f
^
(
x
0
)
)
+
[
b
i
a
s
(
f
^
(
x
0
)
)
]
2
+
v
a
r
(
ϵ
)
E(y_0-\hat f(x_0))^2=var(\hat f(x_0))+[bias(\hat f(x_0))]^2+var( \epsilon)
E(y0−f^(x0))2=var(f^(x0))+[bias(f^(x0))]2+var(ϵ)
也就是说,测试均方误差的期望值=
f
^
(
x
0
)
\hat f(x_0)
f^(x0)的方差、
f
^
(
x
0
)
\hat f(x_0)
f^(x0)偏差的平方和误差项
ϵ
\epsilon
ϵ的方差。由于我们知道偏差平方和方差本身是非负的,因此测试均方误差的期望不可能会低于误差项
ϵ
\epsilon
ϵ的方差,因此我们称 Var(ε) 为建模任务的难度,这个量在我们的任务确定后是无法改变的,也叫做不可约误差。
模型的方差:用不同的数据集去估计 f 时,估计函数值之间的差异。显然,我们希望得到一个稳定的模型,也就是在不同的样本集估计的模型都不会相差太大,即要求f的方差越小越好。一般来说,模型的复杂度越高,f的方差就会越大。 如加入二次项的模型的方差比线性回归模型的方差要大。
模型的偏差:偏差度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力。假如真实的数据X与Y的关系是二次关系,但是我们选择了线性模型进行建模,那由于模型的复杂度引起的这种误差我们称为偏差,它的构成是复杂的。偏差越小,模型具有更好的泛化能力。
偏差度量的是单个模型的学习能力,而方差度量的是同一个模型在不同数据集上的稳定性。
一般而言,增加模型的复杂度,会增加模型的方差,但是会减少模型的偏差,我们要找到一个方差–偏差的权衡,使得测试均方误差最小。
3.3 特征提取
3.3.1 估计测试误差
在前面的讨论中,我们已经明确一个目标,就是:我们要选择一个测试误差达到最小的模型。因此我们要对测试误差进行估计,估计的方式有两种:训练误差修正与交叉验证。
- 训练误差修正
前面的讨论我们已经知道,模型越复杂,训练误差越小,测试误差先减后增。因此,我们先构造一个特征较多的模型使其过拟合,此时训练误差很小而测试误差很大,那这时我们加入关于特征个数的惩罚。因此,当我们的训练误差随着特征个数的增加而减少时,惩罚项因为特征数量的增加而增大,抑制了训练误差随着特征个数的增加而无休止地减小。 - 交叉验证
训练误差修正是通过加入惩罚项抑制训练误差的减小来间接估计测试误差,而交叉验证是直接对测试误差进行估计。
k折交叉验证:我们把训练样本分成K等分,然后用K-1个样本集当做训练集,剩下的一份样本集为测试集去估计由K-1个样本集得到的模型的精度,这个过程重复K次取平均值得到测试误差的一个估计。
C K ( K ) = ∑ i = 1 K M S E i CK_{(K)}=\sum_{i=1}^{K}MSE_i CK(K)=i=1∑KMSEi
3.3.2 特征选择
在测试误差能够被合理的估计出来以后,我们做特征选择的目标就是:从p个特征中选择m个特征,使得对应的模型的测试误差的估计最小。对应的方法有:
- 最优子集选择
(1) 记不含任何特征的模型为 M0 ,计算这个 M0 的测试误差;
(2) 在 M0 基础上增加一个特征(分别增加p个特征),计算p个模型的RSS,选择RSS最小的模型记作 M1 ,并计算该模型 M1 的测试误差;
R S S = ∑ i = 1 N ( y i − f ^ ( x i ) ) 2 RSS=\sum_{i=1}^{N}(y_i-\hat f(x_i))^2 RSS=i=1∑N(yi−f^(xi))2
(3) 再增加一个特征,计算p-1个模型的RSS,并选择RSS最小的模型记作 M2 ,并计算该模型 M2 的测试误差;
(iv) 重复以上过程知道拟合的模型有p个特征为止,并选择p+1个模型 {M0,M1,…,Mp} 中测试误差最小的模型作为最优模型。 - 向前逐步选择
最优子集选择虽然在原理上很直观,但是随着数据特征维度p的增加,子集的数量为 2p ,计算效率非常低下且需要的计算内存也很高,在大数据的背景下显然不适用。因此,我们需要把最优子集选择的运算效率提高,因此向前逐步选择算法的过程如下:
(i) 记不含任何特征的模型为 M0 ,计算这个 M0 的测试误差;
(ii) 在 M0 基础上增加一个特征,计算p个模型的RSS,选择RSS最小的模型记作 M1 ,并计算该模型 M1 的测试误差;
(iii) 在最小的RSS模型下继续增加一个特征,选择RSS最小的模型记作 M2 ,并计算该模型 M2 的测试误差;
(iv) 以此类推,重复以上过程知道拟合的模型有p个特征为止,并选择p+1个模型 {M0,M1,…,Mp} 中测试误差最小的模型作为最优模型。
实例:
案例来源:https://blog.youkuaiyun.com/weixin_44835596/article/details/89763300
根据AIC准则定义向前逐步回归进行变量筛选
补充:AIC即赤池值,是衡量模型拟合优良性和模型复杂性的一种标准,在建立多元线性回归模型时,变量过多,且有不显著的变量时,可以使用AIC准则结合逐步回归进行变量筛选。
A I C = 2 P + n ( l o g ( S S E / n ) ) AIC=2P+n(log(SSE/n)) AIC=2P+n(log(SSE/n))
其中,p是进入模型当中的自变量个数,n为样本量,SSE是残差平方和( S S E = ∑ i = 1 N w i ( y i − y ^ i ) 2 SSE=\sum_{i=1}^{N}w_i(y_i-\hat y_i)^2 SSE=∑i=1Nwi(yi−y^i)2)。在n固定的情况下,而p 越小代表着模型越简洁,SSE越小代表着模型越精准,即拟合度越好,综上所诉,AIC越小,即模型就越简洁和精准。
#定义向前逐步回归函数
def forward_select(data, target):
variate = set(data.columns) #将特征字段名(索引)转换成字典类型
variate.remove(target) #去掉因变量的字段名
selected = [] #留下来的特征
current_score, best_new_score = float('inf'), float('inf') #目前的分数和最好的分数的初始值,inf为无穷大,因为OIC越小越好
#循环筛选变量
while variate:
aic_with_variate = []
for candidate in variate:
formula = "{}~{}".format(target, "+".join(selected+[candidate])) #将自变量名连起来
aic = ols(formula=formula, data=data).fit().aic #利用ols训练模型得出aic值
aic_with_variate.append((aic, candidate)) #将加入每一变量计算出来的值加入列表
aic_with_variate.sort(reverse=True) #降序排序aic值
best_new_score, best_candidate = aic_with_variate.pop()
if current_score > best_new_score:
variate.remove(best_candidate) #移除加进来的变量名,即下次循环时,不考虑此自变量了
selected.append(best_candidate)
current_score = best_new_score
print("aic is {}, continuing!".format(current_score))
else:
print("for selection over!")
break
formula = "{}~{}".format(target, "+".join(selected))
print("final formula is {}".format(formula))
model = ols(formula=formula, data=data).fit()
return(model.summary())
import statsmodels.api as sm #最小二乘
from statsmodels.formula.api import ols #加载ols模型
forward_select(data=boston_data, target='Price')
3.4 压缩估计(正则化)
除了刚刚讨论的直接对特征自身进行选择以外,我们还可以对回归的系数进行约束或者加罚的技巧对p个特征的模型进行拟合,显著降低模型方差,这样也会提高模型的拟合效果。具体来说,就是将回归系数往零的方向压缩,这也就是为什么叫压缩估计的原因了。
- 岭回归(L2正则化的例子):
在线性回归中,损失函数为 J ( w ) = ∑ i = 1 N ( y i − w 0 − ∑ j = 1 p w j x i j ) 2 J(w)=\sum_{i=1}^{N}(y_i-w_0-\sum_{j=1}^{p}w_jx_{ij})^2 J(w)=∑i=1N(yi−w0−∑j=1pwjxij)2,我们在线性回归的损失函数的基础上添加对系数的约束或者惩罚,即:
J ( w ) = ∑ i = 1 N ( y i − w 0 − ∑ j = 1 p w j x i j ) 2 + λ ∑ j = 1 p w j 2 , 其 中 λ ≥ 0 J(w)=\sum_{i=1}^{N}(y_i-w_0-\sum_{j=1}^{p}w_jx_{ij})^2+\lambda \sum_{j=1}^{p}w_j^2,其中\lambda \geq0 J(w)=i=1∑N(yi−w0−j=1∑pwjxij)2+λj=1∑pwj2,其中λ≥0
w ^ = ( X T X + λ I ) − 1 X T Y \hat w=(X^TX+\lambda I)^{-1}X^TY w^=(XTX+λI)−1XTY
调节参数 λ 的大小是影响压缩估计的关键, λ 越大,惩罚的力度越大,系数则越趋近于0,反之,选择合适的 λ 对模型精度来说十分重要。岭回归通过牺牲线性回归的无偏性降低方差,有可能使得模型整体的测试误差较小,提高模型的泛化能力。
实例:
[sklearn.linear_model.ridge_regression(X, y, alpha, *, sample_weight=None, solver=‘auto’, max_iter=None, tol=0.001, verbose=0, random_state=None, return_n_iter=False, return_intercept=False, check_input=True)](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.ridge_regression.html?highlight=rid#sklearn.linear_model.ridge_regression
参数:
alpha:较大的值表示更强的正则化。浮点数
sample_weight:样本权重,默认无。
solver:求解方法,{‘auto’, ‘svd’, ‘cholesky’, ‘lsqr’, ‘sparse_cg’, ‘sag’, ‘saga’}, 默认=’auto’。“ svd”使用X的奇异值分解来计算Ridge系数。'cholesky’使用标准的scipy.linalg.solve函数通过dot(XT,X)的Cholesky分解获得封闭形式的解。'sparse_cg’使用scipy.sparse.linalg.cg中的共轭梯度求解器。作为一种迭代算法,对于大规模数据(可能设置tol和max_iter),此求解器比“ Cholesky”更合适。 lsqr”使用专用的正则化最小二乘例程scipy.sparse.linalg.lsqr。它是最快的,并且使用迭代过程。“ sag”使用随机平均梯度下降,“ saga”使用其改进的无偏版本SAGA。两种方法都使用迭代过程,并且当n_samples和n_features都很大时,通常比其他求解器更快。请注意,只有在比例大致相同的要素上才能确保“ sag”和“ saga”快速收敛。您可以使用sklearn.preprocessing中的缩放器对数据进行预处理。最后五个求解器均支持密集和稀疏数据。但是,当fit_intercept为True时,仅’sag’和’sparse_cg’支持稀疏输入。)
from sklearn import linear_model
reg_rid = linear_model.Ridge(alpha=.5)
reg_rid.fit(X,y)
reg_rid.score(X,y)
# 0.739957023371629
- Lasso回归(L1正则化的例子):
岭回归的一个很显著的特点是:将模型的系数往零的方向压缩,但是岭回归的系数只能呢个趋于0但无法等于0,换句话说,就是无法做特征选择。能否使用压缩估计的思想做到像特征最优子集选择那样提取出重要的特征呢?答案是肯定的!我们只需要对岭回归的优化函数做小小的调整就行了,我们使用系数向量的L1范数替换岭回归中的L2范数:
J ( w ) = ∑ i = 1 N ( y i − w 0 − ∑ j = 1 p w j x i j ) 2 + λ ∑ j = 1 p ∣ w j ∣ , 其 中 λ ≥ 0 J(w)=\sum_{i=1}^{N}(y_i-w_0-\sum_{j=1}^{p}w_jx_{ij})^2+\lambda \sum_{j=1}^{p}|w_j|,其中\lambda \geq0 J(w)=i=1∑N(yi−w0−j=1∑pwjxij)2+λj=1∑p∣wj∣,其中λ≥0
为什么Losso能做到特征选择而岭回归却不能呢个做到呢?(如图:左边为lasso,右边为岭回归)
椭圆形曲线为RSS等高线,菱形和圆形区域分别代表了L1和L2约束,Lsaao回归和岭回归都是在约束下的回归,因此最优的参数为椭圆形曲线与菱形和圆形区域相切的点。但是Lasso回归的约束在每个坐标轴上都有拐角,因此当RSS曲线与坐标轴相交时恰好回归系数中的某一个为0,这样就实现了特征提取。反观岭回归的约束是一个圆域,没有尖点,因此与RSS曲线相交的地方一般不会出现在坐标轴上,因此无法让某个特征的系数为0,因此无法做到特征提取。
实例:
class sklearn.linear_model.Lasso(alpha=1.0, *, fit_intercept=True, normalize=False, precompute=False, copy_X=True, max_iter=1000, tol=0.0001, warm_start=False, positive=False, random_state=None, selection=‘cyclic’).
参数:
alpha:正则化强度,1.0代表标准最小二乘。
fit_intercept:是否计算模型截距。默认true。
normalize:是否标准化,默认false。
positive:是否强制系数为正,默认false。
from sklearn import linear_model
reg_lasso = linear_model.Lasso(alpha = 0.5)
reg_lasso.fit(X,y)
reg_lasso.score(X,y)
# 0.7140164719858566