MLP-入门教程

【总结】带权重衰减与动量的SGD

作者 : 老饼 发表日期 : 2023-03-08 15:26:37 更新日期 : 2025-04-24 18:31:47
本站原创文章,转载请说明来自《老饼讲解-深度学习》www.bbbdata.com



带动量、带权重衰减的SGD算法,是深度学习最基本的有效训练算法,它使得深度学习模型的训练得以实施

本文讲解带动量、带权重衰减的SGD算法流程,并展示一个具体的代码实例,用于训练MLP神经网络

通过本文可以了解什么是带动量、带权重衰减的SGD算法,以及它的意义,并进一步了解它的具体代码实现





     01. 带权重衰减、带动量的SGD     





本节讲解什么是带权重衰减、带动量的SGD算法,以及它的算法流程





      什么是SGD算法-带动量、带权重衰减    


带动量、带权重衰减的SGD算法,就是在动量梯度下降法中加入权重衰减、并随机小批进行训练
 梯度下降法解决求解、动量梯度下降解决速度、权重衰减解决过拟合、SGD解决内存不足
 
 总的来说,这一套组合使得深度学习的训练得以实施,它是深度学习最为基本的有效训练算法






     SGD的算法流程(带动量、带权重衰减)     


 SGD(带权重衰减、带动量)的具体算法流程如下:
 一、初始化                                                                   
       1. 初始化参数P                                                        
                可以使用随机初始化,也可以使用其它的初始化算法
2. 初始化速度                                                   
                                                 
 二、训练t步(epoch),每步过程如下:                          
 1. 将数据打乱,并分为n批                                 
 2. 逐批训练样本                                                 
(1) 计算本批次样本的参数P的梯度g           
(2) 更新本次的速度                                   
                  
(2) 将参数往负梯度方向更新                      
                       
 3. 检验终止条件                                                 
 终止条件如下:                      
(1) 如果epoch已经足够大,则终止训练     
              (2) 误差是否满足要求,如果满足,则退出训练         
 三、输出结果                                                                
输出参数P的最终训练结果                              
 可以看到,它与动量梯度下降法并没有太大的区别,
只是分批进行训练,且速度的更新公式中加入了权重衰减








     02. 带权重衰减、带动量的SGD-代码实现     






本节展示用带权重衰减、带动量的SGD训练一个MLP的例子与代码






     SGD算法训练MLP(带动量、权重衰减)-代码示例      


如下所示,在sin函数[-5,5]之间采集20个数据,我们需要训练一个三层MLP神经网络,来拟合sin函数x与y的关系
 MLP神经网络来解决数值预测问题-数据 
由于样本较为简单,我们不妨将三层MLP神经网络设为4个隐节点,并使用均方差作为损失函数
 根据带动量、带权重衰减的SGD算法流程,对三层MLP进行训练,
具体代码实现如下:
import torch
import matplotlib.pyplot as plt 
import random
torch.manual_seed(99)    # 设定torch的随机种子,使每次结果一样
random.seed(99)          # 设定python的随机种子,使每次结果一样

# -----计算网络输出:前馈式计算------
def forward(w1,b1,w2,b2,x):     
    return w2@torch.tanh(w1@x+b1)+b2

# ----计算损失函数: 使用均方差------
def loss(y,py):
    return ((y-py)**2).mean()

# -----样本分批函数-----------------
def data_split(x,y,batch_size):
    sample_num = x.shape[1]                                       # 样本个数
    batch_num  = int(sample_num/batch_size)                       # 计算批数
												                  
    idx = list(range(sample_num))                                 # 样本索引
    random.shuffle(idx)                                           # 对索引随机打乱
    idx = idx[:batch_num*batch_size]                              # 只抽取batch_num*batch_size个样本
    x_batch = x[:,idx]                                            # 打乱x的顺序
    y_batch = y[:,idx]                                            # 打乱y的顺序
    x_batch = x_batch.view(batch_num,x_batch.shape[0],batch_size) # 转换x的维度
    y_batch = y_batch.view(batch_num,y_batch.shape[0],batch_size) # 转换y的维度
    return x_batch,y_batch                                        # 返回分批后的数据
    
# ------训练数据----------------
x = torch.linspace(-5,5,20).reshape(1,20)                       # 在[-5,5]之间生成20个数作为x
y = torch.sin(x)                                                # 模型的输出值y

#-----------训练模型------------------------
in_num  = x.shape[0]                                            # 输入个数
out_num = y.shape[0]                                            # 输出个数
hn  = 4                                                         # 隐节点个数
w1  = torch.randn([hn,in_num],requires_grad=True)               # 初始化输入层到隐层的权重w1
b1  = torch.randn([hn,1],requires_grad=True)                    # 初始化隐层的阈值b1
w2  = torch.randn([out_num,hn],requires_grad=True)              # 初始化隐层到输出层的权重w2
b2  = torch.randn([out_num,1],requires_grad=True)               # 初始化输出层的阈值b2

lr = 0.01                                                       # 学习率
mu = 0.9                                                        # 动量系数
lamb = 0.0005                                                   # 权重衰减系数
batch_size = 3                                                  # 样本批大小

w1_v = 0                                                        # 初始化w1的速度
b1_v = 0                                                        # 初始化b1的速度
w2_v = 0                                                        # 初始化w2的速度
b2_v = 0                                                        # 初始化b2的速度
for epoch in range(5000):                                       # 训练5000步
	# 对样本进行分批,逐批训练
    x_batch,y_batch =   data_split(x,y,batch_size)              # 对样本进行分批
    for i in range(x_batch.shape[0]):                           # 逐批次训练 
        # 计算梯度
        py = forward(w1,b1,w2,b2,x_batch[i])                    # 计算网络的输出
        L = loss(y_batch[i],py)                                 # 计算损失函数
        L.backward()                                            # 用损失函数更新模型参数的梯度
        # 更新速度
        w1_v = mu*w1_v -lr*(1-mu)*(w1.grad+ lamb*w1)            # 更新w1的速度
        b1_v = mu*b1_v -lr*(1-mu)*(b1.grad+ lamb*b1)            # 更新b1的速度
        w2_v = mu*w2_v -lr*(1-mu)*(w2.grad+ lamb*w2)            # 更新w2的速度
        b2_v = mu*b2_v -lr*(1-mu)*(b2.grad+ lamb*b2)            # 更新b2的速度
        # 更新参数
        w1.data=w1.data+w1_v                                    # 更新模型系数w1
        b1.data=b1.data+b1_v                                    # 更新模型系数b1
        w2.data=w2.data+w2_v                                    # 更新模型系数w2
        b2.data=b2.data+b2_v                                    # 更新模型系数b2
        # 清空梯度
        w1.grad.zero_()                                         # 清空w1梯度,以便下次backward
        b1.grad.zero_()                                         # 清空b1梯度,以便下次backward
        w2.grad.zero_()                                         # 清空w2梯度,以便下次backward
        b2.grad.zero_()                                         # 清空b2梯度,以便下次backward
    # 计算当前的整体损失函数
    py = forward(w1,b1,w2,b2,x)                                 # 计算网络的输出
    L = loss(y,py)                                              # 计算损失函数
    print('第',str(epoch),'轮:',L)                             # 打印当前损失函数值
    if(L.item()<0.005):                                         # 如果误差达到要求
        break                                                   # 退出训练
px = torch.linspace(-5,5,100).reshape(1,100)                    # 测试数据,用于绘制网络的拟合曲线    
py = forward(w1,b1,w2,b2,px).detach().numpy()                   # 网络的预测值
plt.scatter(x, y)                                               # 绘制样本
plt.plot(px[0,:],py[0,:])                                       # 绘制拟合曲线  
plt.show()                                                      # 展示画布
print('w1:',w1)                                                 # 打印w1
print('b1:',b1)                                                 # 打印b1
print('w2:',w2)                                                 # 打印w2
print('b2:',b2)                                                 # 打印b2
运行结果如下:
  
 
可以看到,模型根据训练数据,已经较好地拟合出sin函数曲线
 将模型参数代回MLP神经网络的数学表达式,即可得到模型的数学表达式为:
 








好了,以上就是利用带权重衰减、带动量的SGD算法来训练一个MLP的流程与代码了~











 End 





内容纠正