浏览本文须要的背景知识点:岭回归、Lasso回归、一点点编程常识
一、引言
后面学习了岭回归与Lasso回归两种正则化的办法,当多个特色存在相干时,Lasso回归可能只会随机抉择其中一个,岭回归则会抉择所有的特色。这时很容易的想到如果将这两种正则化的办法联合起来,就可能汇合两种办法的劣势,这种正则化后的算法就被称为弹性网络回归1 (Elastic Net Regression)
二、模型介绍
弹性网络回归算法的代价函数联合了Lasso回归和岭回归的正则化办法,通过两个参数 和 来管制惩办项的大小。
$$\operatorname{Cost}(w)=\sum_{i=1}^{N}\left(y_{i}-w^{T} x_{i}\right)^{2}+\lambda \rho\|w\|_{1}+\frac{\lambda(1-\rho)}{2}\|w\|_{2}^{2}$$
同样是求使得代价函数最小时 w 的大小:
$$w=\underset{w}{\operatorname{argmin}}\left(\sum_{i=1}^{N}\left(y_{i}-w^{T} x_{i}\right)^{2}+\lambda \rho\|w\|_{1}+\frac{\lambda(1-\rho)}{2}\|w\|_{2}^{2}\right)$$
能够看到,当 = 0 时,其代价函数就等同于岭回归的代价函数,当 = 1 时,其代价函数就等同于 Lasso 回归的代价函数。与 Lasso 回归一样代价函数中有绝对值存在,不是处处可导的,所以就没方法通过间接求导的形式来间接失去 w 的解析解,不过还是能够用坐标降落法2(coordinate descent)来求解 w。
三、算法步骤
坐标降落法:
坐标降落法的求解办法与 Lasso 回归所用到的步骤一样,惟一的区别只是代价函数不一样。
具体步骤:
(1)初始化权重系数 w,例如初始化为零向量。
(2)遍历所有权重系数,顺次将其中一个权重系数当作变量,其余权重系数固定为上一次计算的后果当作常量,求出以后条件下只有一个权重系数变量的状况下的最优解。
在第 k 次迭代时,更新权重系数的办法如下:
$$\begin{matrix}w_m^k 示意第k次迭代,第m个权重系数 \\w_1^k = \underset{w_1}{\operatorname{argmin}} \left( \operatorname{Cost}(w_1, w_2^{k-1}, \dots, w_{m-1}^{k-1}, w_m^{k-1}) \right) \\ w_2^k = \underset{w_2}{\operatorname{argmin}} \left( \operatorname{Cost}(w_1^{k}, w_2, \dots, w_{m-1}^{k-1}, w_m^{k-1}) \right) \\\vdots \\w_m^k = \underset{w_m}{\operatorname{argmin}} \left( \operatorname{Cost}(w_1^{k}, w_2^{k}, \dots, w_{m-1}^{k}, w_m) \right) \\\end{matrix}$$
(3)步骤(2)为一次残缺迭代,当所有权重系数的变动不大或者达到最大迭代次数时,完结迭代。
四、代码实现
应用 Python 实现弹性网络回归算法(坐标降落法):
def elasticNet(X, y, lambdas=0.1, rhos=0.5, max_iter=1000, tol=1e-4): """ 弹性网络回归,应用坐标降落法(coordinate descent) args: X - 训练数据集 y - 指标标签值 lambdas - 惩办项系数 rhos - 混合参数,取值范畴[0,1] max_iter - 最大迭代次数 tol - 变动量容忍值 return: w - 权重系数 """ # 初始化 w 为零向量 w = np.zeros(X.shape[1]) for it in range(max_iter): done = True # 遍历所有自变量 for i in range(0, len(w)): # 记录上一轮系数 weight = W[i] # 求出以后条件下的最佳系数 w[i] = down(X, y, w, i, lambdas, rhos) # 当其中一个系数变动量未达到其容忍值,持续循环 if (np.abs(weight - w[i]) > tol): done = False # 所有系数都变动不大时,完结循环 if (done): break return wdef down(X, y, w, index, lambdas=0.1, rhos=0.5): """ cost(w) = (x1 * w1 + x2 * w2 + ... - y)^2 / 2n + ... + * * (|w1| + |w2| + ...) + [ * (1 - ) / 2] * (w1^2 + w2^2 + ...) 假如 w1 是变量,这时其余的值均为常数,带入上式后,其代价函数是对于 w1 的一元二次函数,能够写成下式: cost(w1) = (a * w1 + b)^2 / 2n + ... + |w1| + [(1 - )/2] * w1^2 + c (a,b,c, 均为常数) => 开展后 cost(w1) = [aa / 2n + (1 - )/2] * w1^2 + (ab / n) * w1 + |w1| + c (aa,ab,c, 均为常数) """ # 开展后的二次项的系数之和 aa = 0 # 开展后的一次项的系数之和 ab = 0 for i in range(X.shape[0]): # 括号内一次项的系数 a = X[i][index] # 括号内常数项的系数 b = X[i][:].dot(w) - a * w[index] - y[i] # 能够很容易的失去开展后的二次项的系数为括号内一次项的系数平方的和 aa = aa + a * a # 能够很容易的失去开展后的一次项的系数为括号内一次项的系数乘以括号内常数项的和 ab = ab + a * b # 因为是一元二次函数,当导数为零是,函数值最小值,只须要关注二次项系数、一次项系数和 return det(aa, ab, X.shape[0], lambdas, rhos)def det(aa, ab, n, lambdas=0.1, rhos=0.5): """ 通过代价函数的导数求 w,当 w = 0 时,不可导 det(w) = [aa / n + (1 - )] * w + ab / n + = 0 (w > 0) => w = - (ab / n + ) / [aa / n + (1 - )] det(w) = [aa / n + (1 - )] * w + ab / n - = 0 (w < 0) => w = - (ab / n - ) / [aa / n + (1 - )] det(w) = NaN (w = 0) => w = 0 """ w = - (ab / n + lambdas * rhos) / (aa / n + lambdas * (1 - rhos)) if w < 0: w = - (ab / n - lambdas * rhos) / (aa / n + lambdas * (1 - rhos)) if w > 0: w = 0 return w
五、第三方库实现
scikit-learn3 实现:
from sklearn.linear_model import ElasticNet# 初始化弹性网络回归器reg = ElasticNet(alpha=0.1, l1_ratio=0.5, fit_intercept=False)# 拟合线性模型reg.fit(X, y)# 权重系数w = reg.coef_
六、动画演示
上面动图展现了不同的 对弹性网络回归的影响,当 逐步增大时,L1正则项占据主导地位,代价函数越靠近Lasso回归,当 逐步减小时,L2正则项占据主导地位,代价函数越靠近岭回归。
上面动图展现了Lasso回归与弹性网络回归比照,虚线示意Lasso回归的十个特色,实线示意弹性网络回归的十个特色,每一个色彩示意一个自变量的权重系数(训练数据来源于sklearn diabetes datasets)
<center>Lasso回归与弹性网络回归比照</center>
能够看到弹性网络回归绝对Lasso回归来说,保留了Lasso回归的特征选择的性质,又兼顾了岭回归的稳定性。
七、思维导图
八、参考文献
- https://en.wikipedia.org/wiki...
- https://en.wikipedia.org/wiki...
- https://scikit-learn.org/stab...
残缺演示请点击这里
注:本文力求精确并通俗易懂,但因为笔者也是初学者,程度无限,如文中存在谬误或脱漏之处,恳请读者通过留言的形式批评指正
本文首发于——AI导图,欢送关注