Tensorflow快餐教程(7) - 梯度下降

简介: 梯度下降

梯度下降

学习完基础知识和矩阵运算之后,我们再回头看下第一节讲的线性回归的代码:

import tensorflow as tf
import numpy as np

trX = np.linspace(-1, 1, 101)
trY = 2 * trX + np.random.randn(*trX.shape) * 0.33 # 创建一些线性值附近的随机值

X = tf.placeholder("float") 
Y = tf.placeholder("float")

def model(X, w):
    return tf.multiply(X, w) # X*w线性求值,非常简单

w = tf.Variable(0.0, name="weights") 
y_model = model(X, w)

cost = tf.square(Y - y_model) # 用平方误差做为优化目标

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # 梯度下降优化

# 开始创建Session干活!
with tf.Session() as sess:
    # 首先需要初始化全局变量,这是Tensorflow的要求
    tf.global_variables_initializer().run()

    for i in range(100):
        for (x, y) in zip(trX, trY):
            sess.run(train_op, feed_dict={X: x, Y: y})

    print(sess.run(w)) 

除了这一句

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

应该都可以看懂了。
我们这一节就来讲看不懂的这一行,梯度下降优化函数。

从画函数图形说起

所谓梯度下降,其实没有什么神秘的,就是求个函数极值问题而己。
函数比矩阵强的一点是可以画图啊。

所以我们先学习一下如何画函数的图形:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-10,10,1000)
y = x ** 2 - 2 * x+ 1
plt.plot(x, y)
plt.title("matplotlib")
plt.xlabel("height")
plt.ylabel("width")
# 设置图例
plt.legend(["X","Y"], loc="upper right")
plt.grid(True)
plt.show()

上面我们用np.linspace来生成若干点组成的向量,然后取$y=x^2-2x+1$的值。
画出的图像是这样的:
x2_2x_1

求函数的最小值

现在我们想要求这条曲线上的最小值点。因为这个函数的定义域是无限的,我们不可能从负无穷到正无穷挨个试那个最小。
但是,我们可以随便找一个点为起点,然后比较一下它左边相邻一点的点和右边和它相邻一点的点看看哪个更小。然后取较小的那个点,继续这个过程。

假设我们从x=-5这个坐标开始,y=(-5)(-5)-2(-5)+1=36。所以这个点是(-5,36).
我们取0.1为步长,看看-5.1和-4.9的y值是多少,发现同(-5.1, 37.21)和(-4.9,34.81)。-4.9的值更小,于是-4.9就成为新一轮迭代的值。然后从(-4.9,34.81)到(-4.8,33.64),以此类推。一直到(1.0, 0)达到最小值,左右都比它大,这个极值就找到了。
求极值的这个函数我们称为损失函数loss function,或代价函数cost function,或者误差函数error function。这几个名称可以互换使用。
求得的极小值,我们称为$x^*=argmin f(x)$

这种方法可以解决问题。但是它的问题在于慢。在函数下降速度很快的时候可以多移动一点加快速度,下降速度变慢了之后少移一点防止跑过了。

那么这个下降速度如何度量呢?我们都学过,用导数-derivative,也就是切线的斜率。有了导数之后,我们可以沿着导数的反方向,也就是切换下降的方向去寻找即可。
这种沿着x的导数的反方向移动一小步来减少f(x)值的技术,就被称为梯度下降 - gradient descent.
第n+1的值为第n次的值减去导数乘以一个可以调整的系数。
也就是
$x_{n+1}=x_n - \eta\frac{df(x)}{dx}$
其中,这个可调整的系数$\eta$,我们称为学习率。学习率越大下降速度越快,但是也可能错过最小值而产生振荡。
选择学习率的方法一般是取一个小的常数。也可以通过计算导数消失的步长。还可以多取几个学习率值,然后取效果最好的一个。

$f(x)=x^2-2x+1$的导函数为$f^{'}(x)=2x-2$
我们还是选择从(-5,36)这个点为起点,学习率我们取0.1。
则下一个点$x_2=-5 - 0.1 (2(-5)-2)= -3.8$
则第2个点下降到(-3.8,23.04)。比起第一种算法我们右移0.1,这次一下子就右移了1.2,效率提升12倍。

晋阶三维世界

我们看下我们在做线性回归时使用的损失函数:

cost = tf.square(Y - y_model)

这是个平方函数$f_{Loss} = \Sigma_{i=1}^{n}(y_i-(wx_i+b))^2$
其中$y_i$是已知的标注好的结果,$x_i$是已知的输入值。未知数只有两个,w和b。
所以下一步求梯度下降的问题变成求二元二次函数极小值的问题。

我们还是先看图,二元二次函数的图是什么样子的。我们取个$f(x,y)=x^2+y^2+x+y+1$为例吧。

先学画图:

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

# 创建 3D 图形对象
fig = plt.figure()
ax = Axes3D(fig)

X3 = np.linspace(-10,10,100)
Y3 = np.linspace(-10,10,100)
X3, Y3 = np.meshgrid(X3, Y3)
Z3 = X3*X3 + Y3*Y3 + X3 + Y3 + 1
ax.plot_surface(X3, Y3, Z3, cmap=plt.cm.winter)

# 显示图
plt.show()

图绘制出来是这样子的:
x2_y2_1

从一条曲线变成了一个碗一样的曲面。但是仍然是有最小值点的。

现在不只是有x一个方向,而是有x和y两个方向。两个方向也好办,x方向和y方向分别找梯度就是了。分别取x和y方向的偏微分。
$x_{n+1}=x_n-\eta\frac{\partial f(x,y)}{\partial x}, y_{n+1}=y_n-\eta\frac{\partial f(x,y)}{\partial y}$

偏导数虽然从$d$换成了$\partial$,但是求微分的公式还是一样的。只不过在求x的时候把y当成常数就是了。

我们可以用梯度符号来简单表示$\nabla=(\frac{\partial f(x,y)}{\partial x},\frac{\partial f(x,y)}{\partial y})$,这个倒三角符号读作nabla.

在Tensorflow里,梯度下降这样基本的操作自然是不需要我们操心的,我们只需要调用tf.train.GradientDescentOptimizer就好。

例子我们在前面已经看过了,我们复习一下:

cost = tf.square(Y - y_model) # 用平方误差做为优化目标

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # 梯度下降优化

赋给GradientDescentOptimizer的0.01就是我们的$\eta$,学习率。

自适应梯度演化史

如果您的逻辑思维比较强的话,一定会发现从二维导数升级到三维梯度时,有一个参数我们没有跟着一起扩容,那就是学习率$\eta$.

我们还是看一个图,将上面的方程变成$f(x,y)=8x^2+y^2+x+y+1$

Z3 = 8*X3*X3 + Y3*Y3 + X3 + Y3 + 1

图像变成下面这样子:
8x2

大家可以直观地感受到,$\frac{\partial f(x,y)}{\partial x}$和$\frac{\partial f(x,y)}{\partial y}$方向的下降速度是明显不一样的。
如果对于更高维的情况,问题可能更严重,众口难调。

最简单的方法可能是对于每个维度都用一个专门的学习率。不过这样也太麻烦了,这时Adaptive Gradient自适应梯度下降就被当时在UC伯克利读博士的John C. Duchi提出了,简写为AdaGrad.
在Tensorflow中,我们可以通过tf.train.AdagradOptimizer来使用AdaGrad.

AdaGrad的公式为$(x_{t+1})_i=(x_t)_i-\frac{\eta}{\sqrt{\Sigma_{\tau=1}^t(\nabla f(x_\tau))_i^2}}(\nabla f(x_t))_i$

但是,AdaGrad也有自己的问题。虽然初始指定一个$\eta$值之后可以自适应,但是如果这个$\eta$太大的话仍然会导致优化不稳定。但是如果太小了,随着优化,这个学习率会越来越小,可能就到不了极小值就停下来了。

于是当时在Google做实习生的Mathew Zeiler做出了两点改进:一是引入时间窗口概念,二是不用手动指定学习率。改进之后的算法叫做AdaDelta.
公式中用到的RMS是指Root Mean Square,均方根.
AdaDelta的公式为$(\nabla x_i)_i = -\frac{(RMS[\Delta x]_{t-1})_i}{(RMS[g]_t)_i}(\nabla f(x_i))_i$
具体过程请看Zeiler同学写的paper: http://arxiv.org/pdf/1212.5701v1.pdf

在Tensorflow中,我们只要使用tf.train.AdadeltaOptimizer这个封装就好。

AdaDelta并不是唯一的改进方案,类似的方案还有RMSProp, Adam等。我们可以使用tf.train.RMSPropOptimizer和tf.train.AdamOptimizer来调用就好了。

还记得第1节中我们讲卷积神经网络的例子吗?我们用的不再是梯度下降,而是RMSProp:

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)

物理学的启示

虽然各种自适应梯度下降方法洋洋大观,但是复杂的未必就是最好的。导数的物理意义可以认为是速度。速度的变化我们可以参考物理学的动量的概念,同时引入惯性的概念。如果遇到坑,靠动量比较容易冲出去,而其它方法就要迭代很久。
在Tensorflow中,可以通过tf.train.MomentumOptimizer来使用动量方法。

我们可以看一下Mathew Zeiler的论文中对于几种方法的对比:
AdaDelta

从中可以看到,虽然在前期,动量方法上窜下跳不像AdaGrad稳定,但是后期效果仍然是最好的。

目录
相关文章
|
7月前
|
机器学习/深度学习 算法 TensorFlow
TensorFlow的自动微分与梯度下降
【4月更文挑战第17天】本文探讨了TensorFlow中的自动微分和梯度下降在机器学习模型优化中的作用。自动微分通过计算图实现,简化了深度学习模型中梯度的计算。TensorFlow利用`tf.GradientTape`进行反向传播以求梯度。梯度下降算法用于更新参数,`tf.train.GradientDescentOptimizer`是实现这一过程的一种方式。此外,TensorFlow还提供了其他优化器以提升性能。理解这些概念有助于更有效地构建和优化机器学习模型。
|
机器学习/深度学习 PyTorch TensorFlow
深度学习框架教程:介绍一些流行的深度学习框架 (如TensorFlow、PyTorch等)
深度学习框架教程:介绍一些流行的深度学习框架 (如TensorFlow、PyTorch等)
169 0
|
XML TensorFlow API
TensorFlow Object Detection API 超详细教程和踩坑过程
TensorFlow Object Detection API 超详细教程和踩坑过程
227 1
|
机器学习/深度学习 并行计算 Ubuntu
系统类配置(二)【深度学习装机详细教程-ubuntu16.04下安装cuda9.0+nvidia-384+cudnn7.1.4+tensorflow1.9。】(下)
系统类配置(二)【深度学习装机详细教程-ubuntu16.04下安装cuda9.0+nvidia-384+cudnn7.1.4+tensorflow1.9。】(下)
207 0
|
机器学习/深度学习 并行计算 Ubuntu
系统类配置(二)【深度学习装机详细教程-ubuntu16.04下安装cuda9.0+nvidia-384+cudnn7.1.4+tensorflow1.9。】(上)
系统类配置(二)【深度学习装机详细教程-ubuntu16.04下安装cuda9.0+nvidia-384+cudnn7.1.4+tensorflow1.9。】(上)
164 0
|
机器学习/深度学习 存储 前端开发
使用Tensorflow实现的MTCNN和MobileFaceNet实现的人脸识别系统GUI界面 附代码 详细教程
使用Tensorflow实现的MTCNN和MobileFaceNet实现的人脸识别系统GUI界面 附代码 详细教程
360 0
使用Tensorflow实现的MTCNN和MobileFaceNet实现的人脸识别系统GUI界面 附代码 详细教程
BXA
|
机器学习/深度学习 自然语言处理 算法
Python深度学习:TensorFlow基础教程
深度学习是一种机器学习方法借助多层神经网络进行大规模数据分析和处理,以获取隐藏在数据中的特征模式,常应用于图像识别、语音识别、自然语言处理等领域
BXA
185 0
|
机器学习/深度学习 PyTorch TensorFlow
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(三)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(三)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(三)
|
机器学习/深度学习 Web App开发 IDE
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(一)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(一)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(一)
|
机器学习/深度学习 TensorFlow 算法框架/工具
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(二)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(二)
史上最全深度学习环境配置教程---适用于各种深度学习框架---Pytorh TensorFlow Keras-等和各种python环境(二)