梯度下降简介
梯度下降是迭代法的一种,可以用于求解最小二乘问题(线性和非线性都可以)。在求解机器学习算法的模型参数,即无约束优化问题时,梯度下降(Gradient Descent)是最常采用的方法之一,另一种常用的方法是最小二乘法。
基本思想
L o s s = f ( α , β , . . . , θ ) ( 1 ) θ i + 1 = θ i − l e a r n i n g _ r a t e ∗ ∂ L o s s ∂ θ i ( 2 ) Loss = f(\alpha,\beta,...,\theta) \quad (1) \\ \quad \\ \theta_{i+1} = \theta_i - learning\_rate*\frac {\partial Loss}{\partial \theta_i} \quad (2) \\ \quad \\ Loss=f(α,β,...,θ)(1)θi+1=θi−learning_rate∗∂θi∂Loss(2)
Loss可以表示成若干参数的函数,每个参数更新时朝着负梯度的方向前进
常见梯度下降法:
全量梯度下降法(Batch gradient descent):
学习使用整个训练集,因此每次更新都会朝着正确的方向进行,缺陷就是学习时间太长
SGD(Stochastic Gradient Descent):
随机梯度下降法,按照一个随机样本的梯度下降
小批量梯度下降法(Mini-Batch Gradient Descent)
Momentum梯度下降法:
θ i + 1 = θ i − l e a r n i n g _ r a t e ∗ ∂ L o s s ∂ θ i − m o m e n t u m ∗ l e a r n i n g _ r a t e ∗ ∂ L o s s ∂ θ i − 1 ( 3 ) \theta_{i+1} = \theta_i -learning\_rate*\frac {\partial Loss}{\partial \theta_i} - momentum*learning\_rate*\frac {\partial Loss}{\partial \theta_{i-1}} \quad (3) θi+1=θi−learning_rate∗∂θi∂Loss−momentum∗learning_rate∗∂θi−1∂Loss(3)
这种方法可以减小震荡,见下图中下面那条折线
NAG(Nesterov Accelerated Gradient
不仅仅把SGD梯度下降以前的方向考虑,还将Momentum梯度变化的幅度也考虑了进来。
AdaGrad
二阶动量; 对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。
AdaDelta / RMSProp
AdaGrad单调递减的学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历(指数移动平均)
Adam(最常用)
是前述方法的集大成者。我们看到,SGD-Momentum在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。
全量梯度下降法举例
y ^ = a x 2 + b x ( 4 ) y = x 2 + 2 x ( 5 ) L o s s = 1 n ∑ i = 1 n ( y i ^ − y i ) 2 ( 6 ) a = a − l e a r n i n g _ r a t e ∗ ∂ L o s s ∂ a ( 7 ) b = b − l e a r n i n g _ r a t e ∗ ∂ L o s s ∂ b ( 8 ) ∂ L o s s ∂ a = 1 n ∑ i = 1 n 2 x i 2 [ ( a − 1 ) x i 2 + ( b − 2 ) x i ] = 2 x 2 [ ( a − 1 ) x 2 + ( b − 2 ) x ] ( 9 ) ∂ L o s s ∂ b = 1 n ∑ i = 1 n 2 x i [ ( a − 1 ) x i 2 + ( b − 2 ) x i ] = 2 x [ ( a − 1 ) x 2 + ( b − 2 ) x ] ( 10 ) \hat{y} = ax^2+bx \quad (4) \\ \quad \\ y = x^2+2x \quad (5) \\ \quad \\ Loss = \frac{1}{n}\sum_{i=1}^n(\hat{y_i}-y_i)^2\quad (6) \\ \quad \\ a = {a - learning\_rate*\frac{\partial{Loss}}{\partial{a}}} \quad(7) \\ \quad \\ b = {b - learning\_rate*\frac{\partial{Loss}}{\partial{b}}} \quad(8) \\ \quad \\ \frac{\partial{Loss}}{\partial{a}} = \frac{1}{n}\sum_{i=1}^n2x_i^2[(a-1)x_i^2+(b-2)x_i]=2x^2[(a-1)x^2+(b-2)x]\quad (9) \\ \quad \\ \frac{\partial{Loss}}{\partial{b}} = \frac{1}{n}\sum_{i=1}^n2x_i[(a-1)x_i^2+(b-2)x_i]=2x[(a-1)x^2+(b-2)x]\quad (10) y^=ax2+bx(4)y=x2+2x(5)Loss=n1i=1∑n(yi^−yi)2(6)a=a−learning_rate∗∂a∂Loss(7)b=b−learning_rate∗∂b∂Loss(8)∂a∂Loss=n1i=1∑n2xi2[(a−1)xi2+(b−2)xi]=2x2[(a−1)x2+(b−2)x](9)∂b∂Loss=n1i=1∑n2xi[(a−1)xi2+(b−2)xi]=2x[(a−1)x2+(b−2)x](10)
一个简单的二次曲线拟合,先生成公式(5)中二次函数,然后添加随机扰动,最后尝试去用公式(4)
拟合,拟合示意图和代码如下:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import os
###设置GPU###
#os.environ["CUDA_VISIBLE_DEVICES"] = "0" #GPU编号
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.6) #GPU使用率
###自定义数据###
x_data=np.linspace(-5,5,1000)[:,np.newaxis]
noise=np.random.normal(0,1,x_data.shape)
y_data=np.square(x_data)+noise+2*x_data
a_init=-2
b_init=-2
x_square = np.square(x_data)
"""tensorflow code"""
###定义变量###
weights =tf.Variable(initial_value=[a_init,b_init],dtype=tf.float32)
###占位符###
with tf.name_scope('Inputs'):
x_placehold=tf.placeholder(tf.float32,[None,1],name='x_input')
y_placehold=tf.placeholder(tf.float32,[None,1],name='y_input')
###定义一个无常数项的二次函数###
prediction = weights[0]*x_data*x_data + weights[1]*x_data
###损失###
with tf.name_scope('Loss'):
loss=tf.reduce_mean(tf.reduce_sum(tf.square(y_placehold-prediction),reduction_indices=[1]),name='loss')
tf.summary.scalar('loss',loss)
###训练步骤(主要指定方式)###
with tf.name_scope('Train'):
train_step=tf.train.GradientDescentOptimizer(0.001,name="GD").minimize(loss,name='train')
###初始化所有变量###
init=tf.global_variables_initializer()
###设置迭代步数###
steps=1000
###绘制并显示初始数据###
fig=plt.figure()
fig=fig.add_subplot(1,1,1)
fig.set_title('fitting process')
fig.scatter(x_data,y_data)
plt.ion()
plt.show()
fig2=plt.figure()
fig2_a = fig2.add_subplot(1,2,1)
fig2_b = fig2.add_subplot(1,2,2)
fig3 = plt.figure()
fig_3d = Axes3D(fig3)
a = np.arange(-2, 4, 0.25)
b = np.arange(-2, 4, 0.25)
a, b = np.meshgrid(a, b)
Z = np.mean(x_data**4,keepdims=False)*((a-1)**2) + np.mean(x_data**2,keepdims=False)*((b-2)**2) + 2*(a-1)*(b-2)*np.mean(x_data**3)
fig_3d.set_xlabel('a')
fig_3d.set_ylabel('b')
fig_3d.set_zlabel('loss')
fig_3d.plot_surface(a, b, Z, rstride=1, cstride=1,cmap="Blues",alpha=0.5)
fig_3d.set_title('gradient descent')
### 融合所有可视化信息图表 ###
merged=tf.summary.merge_all()
###创建会话###
with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:
### 保存Graph ###
writer=tf.summary.FileWriter('logs/',sess.graph)
sess.run(init)
steps_array, weights_a_array, weights_b_array,loss_array = [], [], [], []
for i in range(steps):
sess.run(train_step,feed_dict={x_placehold:x_data,y_placehold:y_data})
if i%15==0:
steps_array.append(i)
try:
fig.lines.remove(lines_prediction[0])
except Exception:
pass
loss_value = sess.run(loss,feed_dict={x_placehold:x_data,y_placehold:y_data})
print('step %d '%i , loss_value)
prediction_value=sess.run(prediction,feed_dict={x_placehold:x_data})
lines_prediction=fig.plot(x_data,prediction_value,'r-',lw=5)
weights_a, weights_b = sess.run(weights)
weights_a_array.append(weights_a)
weights_b_array.append(weights_b)
loss_array.append(loss_value)
fig2_a.plot(steps_array,weights_a_array)
fig2_b.plot(steps_array,weights_b_array)
fig2_a.set_xlabel('step')
fig2_a.set_ylabel('a')
fig2_a.set_title('a')
fig2_b.set_xlabel('step')
fig2_b.set_ylabel('b')
fig2_b.set_title('b')
fig_3d.plot(weights_a_array,weights_b_array,loss_array,'r-',lw=6)
plt.pause(0.1)
summary_result=sess.run(merged,feed_dict={x_placehold:x_data,y_placehold:y_data})
writer.add_summary(summary_result,i)
array,loss_array,‘r-’,lw=6)
plt.pause(0.1)
summary_result=sess.run(merged,feed_dict={x_placehold:x_data,y_placehold:y_data})
writer.add_summary(summary_result,i)