import tensorflow as tf
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
from datetime import datetime
import random
t0 = datetime.now()
x_data = np.random.randn(int(1.0e2)).astype(np.float32)
y_data = x_data * 0.3 + 0.15
a = np.sum(x_data**2); b = float(len(x_data))
c = 2*np.sum(x_data); d = -2*np.sum(x_data*y_data)
e = -2*np.sum(y_data); f = np.sum(y_data**2)
fig = plt.figure()
ax = Axes3D(fig)
W = np.arange(-100, 100+0.1, 1)
B = np.arange(-100, 100+0.1, 1)
W, B = np.meshgrid(W, B)
loss1 = a*W**2 + b*B**2 + c*W*B + d*W + e*B + f
plt.xlabel('W')
plt.ylabel('B')
ax.plot_surface(W, B, loss1, rstride=1, cstride=1, cmap='rainbow')
plt.show()
x_ph = tf.placeholder(tf.float32)
y_ph = tf.placeholder(tf.float32)
weight_initial = 1.0e4
bias_initial = 1.0e4
weight = tf.Variable(weight_initial)
bias = tf.Variable(bias_initial)
y_model = weight * x_ph + bias
loss2 = tf.reduce_sum((y_model - y_ph)**2)
loss2_mean = loss2/len(x_data)
learning_rate = 1e-1
train_op = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss2)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
print('初始点是:({},{})'.format(weight_initial, bias_initial))
step = 0
loop = 0
threshold = 1e-5
algorithm = 3
mini_batch_size = 5
flag = True
while flag:
if algorithm == 1:
sess.run(train_op, feed_dict={x_ph: x_data, y_ph: y_data})
step += 1
print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
elif algorithm == 2:
random.shuffle(x_data)
y_data = x_data * 0.3 + 0.15
for (x, y) in zip(x_data, y_data):
sess.run(train_op, feed_dict={x_ph: x, y_ph: y})
step += 1
print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
loop += 1
print('完成第%s轮循环' % loop)
elif algorithm == 3:
random.shuffle(x_data)
y_data = x_data * 0.3 + 0.15
for i in range(0, len(x_data), mini_batch_size):
x_mini_batch = []
y_mini_batch = []
for j in range(i, i + mini_batch_size, 1):
x_mini_batch.append(x_data[j])
y_mini_batch.append(y_data[j])
sess.run(train_op, feed_dict={x_ph: x_mini_batch, y_ph: y_mini_batch})
step += 1
print('第%s次迭代,W is %.2f, B is %.2f' % (step, weight.eval(sess), bias.eval(sess)))
loop += 1
print('完成第%s轮循环' % loop)
if sess.run(loss2_mean, feed_dict={x_ph: x_data, y_ph: y_data}) <= threshold:
print('满足精度要求')
break
if abs(weight.eval(sess)) > weight_initial*2 or abs(bias.eval(sess)) > bias_initial*2:
print('learning_rate选得过大')
break
plt.plot(x_data, y_data, 'ro', label='Original data')
plt.plot(x_data, sess.run(weight) * x_data + sess.run(bias), label='Fitted line')
plt.legend()
plt.show()
t1 = datetime.now()
print('耗时:', t1-t0)
- 补充说明:
Gradient Desecnt通常指全量梯度下降,Stochastic Gradient Desecnt(随机梯度下降)通常指逐样本梯度下降,mini-batch Gradient Desecnt指批量梯度下降。
三者都是对目标函数的所有自变量求偏导,区别是全量梯度下降是将所有样本值代入所有偏导项,求出当前自变量点上的梯度,乘以learning rate,得到基于当前自变量点的自变量各分量的变化量,用当前自变量点减去(不能加,否则就变成了求极大值点,因为梯度的正方向是函数增加最快的方向)各分量的变化量,得到一个新的自变量点,则完成了一步挪动,按此步骤一步步挪,直到满足终止条件。
随机梯度下降是一次只代入一个样本值到所有偏导项,每挪一步是以随机取到的这个样本值算出的梯度为依据,而不是以全样本算出的梯度为根据;所以每挪一步目标函数的形态就会发生一次变化,而不像全量梯度下降那样,从始至终都在同一个目标函数上挪动。由此也可知,无论做何种梯度下降,样本的数量是可以远小于自变量的数量的。这一点明显优于方程求解法,因为后者要求求解方程组时,系数矩阵满秩;例如有n个自变量,就要求有n个线性无关的样本。
批量梯度下降是上述二者的折中,每次代入mini-batch size个样本值到所有偏导项,每挪一步是以随机取出的这mini-batch size个样本值算出的梯度为依据;同样地,每挪一步目标函数会发生一次变化。
三者的优劣分别是:
当迭代步数相同时,计算速度:SGD > BGD > GD,内存缓存占用:SGD < BGD < GD;
每步迭代时目标函数对全体样本的代表性:SGD < BGD < GD。
注意事项:
虽然SGD每步迭代时的目标函数可能不同,因为取决于随机采到的样本点,但如果把所有样本都扫描一遍,还是能够获取到整体的信息;只是终止条件不能用梯度的范数或自变量的改变量,因为每一步迭代目标函数都不同,大概率是不会得到稳定的趋近于0的梯度范数或自变量变化量,可用与梯度无关的超参数如迭代次数等作为终止条件。
对于批量梯度下降,mini-batch size越小,相同迭代步数的条件下,计算速度越快,内存占用越少,每步迭代时看见的全体样本信息越少;与前述原理一致。在不考虑计算速度和内存的情况下,mini-batch size大概率是越大越好。