一个例子理解梯度下降法


最近听课时,AI导论老师留下了一个简单的线性回归例子。这个例子属于单变量线性回归,可以用梯度下降法拟合也可以用最小二乘法拟合,非常适合初学者来验证梯度下降法实际的效果。所以下面,我将用两种方法同时拟合这个小例子,便于让读者看清楚二者的差别。
由于单变量且数据量小,本文不会比较二者时间上的差别 (不是因为作者懒的比较

例子描述

An athlete’s performance and the number of his trainings are as follows:
运动员训练量和表现的关系
1)Plot the data
2)Write a program to fit the linear regression parameters ? to the data using gradient descent
3)Predict his performance in the 47th and the 55th training

其实这个例子似乎是被阉割的,不知道在哪里看到过这个例子的多变量版本

梯度下降法简介

好了,问题来了,我们一直说梯度下降法梯度下降法,那么什么是梯度下降法呢,为啥我们不能就用最小二乘法呢,勒让德和高斯的方法难道不对吗?答案当然是否定的,这两种方法各有优劣。只是因为当今计算机计算速度发展,应生了梯度下降法,这种新思路使得人们能从更多方面来看待问题解决问题。形象来说,这就好比最小二乘法是基于大量的思考的方法,而另者则是基于大量的尝试的方法。

我们用经典的数学书上 爬山/下山 例子来说明。
想象我们在下山,从我所在的位置出发可以从很多方向下山,而最陡的那个方向就是梯度方向。梯度方向是函数变化最快的方向,沿着梯度方向可以最快地找到函数的最大值,而我们要求误差的最小值,所以在梯度下降中我们要沿着梯度相反的方向。具体来说就是,以你当前的所处的位置为基准,寻找这个位置最陡峭的地方,然后朝着山的高度下降的地方走,同理,如果我们的目标是上山,也就是爬到山顶,那么此时应该是朝着最陡峭的方向往上走。然后每走一段距离,都反复采用同一个方法,最后就能成功的抵达山谷。

只有一个下坡时(传统凸优化)
在这里插入图片描述
凹凸不平时(非凸函数)
在这里插入图片描述

在这里插入图片描述
ok,现在我们已经知道不断沿着梯度可以找到极值点了(对于凸函数是最值点)!
下一步,我们来看看我们下山找最值点有什么实际意义呢

类似于最小二乘法,我们定义一个想要优化的函数(称为损失函数),当这个函数取得极值时,我们得到最佳的拟合参数。
这里就以本题为例,x可以视为单变量。但事实上,多变量矩阵形式对本式同样适用。
在这里插入图片描述
因为是对于梯度来说,我们需要对刚刚定义的损失函数进行求导,并且定义一个系数a,用a来控制每次更新theta的速度(下面其实是两步合并,第一步我们求出导并用a倍乘,第二步我们用刚刚的结果来更新手头视为theta,也就是下山一步后再环顾四周再考虑新的路线)
在这里插入图片描述
好,以上就是梯度下降法的简介与数学表达。
下面为了联系理论与实际,我们来一起看看这种思路是如何计算拟合开头给出的例子!

鲁迅说过,写博客不给实现代码,都是耍流氓
[雾 ٩( ‘ω’ )و

基于python3 matplotlib的实现代码

梯度下降法实现

import numpy as np
import matplotlib.pyplot as plt

N = 8 
x = np.array([30,33,35,37,39,44,46,50]).reshape(N,1)       
y = np.array([30,34,37,39,42,46,48,51]).reshape(N,1) 
plt.scatter(x,y,color="orange",linewidth=0.5)
ones = np.ones((N, 1))
x = np.hstack((x, ones))

def CostFunction(theta, x, y):
    diff = np.dot(x, theta) - y
    loss = (1./2*N)*(np.dot(np.transpose(diff), diff))
    return loss

def CostGradient(theta, x, y):
    diff = np.dot(x, theta) - y
    gradient = (1./N)*(np.dot(np.transpose(x), diff))
    return gradient

def Iteration():
    alpha = 0.001
    theta = np.array([1, -0.1]).reshape(2, 1)         
    gradient = CostGradient(theta, x, y)
    epsilon = 0.001
    while np.linalg.norm(gradient) > epsilon:
        theta = theta - alpha * gradient
        gradient = CostGradient(theta, x, y)
    return theta

Right_theta = Iteration()
print('下降后θ值为:')
print('θ(0)=',Right_theta[1][0],'θ(1)=',Right_theta[0][0])
print('拟合函数为:')
print('y=',Right_theta[1][0],'+',Right_theta[0][0],'x')
print('预测第47次训练后,运动员表现:',Right_theta[1][0]+Right_theta[0][0]*47)
print('预测第55次训练后,运动员表现:',Right_theta[1][0]+Right_theta[0][0]*55)

x1 = np.linspace(25, 55, 100)
y1 = Right_theta[0]*x1 + Right_theta[1]
plt.plot(x1, y1,color="black",linewidth=0.5)
plt.rcParams['figure.figsize'] = (8.0, 8.0)
plt.show()

这里关于python的基础知识就不再赘叙了。matplotlib画图包和numpy的矩阵操作函数网上都有很详细的教程,唯一需要大家注意的就是矩阵运算时的复杂性,稍不注意就会出现逻辑错误(不要问我怎么知道
如果有细节问题可以QQ

最小二乘法实现

import numpy as np   
import scipy as sp   
import matplotlib.pyplot as plt  
from scipy.optimize import leastsq  

x = np.array([30,33,35,37,39,44,46,50])
y = np.array([30,34,37,39,42,46,48,51])
plt.scatter(x,y,color="orange",linewidth=0.5)

def func(p,x):
    k,b=p
    return k*x+b

def error(p,x,y):
    return func(p,x)-y

p0=[1, -0.1]
Para=leastsq(error,p0,args=(x,y))
k,b=Para[0]

print('最小二乘法求解系数:')
print('k=',k,'b=',b)
print('拟合函数为:')
print('y=',b,'+',k,'x')
print('预测第47次训练后,运动员表现:',b+k*47)
print('预测第55次训练后,运动员表现:',b+k*55)

x1 = np.linspace(25, 55, 100)
y1 = k*x1+b 
plt.plot(x1, y1,color="black",linewidth=0.5)
plt.rcParams['figure.figsize'] = (8.0, 8.0)
plt.show()

比较结果&总结

贴图,下面为梯度下降法
在这里插入图片描述
最小二乘法
在这里插入图片描述
可以看出,二者差别甚微,梯度下降法的拟合思路是正确的。
具体来说,二者的差别如下:
在这里插入图片描述
两种模型各有利弊,至于选择哪种模型,需要基于数据量与需求综合考量,二者本质上并无优劣之分。
CSDN首篇,AI本科在读,欢迎大佬来指出错误。