(主要参考《机器学习》周志华 第三章)
首先,多元线性回归的基本形式为 f ( x ) = w T x + b f(\pmb{x})=\pmb{w}^T\pmb{x}+b f(xxx)=wwwTxxx+b,为便于讨论,把 w \pmb{w} www 和 b b b 吸收入向量形式 w ^ = ( w ; b ) \hat{\pmb{w}}=(\pmb{w};b) www^=(www;b),相应的,把数据集 D D D 表示为一个 m × ( d + 1 ) m\times(d+1) m×(d+1) 大小的矩阵 X \pmb{X} XXX (其中 m m m为样本条数, d d d为特征数),即
X = ( x 11 x 12 ⋯ x 1 d 1 x 21 x 22 ⋯ x 2 d 1 ⋮ ⋮ ⋱ ⋮ ⋮ x m 1 x m 2 ⋯ x m d 1 ) \pmb{X}=\left( \begin{matrix} x_{11} & x_{12} & \cdots &x_{1d} & 1 \\ x_{21} & x_{22} & \cdots &x_{2d} & 1 \\ \vdots & \vdots & \ddots &\vdots & \vdots \\ x_{m1} & x_{m2} & \cdots &x_{md} & 1 \\ \end{matrix} \right) XXX=⎝⎜⎜⎜⎛x11x21⋮xm1x12x22⋮xm2⋯⋯⋱⋯x1dx2d⋮xmd11⋮1⎠⎟⎟⎟⎞
再把标记也写成向量形式 y = ( y 1 ; y 2 ; ⋯ ; y m ) y=(y_1;y_2;\cdots;y_m) y=(y1;y2;⋯;ym),
则根据最小二乘法,参数估计值 w ^ ∗ \hat{\pmb{w}}^* www^∗应该满足
w ^ ∗ = arg min w ^ ( y − X w ^ ) T ( y − X w ^ ) \hat{\pmb{w}}^*= {\underset {\hat{w}}{\operatorname {arg\,min} }}(\pmb{y}-\pmb{X}\hat{\pmb{w}})^T(\pmb{y}-\pmb{X}\hat{\pmb{w}}) www^∗=w^argmin(yyy−XXXwww^)T(yyy−XXXwww^)
令 E w ^ = ( y − X w ^ ) T ( y − X w ^ ) E_{\hat{w}}=(\pmb{y}-\pmb{X}\hat{\pmb{w}})^T(\pmb{y}-\pmb{X}\hat{\pmb{w}}) Ew^=(yyy−XXXwww^)T(yyy−XXXwww^),对 w ^ \hat{\pmb{w}} www^求导得到(相关矩阵求导知识链接)
∂ E w ^ ∂ w ^ = 2 X T ( X w ^ − y ) \frac{\partial E_{\hat{w}}}{\partial \hat w}=2\pmb X^T(\pmb X \hat{\pmb{w}}-\pmb y) ∂w^∂Ew^=2XXXT(XXXwww^−yyy)
令上式得零则可得到 w ^ \hat{\pmb{w}} www^ 的最优解,有两种情况:
首先介绍常见范数——P-范数 (范数详见百度百科),即
∣ ∣ x ∣ ∣ p = ( ∣ x 1 ∣ p + ∣ x 2 ∣ p + ⋯ + ∣ x n ∣ p ) 1 p ||\pmb x||_p=(|x_1|^p+|x_2|^p+\cdots+|x_n|^p)^{\frac{1}{p}} ∣∣xxx∣∣p=(∣x1∣p+∣x2∣p+⋯+∣xn∣p)p1
L1范数正则化项,即令 p p p = 1,并针对参数向量,即为 λ ∣ ∣ w ∣ ∣ 1 \lambda ||\pmb w||_1 λ∣∣www∣∣1 ,其中 λ \lambda λ 为正则化参数。对于 L1 正则化, λ \lambda λ 越大可以使越多的参数为零或接近零。
类似地,L2范数正则化项为 λ ∣ ∣ w ∣ ∣ 2 2 \lambda ||\pmb w||_2^2 λ∣∣www∣∣22
(参考文章链接,下面是利用网站GeoGebra画出的函数图像)
为了可视化,假设一个回归函数及相应的损失函数如下:
回归函数: f ( x i ) = w 1 x i + b f(x_i)=w_1x_i+b f(xi)=w1xi+b
损失函数(均方差): L = 1 m ∑ i = 1 m ( f ( x i ) − y i ) 2 L=\frac{1}{m}\sum\limits_{i=1}^m(f(x_i)-y_i)^2 L=m1i=1∑m(f(xi)−yi)2其中m表示样本量,
为了保持简单,只取一个样本点(1,1)代入上面的代价函数方程中,即 L = ( w 1 + b − 1 ) 2 L=(w_1+b-1)^2 L=(w1+b−1)2
以下则是上式的图像
可以看出,有无穷多个解可以使损失函数达到最小值。根据前面的论证,在只有一个样本点情况下,外加上最后一列是 1,得到的反应数据集 D D D 的矩阵 X \pmb{X} XXX是一行两列, X T X \pmb X^T \pmb X XXXTXXX 不是满秩矩阵,解不唯一,没毛病。
加了 L1 正则项之后的损失函数: L = w 1 + b − 1 + ∣ w 1 ∣ L=w_1+b-1+|w_1| L=w1+b−1+∣w1∣ (令 λ = 1 \lambda=1 λ=1)
上式图像为
可以看出,有唯一最优解了
经典图片了,不多说了。
就是在损失函数中加入 L1正则化项的线性回归,值得注意的是在测试集中验证预测效果的时候的损失函数是不带正则化项的。
(参考:为什么梯度反方向是函数下降最快的方向?、《2022张宇高数18讲(第17讲)》)
首先介绍经典的梯度下降算法。
可能都知道,梯度下降算法的迭代公式是
w ^ k + 1 = w ^ k − η ∂ L ( w ^ k ) ∂ w ^ \hat{\pmb w}^{k+1}=\hat{\pmb w}^k- \eta \frac{\partial L(\hat{\pmb w}^k) } {\partial\hat{ \pmb w}} www^k+1=www^k−η∂www^∂L(www^k)
其中 w ^ = ( w ; b ) \hat{\pmb{w}}=(\pmb{w};b) www^=(www;b),而
∂ L ( w ^ k ) ∂ w ^ \frac{\partial L(\hat{\pmb w}^k) }{\partial\hat{ \pmb w}} ∂www^∂L(www^k)
便是优化目标中的损失函数 L ( w ^ ) L(\hat{\pmb{w}}) L(www^) 的梯度,接下来只要弄清为什么梯度方向是函数上升最快的方向,便就清楚这个式子了。
(1)非零向量 a \pmb a aaa 与 x x x 轴、 y y y 轴、 z z z 轴正向的夹角 α \alpha α、 β \beta β、 γ \gamma γ 称为 a \pmb a aaa 的方向角。
(2) c o s α cos\alpha cosα、 c o s β cos\beta cosβ、 c o s γ cos\gamma cosγ 称为 a \pmb a aaa 的方向余弦,且 c o s α = a x ∣ a ∣ cos\alpha=\frac{a_x}{|a|} cosα=∣a∣ax、 c o s β = a y ∣ a ∣ cos\beta=\frac{a_y}{|a|} cosβ=∣a∣ay、 c o s γ = a z ∣ a ∣ cos\gamma=\frac{a_z}{|a|} cosγ=∣a∣az
(3)由(2)可知 a ∣ a ∣ = ( c o s α , c o s β , c o s γ ) \frac{a}{|a|}=(cos\alpha,cos\beta,cos\gamma) ∣a∣a=(cosα,cosβ,cosγ) 称为向量 a \pmb a aaa 的单位向量
以上基础知识有利于从二元直接向更多元推广。
要想解决以上疑问,首先要知道方向导数。在许多问题中,不仅要知道函数在坐标轴方向上的变化率(即偏导数),而且还要设法求得函数在某点沿着其他特定方向上的变化率。这就是方向倒数。
首先给出明确定义:(此部分我感觉参考文章写的更好理解,以下定义更精确、好推广)
设二元函数 u = u ( x , y ) u=u(x,y) u=u(x,y)在点 P 0 ( x 0 , y 0 ) P_0(x_0,y_0) P0(x0,y0) 有定义, l l l 为从 P 0 P_0 P0 出发的射线, P ( x , y ) P(x,y) P(x,y) 为在 l l l 上的任一点,则
{ x − x 0 = Δ x = t c o s α y − y 0 = Δ y = t c o s β \left\{ \begin{aligned} x -x_0 = \Delta x = t cos\alpha\\ y-y_0 = \Delta y = t cos\beta\\ \end{aligned} \right. {x−x0=Δx=tcosαy−y0=Δy=tcosβ
其中 t = ( Δ x ) 2 + ( Δ y ) 2 t=\sqrt{(\Delta x)^2+(\Delta y)^2} t=(Δx)2+(Δy)2 表示 P P P 与 P 0 P_0 P0 之间的距离。
则极限
l i m t → 0 + u ( P ) − u ( P 0 ) t = l i m t → 0 + u ( x 0 + t c o s α , y 0 + t c o s β ) − u ( x 0 , y 0 ) t \underset{t\rightarrow 0^+}{lim}\frac{u(P)-u(P_0)}{t} \\ =\underset{t\rightarrow 0^+}{lim}\frac{u(x_0+ t cos\alpha,y_0+ t cos\beta)-u(x_0,y_0)}{t} t→0+limtu(P)−u(P0)=t→0+limtu(x0+tcosα,y0+tcosβ)−u(x0,y0)
此极限便是函数 u u u 在点 P 0 P_0 P0 沿方向 l \pmb l lll 的方向导数,记作
∂ u ∂ l ∣ P 0 \frac{\partial u}{\partial \pmb l}|_{P_0} ∂lll∂u∣P0
方向导数计算公式:设函数 u = u ( x , y ) u=u(x,y) u=u(x,y) 在点 P 0 P_0 P0 点出可微分,则 u u u 在点 P 0 P_0 P0 处沿任意方向 l \pmb l lll 的方向导数都存在,且
∂ u ∂ l ∣ P 0 = l i m t → 0 + u ( P ) − u ( P 0 ) t = l i m t → 0 + u ( x 0 + Δ x , y 0 + Δ y ) − u ( x 0 , y 0 ) ( Δ x ) 2 + ( Δ y ) 2 = ∗ l i m t → 0 + u x ′ ( P 0 ) Δ x + u y ′ ( P 0 ) Δ y + o ( t ) ( Δ x ) 2 + ( Δ y ) 2 = u x ′ ( P 0 ) c o s α + u y ′ ( P 0 ) c o s β \begin{aligned} \frac{\partial u}{\partial \pmb l}|_{P_0} =& \underset{t\rightarrow 0^+}{lim}\frac{u(P)-u(P_0)}{t} \\ =& \underset{t\rightarrow 0^+}{lim}\frac{u(x_0+\Delta x,y_0+\Delta y)-u(x_0,y_0)}{\sqrt{(\Delta x)^2+(\Delta y)^2}}\\ \overset{*}{=}& \underset{t\rightarrow 0^+}{lim}\frac{u_x^\prime(P_0)\Delta x + u_y^\prime(P_0)\Delta y + o(t)}{\sqrt{(\Delta x)^2+(\Delta y)^2}}\\ =& u_x^\prime(P_0) cos\alpha + u_y^\prime(P_0) cos\beta \end{aligned} ∂lll∂u∣P0===∗=t→0+limtu(P)−u(P0)t→0+lim(Δx)2+(Δy)2u(x0+Δx,y0+Δy)−u(x0,y0)t→0+lim(Δx)2+(Δy)2ux′(P0)Δx+uy′(P0)Δy+o(t)ux′(P0)cosα+uy′(P0)cosβ
其中 c o s α cos\alpha cosα、 c o s β cos\beta cosβ 为方向 l \pmb l lll 的方向余弦, = ∗ \overset{*}{=} =∗ 部分利用的多元函数的泰勒展开式
从上式可以看出,方向导数其实就是 梯度 g r a d u = ( u x ′ , u y ′ ) \pmb{grad} \, u = (u_x^\prime,u_y^\prime) gradgradgradu=(ux′,uy′) 与 方向余弦组成的单位向量 I = ( c o s α , c o s β ) I=(cos\alpha,cos\beta) I=(cosα,cosβ) 的内积,即
∂ u ∂ l = g r a d u ⋅ I = ∣ g r a d u ∣ ∣ I ∣ c o s θ = ∣ g r a d u ∣ c o s θ \frac{\partial u}{\partial \pmb l}=\pmb{grad} \, u \cdot I=|\pmb{grad} \, u| \, |I| \, cos\theta =|\pmb{grad} \, u| \, cos\theta ∂lll∂u=gradgradgradu⋅I=∣gradgradgradu∣∣I∣cosθ=∣gradgradgradu∣cosθ
其中 θ \theta θ 为两向量之间的夹角。
那么如果此时想让 ∂ u ∂ l \frac{\partial u}{\partial \pmb l} ∂lll∂u 最大,自然是 θ \theta θ 为 0 o 0^o 0o 的时候,也就是方向与梯度一致的时候的方向导数最大,即变化率最大且是正的,那么如果沿着该方向前进,可以上升(增加)的最快;同理,如果 θ \theta θ 为 18 0 o 180^o 180o 的时候,也就是方向与梯度相反的时候的方向导数最小且为负(两者的绝对值相同),那么沿着该方向前进就可以下降(减小)的最快。
【注】以上证明角度最直观好想,对于梯度下降算法的证明还有其他角度,例如在一定条件下直接利用泰勒展开近似目标函数,然后直接求解,得到的就是梯度下降迭代式,在下文中的近端梯度下降其实就利用到了这个思想。
参考文章(
近端梯度下降法,
《机器学习》(周志华)P253,
《机器学习公式详解》(俗称南瓜书)P122(pdf页数),
矩阵微分与向量函数泰勒展开)
近端梯度下降法是众多梯度下降 (gradient descent) 方法中的一种,将proximal翻译成“近端”可能是想表达 “接近,近似” 的意思,但在这点上,经典梯度算法其实也是近似的。与经典的梯度下降法相比,近端梯度下降法 主要是想解决目标函数中存在不可微或不方便微分的部分。如对于凸优化问题,当其目标函数存在 L1正则化项,近端梯度下降法就会派上用场。
(个人理解:其实就是在经典梯度下降的基础上,最后用分段函数的方法对L1正则化项求导)
具体来说,设优化目标
m i n f ( x ) + λ ∣ ∣ x ∣ ∣ 1 min \, f(\pmb x)+\lambda ||\pmb x||_1 minf(xxx)+λ∣∣xxx∣∣1
令 ∇ \nabla ∇ 表示微分算子, ∇ f ( x ) \nabla f(\pmb x) ∇f(xxx) 即为 ∂ f ( x ) ∂ x \frac{\partial f(x)}{\partial x} ∂x∂f(x)( x x x 为向量,粗的打不出来了)
(1)在一定条件下,通过泰勒展开式将目标函数可微的部分化简
(2)以分段函数的形式,对 ∣ ∣ x ∣ ∣ 1 ||\pmb x||_1 ∣∣xxx∣∣1 求导,令导数得零,最后自然得到了分段函数形式的迭代公式 (加粗的是向量)。
L-Lipschitz 条件简介:它是一个比“连续”更强的光滑性条件.直觉上,利普希茨连续函数限制了函数改变的速度,符合利普希茨条件的函数的斜率,必小于一个称为利普希茨常数的实数 L(该常数依函数而定,在梯度下降中,可以将此参数理解为学习率 η \eta η 的倒数,在训练中应该自己给出)(以上仅为个人理解,详见百度百科)
若 f ( x ) f(\pmb x) f(xxx) 可导,且 ∇ f ( x ) \nabla f(\pmb x) ∇f(xxx)满足 L-Lipschitz 条件,即存在常数 L > 0 L>0 L>0 使得
∣ ∇ f ( x ′ ) − ∇ f ( x ) ∣ ∣ x ′ − x ∣ ≤ L \frac{|\nabla f(\pmb x^\prime)-\nabla f(\pmb x)|}{|\pmb x^\prime-\pmb x|} \leq L ∣xxx′−xxx∣∣∇f(xxx′)−∇f(xxx)∣≤L
(上式可看成是 f ( x ) f(\pmb x) f(xxx) 的二阶导数恒不大于 L L L ,即 ∇ 2 f ( x ) ≤ L \nabla^2 f(\pmb x) \leq L ∇2f(xxx)≤L )
则在 x k x_k xk 附近可将 f ( x ) f(x) f(x) 通过二阶泰勒展开式近似为
f ( x ) ^ ≈ f ( x k ) + ( ∇ f ( x k ) , ( x − x k ) ) + ∇ 2 f ( x k ) 2 ∣ ∣ x − x k ∣ ∣ 2 2 ≤ f ( x k ) + ( ∇ f ( x k ) , ( x − x k ) ) + L 2 ∣ ∣ x − x k ∣ ∣ 2 2 = L 2 ∣ ∣ x − ( x k − 1 L ∇ f ( x k ) ) ∣ ∣ 2 2 + c o n s t \begin{aligned} \hat{f(\pmb x)} \approx& f(\pmb x_k)+(\nabla f(\pmb x_k),(\pmb x-\pmb x_k))+\frac{\nabla^2f(x_k)}{2}||\pmb x - \pmb x_k||_2^2\\ \leq& f(\pmb x_k) + (\nabla f(\pmb x_k),(\pmb x-\pmb x_k))+\frac{L}{2}||\pmb x - \pmb x_k||_2^2\\ =& \frac{L}{2}||\pmb x- (\pmb x_k-\frac{1}{L} \nabla f(\pmb x_k))||_2^2 + const \end{aligned} f(xxx)^≈≤=f(xxxk)+(∇f(xxxk),(xxx−xxxk))+2∇2f(xk)∣∣xxx−xxxk∣∣22f(xxxk)+(∇f(xxxk),(xxx−xxxk))+2L∣∣xxx−xxxk∣∣222L∣∣xxx−(xxxk−L1∇f(xxxk))∣∣22+const
其中 c o n s t const const 是与 x \pmb x xxx 无关的常数(后续的求解过程也是求导,就直接忽略了,上述具体推导过程参见《机器学习公式详解》(俗称南瓜书)第11章)
也就是说在 x k \pmb x_k xxxk 附近可将目标函数进行简化,那么问题可转化为在这个小范围内寻找可使目标函数最小的 x k + 1 \pmb x_{k+1} xxxk+1,然后再进行迭代(对于经典梯度下降算法来说,其实也是这么个思想),即
x k + 1 = a r g m i n x L 2 ∣ ∣ x − ( x k − 1 L ∇ f ( x k ) ) ∣ ∣ 2 2 + λ ∣ ∣ x ∣ ∣ 1 \pmb x_{k+1}=\underset{x}{arg\,min}\frac{L}{2}||\pmb x- (\pmb x_k-\frac{1}{L} \nabla f(\pmb x_k))||_2^2 + \lambda||\pmb x||_1 xxxk+1=xargmin2L∣∣xxx−(xxxk−L1∇f(xxxk))∣∣22+λ∣∣xxx∣∣1
令 x i x^i xi 表示 x \pmb x xxx 的第 i i i 个分量,将上式按分量展开可看出,其中不存在 x i x j ( i ≠ j ) x^ix^j(i \neq j) xixj(i=j) 这样的项,也就是说各分量直间互不影响,假设只有一个分量 x 1 x^1 x1 带入展开,求导,令导得零,便可以求得这个小范围内的最小值点,推广可得上式解为:
可先计算 z = x k − 1 L ∇ f ( x k ) \pmb z=\pmb x_k-\frac{1}{L} \nabla f(\pmb x_k) zzz=xxxk−L1∇f(xxxk)
x k + 1 i = { z i − λ / L , λ / L < z i 0 , ∣ z i ∣ ≤ λ / L z i + λ / L , z i < − λ / L x_{k+1}^i= \left\{ \begin{aligned} z^i& -\lambda/L, &\lambda /L& < z^i\\ 0&, &|z^i|& \leq \lambda/L\\ z^i& +\lambda/L, &z^i&<-\lambda/L \end{aligned} \right. xk+1i=⎩⎪⎨⎪⎧zi0zi−λ/L,,+λ/L,λ/L∣zi∣zi<zi≤λ/L<−λ/L
(如果不考虑 L1 正则化项,该步骤得解就是 经典梯度下降 的迭代公式)
import numpy as np
# 导入数据
filepath = "D:\\机器学习\\housing.csv"
data = np.loadtxt(filepath, delimiter=',', skiprows=1, encoding='utf-8')
# 选择特征与标签
x = data[0:20, 1:]
y = data[0:20, 0]
# 加一列
X = np.column_stack((x, np.ones((x.shape[0], 1))))
# 划分训练集与测试集
X_train, y_train = x[:10], y[:10]
X_test, y_test = x[10:], y[10:]
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
# 定义初始化参数
def initialize(dims):
w = np.zeros((dims))
return w
# 在 MSE 基础上定义 Lasso 损失函数值
def l1_loss(X, y, w, lambda_):
num_train = X.shape[0]
y_hat = np.dot(X, w)
loss = np.sum((y_hat - y) ** 2) / num_train + np.sum(lambda_ * abs(w))
return y_hat, loss, num_train
# 定义训练过程
def lasso_train(X, y, learn_rate=50, lambda_=50, epochs=300):
loss_list = []
w = initialize(X.shape[1])
temporary_para = lambda_ * learn_rate
for i in range(epochs):
y_hat, loss, num_train = l1_loss(X, y, w, lambda_)
z = w - learn_rate * (np.dot(X.T, (y_hat - y)) * 2 / num_train)
a = np.dot(X.T, (y_hat - y))
b = X.T
c = (y_hat - y)
y_hat[0]
w_list = []
for z_i in z:
if z_i > temporary_para:
w_i = z_i - temporary_para
elif z_i < temporary_para:
w_i = z_i + temporary_para
else:
w_i = 0
w_list.append(w_i)
w = np.array(w_list)
loss_list.append(loss)
if i % 50 == 0:
print('epoch %d loss %f' % (i, loss))
params = w
return loss, loss_list, params
loss, loss_list, params = lasso_train(X,
y,
learn_rate=50,
lambda_=50,
epochs=300)