赞
踩
梯度下降法是深度学习中常用的优化算法,主要有批量随机梯度下降(Batch Stochastic Gradient Descent)、随机梯度下降(Stochastic Gradient Descent)、小批量随机梯度下降(Mini-Batch Stochastic Gradient Descent)三种形式,这边大致介绍下三者的区别:
1.Batch SGD:基于所有的样本求解梯度,而后更新参数完成一次迭代;
2.SGD:基于一个样本求解梯度,而后更新参数完成一次迭代;
3.Mini Batch SGD:从整个数据集取部分的样本,而后基于这些样本求解梯度,并更新参数完成一次迭代
Mini Batch SGD利用小批量样本进行优化,一方面避免的SGD容易陷入局部收敛的问题,一方面较Batch SGD更快地完成一次更新,因此可以有效地加快训练速度。
为了掌握Mini Batch SGD算法,在网上学习了一些博主的内容,现在总结总结,并就一元一次函数、二元二次函数来玩一玩Mini Batch SGD。
详细了解三者的区别可以参考:批量梯度下降(BGD)、随机梯度下降(SGD)、小批量梯度下降(MBGD)
整体算法流程很简单,主要以下4步,然后不断迭代,直到收敛就行了。
1.构建前向传播模型
2.计算损失函数
3.求解梯度
4.反向传播, 更新权重
从知乎看到一篇文章,通过公式十分直观地地介绍了Mini Batch SGD的基本原理,看后十分受益,公式我就不再推导了,链接直接放这:手撕公式代码比较GD/SGD/mini-batch SGD
上面的博文主要介绍了基于线性模型的公式推导,因此我们先就一次函数来了解Mini Batch SGD整体算法的编写。
假设我们要求解一个一次函数的系数,形式为:
y=a*x+b
为了方便理解算法的编写过程,这里直接求解y=3x+4这个函数,看看Mini Batch SGD是怎么把a=3,b=4给优化出来的。
def forward_single(a,b,x): # 单个样本作为输入
return a*x+b
def forward_multi(a,b,X): # 多个样本作为输入
y_list=[]
for j in range(len(X)):
y_list.append(forward_single(a,b,X[j]))
return np.array(y_list)
为了与上面的博文一致,损失函数也选择均方误差(Mean Squared Error,MSE)。
def loss_single (y, ypred): # 单个样本的MSE
return (y - ypred)**2
def loss_whole(y, ypred): # 多个样本的MSE
loss_sum = 0.0
n = len(y)
for j in range(n):
loss_sum += loss_single(y[j], ypred[j])
return loss_sum/n
def loss_a_single(X, y, ypred): # dl/da 单样本计算 return -1*X*(y - ypred) def loss_a(X, y, ypred): # dl/da 多样本计算 s = 0 n = len(y) for i in range(n): s += loss_a_single(X[i], y[i], ypred[i]) return (2/n)*s def loss_b_single(y, ypred): # dl/db 单样本计算 return -1*(y - ypred) def loss_b(y, y_pred): # dl/db 多样本计算 n=len(y) s=int(0) for i in range(len(y)): s += loss_b_single(y[i], y_pred[i]) return (2/n) * s
def mb_SGD(X, y, epochs): a = np.random.randn() b = np.random.rand() mse_loss_list = [] lr = 0.005 # learning rate,暂定个0.005 n = len(X) # n 为数据集大小 n_iter = [] # 分析迭代次数 c_iter = 1 # 储存迭代次数 batch_size = 100 for e in range(1, epochs + 1): iter_per_epoch = int(n / batch_size) for j in range(iter_per_epoch): rand_ind_list = np.random.choice(X.shape[0], batch_size, replace=False) # 从整个数据集X中随机抽取batch_size个元素 X_samples = X[rand_ind_list] y_samples = y[rand_ind_list] y_pred = forward_multi(a, b,X_samples) # 根据求解的梯度更新权重 a = a - lr * loss_a(X_samples, y_samples, y_pred) b = b - lr * loss_b(y_samples, y_pred) mse_loss_list.append(loss_whole(y_samples, y_pred)) n_iter.append(c_iter) c_iter += 1 if e % 100== 0: print( f"[mbSGD] | epoch: [{e} / {epochs}], batch_szie: {len(rand_ind_list)}, {iter_per_epoch} iterations per epoch") return a, b, n_iter, mse_loss_list
上面的算法基本就完成整个mini batch SGD的编写了,为了验证,首先需要搞一个数据集,然后迭代一圈看看效果。
# A.定义目标函数
def target_y(x):
y=3*x+4 # 直接利用目标函数生成数据集
return y
# B. 根据所需数量产生数据集
def my_dataset(size): # size: 数据集大小
X=np.random.rand(size)
Y=target_y(X)
dataset=(X,Y)
return dataset
if __name__ == "__main__":
EPOCH=500 # 进行500轮的全数据集遍历
dataset=my_dataset(5000)
a, b, n_iter, mse_loss_list=mb_SGD(dataset[0],dataset[1],EPOCH)
print('a= %10.6f b= %10.6f' % (a,b))
plt.plot(n_iter, mse_loss_list)
plt.xlabel('iteration')
plt.ylabel('loss')
plt.show()
运行结果如下:
收敛还是蛮快的,求解得到a=3, b=4,与我们预期的一致。
别忘了import:
import numpy as np
import matplotlib.pyplot as plt
ok,现在进阶一下,看看二次函数的Mini Batch SGD。
假设要求解的二元二次函数形式是:
y=ax1+bx22
为了方便演示,直接求解y=3x1+2x22,此时我们知道如果算法正确的话,迭代优化后应该得到a=3, b=2。
过程和上面一致,就不一步步说了,直接上代码:
import math import numpy as np import matplotlib.pyplot as plt ''' step1: 定义随机梯度下降法需要的函数 ''' # A.前向传播模型 def forward_single (a,b, X): # 单个样本作为输入 return a*X[0]+b*X[1]**2 def forward_multi(a,b,X): # 多个样本作为输入 y_list=[] for j in range(len(X)): y_list.append(forward_single(a,b,X[j])) return np.array(y_list) # B.计算损失函数 def loss_single (y, ypred): # 单个样本的MSE return (y - ypred)**2 def loss_whole(y, ypred): # 多个样本的MSE loss_sum = 0.0 n = len(y) for j in range(n): loss_sum += loss_single (y[j], ypred[j]) return loss_sum/n # C.求解梯度 def loss_a_single(X, y, ypred): # dl/da 单样本计算 return -1*X[0]*(y - ypred) def loss_a(X, y, ypred): # dl/da 多样本计算 s = 0 n = len(y) for i in range(n): s += loss_a_single(X[i], y[i], ypred[i]) return (2/n)*s def loss_b_single(X, y, ypred): # dl/db 单样本计算 return -1*X[1]**2*(y - ypred) def loss_b(X, y, ypred): # dl/db 多样本计算 s = 0 n = len(y) for i in range(n): s += loss_b_single(X[i], y[i], ypred[i]) return (2/n)*s ''' step2: 反向传播求解梯度,并更新权重 ''' # A.定义mini_batch SGD函数 def mb_SGD(X, y,epochs): # y_max是防止溢出 a = np.random.randn() b = np.random.rand() mse_loss_list = [] lr = 0.005 n = len(X) # n 为数据集大小 n_iter = [] # 分析迭代次数 c_iter = 1 # 储存迭代次数 batch_size = 50 for e in range(1, epochs + 1): iter_per_epoch = int(n / batch_size) for j in range(iter_per_epoch): # 从整个数据集X中随机抽取batch_size个元素 rand_ind_list = np.random.choice(X.shape[0], batch_size, replace=False) X_samples = X[rand_ind_list] y_samples = y[rand_ind_list] y_pred = forward_multi(a,b, X_samples) # 根据求解的梯度更新权重 a = a - lr * loss_a(X_samples, y_samples, y_pred) b = b- lr * loss_b(X_samples, y_samples, y_pred) mse_loss_list.append(loss_whole(y_samples, y_pred)) n_iter.append(c_iter) c_iter += 1 if e % 100 == 0: print( f"[mbSGD] | epoch: [{e} / {epochs}], batch_szie: {len(rand_ind_list)}, {iter_per_epoch} iterations per epoch") return a,b, n_iter, mse_loss_list ''' step3: 生成数据集 ''' # A.定义目标函数 def target_y(X): y=3*X[:,0]+2*X[:,1]**2 return y # B. 根据所需数量产生数据集 def my_dataset(size): X=np.random.rand(size,2) Y=target_y(X) dataset=(X,Y) return dataset if __name__ == "__main__": EPOCH = 500 # 进行500轮的全数据集遍历 dataset = my_dataset(5000) a, b, n_iter, mse_loss_list = mb_SGD(dataset[0], dataset[1], EPOCH) print('a= %10.6f b= %10.6f' % (a, b)) plt.plot(n_iter, mse_loss_list) plt.xlabel('iteration') plt.ylabel('loss') plt.show()
运行结果如下:
跟一次函数一样,收敛很快,求解也很正确。
以上是关于小批量随机梯度下降算法基本原理的示例,为了避免套公式求解梯度,可以考虑把权重放到神经网络里面。参考:
玩一玩小批量随机梯度下降——进阶 by tensflow
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。