Pytorch_2_神经网络基础

Pytorch_神经网络基础


加速训练的方法不一定会有1+1>2的效果,要具体问题具体分析!

API:torch.nn — PyTorch 2.1 documentation

神经网络实现的三大核心:

  • 激活函数

torch.nn.functional — PyTorch 2.1 documentation

  • 损失函数

torch.nn — PyTorch 2.1 documentation

1 激活函数

API:torch.nn.functional — PyTorch 2.1 documentation

​ 常用的激活函数有 relu, sigmoid, tanh, softplus,可以直接在torch库中的torch.nn.functional调用,调用方式如下:

import torch.nn .functional as F
x = torch.linspace(-5, 5, 200)  # x data (tensor), shape=(100, 1)
x = Variable(x)

F.relu(x)
F.sigmoid(x)
F.tanh(x)
F.softplus(x)
F.softmax(x) #通常用于多类别分类问题中。它的原理是将一个向量的元素转化为概率分布,使得每个元素的取值范围在0到1之间,并且所有元素的和为1

在这里插入图片描述

2 损失函数

3 优化器与训练方法

3.1 优化器

API:torch.optim — PyTorch 2.1 documentation

资料链接:CS231n Convolutional Neural Networks for Visual Recognition

3.2 优化方法及理论

3.2.1 分类与对比
方法用法优势劣势
随机梯度下降法
SGD:Stochastic Gradient Descent
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
Momentumoptimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
AdaGrad
RMSProptorch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
Adamtorch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9,0.99))相较于前面,最优的方法
方法简约公式用法伪代码
随机梯度下降法
SGD:Stochastic Gradient Descent
x = x − α ⋅ ∂ E ∂ x x=x-\alpha·\frac{\partial E }{\partial x} x=xαxE
选择Loss减少最快的方向,负梯度方向
与梯度下降法的不同之处在于:SGD计算批次数据,GD计算全部数据
在这里插入图片描述
动量
Momentum(b)
m = b ⋅ m − α ⋅ ∂ E ∂ x m=b·m-\alpha·\frac{\partial E }{\partial x} m=bmαxE
x = x + m x=x+m x=x+m
选择Loss减少的方向,不一定要走最快的方向;寻优走下坡,动量b越大,越能摆脱局域最小值。
自适应梯度法
AdaGrad:Adaptive Gradient
v = v + ( ∂ E ∂ x ) 2 v=v+(\frac{\partial E }{\partial x})^2 v=v+(xE)2
x = x − α ⋅ ∂ E ∂ x / v x=x-\alpha·\frac{\partial E }{\partial x}/\sqrt{v} x=xαxE/v
每次参数更新都有不同的学习率 α / v \alpha/\sqrt{v} α/v ,越学习,学习率越小;给错误方向添加阻力在这里插入图片描述
均方根传播算法
RMSProp(b):Root Mean Square Propagation
v = b ⋅ v + ( 1 − b ) ⋅ ( ∂ E ∂ x ) 2 v=b·v+(1-b)·(\frac{\partial E }{\partial x})^2 v=bv+(1b)(xE)2
x = x − α ⋅ ∂ E ∂ x / v x=x-\alpha·\frac{\partial E }{\partial x}/\sqrt{v} x=xαxE/v
RMSProp约= Momentum+ AdaGrad在这里插入图片描述
适应性矩估计法:Adam(b1,b2):adaptive moment estimation m = b 1 ⋅ m + ( 1 − b 1 ) ⋅ ∂ E ∂ x m=b_1·m+(1-b_1)·\frac{\partial E }{\partial x} m=b1m+(1b1)xE
v = b 2 ⋅ v + ( 1 − b 2 ) ⋅ ( ∂ E ∂ x ) 2 v=b_2·v+(1-b_2)·(\frac{\partial E }{\partial x})^2 v=b2v+(1b2)(xE)2
x = x − α ⋅ ∂ E ∂ x / v x=x-\alpha·\frac{\partial E }{\partial x}/\sqrt{v} x=xαxE/v
Adam= Momentum+ AdaGrad在这里插入图片描述

注意: E E E为损失函数, x x x为神经网络权重, α \alpha α为步长。

彼此关系:
S G D ⊆ m o m e n t u m ⊆ R M S P r o p S G D ⊆ m o m e n t u m ⊆ A d a m SGD\subseteq momentum\subseteq RMSProp\\ SGD\subseteq momentum\subseteq Adam\\ SGDmomentumRMSPropSGDmomentumAdam

在这里插入图片描述

3.3 代码展示

此代码融合了多个网络同时训练和批训练的方法!!

optiSolution.py

在这里插入图片描述

#对比不同的优化器方法
import torch
import torch.utils.data as Data
import matplotlib.pyplot as plt


torch.manual_seed(1)    # reproducible

LR = 0.01
BATCH_SIZE = 20      # 批训练的数据个数
EPOCH = 12 #整体数据训练次数

#建立数据库
x = torch.unsqueeze(torch.linspace(-1, 1, 1000, requires_grad=True), dim=1)  # x data (tensor), shape=(1000, 1) unsqueeze升维 
y = x.pow(2) + 0.2*torch.rand(x.size())                 # noisy y data (tensor), shape=(100, 1)

# 先转换成 torch 能识别的 Dataset数据库(全部数据)
torch_dataset = Data.TensorDataset(x, y)

# 把 dataset 放入 DataLoader
loader = Data.DataLoader(
    dataset=torch_dataset,      # torch TensorDataset format
    batch_size=BATCH_SIZE,      # mini batch size
    shuffle=True,               # 要不要打乱数据 (打乱比较好)
    # num_workers=2,              # 多线程来读数据 CPU版本不支持
)


n_feature, n_hidden, n_output=1, 20, 1  #类的实例化
net_SGD = torch.nn.Sequential(
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)
net_Momentum = torch.nn.Sequential(
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)
net_RMSprop = torch.nn.Sequential(
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)
net_Adam = torch.nn.Sequential(
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)
#将所有网络放入list中
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]


#神经网络训练过程
# optimizer 是训练的工具
opti_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)  
opti_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum= 0.8) 
opti_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9) 
opti_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9,0.99)) 
optimizers = [opti_SGD, opti_Momentum, opti_RMSprop, opti_Adam]

loss_func = torch.nn.MSELoss()      # 预测值和真实值的误差计算公式 (均方差)
#记录误差
loss_record = [[],[],[],[]]
for epoch in range(EPOCH):   # 训练所有!整套!数据 3 次 epoch是整体数据训练次数;step是数据拆分组数
    for step, (batch_x, batch_y) in enumerate(loader):  # 每一步 loader 释放一小批数据用来学习,可以设置是否乱序
         # 打出来一些数据
        # print('Epoch: ', epoch, '| Step: ', step, '| batch x: ',
        #       batch_x, '| batch y: ', batch_y)
        
        #多个网络同时训练的方法!!!
        for net, opti, l_record in zip(nets, optimizers, loss_record):  #列表形式
            out = net(batch_x)     # 前向传播,喂给 net 训练数据 x, 输出预测值
            loss = loss_func(out, batch_y)     # 计算两者的误差:预测值在前,真实值在后
            opti.zero_grad()   # 清空上一步的梯度参数值(此步必须!!)
            loss.backward(retain_graph=True)         # 误差反向传播, 计算参数更新值  保留反向传播计算图
            opti.step()        # 参数更新,将参数更新值施加到 net 的 parameters 上 
            l_record.append(loss.data)
            
labels = ['SGD','Momentum', 'RMSprop', 'Adam']
for i, l_record in enumerate(loss_record):
    plt.plot(l_record, label = labels[i])
plt.legend(loc='best')
plt.xlabel('step')   #step = (1000/20)*12 =600
plt.ylabel('loss')
plt.ylim(0, 0.2)
plt.show() 

4 回归:关系拟合

4.1 神经网络搭建

  • 建立数据集

​ 使用Torch中的随机生成简单训练数据集,y数据增加一些噪声。

  • 建立神经网络

​ 继承torch中的torch.nn.Module,. 先定义所有的层属性(__init__()), 然后再一层层搭建(forward(x))层于层的关系链接。

模块 — PyTorch 2.1 文档

在这里插入图片描述

  • 训练网络

​ 训练网络包括前向传播、后向传播、参数更新。

每5轮可视化一次,保持窗口停顿。

Matplotlib API:

Using Matplotlib — Matplotlib 3.8.2 documentation

4.2 代码展示

Regression.py

import torch
#import torch.autograd   #variable变量
import torch.nn.functional as F    #激励函数库
import matplotlib.pyplot as plt

#建立数据库
x = torch.unsqueeze(torch.linspace(-1, 1, 100, requires_grad=True), dim=1)  # x data (tensor), shape=(100, 1) unsqueeze升维
y = x.pow(2) + 0.2*torch.rand(x.size())                 # noisy y data (tensor), shape=(100, 1)


# 画图
# plt.scatter(x.data, y.data)
# plt.show()

#建立神经网络
class Net(torch.nn.Module):  # 继承 torch 的 Module
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()     # 继承 __init__ 功能
        # 定义每层用什么样的形式
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # 隐藏层线性输出模块
        self.predict = torch.nn.Linear(n_hidden, n_output)   # 输出层线性输出模块

    def forward(self, x):   # 这同时也是 Module 中的 forward 功能
        # 正向传播输入值, 神经网络分析出输出值
        x = F.relu(self.hidden(x))      # 激励函数(隐藏层的非线性值)
        x = self.predict(x)             # 输出值
        return x

net = Net(n_feature=1, n_hidden=10, n_output=1)  #类的实例化

# print(net)  # net 的结构 居然可以直接打印!
# """
# Net (
#   (hidden): Linear (1 -> 10)
#   (predict): Linear (10 -> 1)
# )
# """

#可视化
plt.ion() #设置其为实时显示
plt.show()

#神经网络训练过程
# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.2)  # 传入 net 的所有参数, 学习率,采用随机梯度下降法
loss_func = torch.nn.MSELoss()      # 预测值和真实值的误差计算公式 (均方差)

for t in range(100):
    prediction = net(x)     # 前向传播,喂给 net 训练数据 x, 输出预测值

    loss = loss_func(prediction, y)     # 计算两者的误差:预测值在前,真实值在后
    
    optimizer.zero_grad()   # 清空上一步的梯度参数值(此步必须!!)
    loss.backward(retain_graph=True)         # 误差反向传播, 计算参数更新值  保留反向传播计算图
    optimizer.step()        # 参数更新,将参数更新值施加到 net 的 parameters 上
    
    #print(t+1,loss.data)

#可视化    
    if t % 5 == 0:
        plt.cla() #清空
        plt.scatter(x.data, y.data)
        plt.plot(x.data, prediction.data, 'r-', lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % loss.data, fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)
        
plt.ioff() 
plt.show() 


5 分类:区分类别

5.1 神经网络搭建

  • 建立数据集
  • 建立神经网络
  • 训练网络

多分类问题使用误差函数:交叉熵损失 torch.nn.CrossEntropyLoss()

计算的是各个类别的离散概率分布

分类的关键步骤:

 prediction = torch.max(F.softmax(output), 1)[1]  #返回最大值的索引位置
  • 可视化训练过程

5.2 代码展示

Classification.py

import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt

# 假数据
n_data = torch.ones(100, 2, requires_grad=True)         # 数据的基本形态
x0 = torch.normal(2*n_data, 1)      # 类型0 x data (tensor), shape=(100, 1)
y0 = torch.zeros(100)               # 类型0 y data (tensor), shape=(100, )标签0
x1 = torch.normal(-2*n_data, 1)     # 类型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(100)                # 类型1 y data (tensor), shape=(100, )标签1

# 注意 x, y 数据的数据形式是一定要像下面一样 (torch.cat 是在合并数据)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)  # FloatTensor = 32-bit floating 二维数据
y = torch.cat((y0, y1), ).type(torch.LongTensor)    # LongTensor = 64-bit integer 只有浮点数据可求梯度

 # 画图
# plt.scatter(x.data[:, 0], x.data[:, 1], c=y.data, s=100, lw=0, cmap='RdYlGn')
# plt.show()

class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):    #大类的输入
        super(Net, self).__init__()  #规定动作,继承父类
        self.hidden = torch.nn.Linear(n_feature, n_hidden)
        self.predict = torch.nn.Linear(n_hidden, n_output)
        
    def forward(self, x):  
        x = F.relu(self.hidden(x))   #本层调用
        x = self.predict(x)
        return x
        
#初始化网络
net = Net(n_feature=2, n_hidden= 10, n_output=2)   #依据数据维度[0,1] 标签在第二类,为第二类

# print(net)

#训练网络
optimizer = torch.optim.SGD(net.parameters(), lr=0.002)
loss_func = torch.nn.CrossEntropyLoss()  #交叉熵误差

plt.ion()
plt.show()

for t in range(100):
    output = net(x)  #shape = (200,2)
    
    loss = loss_func(output, y)
    
    optimizer.zero_grad() #清空上一批梯度计算器
    loss.backward(retain_graph=True)
    optimizer.step()
   # print(torch.max(F.softmax(output), 1))
    if t % 2 == 0:
        plt.cla()
        prediction = torch.max(F.softmax(output), 1)[1]  #返回最大值的索引位置
        pred_y = prediction.data.squeeze() #降维度
        target_y = y.data
        plt.scatter(x.data[:, 0], x.data[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn')
        accuracy = sum(pred_y == target_y)/200.  # 预测中有多少和真实值一样
        plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)

plt.ioff()
plt.show()

6 快速搭建法

  • 方法一

继承torch中的torch.nn.Module,. 先定义所有的层属性(__init__()), 然后再一层层搭建(forward(x))层于层的关系链接。

其在forward()中才加入激励函数,优势在于可以更加个性化自己的前向传播过程

class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)
        self.predict = torch.nn.Linear(n_hidden, n_output)

    def forward(self, x):
        x = F.relu(self.hidden(x)) #!!!!!
        x = self.predict(x)
        return x

net1 = Net(1, 10, 1)   # 这是我们用这种方式搭建的 net1

"""
Net (
  (hidden): Linear (1 -> 10)
  (predict): Linear (10 -> 1)
)
"""
  • 快速搭建法

将激励函数放到网络结构中,直接调用网络定义模块

net2 = torch.nn.Sequential(
    torch.nn.Linear(1, 10),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(10, 1)
)
"""
Sequential (
  (0): Linear (1 -> 10)
  (1): ReLU ()
  (2): Linear (10 -> 1)
)
"""

7 保存提取

7.1 用法

使用**Torch.save()Torch.load()**保存提取网络或者网络参数。

在这里插入图片描述

7.2 代码展示

Store.py

  • 保存
torch.save(net1, 'net.pkl')  # 保存整个网络 保存文件名称
torch.save(net1.state_dict(), 'net_params.pkl')   # 只保存网络中的参数 ,不保存计算图(速度快, 占内存少)

  • 加载
#加载整个网络
def restore_net():
    # restore entire net1 to net2
    net2 = torch.load('net.pkl')
    prediction = net2(x)  
    
#加载网络参数:首先要建立和Net1一致的网络结构   加载参数快于加载网络
def restore_params():
    # 新建 net3
    net3 = torch.nn.Sequential(
        torch.nn.Linear(1, 10),
        torch.nn.ReLU(),
        torch.nn.Linear(10, 1)
    )

    # 将保存的参数复制到 net3
    net3.load_state_dict(torch.load('net_params.pkl'))
    prediction = net3(x)

8 批训练(DataLoader)

8.1 批训练方法

所有数据同时训练会非常缓慢,使用批次将数据划分为小块,可提升神经网络的训练速度。

批训练依赖:torch.utils.data库

torch.utils.data — PyTorch 2.1 documentation

使用**DataLoader** ,它 torch 用来包装你的数据的工具。我们将 (numpy array 或其他) 数据形式装换成 Tensor, 然后再放进这个包装器中:

  • 将全部数据放入TensorDataset数据库中

  • 将数据封装到DataLoader中

在这里插入图片描述

  • 数据训练

epoch为整体数据的训练次数,step为整体数据被拆分的批次数

s t e p = d a t a . s i z e / b a t c h _ s i z e step = data.size/batch\_size step=data.size/batch_size 向上取整

画loss-step曲线时,
s t e p = [ d a t a . s i z e b a t c h _ s i z e ] ∗ E P O C H step = [\frac{data.size}{batch\_size}]*EPOCH step=[batch_sizedata.size]EPOCH

如果不能被整除,例如十个数据点,batch_size=8,那么step=0时,训练8个数据;当Step=1时,训练2个数据。

批训练并不改变神经网络输入和输出x,y的维度和结构,也不影响神经网络的结构,改变的只是每次网络训练的数据量。

在这里插入图片描述

8.2 代码展示

Batchtrain.py

import torch
import torch.utils.data as Data
import matplotlib.pyplot as plt
torch.manual_seed(1)    # reproducible

BATCH_SIZE = 20      # 批训练的数据个数

#建立数据库
x = torch.unsqueeze(torch.linspace(-1, 1, 100, requires_grad=True), dim=1)  # x data (tensor), shape=(100, 1) unsqueeze升维 
y = x.pow(2) + 0.2*torch.rand(x.size())                 # noisy y data (tensor), shape=(100, 1)

# 1先转换成 torch 能识别的 Dataset数据库(全部数据)
torch_dataset = Data.TensorDataset(x, y)

# 2把 dataset 放入 DataLoader
loader = Data.DataLoader(
    dataset=torch_dataset,      # torch TensorDataset format
    batch_size=BATCH_SIZE,      # mini batch size
    shuffle=True,               # 要不要打乱数据 (打乱比较好)
    # num_workers=2,              # 多线程来读数据 CPU版本不支持
)


n_feature, n_hidden, n_output=1, 10, 1  #类的实例化
net = torch.nn.Sequential(
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)

#神经网络训练过程
# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.2)  # 传入 net 的所有参数, 学习率,采用随机梯度下降法
loss_func = torch.nn.MSELoss()      # 预测值和真实值的误差计算公式 (均方差)

#333
for epoch in range(100):   # 训练所有!整套!数据 3 次 epoch是整体数据训练次数;step是数据拆分组数
    for step, (batch_x, batch_y) in enumerate(loader):  # 每一步 loader 释放一小批数据用来学习,可以设置是否乱序
         # 打出来一些数据
        # print('Epoch: ', epoch, '| Step: ', step, '| batch x: ',
        #       batch_x, '| batch y: ', batch_y)
        
        # 假设这里就是你训练的地方...
        out = net(batch_x)     # 前向传播,喂给 net 训练数据 x, 输出预测值
        loss = loss_func(out, batch_y)     # 计算两者的误差:预测值在前,真实值在后
        optimizer.zero_grad()   # 清空上一步的梯度参数值(此步必须!!)
        loss.backward(retain_graph=True)         # 误差反向传播, 计算参数更新值  保留反向传播计算图
        optimizer.step()        # 参数更新,将参数更新值施加到 net 的 parameters 上 
        
    prediction_y = net(x)
  #可视化    
    if epoch % 2 == 0:
        plt.cla()
        plt.scatter(x.data, y.data)
        plt.plot(x.data, prediction_y.data, 'r-', lw=5)
        plt.text(0.5, 0, 'Loss=%.4f' % loss.data, fontdict={'size': 20, 'color':  'red'})
        plt.pause(0.1)
        
plt.ion() #设置其为实时显示
plt.show() 

9 过拟合(Overfitting)

9.1 回归分类中的过拟合

​ 过拟合即拟合过头,机器学习出现“过度自信”的情况,过于追求把误差最小化,如下图。过拟合的结果在训练集内的数据表现十分好,但在训练集外的数据表现却很差。

在这里插入图片描述

9.2 解决方法

9.2.1 增加数据量

​ 数据量增大后,可以提高拟合效果,扭曲的红线也会慢慢拉直。

9.2.2 正则化Weight_decay
9.2.2.1 原理

​ 正则化包括L1,L2,L3,L4等正则化规则。在过拟合中,神经网络的参数一般非常大或非常小。正则化将神经网络的权重加入损失函数,如果权重过大,损失函数也会增大,实现一种惩罚机制。加入正则化后,loss下降的速度会变慢,准确率Accuracy的上升速度会变慢,并且未加入正则化模型的loss和Accuracy的浮动比较大(或者方差比较大),而加入正则化的模型训练loss和Accuracy较为平滑。随着正则化的权重 λ \lambda λ越大,表现得更加平滑。

正则化公式
L1$(Y_{predict}-Y)^2+
L2$(Y_{predict}-Y)^2+\lambda

Y p r e d i c t = W X c o s t = ( Y p r e d i c t − Y ) 2 L 1 : ( Y p r e d i c t − Y ) 2 + ∣ ∣ W ∣ ∣ 1 L 2 : ( Y p r e d i c t − Y ) 2 + λ ∣ ∣ W ∣ ∣ 2 2 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ Y_{predict}=WX\\ cost = (Y_{predict}-Y)^2\\ L1:(Y_{predict}-Y)^2+||W||_1\\ L2:(Y_{predict}-Y)^2+\lambda ||W||_2^2\\ ······· Ypredict=WXcost=(YpredictY)2L1:(YpredictY)2+∣∣W1L2:(YpredictY)2+λ∣∣W22⋅⋅⋅⋅⋅⋅⋅

9.2.2.2 用法

参考:pytorch实现L2和L1正则化regularization的方法_torch loss函数怎么加正则化项-CSDN博客

  • 在optimer中设置weight_decay( λ \lambda λ)参数,实现L2正则化
optimizer = optim.Adam(model.parameters(),lr=learning_rate,weight_decay=0.01)
#weight_decay (float, optional): weight decay (L2 penalty) (default: 0)

存在的问题:

(1)一般正则化,只是对模型的权重W参数进行惩罚,而偏置参数b是不进行惩罚的,而torch.optim的优化器weight_decay参数指定的权值衰减是对网络中的所有参数,包括权值w和偏置b同时进行惩罚。很多时候如果对b 进行L2正则化将会导致严重的欠拟合,因此这个时候一般只需要对权值w进行正则即可。

(2)根据正则化公式,加入正则化后,loss会变大。比如weight_decay=1的loss为10,那么weight_decay=100时,loss输出应该也提高100倍左右。而采用torch.optim的优化器的方法,采用loss_fun= nn.CrossEntropyLoss()进行计算loss,不管怎么改变weight_decay的大小,loss会跟之前没有加正则化的大小差不多。这是因为loss_fun损失函数没有把权重W的损失加上。(这不影响正则化起作用)

  • 自定义正则化

(可行性未知)

#方法1:自定义实现正则化的Regularization类
# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# device='cuda'
print("-----device:{}".format(device))
print("-----Pytorch version:{}".format(torch.__version__))
 
 
class Regularization(torch.nn.Module):
    def __init__(self,model,weight_decay,p=2):
        '''
        :param model 模型
        :param weight_decay:正则化参数
        :param p: 范数计算中的幂指数值,默认求2范数,
                  当p=0为L2正则化,p=1为L1正则化
        '''
        super(Regularization, self).__init__()
        if weight_decay <= 0:
            print("param weight_decay can not <=0")
            exit(0)
        self.model=model
        self.weight_decay=weight_decay
        self.p=p
        self.weight_list=self.get_weight(model)
        self.weight_info(self.weight_list)
 
    def to(self,device):
        '''
        指定运行模式
        :param device: cude or cpu
        :return:
        '''
        self.device=device
        super().to(device)
        return self
 
    def forward(self, model):
        self.weight_list=self.get_weight(model)#获得最新的权重
        reg_loss = self.regularization_loss(self.weight_list, self.weight_decay, p=self.p)
        return reg_loss
 
    def get_weight(self,model):
        '''
        获得模型的权重列表
        :param model:
        :return:
        '''
        weight_list = []
        for name, param in model.named_parameters():
            if 'weight' in name:
                weight = (name, param)
                weight_list.append(weight)
        return weight_list
 
    def regularization_loss(self,weight_list, weight_decay, p=2):
        '''
        计算张量范数
        :param weight_list:
        :param p: 范数计算中的幂指数值,默认求2范数
        :param weight_decay:
        :return:
        '''
        # weight_decay=Variable(torch.FloatTensor([weight_decay]).to(self.device),requires_grad=True)
        # reg_loss=Variable(torch.FloatTensor([0.]).to(self.device),requires_grad=True)
        # weight_decay=torch.FloatTensor([weight_decay]).to(self.device)
        # reg_loss=torch.FloatTensor([0.]).to(self.device)
        reg_loss=0
        for name, w in weight_list:
            l2_reg = torch.norm(w, p=p)
            reg_loss = reg_loss + l2_reg
 
        reg_loss=weight_decay*reg_loss
        return reg_loss
 
    def weight_info(self,weight_list):
        '''
        打印权重列表信息
        :param weight_list:
        :return:
        '''
        print("---------------regularization weight---------------")
        for name ,w in weight_list:
            print(name)
        print("---------------------------------------------------")      

#使用
# 初始化正则化
if weight_decay>0:
   reg_loss=Regularization(model, weight_decay, p=2).to(device)
else:
   print("no regularization")

if weight_decay > 0:
   loss = loss + reg_loss(model)
total_loss = loss.item()
9.2.3 Dropout机制
9.2.3.1 原理

​ 专门用于神经网络的正则化方法。在训练时, 随机忽略掉一些神经元和神经联结(将这些权重置0) , 是这个神经网络变得”不完整”. 用一个不完整的神经网络训练一次;再次训练时,训练另一个不完整的神经网络。使得每次训练结果都不会依赖于其中某部分特定的神经元。

注意:只在训练net_overfitting.train()的时候开启Dropout,而测试的时候是不用Dropout的,测试使用全部神经元net_overfitting.eval()

9.2.3.2 用法

API:torch.nn — PyTorch 2.1 documentation

在搭建神经网络时,在全连接层后加入稀疏矩阵块。实际操作是随机将权重张量的元素置0,抽象思维是在神经网络中加入稀疏层。

net_dropped = torch.nn.Sequential(
    torch.nn.Linear(1, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, 1),
)
9.2.3.3 代码展示

Dropout.py

在这里插入图片描述

import torch
import matplotlib.pyplot as plt

torch.manual_seed(1)    # reproducible

N_SAMPLES = 20
N_HIDDEN = 300

# training data
x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES,requires_grad=True), 1)
y = x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))

# test data
test_x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES,requires_grad=True), 1)
test_y = test_x + 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))

# # show data
# plt.scatter(x.data, y.data, c='magenta', s=50, alpha=0.5, label='train')
# plt.scatter(test_x.data, test_y.data, c='cyan', s=50, alpha=0.5, label='test')
# plt.legend(loc='upper left')
# plt.ylim((-2.5, 2.5))
# plt.show()

#无dropout
net_overfitting = torch.nn.Sequential(
    torch.nn.Linear(1, N_HIDDEN),
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, N_HIDDEN),
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, 1),
)
#有Dropout
net_dropped = torch.nn.Sequential(
    torch.nn.Linear(1, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, N_HIDDEN),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(N_HIDDEN, 1),
)

optimizer_ofit = torch.optim.Adam(net_overfitting.parameters(), lr=0.01)
optimizer_drop = torch.optim.Adam(net_dropped.parameters(), lr=0.01)
loss_func = torch.nn.MSELoss()

for t in range(500):
    pred_ofit = net_overfitting(x)
    pred_drop = net_dropped(x)

    loss_ofit = loss_func(pred_ofit, y)
    loss_drop = loss_func(pred_drop, y)

    optimizer_ofit.zero_grad()
    optimizer_drop.zero_grad()
    loss_ofit.backward()
    loss_drop.backward()
    optimizer_ofit.step()
    optimizer_drop.step()

     # 接着上面来
    if t % 10 == 0:     # 每 10 步画一次图
        # 将神经网络转换成测试形式, 画好图之后改回 训练形式
        net_overfitting.eval()  #改为测试模式
        net_dropped.eval()  # 因为 drop 网络在 train 的时候和 test 的时候参数不一样.

        # plotting
        plt.cla()
        test_pred_ofit = net_overfitting(test_x)
        test_pred_drop = net_dropped(test_x)
        plt.scatter(x.data, y.data, c='magenta', s=50, alpha=0.3, label='train')
        plt.scatter(test_x.data, test_y.data, c='cyan', s=50, alpha=0.3, label='test')
        plt.plot(test_x.data, test_pred_ofit.data, 'r-', lw=3, label='overfitting')
        plt.plot(test_x.data, test_pred_drop.data, 'b--', lw=3, label='dropout(50%)')
        plt.text(0, -1.2, 'overfitting loss=%.4f' % loss_func(test_pred_ofit, test_y).data.numpy(), fontdict={'size': 20, 'color':  'red'})
        plt.text(0, -1.5, 'dropout loss=%.4f' % loss_func(test_pred_drop, test_y).data.numpy(), fontdict={'size': 20, 'color': 'blue'})
        plt.legend(loc='upper left'); plt.ylim((-2.5, 2.5));plt.pause(0.1)

        # 将两个网络改回 训练形式
        net_overfitting.train()  #改为测试模式
        net_dropped.train()

10 批标准化(Batch Normalization)

10.1 原理

参考论文:《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》

Pytorch学习笔记(8):正则化(L1、L2、Dropout)与归一化(BN、LN、IN、GN)_nn.instancenorm3d-CSDN博客

​ 批标准化与数据标准化类似,其对每一批数据进行归一化,对每一层前向传播数据进行标准化,其可加快模型的收敛速度,并可一定程度缓解深度网络中的**“梯度弥散”问题**,使得训练模型更容易和稳定。BN能减少参数初始化的要求 (initialization)

​ 如果没有标准化,在经过激励函数后,X之间较大的差距不能在WX中体现,神经网络识别能力减弱。例如:
W X = 0.1 ∗ 1 = 0.1   t a n h ( W X ) = 0.1 W X = 0.1 ∗ 20 = 2   t a n h ( W X ) = 0.96 W X = 0.1 ∗ 1000 = 100   t a n h ( W X ) = 1 无法区别 X = 20 和 X = 1000 WX=0.1*1=0.1\ tanh(WX)=0.1\\ WX=0.1*20=2\ tanh(WX)=0.96\\ WX=0.1*1000=100\ tanh(WX)=1\\ 无法区别X=20和X=1000 WX=0.11=0.1 tanh(WX)=0.1WX=0.120=2 tanh(WX)=0.96WX=0.11000=100 tanh(WX)=1无法区别X=20X=1000
而BN层可使得每一层值都在有效得传递下去。

在这里插入图片描述

神奇之处:

BN层自己有调节机制,包含反向操作(最后1步), 将 normalize 后的数据再扩展和平移。 让神经网络自己去学着使用和修改扩展参数 γ \gamma γ 和平移参数 β \beta β, 这样神经网络就能自己判断 normalization 操作是否有优化作用。如果没有起到作用, 就使用 γ \gamma γ β \beta β来抵消一些 normalization 的操作.

在这里插入图片描述

10.2 用法

API:torch.nn — PyTorch 2.1 documentation

BN层添加在每一个全连接层和激励函数层之间。

##定义批标准化层
torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)
#num_features:C一个样本特征维度,即m W(m,n)
#eps:分母修正项,为数值稳定性而加到分母上的值,一般设置比较小的数:1e的-5次方,防止除以0导致错误
#momentum:移动平均的动量值(通常设置为0.1)
#affine:是否需要反向学习映射affine transform(默认为打开)
#track_running_states:是训练状态,还是测试状态。(如果在训练状态,均值、方差需要重新估计;如果在测试状态,会采用当前的统计信息,均值、方差固定的,但训练时这两个数据是会根据batch发生改变。)

#使用
net = torch.nn.Sequential(
    torch.nn.BatchNorm1d(1, eps=1e-05, momentum=0.1, affine=True), #标准化输入
    torch.nn.Linear(n_feature, n_hidden),
    torch.nn.BatchNorm1d(n_hidden, eps=1e-05, momentum=0.1, affine=True),
    torch.nn.ReLU(), #激励函数的库不同 !!!
    torch.nn.Linear(n_hidden, n_output)
)

pytorch定义了三种BatchNorm方法:

BatchNorm1dBatchNorm2dBatchNorm3d
a 2D or 3D inputa 4D inputa 5D input
输入(N,C) or(N,C,L)(N,C,H,W)(N,C,D,H,W)
输出(N,C) or (N,C,L)(N,C,H,W)(N,C,D,H,W)

注意:N is the batch size, C is the number of features or channels, and L is the sequence length。

10.3 代码展示

Batchnormalization.py

激励函数前后与批标准化前后:

relu()tanh()
imgimg
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
import torch
from torch import nn
from torch.nn import init
import torch.utils.data as Data
import matplotlib.pyplot as plt
import numpy as np

# torch.manual_seed(1)    # reproducible
# np.random.seed(1)

# Hyper parameters
N_SAMPLES = 2000
BATCH_SIZE = 64
EPOCH = 12
LR = 0.03
N_HIDDEN = 8
ACTIVATION = torch.tanh
B_INIT = -0.2   # use a bad bias constant initializer

# training data
train_x = torch.linspace(-7, 10, N_SAMPLES, requires_grad=True)[:, np.newaxis]
noise = torch.normal(0, 2, train_x.shape, requires_grad=True)
train_y = torch.square(train_x) - 5 + noise

# test data
test_x = torch.linspace(-7, 10, 200, requires_grad=True)[:, np.newaxis]
noise = torch.normal(0, 2, test_x.shape, requires_grad=True)
test_y = torch.square(test_x) - 5 + noise


train_dataset = Data.TensorDataset(train_x, train_y)
train_loader = Data.DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)


# show data
# plt.scatter(train_x.data, train_y.data, c='#FF9359', s=50, alpha=0.2, label='train')
# plt.legend(loc='upper left')


class Net(nn.Module):
    def __init__(self, batch_normalization=False):
        super(Net, self).__init__()
        self.do_bn = batch_normalization
        self.fcs = []  #数据载体
        self.bns = []
        self.bn_input = nn.BatchNorm1d(1, momentum=0.5)   # for input data

        for i in range(N_HIDDEN):               # build hidden layers and BN layers
            input_size = 1 if i == 0 else 10
            fc = nn.Linear(input_size, 10)
            setattr(self, 'fc%i' % i, fc)       # IMPORTANT set layer to the Module
            self._set_init(fc)                  # parameters initialization
            self.fcs.append(fc)
            if self.do_bn:
                bn = nn.BatchNorm1d(10, momentum=0.5)
                setattr(self, 'bn%i' % i, bn)   # IMPORTANT set layer to the Module
                self.bns.append(bn)

        self.predict = nn.Linear(10, 1)         # output layer
        self._set_init(self.predict)            # parameters initialization

    def _set_init(self, layer):  #神经网络的初始化参数
        init.normal_(layer.weight, mean=0., std=.1)
        init.constant_(layer.bias, B_INIT)

    def forward(self, x):
        pre_activation = [x]
        if self.do_bn: x = self.bn_input(x)     # input batch normalization
        layer_input = [x]
        for i in range(N_HIDDEN):
            x = self.fcs[i](x)
            pre_activation.append(x)
            if self.do_bn: x = self.bns[i](x)   # batch normalization
            x = ACTIVATION(x)
            layer_input.append(x)
        out = self.predict(x)
        return out, layer_input, pre_activation

nets = [Net(batch_normalization=False), Net(batch_normalization=True)]

# print(*nets)    # print net architecture

opts = [torch.optim.Adam(net.parameters(), lr=LR) for net in nets]

loss_func = torch.nn.MSELoss()


def plot_histogram(l_in, l_in_bn, pre_ac, pre_ac_bn):
    for i, (ax_pa, ax_pa_bn, ax, ax_bn) in enumerate(zip(axs[0, :], axs[1, :], axs[2, :], axs[3, :])):
        [a.clear() for a in [ax_pa, ax_pa_bn, ax, ax_bn]]
        if i == 0:
            p_range = (-7, 10);the_range = (-7, 10)
        else:
            p_range = (-4, 4);the_range = (-1, 1)
        ax_pa.set_title('L' + str(i))
        ax_pa.hist(pre_ac[i].data.numpy().ravel(), bins=10, range=p_range, color='#FF9359', alpha=0.5);ax_pa_bn.hist(pre_ac_bn[i].data.numpy().ravel(), bins=10, range=p_range, color='#74BCFF', alpha=0.5)
        ax.hist(l_in[i].data.numpy().ravel(), bins=10, range=the_range, color='#FF9359');ax_bn.hist(l_in_bn[i].data.numpy().ravel(), bins=10, range=the_range, color='#74BCFF')
        for a in [ax_pa, ax, ax_pa_bn, ax_bn]: a.set_yticks(());a.set_xticks(())
        ax_pa_bn.set_xticks(p_range);ax_bn.set_xticks(the_range)
        axs[0, 0].set_ylabel('PreAct');axs[1, 0].set_ylabel('BN PreAct');axs[2, 0].set_ylabel('Act');axs[3, 0].set_ylabel('BN Act')
    plt.pause(0.01)


if __name__ == "__main__":
    f, axs = plt.subplots(4, N_HIDDEN + 1, figsize=(10, 5))
    plt.ion()  # something about plotting
    plt.show()

    # training
    losses = [[], []]  # recode loss for two networks

    for epoch in range(EPOCH):
        print('Epoch: ', epoch)
        layer_inputs, pre_acts = [], []
        for net, l in zip(nets, losses):
            net.eval()              # set eval mode to fix moving_mean and moving_var
            pred, layer_input, pre_act = net(test_x)
            l.append(loss_func(pred, test_y).data.item())
            layer_inputs.append(layer_input)
            pre_acts.append(pre_act)
            net.train()             # free moving_mean and moving_var
        plot_histogram(*layer_inputs, *pre_acts)     # plot histogram

        for step, (b_x, b_y) in enumerate(train_loader):
            for net, opt in zip(nets, opts):     # train for each network
                pred, _, _ = net(b_x)
                loss = loss_func(pred, b_y)
                opt.zero_grad()
                loss.backward()
                opt.step()    # it will also learns the parameters in Batch Normalization

    plt.ioff()

    # plot training loss
    plt.figure(2)
    plt.plot(losses[0], c='#FF9359', lw=3, label='Original')
    plt.plot(losses[1], c='#74BCFF', lw=3, label='Batch Normalization')
    plt.xlabel('step');plt.ylabel('test loss');plt.ylim((0, 2000));plt.legend(loc='best')

    # evaluation
    # set net to eval mode to freeze the parameters in batch normalization layers
    [net.eval() for net in nets]    # set eval mode to fix moving_mean and moving_var
    preds = [net(test_x)[0] for net in nets]
    plt.figure(3)
    plt.plot(test_x.data.numpy(), preds[0].data.numpy(), c='#FF9359', lw=4, label='Original')
    plt.plot(test_x.data.numpy(), preds[1].data.numpy(), c='#74BCFF', lw=4, label='Batch Normalization')
    plt.scatter(test_x.data.numpy(), test_y.data.numpy(), c='r', s=50, alpha=0.2, label='train')
    plt.legend(loc='best')
    plt.show()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值