引入一个参数自然有它的原因,那这里引入动量的原因是什么:
类似这张图,垂直方向的斜率比水平方向的斜率大很多,会导致很多没必要的偏移量,加入一下动量因子可以减少偏移量。
动量法与SGD不同的点在于,它保存了历史的梯度,就像图上画的,B点实际下降方向是A点下降方向和B点下降方向的共同决定的。
# coding=utf-8
"""
基于小批量梯度下降来实现的Momentum(动量)
参考:https://blog.youkuaiyun.com/bvl10101111/article/details/72615621
作用:
在学习率较小的时候,适当的momentum能够起到一个加速收敛速度的作用;
在学习率较大的时候,适当的momentum能够起到一个减小收敛时震荡幅度的作用.
@author: Reynold
@date: 2018-08-21
"""
import numpy as np
import random
# 构造训练数据
x = np.arange(0., 10., 0.2)
print(x)
m = len(x)
print(m)
x0 = np.full(m, 1.0)
print(x0)
input_data = np.vstack([x0, x]).T # 将偏置b作为权向量的第一个分量
print(input_data) #[50,2]的输入数据
target_data = 3 * x + 8 + np.random.randn(m)
print(target_data)
# 两种终止条件
max_iter = 10000 #最大迭代次数
epsilon = 1e-5 #结束条件
# 初始化权值
np.random.seed(0)
w = np.random.randn(2) #权重 θ
v = np.zeros(2) # 更新的速度参数
alpha = 0.001 # 步长
diff = 0. #梯度
error = np.zeros(2)
count = 0 # 循环次数
eps = 0.9 # 衰减力度,可以用来调节,该值越大那么之前的梯度对现在方向的影响也越大
while count < max_iter:
count += 1
sum_m = np.zeros(2)
index = random.sample(range(m), int(np.ceil(m * 0.2))) #取样函数,取完整样本中的20%
sample_data = input_data[index]
sample_target = target_data[index]
for i in range(len(sample_data)):
dif = (np.dot(w, input_data[i]) - target_data[i]) * input_data[i] #计算梯度
sum_m = sum_m + dif #每个样本的梯度和
v = eps * v - alpha * sum_m # 在这里进行速度更新
w = w + v # 使用动量来更新参数
if np.linalg.norm(w - error) < epsilon:
break
else:
error = w
print('loop count = %d' % count, '\tw:[%f, %f]' % (w[0], w[1]))