目录
梯度下降原理
- 在单变量的函数中,梯度其实就是函数的微分,代表着函数在某个给定点的切线的斜率
- 在多变量函数中,梯度是一个向量,向量有方向,梯度的方向就指出了函数在给定点的上升最快的方向
代价函数,初始化,想要改变,使得代价函数达到一个全局最小值(局部最小值),其实是一个迭代的过程。
下图,给定一个初始值,不断地迭代求梯度,找到当前梯度变化最大的方向走一步,不断地迭代求梯度直到找到一个代价函数值最小的地方,确定了。
问题:如果初始值不一样,那么结果就会不一样,如果取的初始值是旁边的那个点呢?最终求得的是最小值没错,但他只是局部的最小值,而不是全局的最小值,这就是梯度下降法本身的一个弊端。
梯度下降法公式:
j可以取0或1,α是学习率,也就是步长意味着我们可以通过α来控制每一步走的距离,公式的意思就是:为初始值,减去学习率(自己定义的)乘以对代价函数求微分,然后将结果赋值给,相当于完成了一次参数的更新。对于梯度下降来说应该采用同步更新,完成一次更新就相当于上图往下走了一步。,不同步的做法不是我们说的梯度下降的算法,是另一种算法,更自然的算法是同步更新的,不同步更新也可能达到最小值但不推荐,我们说的梯度下降就是用的同步更新。
梯度下降法理解:
横坐标为,纵坐标为代价函数值,当取左边第一个点的时候,求他的梯度,显然是个负值,根据公式,减去一个负值就相当于变大了,就到了第二个点的位置,在第二个点的时候梯度(斜率)仍然为负数,仍会变大,就到了第三个点的未位置,这时候第三个点的梯度变成了正值说明计算过后会变小,越接近最小值,斜率(梯度)越小,的变化越来越小,如此往复,是逐渐逼近代价函数的全局最小值。
学习率的选取
学习率意义上面说了是每次迭代更新后走的步长,不能过大也不能过小,过大会导致错过最小点,出现在一个范围内震荡,永远得不到最小值,如果学习率太大会导致无法收敛甚至发散,过小可能太阳下山了,还没找到最小点,所以可以多次尝试调节,找到适合的学习率(需要一定的经验)。
梯度下降算法的过程
左边为梯度下降优化公式,右边为线性回归模型和代价函数。
计算过程就是对代价函数对和求导,即:
了解:
线性回归的代价函数是凸函数:
对于凸函数,无论初始值选取如何,最后都会到达全局最小值。
在二维可以用等高图表示,横纵轴分别是高度为代价函数J,越靠里越逼近真实值:
非凸函数和凸函数
左边为非凸函数,右边为凸函数,对于非凸函数梯度下降法可能会得到局部最小值,而对于凸函数,最后一定会得到以较为接近全局最小值的结果。
补充
对于训练的结果过拟合就类似于左边的图,造成过拟合的原因有很多:
1. 训练集的数量级和模型的复杂度不匹配。训练集的数量级要小于模型的复杂度;
2. 训练集和测试集特征分布不一致;
3. 样本里的噪音数据干扰过大,大到模型过分记住了噪音特征,反而忽略了真实的输入输出间的关 系;
4. 权值学习迭代次数足够多(Overtraining),拟合了训练数据中的噪声和训练样例中没有代表性的特征。
对于合适的训练结果最后应该是一个平滑的曲线,如右图,过拟合会导致最后模型得到的是局部最小值,而不是全局最小值,得到的结果不是预期的结果或有较大偏差。
相对于过拟合就会有欠拟合,例子:
比如要描述一个西瓜的模型,西瓜的属性有:形状、瓜皮,瓜瓤,瓜籽等
过拟合:就会描述的太多了,掺杂了一些没有特征的东西,比如,绿色的瓜皮,瓜皮有条纹,黑色的瓜籽,瓜瓤的纹理,圆的形状等等,拟合了过多的没有代表性的特征,导致最后结果不准确,比如冬瓜的瓜籽也是黑色的,也有瓜瓤,也是圆形的,就会导致最后结果的错误。
欠拟合:就是拟合了过少的特征,比如只拟合了,有瓜皮瓜瓤瓜籽,最后也判断不出正确的结果。
代码实现:
import os
import numpy as np
import matplotlib.pyplot as plt
current_path = os.path.dirname(__file__)
print(current_path)
data = np.genfromtxt(current_path+"/data.csv",delimiter=",")
x_data = data[:,0]
y_data = data[:,1]
plt.scatter(x_data, y_data)
plt.show()
lr=0.0001
#数据
b=0
k=0
#最大迭代次数
epochs=100
#最小二乘法
def compute_error(b,k,x_data,y_data):
total_error=0
for i in range(0,len(x_data)):
total_error+=(y_data[i]-(k*x_data[i]+b))**2
return total_error/float(len(x_data))/2.0
def gradient_descent_runner(x_data,y_data,b,k,lr,epochs):
#计算总数据
m=float(len(x_data))
#循环epochs次
for i in range(epochs):
b_grad=0
k_grad=0
#计算梯度(求导)的总和再求平均值
for j in range(0,len(x_data)):
b_grad+=(1/m)*((k*x_data[j]+b)-y_data[j])
k_grad+=(1/m)*x_data[j]*((k*x_data[j]+b)-y_data[j])
#更新b和k
b=b-lr*b_grad
k=k-lr*k_grad
#每迭代五次输出一次图像
# if i%5==0:
# print("epochs:",i)
# plt.plot(x_data,y_data,"b.")
# plt.plot(x_data,k*x_data+b,"r")
# plt.show()
return b,k;
print("Starting b={0} k={1} error={2}".format(b,k,compute_error(b,k,x_data,y_data)))
print("Running...")
b,k=gradient_descent_runner(x_data,y_data,b,k,lr,epochs)
print("After {0} iterations b={1} k={2} error={3}".format(epochs,b,k,compute_error(b,k,x_data,y_data)))
plt.plot(x_data,y_data,"b.")
plt.plot(x_data,k*x_data+b,"r")
plt.show()
运行结果:
Starting b=0 k=0 error=1574.2411447744369
Running...
After 100 iterations b=0.02597367274813811 k=0.9753969522590787 error=48.95893439033855
第5次迭代:
第10次迭代:
第15次迭代:
............
第100次迭代:
经过100次迭代错误率(代价函数损失值)在减少。
代码二(sklearn):
#encoding=utf-8
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
data = np.genfromtxt("test.csv",delimiter=",")
x_data = data[:,0]
y_data = data[:,1]
plt.scatter(x_data, y_data)
plt.show()
print(x_data.shape)
x_data=data[:,0,np.newaxis]
y_data=data[:,1,np.newaxis]
#创建并拟合模型
model=LinearRegression()
model.fit(x_data,y_data)
plt.plot(x_data,y_data,"b.")
plt.plot(x_data,model.predict(x_data),"r")
plt.show()
结果: