1. 文章
  2. 文章详情

梯度下降理解与在tensorflow中的使用

梯度下降的概念

梯度下降法是一个一阶最优化算法,通常也称为最速下降法。要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对于梯度(或者是近似梯度)的反方向的规定步长距离点进行迭代搜索。所以梯度下降法可以帮助我们求解某个函数的极小值或者最小值。对于n维问题就最优解,梯度下降法是最常用的方法之一。

梯度下降法的推导

首先看一维函数:

f(x) = x^3 + 2 * x - 3

在数学中如果我们要求f(x) = 0处的解,我们可以通过如下误差等式来求得:

error = (f(x) - 0)^2

error趋近于最小值时,也就是f(x) = 0x的解。

我们可以通过对该函数进行求导(即斜率):

derivative(x) = 6 * x^5 + 16 * x^3 - 18 * x^2 + 8 * x - 12

如果要得到最小值,只需令derivative(x) = 0,即x = 1

  • x < 1时, derivative < 0,斜率为负的;
  • x > 1时,derivative > 0,斜率为正的;
  • x 无限接近 1时,derivative也就无限=0,斜率为零。

通过上面的结论,我们可以使用如下表达式来代替x在函数中的移动

x = x - reate * derivative
当斜率为负的时候,x增大,当斜率为正的时候,x减小;因此x总是会向着低谷移动,使得error最小,从而求得 f(x) = 0处的解。其中的rate代表x逆着导数方向移动的距离,rate越大,x每次就移动的越多。反之移动的越少。

这是针对简单的函数,我们可以非常直观的求得它的导函数。为了应对复杂的函数,我们可以通过使用求导函数的定义来表达导函数:若函数f(x)在点x0处可导,那么有如下定义:

导数定义

使用python进行实现

>>> def f(x):
...     return x**3 + 2 * x - 3
...
>>> def error(x):
...     return (f(x) - 0)**2
...
>>> def gradient_descent(x):
...     delta = 0.00000001
...     derivative = (error(x + delta) - error(x)) / delta
...     rate = 0.01
...     return x - rate * derivative
...
>>> x = 0.8
>>> for i in range(50):
...     x = gradient_descent(x)
...     print('x = {:6f}, f(x) = {:6f}'.format(x, f(x)))
...

执行上面程序,我们就能得到如下结果:

x = 0.869619, f(x) = -0.603123
...
...
x = 1.000000, f(x) = -0.000000

结论:x = 1时,f(x) = 0
所以通过该方法,只要步数足够多,就能得到非常精确的值。

梯度下降法--多元函数

多元函数求解

f(x) = x[0] + 2 * x[1] + 4

同样的如果我们要求f(x) = 0处,x[0]x[1]的值,也可以通过求error函数的最小值来间接求f(x)的解。跟一维函数唯一不同的是,要分别对x[0]x[1]进行求导。在数学上叫做偏导数

  • 保持x[1]不变,对x[0]进行求导,即f(x)x[0]的偏导数
  • 保持x[0]不变,对x[1]进行求导,即f(x)x[1]的偏导数

有了上面的理解基础,我们定义的gradient_descent如下:

>>> def gradient_descent(x):
...     delta = 0.00000001
...     derivative_x0 = (error([x[0] + delta, x[1]]) - error([x[0], x[1]])) / delta
...     derivative_x1 = (error([x[0], x[1] + delta]) - error([x[0], x[1]])) / delta
...     rate = 0.01
...     x[0] = x[0] - rate * derivative_x0
...     x[1] = x[1] - rate * derivative_x1
...     return [x[0], x[1]]
...

rate的作用不变,唯一的区别就是分别获取最新的x[0]x[1]。下面是整个代码:

>>> def f(x):
...     return x[0] + 2 * x[1] + 4
...
>>> def error(x):
...     return (f(x) - 0)**2
...
>>> def gradient_descent(x):
...     delta = 0.00000001
...     derivative_x0 = (error([x[0] + delta, x[1]]) - error([x[0], x[1]])) / delta
...     derivative_x1 = (error([x[0], x[1] + delta]) - error([x[0], x[1]])) / delta
...     rate = 0.02
...     x[0] = x[0] - rate * derivative_x0
...     x[1] = x[1] - rate * derivative_x1
...     return [x[0], x[1]]
...
>>> x = [-0.5, -1.0]
>>> for i in range(100):
...     x = gradient_descent(x)
...     print('x = {:6f},{:6f}, f(x) = {:6f}'.format(x[0],x[1],f(x)))
...

输出结果为:

x = -0.560000,-1.120000, f(x) = 1.200000
...
...
x = -0.800000,-1.600000, f(x) = 0.000000
f(x) = 0不止这一个解还可以是x = -2, -1。这是因为梯度下降法只是对当前所处的凹谷进行梯度下降求解,对于error函数并不代表只有一个f(x) = 0的凹谷。所以梯度下降法只能求得局部解,但不一定能求得全部的解。

Tensorflow机器学习中的运用

梯度下降是神经网络中最常用的一种优化方法。

它得名于迭代方向d是由函数f的一阶导数,即梯度决定的。

假设一个函数 ,在某点x处的梯度是一个向量,它的方向是就是该函数f在该点x处函数值增长最快的方向,也就是说,把迭代的每一步沿着当前点的梯度方向的反方向进行迭代,就可以得到一个逐渐递减的序列。在传统上,根据每一次迭代使用的训练数据集的不同,

我们可以将梯度下降分为三类:批量梯度下降、随机梯度下降、小批量梯度下降

1.批量梯度下降,简称Batch Gradient Descent (BGD),它的误差损失函数是由全量训练数据的误差构成,也就是说,当训练数据量非常大时,梯度下降速度会非常慢,此外,它不支持增量更新,当训练数据有新数据的加入,需要对全量的数据进行更新,效率很低。

2.随机梯度下降,简称Stochastic Gradient Descent (SGD),它是对BGD的改进,因为SGD每一次更新时只考虑一个样本数据的误差损失,所以它的速度非常快,并支持在线的参数更新。同时,它也有一些问题存在,如单个样本的重复情况会造成更新的冗余;单个数据之间的差异会比较大,会对每一次迭代的损失函数造成较大的波动。

3.小批量梯度下降,简称mini-Batch Gradient Descent(MBGD),它结合了以上两种梯度下降的优点,同时也克服了以上两种梯度下降的缺点。它的更新策略是指每次的参数更新时,优化的目标函数是由 个样本数据构成的,这个 取值通常不大,这样以来特别适合高效运算,如GPU并行加速计算,虽然训练数据量比SGD多很多,但效率上却与SGD差别不大,因此也适用于在线的模型更新。此外,由于它考虑了一批样本数据,每一批数据之间的整体差异性不大,因此结果会更加稳定。

tensorflow中使用gradientDescent

import tensorflow as tf
 
# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W*x + b
y = tf.placeholder(tf.float32)
 
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
 
# training data
x_train = [1, 2, 3, 4]
y_train = [0, -1, -2, -3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
  sess.run(train, {x: x_train, y: y_train})
 
# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))

上面代码定义了函数linear_model = W * x + b,其中的error函数为linear_model - y。目的是对一组x_trainy_train进行简单的训练求解Wb。为了求得这一组数据的最优解,将每一组的error相加从而得到loss,最后再对loss进行梯度下降求解最优值。

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

在这里rate0.01,因为这个示例也是多维函数,所以也要用到偏导数来进行逐步向最优解靠近。

for i in range(1000):
  sess.run(train, {x: x_train, y: y_train})
   

最后使用梯度下降进行循环推导计算

发表评论

登录后才能评论

评论列表(0条)