深度学习—参数初始化及激活函数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()
相关推荐
池央29 分钟前
深度学习模型:卷积神经网络(CNN)
人工智能·深度学习·cnn
deephub32 分钟前
Scikit-learn Pipeline完全指南:高效构建机器学习工作流
人工智能·python·机器学习·scikit-learn
知来者逆34 分钟前
首次公开用系统审查与评估大语言模型安全性的数据集
人工智能·机器学习·语言模型·自然语言处理·llm·大语言模型
HyperAI超神经2 小时前
NeurIPS 2024 有效投稿达 15,671 篇,数据集版块内容丰富
人工智能·开源·自动驾驶·数据集·多模态·化学光谱·neurips 2024
uhakadotcom2 小时前
AI搜索引擎的尽头是电商?从perplexity开始卖货说起...
前端·人工智能·后端
virtaitech2 小时前
探索 GAN 的演变之路
人工智能·神经网络·生成对抗网络
黑色叉腰丶大魔王2 小时前
《掩码语言模型(Masked Language Model, MLM)》
人工智能·语言模型·自然语言处理
Elastic 中国社区官方博客3 小时前
从 App Search 到 Elasticsearch — 挖掘搜索的未来
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·数据库开发
Jurio.3 小时前
【IEEE独立出版 | 厦门大学主办】第四届人工智能、机器人和通信国际会议(ICAIRC 2024,12月27-29日)
人工智能·深度学习·神经网络·机器学习·自然语言处理·数据挖掘·机器人