上一篇文章中,线性回归关键问题之一:求解系数的方法梯度下降。梯度下降在数据挖掘很多算法中都有应用, 属于比较基本的数学基础方法, 本文对此算法进行基本的介绍。
梯度下降在机器学习中的意义:
机器学习算法会利用某个统计量来刻画目标函数的拟合情况。虽然不同的算法拥有不同的目标函数表示方法和不同的系数值,但是它们拥有一个共同的目标——即通过最优化目标函数来获取最佳参数值。而梯度下降法就是优化目标函数而求得最佳参数值的方法。
梯度下降数学原理
理解梯度概念之前, 先预习下几个基本概念和物理意义:
导数
导数定义:设函数y=f(x)在点x0的某个邻域内有定义,当自变量x在x0处有增量Δx,(x0+Δx)也在该邻域内时,相应地函数取得增量Δy=f(x0+Δx)-f(x0);如果Δy与Δx之比当Δx→0时极限存在,则称函数y=f(x)在点x0处可导,并称这个极限为函数y=f(x)在点x0处的导数记为f'(x0),也记作y'│x=x0或dy/dx│x=x0,即:
导数公式导数的物理意义:表示函数值在这一点的变化率。
偏导数
偏导数定义(以二元函数X方向偏导数为例):
设有二元函数z=f(x,y),点(x0,y0)是其定义域D内一点.把y固定在y0而让x在x0有增量△x,相应地偏导数函数z=f(x,y)有增量(称为对x的偏增量)△z=f(x0+△x,y0)-f(x0,y0)。如果△z与△x之比当△x→0时的极限存在,那么此极限值称为函数z=f(x,y)在(x0,y0)处对x的偏导数(partial derivative)。记作f'x(x0,y0)。
在X方向的偏导数
如上图所示:偏导数表示固定面上一点M0(x0, y0, z0)对x轴的切线斜率;
偏导数的物理意义:函数沿坐标轴正方向的变化率。
方向导数
多元函数的偏导数反映了函数值沿坐标轴方向的变化率,而方向导数则是反应的函数值沿各个方向的变化率。方向导数的定义:
方向导数定义方向导数的求解公式:
方向导数的求解公式说明:其中a1, a2, ..., an指的是向量L与各个坐标轴的夹角。
在了解以上几个概念之后, 正式进入本文的正题:梯度。
梯度
数学对梯度的定义如下:
Paste_Image.png
通过上面的公式, 很显然梯度与方向导数存在某种联系。通过向量的推导,很容易得到如下公式:
方向导数与梯度的关系其中:L0是方向导数中向量L的单位向量
L0向量的定义
要这个向量的点积达到最大, 即方向导数达到最大, gradf和L0必须同方向,也就是说当L0是梯度gradf的方向时, 方向导数到达最大。因此梯度的物理意义:是函数各个方向上变化率最大的那个方向,函数沿着梯度的方向,变化率达到最大。
梯度下降优化目标函数的原理
在理解梯度下降的物理意义之后, 自然就能理解梯度下降在回归算法(其他机器学习算法也会用到)的作用:优化损失函数,让损失函数一直沿着最大的降低方向变化,最终找到最小损失函数(也可能是局部最小)。具体如图所示:
Paste_Image.png实现回归算法损失函数的思路:每次算出损失函数的梯度(就是对每个参数进行偏导数计算),计算出每个参数的偏导数后, 在每个参数上减去相应的偏导数,得到一个新的参数值, 损失函数就得到函数值降低最快的效果。迭代过程持续到参数收敛,参数是否收敛的判断依据是:
1、θ值变化不再明显(差值小于某给定值);
2、用损失函数值变化不明显,训练值加上迭代的参数值观察损失函数的变化情况,直到变化不再明显;
3、存在收敛过慢或者死循环的情况,可以强制限制迭代次数。
下面用数学公式推导下回归算法损失函数求解参数的相关公式。损失函数如下:
Paste_Image.pngPS:1/2是为后续计算偏导数方便,并不影响求解损失函数的最小值。
对一个样本点j,参数偏导数的计算公式进行推导:
参数偏倒数的推导过程
对有M个样本点,第J个参数就基于上面偏导数公式进行更新,其迭代的过程如下所示:
参数的迭代公式
公式中alpha 是步长。
梯度下降的局限性
通过参数最终的迭代公式以及梯度下降的实现思路可知,梯度下降公式存在如下几个问题:
- 计算量很大,因为每次更新theta值时都要将整个训练集的数据代入计算,该算法在训练集合非常大的情况下将会非常低效。
- 计算的初始值对结果影响大。如果该函数不是凸函数(凸函数得到的最终值是全局最小值), 函数得到的是局部最小值, 初始值不一样, 最小值也会不一样。如果函数没有最小值,就会陷入无限循环中。
梯度下降的实现
用代码实现 简单的梯度下降程序。
利用y = 2 * x1 + (x2^2) / 2生成x_train, y_train数据, 具体的实现如下图:
# y = 2 * x1 + (x2^2) / 2
alpha=0.001
max_count = 1000000
x_train = [(1.0,3.0), (2.0, 4.0), (3.0, 5.0), (5.0,9.0), (8.0, 10.0)]
y_train = [6.6, 12.2, 18.8, 50.5, 66.3]
error_end = 0.5
#h(x)
def h(x, theta0, theta1):
#global theta0, theta1
#print theta0, theta1, x[0], x[1]
hx = theta0 * x[0] + theta1 * (x[1]**2)
return hx
def gradf():
theta0 = 0.5
theta1 = 0.1
data_len = len(y_train)
cn = 0
is_finish = False
while True:
for i in range(data_len):
det_theta = alpha * (h(x_train[i], theta0, theta1) - y_train[i])
theta0 = theta0 - (det_theta) * x_train[i][0]
theta1 = theta1 - (det_theta) * x_train[i][1]
error = 0.0
for i in range(data_len):
error = error + ((y_train[i] - h(x_train[i], theta0, theta1))**2)/2
cn = cn + 1
if error < error_end or cn > max_count:
print error
break;
print "theta:%f, %f" %(theta0, theta1)
if __name__=="__main__":
gradf()
程序运行后得到的参数:
theta:1.682974, 0.528715
对比函数y = 2 * x1 + (x2^2) / 2的参数: 2, 0.5。有所差异, 但还是比较接近。
以上是最简单的批量梯度下降方法, 梯度下降还涉及alpha步长, 非凸函数的初始化参数选取问题等等, 后续文章再探讨。
网友评论