深度学习基础

一、神经网络

        

        上图表示 数据传输进来 算出所有特征和权重的乘积之和 再进过激活函数进一步拟合得到output 

        上图中 x1,x2,x3,表示的是特征,而每一个圆圈表示的是一个神经元,每一个神经元等于w1x1+w2x2+w3x3,每一条线代表一个权重值,反向传播实际上更新就是每条线的权重值

 二、激活函数

       

         常见的激活函数有 sigmoid函数、tanh函数、ReLu函数、softmax函数

        以sigmoid函数为例: 

import torch
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] =['SimHei'] # 用来显示正常中文标签
plt.rcParams['axes.unicode_minus'] =False # 用来正常显示符号
# 创建画布和坐标轴
_, axes = plt.subplots(1, 2) # 一行两列
# sigmoid函数图像
x = torch.linspace(-20, 20, 1000)
# 输入值x通过sigmoid函数转换成激活值y
y = torch.sigmoid(x)
axes[0].plot(x, y)
axes[0].grid()
axes[0].set_title('Sigmoid 函数图像')

# sigmoid导数图像
x = torch.linspace(-20, 20, 1000, requires_grad=True)
# print(x)
torch.sigmoid(x).sum().backward()
y=torch.sigmoid(x).sum()
print(y)
# x.detach():输入值x的数值
# x.grad:计算梯度,求导
axes[1].plot(x.detach(), x.grad)
axes[1].grid()
axes[1].set_title('Sigmoid 导数图像')
plt.show()

torch.sigmoid(x).sum().backward() 为什么要sun()

在PyTorch中,当你对一个张量(tensor)调用.backward()方法时,你实际上是在尝试计算关于该张量的梯度。但是,要正确理解.sum().backward()的用途,我们需要先了解PyTorch的自动微分(autograd)系统是如何工作的。

  1. Scalar Outputs:在PyTorch中,为了计算梯度,你通常需要有一个scalar(标量)输出。这是因为梯度是关于某个scalar值相对于输入张量的变化的敏感度。如果你有一个多元素(non-scalar)输出,那么直接调用.backward()可能会引发错误,因为系统不知道你想要哪个元素的梯度。
  2. .sum()的作用:当你对一个多元素张量调用.sum()时,你将得到一个scalar值(除非你在其他维度上指定了keepdim=True,但这不是通常的用法)。这个scalar值是原始张量所有元素的总和。

在给出的例子中,torch.sigmoid(x).sum()计算了x经过sigmoid激活函数后的所有元素的总和。然后,当对这个scalar值调用.backward()时,实际上是在询问:“如果我稍微改变x的值,这个总和会如何变化?” 这将为我提供关于x的每个元素的梯度,这些梯度表示了每个元素对最终scalar输出的贡献。

简而言之,.sum()的使用是为了将多元素输出转换为scalar输出,从而允许我计算梯度。如果你直接对多元素输出调用.backward()(没有使用.sum()或其他聚合操作),PyTorch将无法知道你想要哪个元素的梯度,因此会报错

backward()方法来计算梯度,自动微分。这会将x的梯度(即tanh的导数)存储在x.grad中。

三、参数初始化 

        

        常用初始化方法: 

  

  • 均匀分布初始化

​ 权重参数初始化从区间均匀随机取值。即在(-1/√d,1/√d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量

  • 正态分布初始化

​ 随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化

  • 全0初始化

​ 将神经网络中的所有权重参数初始化为 0

  • 全1初始化

​ 将神经网络中的所有权重参数初始化为 1.

  • 固定值初始化

​ 将神经网络中的所有权重参数初始化为某个固定值.

  • Kaiming 初始化,也叫做 HE 初始化

HE 初始化分为正态分布的 HE 初始化、均匀分布的 HE 初始化.

  • 正态化的he初始化

    • stddev = sqrt(2 / fan_in)
  • 均匀分布的he初始化

    • 它从 [-limit,limit] 中的均匀分布中抽取样本, limit是 sqrt(6 / fan_in)
  • fan_in 输入神经元的个数

  • Xavier 初始化,也叫做 Glorot初始化

该方法也有两种,一种是正态分布的 xavier 初始化、一种是均匀分布的 xavier 初始化.

  • 正态化的Xavier初始化

    • stddev = sqrt(2 / (fan_in + fan_out))
  • 均匀分布的Xavier初始化

    • [-limit,limit] 中的均匀分布中抽取样本, limit 是 sqrt(6 / (fan_in + fan_out))
  • fan_in 是输入神经元的个数, fan_out 是输出的神经元个数

        我们一般用的是最后两个,也就是kaiming初始化和Xavier初始化 ,参数初始化的时候,会连带偏置值一起初始化,所以一般偏置值不用自己手动设置。

import torch
import torch.nn.functional as F
import torch.nn as nn

# 1. 均匀分布随机初始化
def test01():
    linear = nn.Linear(5, 3)
    # 从0-1均匀分布产生参数
    nn.init.uniform_(linear.weight)
    print(linear.weight.data)

# 2.固定初始化
def test02():
    linear = nn.Linear(5, 3)
    nn.init.constant_(linear.weight, 5)
    print(linear.weight.data)

# 3. 全0初始化
def test03():
    linear = nn.Linear(5, 3)
    nn.init.zeros_(linear.weight)
    print(linear.weight.data)

# 4. 全1初始化
def test04():
    linear = nn.Linear(5, 3)
    nn.init.ones_(linear.weight)
    print(linear.weight.data)

# 5. 正态分布随机初始化
def test05():
    linear = nn.Linear(5, 3)
    nn.init.normal_(linear.weight, mean=0, std=1)
    print(linear.weight.data)

# 6. kaiming 初始化
def test06():
    # kaiming 正态分布初始化
    linear = nn.Linear(5, 3)
    nn.init.kaiming_normal_(linear.weight)
    print(linear.weight.data)

    # kaiming 均匀分布初始化
    linear = nn.Linear(5, 3)
    nn.init.kaiming_uniform_(linear.weight)
    print(linear.weight.data)

# 7. xavier 初始化
def test07():
    # xavier 正态分布初始化
    linear = nn.Linear(5, 3)
    nn.init.xavier_normal_(linear.weight)
    print(linear.weight.data)

    # xavier 均匀分布初始化
    linear = nn.Linear(5, 3)
    nn.init.xavier_uniform_(linear.weight)
    print(linear.weight.data)

 四、神经网络的搭建(重点)

        

在pytorch中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法(一个继承 两个方法):

  • __init__方法中定义网络中的层结构,主要是全连接层,并进行初始化

  • forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中可以定义学习率,为初始化定义的layer传入数据等。

我们来构建如下图所示的神经网络模型:

编码设计如下:

  1. 第1个隐藏层:权重初始化采用标准化的xavier初始化 激活函数使用sigmoid

  2. 第2个隐藏层:权重初始化采用标准化的He初始化 激活函数采用relu

  3. out输出层线性层 假若二分类,采用softmax做数据归一化

import torch
import torch.nn as nn
from torchsummary import summary
# 创建神经网络模型类
class Net(nn.Module):
#初始化属性值
    def __init__(self):
        super(Net, self).__init__()#调用父类的初始化属性值
        self.linear1 = nn.Linear(3, 3)
        #初始化权重
        nn.init.xavier_normal_(self.linear1.weight)#会自动初始化偏置
        self.linear2 = nn.Linear(3, 2)
        nn.init.kaiming_normal_(self.linear2.weight)
        self.out = nn.Linear( 2, 2)

        # 创建向前传播方法 自动执行forward的方法
    def forward(self, x):
        # 数据进入第一层
        x = self.linear1(x)
        # 使用sigmoid激活函数
        x = torch.sigmoid(x)
        # 数据进入第二层
        x = self.linear2(x)
        # 使用relu激活函数
        x = torch.relu(x)
        # 数据进入输出层
        x = self.out(x)
        # 使用softmax激活函数
        # dim=-1: 每一维度行数据相加为1
        x = torch.softmax(x,-1)
        # 返回结果
        return x

if __name__ == '__main__':
    my_model = Net()
    # 产生随机数据
    my_data = torch.randn(5,3)
    print('mydata-shape', my_data.shape)
    # 数据经过神经网络模型训练
    my_out = my_model(my_data)
    print('my_out-shape', my_out.shape)
    print('my_out', my_out)
    # 打印神经网络模型结构
    summary(my_model, input_size=(5,3))
    # 打印神经网络模型参数
    print(my_model.state_dict())
    # 打印神经网络模型参数
    print(my_model.state_dict().keys())
    # 打印神经网络模型参数
    print(my_model.state_dict()['linear1.weight'])
    # 打印神经网络模型参数
    print(my_model.state_dict()['linear1.bias'])
    # 打印神经网络模型参数
    print(my_model.state_dict()['linear2.weight'])
    # 打印神经网络模型参数
    print(my_model.state_dict()['linear2.bias'])
    # 打印神经网络模型参数
    print(my_model.state_dict()['out.weight'])
    # 打印神经网络模型参数
    print(my_model.state_dict()['out.bias'])

summary(my_model, input_size=(5,3)) 这个函数是输出模型的权重情况,input_size(5,3)表示传入五个样本,每个样本3个特征

五、损失函数

        

什么是损失函数

在深度学习中, 损失函数是用来衡量模型参数的质量的函数, 衡量的方式是比较网络输出和真实输出的差异:

在多分类任务通常使用softmax将logits转换为概率的形式,所以多分类的交叉熵损失也叫做softmax损失,它的计算方法是:

1.多分类损失: 

其中:

1.y 是样本 x 属于某一个类别的真实概率

2.而 f(x) 是样本属于某一类别的预测分数

3.S 是 softmax 激活函数,将属于某一类别的预测分数转换成概率

4.L 用来衡量真实值 y 和预测值 f(x) 之间差异性的损失结果

在pytorch中使用nn.CrossEntropyLoss()实现,如下所示:

import torch
from torch import nn

# 多分类交叉熵损失,使用nn.CrossEntropyLoss()实现。nn.CrossEntropyLoss()=softmax + 损失计算
def test1():
    # 设置真实值: 可以是热编码后的结果也可以不进行热编码
    # y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32)
    # 注意的类型必须是64位整型数据
    y_true = torch.tensor([1, 2], dtype=torch.int64)
    y_pred = torch.tensor([[0.2, 0.6, 0.2], [0.1, 0.8, 0.1]], dtype=torch.float32)
    # 实例化交叉熵损失
    loss = nn.CrossEntropyLoss()
    # 计算损失结果
    my_loss = loss(y_pred, y_true).numpy()
    print('loss:', my_loss)

2.二分类损失

在处理二分类任务时,我们不再使用softmax激活函数,而是使用sigmoid激活函数,那损失函数也相应的进行调整,使用二分类的交叉熵损失函数:

其中:

  1. y 是样本x属于某一个类别的真实概率

  2. 而 y^ 是样本属于某一类别的预测概率

  3. L 用来衡量真实值y与预测值y^之间差异性的损失结果。

在pytorch中实现时使用nn.BCELoss() ,如下所示:

import torch
from torch import nn

def test2():
    # 1 设置真实值和预测值
    # 预测值是sigmoid输出的结果
    y_pred = torch.tensor([0.6901, 0.5459, 0.2469], requires_grad=True)
    y_true = torch.tensor([0, 1, 0], dtype=torch.float32)
    # 2 实例化二分类交叉熵损失
    criterion = nn.BCELoss()
    # 3 计算损失
    my_loss = criterion(y_pred, y_true).detach().numpy()
    print('loss:', my_loss)

3.回归任务损失函数-MAE损失

Mean absolute loss(MAE)也被称为L1 Loss,是以绝对误差作为距离。损失函数公式:

曲线如下图所示:

特点是:

  1. 由于L1 loss具有稀疏性,为了惩罚较大的值,因此常常将其作为

正则项添加到其他loss中作为约束。

  1. L1 loss的最大问题是梯度在零点不平滑,导致会跳过极小值。

在pytorch中使用nn.L1Loss()实现,如下所示:

import torch
from torch import nn

def test3():
    # 1 设置真实值和预测值
    y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)
    y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)
    # 2 实例MAE损失对象
    loss = nn.L1Loss()
    # 3 计算损失
    my_loss = loss(y_pred, y_true).detach().numpy()
    print('loss:', my_loss)

4.回归任务损失函数-MSE损失

Mean Squared Loss/ Quadratic Loss(MSE loss)也被称为L2 loss,或欧氏距离,它以误差的平方和的均值作为距离

损失函数公式:

曲线如下图所示:

特点是:

1.L2 loss也常常作为正则项。

2.当预测值与目标值相差很大时, 梯度容易爆炸。

在pytorch中使用nn.MSELoss()实现,如下所示:

import torch
from torch import nn

def test4():
    # 1 设置真实值和预测值
    y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)
    y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)
    # 2 实例MSE损失对象
    loss = nn.MSELoss()
    # 3 计算损失
    my_loss = loss(y_pred, y_true).detach().numpy()
    print('myloss:', my_loss)

 5.回归任务损失函数-Smooth L1损失

        

smooth L1说的是光滑之后的L1。损失函数公式:

其中:𝑥 = f(x) − y 为真实值和预测值的差值。

从下图中可以看出,该函数实际上就是一个分段函数

  1. 在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题

  2. 在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题

在pytorch中使用nn.SmoothL1Loss()实现,如下所示:

import torch
from torch import nn

def test5():
    # 1 设置真实值和预测值
    y_true = torch.tensor([0, 3])
    y_pred = torch.tensor([0.6, 0.4], requires_grad=True)
    # 2 实例化smoothL1损失对象
    loss = nn.SmoothL1Loss()
    # 3 计算损失
    my_loss = loss(y_pred, y_true).detach().numpy()
    print('loss:', my_loss)

六、网络优化方法

1. 梯度下降算法

梯度下降法是一种寻找使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向,所以有:

其中,η是学习率,如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。 

 

2.方向传播算法(BP算法)

前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。

反向传播(Back Propagation):利用损失函数 ERROR,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新,链式求导

 

import torch
from torch import nn
from torch import optim

# 创建神经网络类
class Model(nn.Module):
    # 初始化参数
    def __init__(self):
        # 调用父类方法
        super(Model, self).__init__()
        # 创建网络层
        self.linear1 = nn.Linear(2, 2)
        self.linear2 = nn.Linear(2, 2)
        # 初始化神经网络参数
        self.linear1.weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]])
        self.linear2.weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]])
        self.linear1.bias.data = torch.tensor([0.35, 0.35])
        self.linear2.bias.data = torch.tensor([0.60, 0.60])

    # 前向传播方法
    def forward(self, x):
        # 数据经过第一层隐藏层
        x = self.linear1(x)
        # 计算第一层激活值
        x = torch.sigmoid(x)
        # 数据经过第二层隐藏层
        x = self.linear2(x)
        # 计算第二层激活值
        x = torch.sigmoid(x)
        return x    
if __name__ == '__main__':
    # 定义网络输入值和目标值
    inputs = torch.tensor([[0.05, 0.10]])
    target = torch.tensor([[0.01, 0.99]])
    # 实例化神经网络对象
    model = Model()
    output = model(inputs)
    print("output-->", output)
    loss = torch.sum((output - target) ** 2) / 2    # 计算误差
    print("loss-->", loss)
    # 优化方法和反向传播算法
    optimizer = optim.SGD(model.parameters(), lr=0.5)
    optimizer.zero_grad()
    loss.backward()
    print("w1,w2,w3,w4-->", model.linear1.weight.grad.data)
    print("w5,w6,w7,w8-->", model.linear2.weight.grad.data)
    optimizer.step()
    # 打印神经网络参数
    print(model.state_dict())

 3.梯度下降优化方法

动量算法

 通过对梯度指数加权平均 让每一个梯度点的计算参考最近的梯度值,让梯度下降曲线更加平滑,有利于梯度下降的效率。

梯度计算公式:Dt = β * St-1 + (1- β) * Wt

  1. St-1 表示历史梯度移动加权平均值

  2. Wt 表示当前时刻的梯度值

  3. Dt 为当前时刻的指数加权平均梯度值

  4. β 为权重系数

假设:权重 β 为 0.9,例如:

  • 第一次梯度值:s1 = d1 = w1

  • 第二次梯度值:d2=s2 = 0.9 * s1 + w2 * 0.1

  • 第三次梯度值:d3=s3 = 0.9 * s2 + w3 * 0.1

  • 第四次梯度值:d4=s4 = 0.9 * s3 + w4 * 0.1

adagrad

AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小

其计算步骤如下:

1.初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6

2.初始化梯度累积变量 s = 0

3.从训练集中采样 m 个样本的小批量,计算梯度 g

4.累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘

学习率 α 的计算公式如下:

参数更新公式如下:

重复 2-4 步骤,即可完成网络训练。

AdaGrad 缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。

RMSProp

RMSProp 优化算法是对 AdaGrad 的优化. 最主要的不同是,其使用指数移动加权平均梯度替换历史梯度的平方和。其计算过程如下:

  1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6

  2. 初始化参数 θ

  3. 初始化梯度累计变量 s

  4. 从训练集中采样 m 个样本的小批量,计算梯度 g

  5. 使用指数移动平均累积历史梯度,公式如下:

学习率 α 的计算公式如下:

参数更新公式如下:

Adam 

  • Momentum 使用指数加权平均计算当前的梯度值

  • AdaGrad、RMSProp 使用自适应的学习率

  • Adam优化算法(Adaptive Moment Estimation,自适应矩估计)将 Momentum 和 RMSProp 算法结合在一起。

  • 修正梯度: 使⽤梯度的指数加权平均

  • 修正学习率: 使⽤梯度平⽅的指数加权平均。

import torch
import matplotlib.pyplot as plt

def test06():
    # 1 初始化权重参数
    w = torch.tensor([1.0], requires_grad=True)
    y = ((w ** 2) / 2.0).sum()
    # 2 实例化优化方法:Adam算法,其中betas是指数加权的系数
    optimizer = torch.optim.Adam([w], lr=0.01, betas=[0.9, 0.99])
    # 3 第1次更新 计算梯度,并对参数进行更新
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))
    # 4 第2次更新 计算梯度,并对参数进行更新
    # 使用更新后的参数机选输出结果
    y = ((w ** 2) / 2.0).sum()
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy()))

 七、正则化方法

1. Dropout正则化 

import torch
import torch.nn as nn

def test():
    # 初始化随机失活层
    dropout = nn.Dropout(p=0.4)
    # 初始化输入数据:表示某一层的weight信息
    inputs = torch.randint(0, 10, size=[1, 4]).float()
    layer = nn.Linear(4,5)
    y = layer(inputs)
    print("未失活FC层的输出结果:\n", y)

    y =  dropout(y)
    print("失活后FC层的输出结果:\n", y)

 2.批量归一化

 

先对数据标准化,再对数据重构(缩放+平移),如下所示:

1.λ 和 β 是可学习的参数,它相当于对标准化后的值做了一个线性变换λ 为系数,β 为偏置;

2.eps 通常指为 1e-5,避免分母为 0;

3.E(x) 表示变量的均值;

4.Var(x) 表示变量的方差

 

 

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值