Gadzan

线性回归中应用梯度下降法详解

损失函数的作用

定义以下三个点(-1,-3),(0,0),(1,1), 要求回归成一个线性方程,怎么做呢?

-3-2-101234-3-2-10123

先随机生成一个F(x)₀, 假设是y=3x+1

-3-2-101234-3-2-10123

要怎么知道F(x)₀有多接近三个目标点呢?

很简单,把三个目标点的x值代入到函数i就能得到F(x)₀的y值,如下蓝点(-1,-2),(0,1),(1,4).

红点为目标点.

-3-2-101234-3-2-10123

看看函数i的输出跟三个目标点到底差多远

目标点X值目标点Y值函数i:y=3x+1距离(y-F(x))²
-1-3-21
0011
1149

总距离: 1+1+9=11

F(x)₀跟目标点一共差了11的距离

总距离跟某目标点距离的公式:

总距离 = (每个目标点的Y值 - F(x)₀对应每个目标点X值的输出值)²

我们暂时只看某个目标点,
某目标点距离 = (某目标点的Y值 - F(x)₀对应某目标点X值的输出值)²

这就是某目标点的损失函数L

我们先以某目标点的损失函数L为例.

距离目标点越近(数值越小),损失越少,如果有一个函数可以完全符合三个目标点,那这样损失就是0.

我们将F(x)₀: y=(3x + 1)的带入损失函数L

损失函数L = (y - (3x + 1))² = (y - 3x -1)²

其中y为目标点的y值
x为目标点的x值
3是猜的
1也是猜的

要让L最小,三个目标点的x和y固定的情况下,需要改变3和1,使之成为变量可得:

其中y为目标点的y值
x为目标点的x值
w是用3替换的
b是用1替换的

L = (y - wx - b)²就是我们要处理的损失函数

把目标点(-1,-3)代入损失函数L

得到损失函数L (x = -1, y = -3) = (-3 + w - b)²

要让L=0的话w-b=3

这样就找到了点(-1,-3)的目标函数关系了.

结论A: 损失函数L可以帮助我们找到接近目标的新函数.


理解梯度下降方向

不过就算这么说,w跟b有无限种组合,而且w-b接近3只是满足(-3,-1)的这点,也不一定能满足x=0,y=0或是x=1,y=1的组合。

那该怎么做呢? 函数已经有了,我们该怎么找到建议值呢?

伟大的数学告诉我们,要找到数值是怎么移动的,我们可以做微分!

微分的意义在于,微分某变量后产生的函数,可以指出原函数在每点的变化。

以函数y = x³ + x² + x为例

-3-2-101234-3-2-10123

y = x³ + x² + x对x作微分,会得到函数y’

y' = 3x + 2x + 1

得到的y'就是该点斜率也就是==梯度==.

代入几个点来看看数值是怎么移动的

当x = 1, y=3, y'= 6
当x = 0, y=0, y'= 1
当x = -1, y=-1, y'= -1
当x = -2, y=-6, y'= -6

也就是说当x为正数时,y会越来越大,x为负数时,y会越来越小.

x=0的时候,y'=1,是一个正数,代表接下来的y是会往正数移动,的确x从0增长到1的时候,y也从0增长到3,y是有越来越大的倾向。

x=-1的时候,y'=-1,是一个负数,代表接下来的y是会往负数移动,的确x从-1增长到-2的时候,y也从-1增长到-6,y是有越来越小的倾向。

结论B: 数值移动的方向是可以从梯度判定的.


损失函数与梯度的关系

我们回到最开始的函数.

继续处理损失函数L(x=-1,y=-3) = (-3-(-1)w-b) = (-3+w-b)²

最终的目的是希望(-3+w-b)²越来越小,但是L有两个变量,w跟b.

所以需要分别对w跟b做微分,来得知w跟b怎么移动才会让(-3+w-b)²越来越小.

展开(-3+w-b)² = 9-6w+6b-2wb+w²+b²

  1. 对w取微分后得到函数L'w=-6-2b+2w

L'w可以告诉我们变量w是怎么移动L函数的

我们取个起始点(3,1), 将(w=3, b=1)带入L'w = -6-2b+2w = -6-2+6 = -2

这表示w在(w=3, b=1)时,梯度为-2,在此点,w越往负数方向移动,损失函数会越小.

  1. 对b取微分后得到函数L'b:6-2w+2b

L'b可以告诉我们变量b是怎么移动L函数的

(w=3,b=1)带入L'b = 6-2w+2b = 6-6+2 = 2,这表示b在(w=3,b=1)时,梯度为2,在此点,b越往正数方向移动,损失函数会越大.

当知道方向后,我们就知道该如何调整数值.

w在3之后,梯度是负数↘,所以越减少越会让损失函数L减少,但我们不能无穷尽的减下去,刚好就好,所以对w应该要增加,与L’w产生的梯度相反.

b在1之后,梯度是正数↗,所以越增加越会让损失函数L变大,所以对b要减少数值,与L’b产生的梯度相反.

结论C: 损失函数L移动的方向刚好跟梯度的方向相反.


可是,该把w变多大,该把b变多小呢?

不知道,但是我们可以随意设置一个初始值,在梯度下降的方向行进一段距离,不断迭代更新直至w和b合适,那么问题又来了,到底迭代一次计算要行进多长的一段距离,这时我们需要一个参数来搭配,这个数值叫学习率,它的意义在于动态调整移动的步伐,我们用α来代替,先把α设定成0.5.

接着,开始尝试找下一个函数,这边取名F(x)₁

因为F(x)₁F(x)₀会有关系,所以我们会用F(x)₀的w以及b与其在(w=3,b=1)梯度来做计算,更新的方式如下:

F(x)₁的w = F(x)₀的w - 学习率α * F(x)₀的w=3在L(x = -1, y = -3)的梯度

F(x)₁的b = F(x)₀的b - 学习率α * F(x)₀的b=1在L(x = -1, y = -3)的梯度

注意: 这边的减去,是来自于结论C (损失函数L移动的方向刚好跟梯度的方向相反)

F(x)₁的w = 3 - (0.5) *(-2) = 4

F(x)₁的b= 1 - (0.5) *(2) = 0

所以
F(x)₁ : y = 4x + 0 = 4x

我们实际把F(x)₁代入每一点看看与目标点的距离

目标点X值目标点Y值函数j:y=4x距离(y-J(x))²
-1-3-41
0000
1149

总距离: 1+0+9=10

可以看到F(x)₁F(x)₀更靠近三个目标点了

这样重复迭代直到距离接近0, 就能得到最接近目标点的函数.

但这只是其中一个点的情况,我们如果考虑到所有点的总距离,就能最终迭代出目标函数。

我们回头看一下总距离的公式

代入可得

这就是目标函数的损失函数L,即代价函数.

让我们深入一点,下面尝试分解一下目标函数的表达公式:

对于目标函数f*的输入,其实就是不同w,b的函数 f₁,f₂,f₃...,

那么我们可以总结为:

代价函数也就是:

它的w和b微分方程为:

其中的?号是因为有些人会直接在代价函数除以2n,为的是w和b微分后方便数学计算,其实加不加影响不大,因为这跟步长的效果一样.

总结梯度下降法回归线性函数步骤

现在我们来总结一下梯度下降法的步骤:

  1. 已知有三点(x₀,y₀),(x₁,y₁),(x₂,y₂);
  2. 首先,根据猜想设定一个线性模型: y=wx+b,目标是找到最合适的w和b;
  3. 根据总距离公式 ∑(yᵢ-y'ᵢ)² 可得 ∑(yᵢ - (w'xᵢ+b'))²;
  4. 随机选择一个起始(w₀,b₀);
  5. 可得总距离: (y₀-(w₀x₀+b₀))² + (y₁-(w₀x₁+b₀))² + (y₂-(w₀x₂+b₀))²;
  6. 寻找(w₁,b₁)
  7. 展开(y-(wx+b))²,可得 y²-2(wx+b)y+(wx+b)²,即y²-2xyw+2yb+x²w²+2xwb+b²,w微分后得-2xy+2x²w+2xb, b微分后得2y+2xw+2b;
  8. 设置步长α;
  9. 获得(w₁,b₁), w₁ = w₀-α*(-2xᵢyᵢ+2xᵢ²w+2xᵢb) , b₁ = b₀-α*(2yᵢ+2xᵢw+2b)
  10. 计算新的总距离(y₀-(w₁x₀+b₁))² + (y₁-(w₁x₁+b₁))² + (y₂-(w₁x₂+b₁))²
  11. 直到总距离小得令人满意。

JavaScript 实现

// (-1,-3),(0,0),(1,1)
const x_i = [-1,0,1];
const y_i = [-3,0,1];

// assume model is y = w*x + b
// let variable begins with w=1, b=1
let w = 3, b = 1, alpha = 0.001, iter = 500;

// Loss = (y - y')^2
// y^2 - 2*y*y' + y'^2
// y^2 - 2*y*(wx + b) + (wx + b)^2

// df/dw = -2*x*y + 2*x^2*w + 2*x*b
// df/dw = 2*x*(x*w + b - y)

// df/db = -2*y + 2*w*x + 2*b
// df/db = 2*(w*x + b - y)

let w_next, b_next, step_result={}, loss=0, dw=0, db=0, loss_history=[];

for(let j=0; j<iter; j++){
    dw = 0;
    db = 0;
    for(let i=0; i<x_i.length; i++){
        dw += 2 * x_i[i] * (w*x_i[i] + b - y_i[i]);
        db += 2 * (w*x_i[i] + b - y_i[i]); 
    }

    w_next = w - (alpha * dw);
    b_next = b - (alpha * db);

    for(let i=0; i<x_i.length; i++){
        loss += Math.pow((y_i[i] - (w_next*x_i[i] + b_next)), 2);
    }
    step_result = {
        w_next: w_next,
        b_next: b_next,
        loss: loss,
    }
    console.log(
        step_result
    )
    loss_history.push(step_result);
    loss = 0;
    w = w_next;
    b = b_next;
}
let loss_array = loss_history.map(i=>i.loss);
console.log('Minimum Loss:')
console.log(loss_history[loss_array.indexOf(Math.min(...loss_array))] );

参考: 深度學習講中文 簡單解釋梯度下降法 (Gradient Descent)


打赏码

知识共享许可协议 本作品采用知识共享署名 4.0 国际许可协议进行许可。

评论