假设整个数据集大小。我们可以把数据集划分成5000个mini-batch,其中每一个batch包含1000个数据。做梯度下降时,我们每跑完一个batch里的1000个数据,就用它们的平均梯度去更新参数,再去跑下一个batch。
这里要介绍一个新的标记。设整个数据集的形状是,则第个数据集的标记为,形状为。
再次总结一下标记:中的上标分别表示和第i个样本相关、和第j层相关、和第k个批次的样本集相关。实际上这三个标记几乎不会同时出现。
使用了分批梯度下降后,算法的写法由
for i in range(m):
update parameters
变成
for i in range(m / batch_size)
for j in range(batch_size):
update parameters
。现在的梯度下降法每进行一次内层循环,就更新一次参数。我们还是把一次内层循环称为一个"step(步)"。此外,我们把一次外层循环称为一个"epoch(直译为'时代’,简称‘代’)",因为每完成一次外层循环就意味着训练集被遍历了一次。
mini-batch 的损失函数变化趋势
通过观察,我们可以发现,。
也就是说,在算n天里的m天移动平均数(我们刚刚计算的是5天移动平均数)时,我们不用在n次的外层循环里再写一个m次的循环,只需要根据前一天的移动平均数,减一个值加一个值即可。这种依次输出移动平均数的算法如下:
input temperature[0:n]
input m
def get_temperature(i):
return temperature if i >= 0 and i < n else 0
ma = 0
for i in range(n):
ma += (get_temperature(i) - get_temperature(i - m)) / m
ma_i = ma
output ma_i
这种求移动平均数的方法确实很高效。但是,我们上面这个算法是基于所有温度值一次性给出的情况。假如我们正在算今年每天温度的移动平均数,每天的温度是一天一天给出的,而不是一次性给出的,上面的算法应该怎么修改呢?让我们来看修改后的算法:
input m
temp_i_day_ago = zeros((m))
def update_temperature(t):
for i in range(m - 1):
temp_i_day_ago[i+1] = temp_i_day_ago
temp_i_day_ago[0] = t
ma = 0
for i in range(n):
input t_i
update_temperature(t_i)
ma += (temp_i_day_ago[0] - temp_i_day_ago[m]) / m
ma_i = ma
output ma_i
由于我们不能提前知道每天的天气,我们需要一个大小为m的数组temp_i_day_ago记录前几天的天气,以计算m天移动平均数。
上述代码的时间复杂度还是有优化空间的。可以用更好的写法去掉update_temperature里的循环,把计算每天移动平均数的时间复杂度变为。但是,这份代码的空间复杂度是无法优化的。为了算m天移动平均数,我们必须要维护一个长度为m的数组,空间复杂度一定是。
对于一个变量的m移动平均数,的空间复杂度还算不大。但假如我们要同时维护l个变量的m移动平均数,整个算法的空间复杂度就是。在l很大的情况下,m对空间的影响是很大的。哪怕m取5这种很小的数,也意味着要多花4倍的空间去存储额外的数据。空间复杂度里这多出来的这个是不能接受的。
指数加权移动平均