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

摘要: 梯度降低git

梯度降低

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

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)

应该均可以看懂了。
咱们这一节就来说看不懂的这一行,梯度降低优化函数。dom

从画函数图形提及

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

因此咱们先学习一下如何画函数的图形:学习

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=x2−2x+1的值。
画出的图像是这样的:优化

clipboard.png

求函数的最小值

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

假设咱们从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∗=argminf(x)
这种方法能够解决问题。可是它的问题在于慢。在函数降低速度很快的时候能够多移动一点加快速度,降低速度变慢了以后少移一点防止跑过了。3d

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

f(x)=x2−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)

这是个平方函数fLoss=Σni=1(yi−(wxi+b))2
其中yi是已知的标注好的结果,xi是已知的输入值。未知数只有两个,w和b。
因此下一步求梯度降低的问题变成求二元二次函数极小值的问题。

咱们仍是先看图,二元二次函数的图是什么样子的。咱们取个f(x,y)=x2+y2+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()

图绘制出来是这样子的:

clipboard.png

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

如今不仅是有x一个方向,而是有x和y两个方向。两个方向也好办,x方向和y方向分别找梯度就是了。分别取x和y方向的偏微分。
xn+1=xn−η∂f(x,y)∂x,yn+1=yn−η∂f(x,y)∂y
偏导数虽然从d换成了∂,可是求微分的公式仍是同样的。只不过在求x的时候把y当成常数就是了。

咱们能够用梯度符号来简单表示∇=(∂f(x,y)∂x,∂f(x,y)∂y),这个倒三角符号读做nabla.

在Tensorflow里,梯度降低这样基本的操做天然是不须要咱们操心的,咱们只须要调用tf.train.GradientDescentOptimizer就好。

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

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

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

赋给GradientDescentOptimizer的0.01就是咱们的η,学习率。

自适应梯度演化史

若是您的逻辑思惟比较强的话,必定会发现从二维导数升级到三维梯度时,有一个参数咱们没有跟着一块儿扩容,那就是学习率η.

咱们仍是看一个图,将上面的方程变成f(x,y)=8x2+y2+x+y+1

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

图像变成下面这样子:

clipboard.png

你们能够直观地感觉到,∂f(x,y)∂x和∂f(x,y)∂y方向的降低速度是明显不同的。
若是对于更高维的状况,问题可能更严重,众口难调。

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

AdaGrad的公式为(xt+1)i=(xt)i−ηΣtτ=1(∇f(xτ))2i√(∇f(xt))i
可是,AdaGrad也有本身的问题。虽然初始指定一个η值以后能够自适应,可是若是这个η太大的话仍然会致使优化不稳定。可是若是过小了,随着优化,这个学习率会愈来愈小,可能就到不了极小值就停下来了。

因而当时在Google作实习生的Mathew Zeiler作出了两点改进:一是引入时间窗口概念,二是不用手动指定学习率。改进以后的算法叫作AdaDelta.
公式中用到的RMS是指Root Mean Square,均方根.
AdaDelta的公式为(∇xi)i=−(RMS[Δx]t−1)i(RMS[g]t)i(∇f(xi))i
具体过程请看Zeiler同窗写的paper: http://arxiv.org/pdf/1212.570...

在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的论文中对于几种方法的对比:

做者:lusing

原文连接

本文为云栖社区原创内容,未经容许不得转载。

相关文章
相关标签/搜索