损失函数的作用
定义以下三个点(-1,-3)
,(0,0)
,(1,1)
, 要求回归成一个线性方程,怎么做呢?
先随机生成一个F(x)₀
, 假设是y=3x+1
要怎么知道F(x)₀
有多接近三个目标点呢?
很简单,把三个目标点的x值代入到函数i就能得到F(x)₀
的y值,如下蓝点(-1,-2)
,(0,1)
,(1,4)
.
红点为目标点.
看看函数i的输出跟三个目标点到底差多远
目标点X值 | 目标点Y值 | 函数i:y=3x+1 | 距离(y-F(x))² |
---|---|---|---|
-1 | -3 | -2 | 1 |
0 | 0 | 1 | 1 |
1 | 1 | 4 | 9 |
总距离: 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
为例
把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²
- 对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越往负数方向移动,损失函数会越小.
- 对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 | -4 | 1 |
0 | 0 | 0 | 0 |
1 | 1 | 4 | 9 |
总距离: 1+0+9=10
可以看到F(x)₁
比F(x)₀
更靠近三个目标点了
这样重复迭代直到距离接近0, 就能得到最接近目标点的函数.
但这只是其中一个点的情况,我们如果考虑到所有点的总距离,就能最终迭代出目标函数。
我们回头看一下总距离的公式
代入可得
这就是目标函数的损失函数L,即代价函数.
让我们深入一点,下面尝试分解一下目标函数的表达公式:
对于目标函数f*
的输入,其实就是不同w,b的函数 f₁,f₂,f₃...,
那么我们可以总结为:
代价函数也就是:
它的w和b微分方程为:
其中的?号是因为有些人会直接在代价函数除以2n,为的是w和b微分后方便数学计算,其实加不加影响不大,因为这跟步长的效果一样.
总结梯度下降法回归线性函数步骤
现在我们来总结一下梯度下降法的步骤:
- 已知有三点
(x₀,y₀)
,(x₁,y₁)
,(x₂,y₂)
; - 首先,根据猜想设定一个线性模型:
y=wx+b
,目标是找到最合适的w和b; - 根据总距离公式
∑(yᵢ-y'ᵢ)²
可得∑(yᵢ - (w'xᵢ+b'))²
; - 随机选择一个起始
(w₀,b₀)
; - 可得总距离:
(y₀-(w₀x₀+b₀))² + (y₁-(w₀x₁+b₀))² + (y₂-(w₀x₂+b₀))²
; - 寻找
(w₁,b₁)
- 展开
(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
; - 设置步长α;
- 获得
(w₁,b₁)
,w₁ = w₀-α*(-2xᵢyᵢ+2xᵢ²w+2xᵢb)
,b₁ = b₀-α*(2yᵢ+2xᵢw+2b)
- 计算新的总距离
(y₀-(w₁x₀+b₁))² + (y₁-(w₁x₁+b₁))² + (y₂-(w₁x₂+b₁))²
- 直到总距离小得令人满意。
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))] );