深度学习—参数初始化及激活函数Day35

1.模型的保存和加载

python 复制代码
import torch
import torch.nn as nn

class MyModle(nn.Module):
    def __init__(self, input_size, output_size):
        super(MyModle, self).__init__()
        self.fc1 = nn.Linear(input_size, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(x)
        output = self.fc3(x)
        return output

def train():
    model = MyModle(10,5)
    model_dict = model.state_dict()
    torch.save(model_dict,'data/model.pt')
def detect():
    #'cuda:0' cuda
    # model = torch.load('data/model',map_location=torch.device('cpu'))
    # print(model)
    model = MyModle(10,5)
    model_dict = torch.load('data/model.pt')
    model.load_state_dict(model_dict)


if __name__ == '__main__':
    train()

MyModel 类定义了一个简单的前馈神经网络模型。这个模型包括三个全连接层 (nn.Linear),用于从输入数据到输出数据的转换。

模型的结构如下:

输入层到第一个隐藏层:输入大小为 input_size,输出大小为 128。

第一个隐藏层到第二个隐藏层:输入大小为 128,输出大小为 64。

第二个隐藏层到输出层:输入大小为 64,输出大小为 output_size。

forward 方法定义了模型的数据流动过程,即如何从前一层传递到下一层,并最终产生输出。

其中input_size为输入数据维度,128为第一个全连接层的输出特征数量(也就是第二个全连接层的输入特征数量),64为第二个全连接层的输出特征数量(第三个全连接层的输入特征数量),然后将其转换为outp_size维度的输出数据。128和64分布为不同隐藏层的神经元数量。

train 函数创建了一个 MyModel 实例,并保存了模型的状态字典(即模型参数)到文件 'data/model.pt' 中。detect 函数负责加载模型参数。注释部分展示了如何将模型加载到 CPU 或 GPU 上,这取决于你的设备配置。

2.神经网络

神经网络是模拟人体信号传递的一个模型。

生物神经元结构

  • 树突:从其他神经元接收信息的分支
  • 细胞核:处理从树突接收到的信息
  • 轴突:被神经元用来传递信息的生物电缆
  • 突触:轴突和其他神经元树突之间的连接

神经网络

将多个输入数据进行加权求和,再通过激活函数处理,然后输出。

  • 输入(Inputs): 代表输入数据,通常用向量表示,每个输入值对应一个权重。
  • 权重(Weights): 每个输入数据都有一个权重,表示该输入对最终结果的重要性。
  • 偏置(Bias): 一个额外的可调参数,作用类似于线性方程中的截距,帮助调整模型的输出。
  • 加权求和: 神经元将输入乘以对应的权重后求和,再加上偏置。
  • 激活函数(Activation Function): 用于将加权求和后的结果转换为输出结果,引入非线性特性,使神经网络能够处理复杂的任务。常见的激活函数有Sigmoid、ReLU(Rectified Linear Unit)、Tanh等。

3.参数初始化

3.1固定值初始化

将所有权重或偏置初始化为一个特定的常数值,这种初始化方法虽然简单,但在实际深度学习应用中通常并不推荐。

python 复制代码
import torch

def test01():
    #全0初始化
    model = torch.nn.Linear(5,1)
    torch.nn.init.zeros_(model.weight)
    # print(model.weight)
    # model.weight.data.fill_(0)
    print(model.weight)

def test02():
    #全1初始化
    model = torch.nn.Linear(4,1)#不是输入的数据有4条,而是每条数据有4个特征
    torch.nn.init.ones_(model.weight)
    print(model.weight)

def test03():
	#任意常数初始化
    model = torch.nn.Linear(4, 1)
    torch.nn.init.constant_(model.weight,5)
    print(model.weight)

if __name__ == '__main__':
    test01()

3.2随机初始化

方法:将权重初始化为随机的小值,通常从正态分布或均匀分布中采样。

应用场景:这是最基本的初始化方法,通过随机初始化避免对称性破坏。

python 复制代码
import torch

def test04():
	#均匀分布随机初始化
    model = torch.nn.Linear(4, 3)
    torch.nn.init.uniform_(model.weight)
    print(model.weight)

def test05():
	#正态分布随机初始化
    model = torch.nn.Linear(6,8)
    torch.nn.init.normal_(model.weight,mean=1,std=0.2)
    print(model.weight)


if __name__ == '__main__':
    test05()

3.3xavier初始化

也叫做Glorot初始化。

方法 :根据输入和输出神经元的数量来选择权重的初始值。权重从以下分布中采样:
W ∼ U ( − 6 n i n + n o u t , 6 n i n + n o u t ) W\sim\mathrm{U}\left(-\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}+n_\mathrm{out}}},\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}+n_\mathrm{out}}}\right) W∼U(−nin+nout 6 ,nin+nout 6 )

或者
W ∼ N ( 0 , 2 n i n + n o u t ) W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}+n_\mathrm{out}}\right) W∼N(0,nin+nout2)

N ( 0 , std 2 ) N ( 0 , std 2 ) \mathcal{N}(0, \text{std}^2)\mathcal{N}(0, \text{std}^2) N(0,std2)N(0,std2)

其中 n in n_{\text{in}} nin 是当前层的输入神经元数量, n out n_{\text{out}} nout是输出神经元数量。

优点 :平衡了输入和输出的方差,适合 S i g m o i d Sigmoid Sigmoid 和 T a n h Tanh Tanh 激活函数。

应用场景 :常用于浅层网络或使用 S i g m o i d Sigmoid Sigmoid 、 T a n h Tanh Tanh 激活函数的网络。

python 复制代码
import torch

def test06():
    model = torch.nn.Linear(6,8)
    torch.nn.init.xavier_uniform_(model.weight)
    model2 = torch.nn.Linear(6,8)
    torch.nn.init.xavier_normal_(model2.weight)
    print(model.weight)
    print(model2.weight)

if __name__ == '__main__':
	test06()

3.4He初始化

也叫kaiming 初始化。

方法 :专门为 ReLU 激活函数设计。权重从以下分布中采样:
W ∼ N ( 0 , 2 n i n ) W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}}\right) W∼N(0,nin2)

其中 n in n_{\text{in}} nin 是当前层的输入神经元数量。

优点 :适用于 R e L U ReLU ReLU 和 L e a k y R e L U Leaky ReLU LeakyReLU 激活函数。

应用场景:深度网络,尤其是使用 ReLU 激活函数时。

python 复制代码
import torch

def test07():
    model = torch.nn.Linear(6,8)
    torch.nn.init.kaiming_normal_(model.weight)
    model2 = torch.nn.Linear(6,8)
    torch.nn.init.kaiming_uniform_(model2.weight)
    print(model.weight)
    print(model2.weight)

if __name__ == '__main__':
	test07()

3.5手动初始化

python 复制代码
import torch

def test08():
    model = torch.nn.Linear(6,8)
    model.weight.data =torch.tensor([[],[],[],[],[],[]])
    print(model.weight)

if __name__ == '__main__':
	test08()

4.激活函数

4.1sigmoid函数

公式

Sigmoid函数的数学表达式为:
f ( x ) = σ ( x ) = 1 1 + e − x f(x) = \sigma(x) = \frac{1}{1 + e^{-x}} f(x)=σ(x)=1+e−x1

其中, e e e 是自然常数(约等于2.718), x x x 是输入。

特点

  1. 将任意实数输入映射到 (0, 1)之间,因此非常适合处理概率场景。

  2. sigmoid函数一般只用于二分类的输出层。

  3. 微分性质: 导数计算比较方便,可以用自身表达式来表示:
    σ ′ ( x ) = σ ( x ) ⋅ ( 1 − σ ( x ) ) \sigma'(x)=\sigma(x)\cdot(1-\sigma(x)) σ′(x)=σ(x)⋅(1−σ(x))

缺点

  • 梯度消失:
    • 在输入非常大或非常小时,Sigmoid函数的梯度会变得非常小,接近于0。这导致在反向传播过程中,梯度逐渐衰减。
    • 最终使得早期层的权重更新非常缓慢,进而导致训练速度变慢甚至停滞。
  • 信息丢失:输入100和输入10000经过sigmoid的激活值几乎都是等于 1 的,但是输入的数据却相差 100 倍。
  • 计算成本高: 由于涉及指数运算,Sigmoid的计算比ReLU等函数更复杂,尽管差异并不显著。
python 复制代码
import torch
import matplotlib.pyplot as plot

def test01():
    x = torch.linspace(-10,10,100,requires_grad=True)
    # print(x)
    y = torch.sigmoid(x)
    _,ax = plot.subplots(1,2)
    ax[0].plot(x.detach().numpy(),y.detach().numpy())
    ax[0].set_title('sigmoid')

    #求导
    y.sum().backward()
    ax[1].plot(x.detach().numpy(),x.grad.detach().numpy())
    ax[1].set_title('gradient')
    plot.show()


if __name__ == '__main__':
    test01()

4.2tanh函数

公式

tanh数学表达式为:
t a n h ( x ) = e x − e − x e x + e − x {tanh}(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} tanh(x)=ex+e−xex−e−x

特征

  1. 输出范围: 将输入映射到 ( − 1 , 1 ) (-1, 1) (−1,1)之间,因此输出是零中心的。相比于Sigmoid函数,这种零中心化的输出有助于加速收敛。

  2. 对称性: Tanh函数关于原点对称,因此在输入为0时,输出也为0。这种对称性有助于在训练神经网络时使数据更平衡。

  3. 平滑性: Tanh函数在整个输入范围内都是连续且可微的,这使其非常适合于使用梯度下降法进行优化。
    d d x tanh ( x ) = 1 − tanh 2 ( x ) \frac{d}{dx} \text{tanh}(x) = 1 - \text{tanh}^2(x) dxdtanh(x)=1−tanh2(x)

缺点

  1. 梯度消失: 虽然一定程度上改善了梯度消失问题,但在输入值非常大或非常小时导数还是非常小,这在深层网络中仍然是个问题。
  2. 计算成本: 由于涉及指数运算,Tanh的计算成本还是略高,尽管差异不大。
python 复制代码
import torch
import matplotlib.pyplot as plot

def test01():
    x = torch.linspace(-10,10,100,requires_grad=True)
    # print(x)
    y = torch.tanh(x)
    _,ax = plot.subplots(1,2)
    ax[0].plot(x.detach().numpy(),y.detach().numpy())
    ax[0].set_title('tanh')

    #求导
    y.sum().backward()
    ax[1].plot(x.detach().numpy(),x.grad.detach().numpy())
    ax[1].set_title('gradient')
    plot.show()


if __name__ == '__main__':
    test01()

4.3ReLU函数

公式

ReLU 函数定义如下:
ReLU ( x ) = max ⁡ ( 0 , x ) \text{ReLU}(x) = \max(0, x) ReLU(x)=max(0,x)

即 R e L U ReLU ReLU对输入 x x x进行非线性变换:
∙ 当 x > 0 时,ReLU ( x ) = x ∙ 当 x ≤ 0 时,ReLU ( x ) = 0 \bullet\quad\text{当 }x>0\text{ 时,ReLU}(x)=x\text{}\\\bullet\quad\text{当 }x\leq0\text{ 时,ReLU}(x)=0\text{} ∙当 x>0 时,ReLU(x)=x∙当 x≤0 时,ReLU(x)=0

特征

  1. 计算简单:ReLU 的计算非常简单,只需要对输入进行一次比较运算,这在实际应用中大大加速了神经网络的训练。

  2. ReLU 函数的导数是分段函数:
    ReLU ′ ( x ) = { 1 , if x > 0 0 , if x ≤ 0 \text{ReLU}'(x)=\begin{cases}1,&\text{if } x>0\\0,&\text{if }x\leq0\end{cases} ReLU′(x)={1,0,if x>0if x≤0

  3. 缓解梯度消失问题:相比于 Sigmoid 和 Tanh 激活函数,ReLU 在正半区的导数恒为 1,这使得深度神经网络在训练过程中可以更好地传播梯度,不存在饱和问题。

  4. 稀疏激活:ReLU在输入小于等于 0 时输出为 0,这使得 ReLU 可以在神经网络中引入稀疏性(即一些神经元不被激活),这种稀疏性可以提升网络的泛化能力。

缺点

神经元死亡:由于 R e L U ReLU ReLU在 x ≤ 0 x≤0 x≤0时输出为 0 0 0,如果某个神经元输入值是负,那么该神经元将永远不再激活,成为"死亡"神经元。随着训练的进行,网络中可能会出现大量死亡神经元,从而会降低模型的表达能力。

python 复制代码
import matplotlib.pyplot as plt
import torch
import torch.nn.functional as F

def t001():
    # 一行两列的图像绘制
    _, ax = plt.subplots(1, 2)
    # 绘制函数图像
    x = torch.linspace(-10, 10, 100)
    y = F.relu(x)
    # 网格
    ax[0].grid(True)
    ax[0].set_title("ReLU")
    ax[0].set_xlabel("x")
    ax[0].set_ylabel("y")
    # 绘制
    ax[0].plot(x, y)

    # 绘制sigmoid导数曲线图
    x = torch.linspace(-10, 10, 100, requires_grad=True)
    # 自动求导
    F.relu(x).sum().backward()
    ax[1].grid(True)
    ax[1].set_title("ReLU plot", color="red")
    ax[1].set_xlabel("x")
    ax[1].set_ylabel("y")
    # 用自动求导的结果绘制曲线图
    ax[1].plot(x.detach().numpy(), x.grad.detach().numpy())

    plt.show()


if __name__ == '__main__':
    t001()

4.4LeakyReLU函数

公式

Leaky ReLU 函数的定义如下:
Leaky ReLU ( x ) = { x , if x > 0 α x , if x ≤ 0 \text{Leaky ReLU}(x)=\begin{cases}x,&\text{if } x>0\\\alpha x,&\text{if } x\leq0\end{cases} Leaky ReLU(x)={x,αx,if x>0if x≤0

其中, α \alpha α 是一个非常小的常数(如 0.01),它控制负半轴的斜率。这个常数 α \alpha α是一个超参数,可以在训练过程中可自行进行调整。

特征

  1. 避免神经元死亡:通过在 x ≤ 0 x\leq 0 x≤0 区域引入一个小的负斜率,这样即使输入值小于等于零,Leaky ReLU仍然会有梯度,允许神经元继续更新权重,避免神经元在训练过程中完全"死亡"的问题。
  2. 计算简单:Leaky ReLU 的计算与 ReLU 相似,只需简单的比较和线性运算,计算开销低。

缺点

  1. 参数选择: α \alpha α 是一个需要调整的超参数,选择合适的 α \alpha α 值可能需要实验和调优。
  2. 出现负激活:如果 α \alpha α 设定得不当,仍然可能导致激活值过低。
python 复制代码
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt

# 中文设置
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False


def test006():
    x = torch.linspace(-5, 5, 200)
    # 设置leaky_relu的
    slope = 0.03
    y = F.leaky_relu(x, slope)
    # 一行两列
    _, ax = plt.subplots(1, 2)
    # 开始绘制函数曲线图
    ax[0].plot(x, y)
    ax[0].set_title("Leaky ReLU 函数曲线图")
    ax[0].set_xlabel("x")
    ax[0].set_ylabel("y")
    ax[0].grid(True)

    # 绘制leaky_relu的梯度曲线图
    x = torch.linspace(-5, 5, 200, requires_grad=True)
    F.leaky_relu(x, slope).sum().backward()
    ax[1].plot(x.detach().numpy(), x.grad)
    ax[1].set_title("Leaky ReLU 梯度曲线图", color="red")
    ax[1].set_xlabel("x")
    ax[1].set_ylabel("x.grad")
    ax[1].grid(True)
    # 设置线的颜色
    ax[1].lines[0].set_color("red")

    plt.show()


if __name__ == "__main__":
    test006()

4.5softmax函数

公式

假设神经网络的输出层有 n n n个节点,每个节点的输出为 z i z_i zi,则 Softmax 函数的定义如下:
S o f t m a x ( z i ) = e z i ∑ j = 1 n e z j \mathrm{Softmax}(z_i)=\frac{e^{z_i}}{\sum_{j=1}^ne^{z_j}} Softmax(zi)=∑j=1nezjezi

特征

  1. 将输出转化为概率:通过 S o f t m a x Softmax Softmax,可以将网络的原始输出转化为各个类别的概率,从而可以根据这些概率进行分类决策。

  2. 概率分布: S o f t m a x Softmax Softmax的输出是一个概率分布,即每个输出值 Softmax ( z i ) \text{Softmax}(z_i) Softmax(zi)都是一个介于 0 0 0和 1 1 1之间的数,并且所有输出值的和为 1:
    ∑ i = 1 n Softmax ( z i ) = 1 \sum_{i=1}^n\text{Softmax}(z_i)=1 i=1∑nSoftmax(zi)=1

  3. 突出差异: S o f t m a x Softmax Softmax会放大差异,使得概率最大的类别的输出值更接近 1 1 1,而其他类别更接近 0 0 0。

  4. 在实际应用中, S o f t m a x Softmax Softmax常与交叉熵损失函数 C r o s s − E n t r o p y L o s s Cross-Entropy Loss Cross−EntropyLoss结合使用,用于多分类问题。在反向传播中, S o f t m a x Softmax Softmax的导数计算是必需的。

设 p i = S o f t m a x ( z i ) ,则对于 z i 的导数为: ∙ 当 i = j 时: ∂ p i ∂ z i = p i ( 1 − p i ) ∙ 当 i ≠ j 时 : ∂ p i ∂ z j = − p i p j \begin{aligned} &\text{设 }p_i=\mathrm{Softmax}(z_i)\text{,则对于 }z_i\text{ 的导数为:} \\ &\bullet\text{ 当 }i=j\text{ 时:} \\ &&&\frac{\partial p_i}{\partial z_i}=p_i(1-p_i) \\ & \bullet\text{ 当 }i\neq j\text{ 时}: \\ &&&\frac{\partial p_i}{\partial z_j} =-p_{i}p_{j} \end{aligned} 设 pi=Softmax(zi),则对于 zi 的导数为:∙ 当 i=j 时:∙ 当 i=j 时:∂zi∂pi=pi(1−pi)∂zj∂pi=−pipj

缺点

  1. 数值不稳定性:在计算过程中,如果 z i z_i zi的数值过大, e z i e^{z_i} ezi可能会导致数值溢出。因此在实际应用中,经常会对 z i z_i zi进行调整,如减去最大值以确保数值稳定。

S o f t m a x ( z i ) = e z i − max ⁡ ( z ) ∑ j = 1 n e z j − max ⁡ ( z ) \mathrm{Softmax}(z_i)=\frac{e^{z_i-\max(z)}}{\sum_{j=1}^ne^{z_j-\max(z)}} Softmax(zi)=∑j=1nezj−max(z)ezi−max(z)

解释:

z i − max ⁡ ( z ) z_i-\max(z) zi−max(z)是一个非正数,那么 e z i − max ⁡ ( z ) e^{z_i - \max(z)} ezi−max(z)的值就位于 0 0 0到 1 1 1之间,有效避免了数值溢出。
这中调整不会改变 S o f t m a x Softmax Softmax的概率分布结果,因为从数学的角度讲相当于分子、分母都除以了 e max ⁡ ( z ) e^{\max(z)} emax(z)。

  1. 难以处理大量类别: S o f t m a x Softmax Softmax在处理类别数非常多的情况下(如大模型中的词汇表)计算开销会较大。
python 复制代码
import torch
import matplotlib.pyplot as plot

def test01():
    x = torch.linspace(-10,10,100,requires_grad=True)
    y = torch.nn.functional.softmax(x)
    print(y.shape)
    _,ax = plot.subplots(1,2)
    ax[0].polt(x.detach().numpy(),y.detach().numpy())
    ax[0].set_title('softmax函数')
    ax[0].set.xlabel('x')
    ax[0].set.ylabel('y')

    #求导
    y.sum().backward()
    ax[1].plot(x.detach().numpy(),x.grad.detach().numpy())
    ax[1].set_title('softmax导函数')
    ax[1].set.xlabel('x')
    ax[1].set.ylabel('y导')

if __name__ == '__main__':
    test01()
相关推荐
TaoSense39 分钟前
未来量子计算技术会如何影响音频DSP的发展?
人工智能·音频·量子计算
AI2AGI1 小时前
天天 AI-250110:今日热点-字节豆包Web端反超百度文心一言,DeepSeek也发力了|量子位智库月报
大数据·人工智能·百度·ai·aigc·文心一言
Loving_enjoy2 小时前
解锁人工智能的核心:人工神经网络全面解析
人工智能·神经网络
程序员非鱼3 小时前
深度学习中常见的激活函数详解
人工智能·python·深度学习·神经网络·机器学习·激活函数
蒙娜丽宁4 小时前
【人工智能】自然语言生成的前沿探索:利用GPT-2和BERT实现自动文本生成与完形填空
人工智能·gpt·bert
早安&早安4 小时前
深入了解 NLTK:Python 的自然语言处理工具
人工智能·python·深度学习·自然语言处理
繁华落尽,寻一世真情4 小时前
大语言模型预训练、微调、RLHF
人工智能·语言模型·自然语言处理
赵大仁4 小时前
大语言模型的分层架构:高效建模的全新探索
人工智能·深度学习·神经网络·机器学习·自然语言处理·数据挖掘·数据分析
Noos_4 小时前
如何训练大型语言模型?
人工智能·语言模型·自然语言处理
早安&早安4 小时前
什么是NLP语言:一文详解
人工智能·自然语言处理