Lasso原理与Python实现
-
lasso原理
-
Python实现
Lasso 原理
在一般的回归情形中,对于数据集
(
x
i
,
y
i
)
\left(\mathrm{x}^{i}, y^{i}\right)
(xi,yi),最小二乘法估计通过计算预测值与真实值之间的最小平方和对数据进行预测,但是数据分析不常满足最小二乘估计,而提升最小二乘估计的两个标准技术:子集选择与岭回归都存在缺陷。子集选择通过从特征中选择部分特征的方法建立模型,但数据中很小的变化就能导致不一样的模型,因此子集选择降低了预测的准确度。而岭回归不把任何系数设置为0,并不提供易于解释的模型。
针对以上问题,作者提出了Lasso方法,即“最小绝对值收敛和选择算子”,从本质上说,Lasso方法能够收缩系数并将一些系数设置为0,保留了子集选择和岭回归的优点。
Lasso的公式为:
min
β
1
2
∥
y
−
∑
i
=
1
n
x
i
β
i
∥
2
2
+
λ
∥
β
∥
1
\min _{\beta} \frac{1}{2}\left\|\mathbf{y}-\sum_{i=1}^{n} \mathbf{x}_{i} \beta_{i}\right\|_{2}^{2}+\lambda\|\beta\|_{1}
minβ21∥y−∑i=1nxiβi∥22+λ∥β∥1
其中,前一项为最小二乘估计公式,后一项实际为正则化项L1。为计算使得误差最小的beta值,对公式做如下推导:
f
=
R
S
S
(
w
)
+
λ
∥
w
1
∥
1
f=R S S(w)+\lambda\left\|w_{1}\right\|_{1}
f=RSS(w)+λ∥w1∥1
=
∑
i
=
0
N
(
y
i
−
∑
j
=
0
D
w
j
x
i
j
)
2
+
λ
∑
j
=
0
D
∣
w
j
∣
=\sum_{i=0}^{N}\left(y_{i}-\sum_{j=0}^{D} w_{j} x_{i j}\right)^{2}+\lambda \sum_{j=0}^{D}\left|w_{j}\right|
=∑i=0N(yi−∑j=0Dwjxij)2+λ∑j=0D∣wj∣
∂ R S S ∂ W j \frac{\partial R S S}{\partial W_{j}} ∂Wj∂RSS = − 2 ∑ i = 0 N x i j ( y i − ∑ j = 0 D w j x i j ) -2 \sum_{i=0}^{N} x_{i j}\left(y_{i}-\sum_{j=0}^{D} w_{j} x_{i j}\right) −2∑i=0Nxij(yi−∑j=0Dwjxij)
= − 2 ∑ i = 0 N x i j =-2 \sum_{i=0}^{N} x_{i j} =−2∑i=0Nxij ( y i − ∑ k ≠ j w k x i k − W j x i j ) \left(y_{i}-\sum_{k \neq j} w_{k} x_{i k}-W_{j} x_{i j}\right) (yi−∑k=jwkxik−Wjxij)
=
−
2
∑
i
=
0
N
x
i
j
=-2 \sum_{i=0}^{N} x_{i j}
=−2∑i=0Nxij
(
y
i
−
∑
k
≠
j
w
k
x
i
k
)
\left(y_{i}-\sum_{k \neq j} w_{k} x_{i k}\right)
(yi−∑k=jwkxik)+2
∑
i
=
0
N
w
j
x
i
j
2
\sum_{i=0}^{N} w_{j} x_{i j}^{2}
∑i=0Nwjxij2
令
p
j
=
p_{j}=
pj=
∑
i
=
0
N
x
i
j
\sum_{i=0}^{N} x_{i j}
∑i=0Nxij
(
y
i
−
∑
k
≠
j
w
k
x
i
k
)
\left(y_{i}-\sum_{k \neq j} w_{k} x_{i k}\right)
(yi−∑k=jwkxik)
z j = z_{j}= zj= ∑ i = 0 N x i j 2 \sum_{i=0}^{N} x_{i j}^{2} ∑i=0Nxij2
则有
∂
R
S
S
∂
W
j
=
−
2
p
j
+
2
w
j
z
j
\frac{\partial R S S}{\partial W_{j}}=-2 p_{j}+2 w_{j} z_{j}
∂Wj∂RSS=−2pj+2wjzj
此外,对于正则项的求导本质上即为y=|x|的形式,求导结果为:
{
λ
,
w
j
>
0
[
−
λ
,
λ
]
,
w
j
=
0
−
λ
,
w
j
<
0
\left\{\begin{array}{c}{\lambda, w_{j}>0} \\ {[-\lambda, \lambda], w_{j}=0} \\ {-\lambda, w_{j}<0}\end{array}\right.
⎩⎨⎧λ,wj>0[−λ,λ],wj=0−λ,wj<0
因此当
w
j
>
0
w_{j}>0
wj>0时,
∂
f
∂
w
=
2
z
j
w
j
−
2
p
j
−
λ
\frac{\partial f}{\partial w}=2 z_{j} w_{j}-2 p_{j}-\lambda
∂w∂f=2zjwj−2pj−λ ,令其为0解得:
w
j
=
(
p
j
−
λ
2
)
/
z
j
w_{j}=\left(p_{j}-\frac{\lambda}{2}\right) / z_{j}
wj=(pj−2λ)/zj,且
p
j
>
λ
2
{p}_{j}>\frac{\lambda}{2}
pj>2λ。同理,当
w
j
<
0
w_{j}<0
wj<0可解得
w
j
=
(
p
j
+
λ
2
)
/
z
j
w_{j}=\left(p_{j}+\frac{\lambda}{2}\right) / z_{j}
wj=(pj+2λ)/zj,且
p
j
<
−
λ
2
{p}_{j}<-\frac{\lambda}{2}
pj<−2λ。
根据解得的
w
j
w_{j}
wj公式,再计算对应的
p
j
p_{j}
pj、
z
j
z_{j}
zj,即可得到lasso回归的权重矩阵。
具体实现时,
z
j
z_{j}
zj即为向量的内积,只需取出矩阵中的该列,并以内积的形式相乘即可得到
z
j
z_{j}
zj。计算
p
j
p_{j}
pj时,需设置双重循环。
Python实现
import numpy as np
# 计算残差
def rss(X,y,w):
return (y-X*w).T*(y-X*w)
# lasso 回归
def lassoRegression(X,y,a):
threshold = 0.1
# init para
m,n = X.shape
w = np.matrix(np.zeros((n,1)))
r = rss(X,y,w)
z = [0 for i in range(n)]
tempw = [0 for i in range(n)]
# 记录迭代次数
iteration = 0
# 迭代以计算参数
while True:
iteration += 1
for k in range(n):
temp_X = X[:, k].reshape(-1,1)
z[k] = (temp_X.T*temp_X)[0, 0]
p = [0 for i in range(n)]
p_part = [0 for i in range(n)]
for i in range(m):
for j in range(n):
if k != j:
# print w[j,0]
p_part[j] = X[i, j]*w[j, 0]
p[k] += X[i, k] * (y[i, 0] - sum(p_part))
if p[k] < -a/2:
tempw[k] = (p[k] + a/2)/z[k]
elif p[k] > a/2:
tempw[k] = (p[k] - a/2)/z[k]
else:
tempw[k] = 0
w[k, 0] = tempw[k]
tempr = rss(X, y, w)
delta = abs(tempr - r)[0, 0]
r = tempr
if delta < threshold:
print 'iteration', iteration
print 'delta', delta
break
return w
def load_data(filename):
X, Y = [], []
with open(filename, 'r') as f:
for line in f:
splited_line = [float(i) for i in line.split()]
x, y = splited_line[: -1], splited_line[-1]
X.append(x)
Y.append(y)
X, Y = np.matrix(X), np.matrix(Y).T
return X, Y
def std(X):
std_deviation = np.std(X, 0)
mean = np.mean(X, 0)
return (X - mean)/std_deviation
if '__main__'==__name__:
# 使用鲍鱼数据集
X, y = load_data('abalone.txt')
X, y = std(X), std(y)
enum = 30
w_all = lassoTrace(X, y, enum)
lambdas = [i-10 for i in range(enum)]
data = pd.DataFrame({
'x1': w_all[:, 0],
'x2': w_all[:, 1],
'x3': w_all[:, 2],
'x4': w_all[:, 3],
'x5': w_all[:, 4],
'x6': w_all[:, 5],
'x7': w_all[:, 6],
'x8': w_all[:, 7],
})
plt.figure(12)
plt.subplot(221)
data.boxplot()
plt.subplot(222)
plt.plot(lambdas, w_all)
plt.show()
输出各系数随惩罚力度变化的曲线图以及各系数的箱形图。