news 2026/5/15 6:12:39

人工神经网络知识点讲解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
人工神经网络知识点讲解

人工神经网络知识点讲解

知识导图

人工神经网络 ├── 基础认知 │ ├── 神经网络的核心概念 │ ├── 神经元的工作机制 │ └── 网络的层级结构 ├── 激活函数 │ ├── 激活函数的作用 │ ├── 常见激活函数:sigmoid/tanh/ReLU/Softmax │ └── 激活函数的选择 ├── 参数初始化 │ ├── 7种初始化方法 │ └── 初始化方法的选择 ├── 损失函数 │ ├── 分类任务损失 │ └── 回归任务损失 ├── 网络优化 │ ├── 优化算法 │ └── 学习率衰减 ├── 正则化方法 │ ├── 正则化的作用 │ ├── Dropout 随机失活 │ └── 批量归一化 └── 实战案例 └── 手机价格分类案例

核心名词解释

  • 人工神经网络 (ANN):也简称为神经网络 (NN),是一种模仿生物神经网络结构和功能的计算模型,用来拟合复杂的非线性关系。

  • 神经元:神经网络的基本单元,接收输入后进行加权求和,再通过激活函数输出结果,模拟生物神经元的工作方式。

  • 输入层 (Input Layer):神经网络的输入层,负责接收原始的输入数据,每个输入特征对应一个神经元。

  • 隐藏层 (Hidden Layers):输入层和输出层之间的层,负责对输入数据进行特征提取和变换,神经网络的深度由隐藏层的数量决定。

  • 输出层 (Output Layer):神经网络的输出层,负责生成最终的预测结果,根据任务的不同(分类 / 回归)有不同的设计。

  • 全连接层:相邻层的所有神经元都相互连接的层,也叫稠密层,每个连接都有对应的权重和偏置。

  • 内部状态值:神经元的加权求和结果,公式为z = W⋅x\+b,是激活函数的输入。

  • 激活值:激活函数对内部状态值进行非线性变换后得到的结果,是神经元的最终输出。

  • 激活函数:为神经网络引入非线性因素的函数,让网络可以拟合复杂的非线性函数。

  • sigmoid:将输入映射到 (0,1) 区间的激活函数,常用于二分类的输出层。

  • tanh:将输入映射到 (-1,1) 区间的激活函数,是 sigmoid 的平移版本,解决了 sigmoid 的输出非零均值的问题。

  • ReLU:修正线性单元,是目前最常用的隐藏层激活函数,计算简单,缓解了梯度消失问题。

  • Softmax:多分类任务的输出激活函数,将输出转换为概率,所有输出的概率和为 1。

  • 参数初始化:对神经网络的权重和偏置进行初始赋值的过程,合适的初始化可以让网络更快收敛,避免梯度消失 / 爆炸。

  • Xavier 初始化:也叫 Glorot 初始化,用来平衡输入和输出的方差,适合 sigmoid、tanh 这类激活函数。

  • Kaiming 初始化:也叫 HE 初始化,针对 ReLU 这类激活函数设计,考虑了 ReLU 的半正则特性,让深层网络更容易训练。

  • 损失函数:用来衡量模型预测值和真实值之间差异的函数,是网络训练的优化目标。

  • 交叉熵损失:分类任务中常用的损失函数,衡量预测概率分布和真实分布的差异。

  • MAE 损失:也叫 L1 损失,回归任务的损失,计算预测和真实的绝对误差。

  • MSE 损失:也叫 L2 损失,回归任务的损失,计算预测和真实的均方误差。

  • 优化器:用来更新神经网络参数,最小化损失函数的算法。

  • 学习率衰减:随着训练进行逐渐降低学习率的策略,让模型训练更稳定。

  • Dropout:随机失活正则化,训练时随机让部分神经元失活,防止过拟合。

  • 批量归一化 (BN):对批量数据进行标准化和重构的层,缓解梯度消失,加速训练。

  • 正则化:用来防止模型过拟合的方法,通过限制模型的复杂度,提升模型的泛化能力。


一、神经网络基础认知

本章节学习目标:

  • 知道什么是人工神经网络

  • 了解神经网络的构成

  • 理解内部状态和激活值的概念

1.1 什么是人工神经网络

人工神经网络( Artificial Neural Network, 简写为 ANN)也简称为神经网络(NN),是一种模仿生物神经网络结构和功能的计算模型。

人脑可以看做是一个生物神经网络,由众多的神经元连接而成。各个神经元传递复杂的电信号,树突接收到输入信号,然后对信号进行处理,通过轴突输出信号。当电信号通过树突进入到细胞核时,会逐渐聚集电荷。达到一定的电位后,细胞就会被激活,通过轴突发出电信号。

1.2 神经元的工作机制

神经网络是由多个神经元组成,构建神经网络就是在构建神经元。

这个过程就像,来源不同树突 (树突都会有不同的权重) 的信息,进行的加权计算,输入到细胞中做加和,再通过激活函数输出细胞值。

1.3 神经网络的层级结构

神经网络中信息只向一个方向移动,即从输入节点向前移动,通过隐藏节点,再向输出节点移动。其中的基本部分是:

  1. 输入层(Input Layer):即输入 x 的那一层(如图像、文本、声音等)。每个输入特征对应一个神经元。输入层将数据传递给下一层的神经元。

  2. 输出层(Output Layer):即输出 y 的那一层。输出层的神经元根据网络的任务(回归、分类等)生成最终的预测结果。

  3. 隐藏层(Hidden Layers):输入层和输出层之间的层,负责对输入数据进行特征提取和变换,神经网络的深度由隐藏层的数量决定。

网络的特点:

  • 同一层的神经元之间没有连接

  • 第 N 层的每个神经元和第 N-1 层 的所有神经元相连,这就是全连接神经网络

  • 全连接神经网络接收的样本数据是二维的,数据在每一层之间需要以二维的形式传递

  • 第 N-1 层神经元的输出就是第 N 层神经元的输入

  • 每个连接都有一个权重值(w 系数和 b 系数)

1.4 内部状态值和激活值

每一个神经元工作时,会产生 4 个值:
前向传播会产生内部状态值(加权求和值)和激活值;反向传播时会产生激活值梯度和内部状态值梯度。

  • 内部状态值:z = W⋅x\+b

    • W:权重矩阵

    • x:输入值

    • b:偏置

  • 激活值:a=f\(z\)

    • f:激活函数

    • z:内部状态值


二、激活函数

本章节学习目标:

  • 知道常见的激活函数

  • 了解激活函数的作用

  • 掌握激活函数的选择方法

2.1 激活函数的作用

激活函数用于对每层的输出数据进行变换,进而为整个网络注入了非线性因素。此时,神经网络就可以拟合各种曲线。

没有引入非线性因素的网络等价于使用一个线性模型来拟合。通过给网络输出增加激活函数,实现引入非线性因素,使得网络模型可以逼近任意函数,提升网络对复杂问题的拟合能力。

2.2 常见的激活函数

2.2.1 sigmoid 激活函数

sigmoid 激活函数可以将输入映射到 (0,1) 的区间,它的公式和导数公式如下:

对应的绘制代码:

# 解决OMP冲突的代码 import os os.environ['KMP_DUPLICATE_LIB_OK']='True' # 加这行!切换Matplotlib的后端,绕过PyCharm的兼容bug import matplotlib matplotlib.use('TkAgg') # 导包 import torch import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 指定用Windows自带的微软雅黑,支持中文 plt.rcParams['axes.unicode_minus'] = False # 顺便解决负号显示成方框的问题 # 准备数据 # 准备 -20,20的等差张量,1000点 --> x x = torch.linspace(start=-20,end=20,steps=1000,requires_grad=False) print(x.shape,x.ndim) y = torch.sigmoid(x) print(y.shape,y.ndim) # 绘制图像 # **todo 为了把值域图像和导数图像画到一张画布上,创建一个一行两列的画布** fig,axs = plt.subplots(1,2) axs[0].plot(x,y) axs[0].grid() axs[0].set_title('sigmoid 值域图像') # plt.show() print('=======1=======') # 准备数据 # # 准备x,y数据 -> **todo 注意此处的x开启自动微分,y的本质是梯度** x = torch.linspace(start=-20,end=20,steps=1000,requires_grad=True) print(x.shape,x.ndim) torch.sigmoid(x).sum().backward() # -> # backward():触发反向传播,PyTorch自动帮你计算Sigmoid对x的导数,结果会自动存在 x.grad 里 y = x.grad print(y.shape,y.ndim) # 绘制图像 axs[1].plot(x.detach(),x.grad) axs[1].grid() axs[1].set_title('Sigmoid 导数图像') plt.show() print('=======2=======')
2.2.2 tanh 激活函数

tanh 激活函数可以将输入映射到 (-1,1) 的区间,解决了 sigmoid 输出非零均值的问题,它的公式和导数公式如下:

对应的绘制代码:

# 创建画布和坐标轴 _, axes = plt.subplots(1, 2) # 函数图像 x = torch.linspace(-20, 20, 1000) y = torch.tanh(x) axes[0].plot(x, y) axes[0].grid() axes[0].set_title('Tanh 函数图像') # 导数图像 x = torch.linspace(-20, 20, 1000, requires_grad=True) torch.tanh(x).sum().backward() axes[1].plot(x.detach(), x.grad) axes[1].grid() axes[1].set_title('Tanh 导数图像') plt.show()
2.2.3 ReLU 激活函数

ReLU 是修正线性单元,是目前最常用的隐藏层激活函数,它的公式和导数公式如下:



对应的绘制代码:

# 创建画布和坐标轴 _, axes = plt.subplots(1, 2) # 函数图像 x = torch.linspace(-20, 20, 1000) y = torch.relu(x) axes[0].plot(x, y) axes[0].grid() axes[0].set_title('ReLU 函数图像') # 导数图像 x = torch.linspace(-20, 20, 1000, requires_grad=True) torch.relu(x).sum().backward() axes[1].plot(x.detach(), x.grad) axes[1].grid() axes[1].set_title('ReLU 导数图像') plt.show()
2.2.4 SoftMax 激活函数

softmax 用于多分类过程中,它是二分类函数 sigmoid 在多分类上的推广,目的是将多分类的结果以概率的形式展现出来。

Softmax 就是将网络输出的 logits 通过 softmax 函数,就映射成为 (0,1) 的值,而这些值的累和为 1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。

对应的使用代码:

import torch scores = torch.tensor([0.2, 0.02, 0.15, 0.15, 1.3, 0.5, 0.06, 1.1, 0.05, 3.75]) # dim = 0,按行计算 probabilities = torch.softmax(scores, dim=-1) print(probabilities) # 输出结果: # tensor([0.0212, 0.0177, 0.0202, 0.0202, 0.0638, 0.0287, 0.0185, 0.0522, 0.0183,0.7392])

2.3 其他激活函数

还有其他的常见激活函数,比如 Leaky ReLU、PReLU、ELU 等:

2.4 激活函数的选择方法

隐藏层的选择
  • 优先选择 ReLU 激活函数

  • 如果 ReLu 效果不好,那么尝试其他激活,如 Leaky ReLu 等。

  • 如果你使用了 ReLU, 需要注意一下 Dead ReLU 问题, 避免出现 0 梯度从而导致过多的神经元死亡。

  • 少用使用 sigmoid 激活函数,可以尝试使用 tanh 激活函数

输出层的选择
  • 二分类问题选择 sigmoid 激活函数

  • 多分类问题选择 softmax 激活函数

  • 回归问题选择 identity 激活函数

激活函数对比辨析

激活函数输出区间特点适用场景
sigmoid(0, 1)输出非零均值,容易梯度消失二分类输出层
tanh(-1, 1)输出零均值,缓解了 sigmoid 的部分问题隐藏层(早期)
ReLU[0, +∞)计算简单,缓解梯度消失,可能出现 Dead ReLU隐藏层(主流)
Softmax(0, 1),总和为 1输出概率分布多分类输出层

三、参数初始化

本章节学习目标:

  • 了解常见的参数初始化方法

  • 掌握初始化方法的选择

3.1 常见的初始化方法

参数初始化是对神经网络的权重和偏置进行初始赋值的过程,常见的 7 种初始化方法如下:

基础初始化方法
  1. 均匀分布初始化:权重参数初始化从区间均匀随机取值,默认区间为(0,1)。可以设置为在 (-1/√d,1/√d) 均匀分布中生成当前神经元的权重,其中 d 为神经元的输入数量

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

  3. 全 0 初始化:将神经网络中的所有权重参数初始化为 0

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

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

进阶初始化方法
  1. kaiming 初始化(HE 初始化)
    HE 初始化分为正态分布的 HE 初始化、均匀分布的 HE 初始化.

    • 正态分布的 he 初始化:它是从 [0, std] 中抽取样本的,std = sqrt\(2 / fan\_in\)

    • 均匀分布的 he 初始化:它从 [-limit,limit] 中的均匀分布中抽取样本,limit是 sqrt\(6 / fan\_in\)

    • fan_in:输入层神经元的个数

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

    • 正态化的 Xavier 初始化:它是从 [0, std] 中抽取样本的,std = sqrt\(2 / \(fan\_in \+ fan\_out\)\)

    • 均匀分布的 Xavier 初始化:[-limit,limit] 中的均匀分布中抽取样本,limit 是 sqrt\(6 / \(fan\_in \+ fan\_out\)\)

    • fan_in:输入层神经元的个数, fan_out:输出层神经元个数

3.2 初始化的代码示例

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)

3.3 初始化方法的选择

根据激活函数选择
  • Sigmoid/Tanh:选择 Xavier 初始化

  • ReLU/Leaky ReLU:选择 kaiming 初始化

根据网络深度选择
  • 浅层网络:随机初始化即可

  • 深层网络:需要考虑方差平衡,如 Xavier 或 kaiming 初始化

初始化方法对比辨析

初始化方法核心特点适用激活函数适用场景
均匀 / 正态随机简单随机初始化浅层网络的激活函数简单的浅层网络
全 0 / 全 1 / 固定值固定值初始化不推荐,容易导致对称问题仅偏置可以使用,权重不推荐
Xavier 初始化平衡输入输出方差Sigmoid、Tanh早期的深层网络
Kaiming 初始化适配 ReLU 的半正则特性ReLU、Leaky ReLU现代深层网络

四、神经网络搭建实战

我们来构建一个简单的神经网络模型,结合我们学到的激活函数和参数初始化:

搭建步骤

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

  1. \_\_init\_\_方法中定义网络中的层结构,主要是全连接层,并进行初始化

  2. forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中为初始化定义的 layer 传入数据,进行前向传播等。

代码实现

from torch.nn import Module, Linear import torch # TODO 1.自定义模型类继承Module类 class My_Model(Module): # TODO 2.重写init魔法方法和forward前向传播方法 def __init__(self, *args, **kwargs): # 1.调用父类的init初始化方法 super().__init__(*args, **kwargs) # TODO 定义神经网络结构 self.linear1 = Linear(3, 3) self.linear2 = Linear(3, 2) self.out = Linear(2, 2) # 3.参数初始化(生成权重矩阵) # 隐藏层初始化权重矩阵 torch.nn.init.xavier_normal_(self.linear1.weight) torch.nn.init.kaiming_normal_(self.linear2.weight) def forward(self, x): # TODO 前向传播计算(每层都是加权求和+激活函数) x = torch.sigmoid(self.linear1(x)) x = torch.relu(self.linear2(x)) # 下面-1代表最后一维, 也就是按照每个样本概率和为1. x = torch.softmax(self.out(x), dim=-1) return x # TODO 3.创建模型对象并使用模型对象 # 创建模型对象 model = My_Model() # 自动调用init魔法方法 # 准备数据集(正态分布数据) torch.manual_seed(66) data = torch.randn(5, 3) # 5个样本,3个特征 # 把数据传入模型对象 output = model(data) # 自动调用forward方法 print(output)

五、损失函数

本章节学习目标:

  • 知道分类任务的损失函数

  • 知道回归任务的损失函数

5.1 什么是损失函数

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

损失函数在不同的文献中名称是不一样的,主要有以下几种命名方式:

  • 损失函数(Loss Function)

  • 代价函数(Cost Function)

  • 目标函数(Objective Function)

5.2 分类任务的损失函数

5.2.1 多分类任务:交叉熵损失

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

其中:

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

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

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

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

从概率角度理解,我们的目的是最小化正确类别所对应的预测概率的对数的负值 (损失值最小):

上图中的交叉熵损失为:

从概率角度理解,我们的目的是最小化正确类别所对应的预测概率的对数的负值(损失值最小),如下图所示:

在 pytorch 中使用nn\.CrossEntropyLoss\(\)实现,代码如下:

# 分类损失函数:交叉熵损失使用nn.CrossEntropyLoss()实现。nn.CrossEntropyLoss()=softmax + 损失计算 def test01(): # 设置真实值: 可以是热编码后的结果也可以不进行热编码 # 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]], requires_grad=True, dtype=torch.float32) # 实例化交叉熵损失 loss = nn.CrossEntropyLoss() # 计算损失结果 my_loss = loss(y_pred, y_true).detach().numpy() print('loss:', my_loss)
5.2.2 二分类任务:二分类交叉熵损失

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

其中:

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

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

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

在 pytorch 中实现时使用nn\.BCELoss\(\),代码如下:

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

5.3 回归任务的损失函数

5.3.1 MAE 损失函数(L1 Loss)

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

特点是:

  1. 由于 L1 loss 具有稀疏性,为了惩罚较大的值,因此常常将其作为正则项添加到其他 loss 中作为约束。

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

在 pytorch 中使用nn\.L1Loss\(\)实现,代码如下:

# 计算inputs与target之差的绝对值 def test03(): # 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)
5.3.2 MSE 损失函数(L2 Loss)

Mean Squared Loss (MSE) 也被称为 L2 Loss,是以均方误差作为距离。损失函数公式:


特点是:

  1. L2 loss 的梯度会随着误差减小而减小,因此更容易收敛到最小值。

  2. 对异常值更敏感,因为它会对大的误差进行平方惩罚。

在 pytorch 中使用nn\.MSELoss\(\)实现,代码如下:

# 计算inputs与target之差的平方 def test04(): # 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('loss:', my_loss)

损失函数对比辨析

损失函数适用任务特点适用场景
交叉熵损失(CrossEntropy)多分类衡量预测概率分布和真实分布的差异,梯度稳定多分类任务
二分类交叉熵损失(BCELoss)二分类针对二分类的交叉熵,配合 sigmoid 使用二分类任务
MAE 损失(L1 Loss)回归对异常值不敏感,零点梯度不平滑回归任务,对异常值鲁棒的场景
MSE 损失(L2 Loss)回归梯度随误差减小,更容易收敛,对异常值敏感回归任务,数据质量较好的场景

六、网络优化方法

本章节学习目标:

  • 了解常见的优化算法

  • 掌握学习率的调整方法

6.1 梯度下降优化算法

梯度下降是神经网络优化的基础算法,常见的梯度下降变种有:

  • 批量梯度下降(BGD):使用全部样本计算梯度,更新稳定但速度慢

  • 随机梯度下降(SGD):使用单个样本计算梯度,速度快但波动大

  • 小批量梯度下降(MBGD):使用小批量样本,平衡速度和稳定性,是目前的主流

6.2 带动量的梯度下降

动量(Momentum)是模拟物理中的动量概念,积累之前的梯度方向,加速收敛,减少震荡:

6.3 自适应学习率算法

6.3.1 AdaGrad

自适应调整每个参数的学习率,对稀疏数据效果好,但学习率会单调递减:

6.3.2 RMSProp

解决 AdaGrad 学习率递减的问题,使用指数移动平均来累积梯度:

6.3.3 Adam

结合了动量和 RMSProp 的优点,是目前最常用的优化算法,自适应调整学习率,收敛速度快:

6.4 学习率衰减

随着训练的进行,逐渐降低学习率,让模型在训练后期更稳定,避免震荡:

6.4.1 等间隔学习率衰减

每经过固定的轮次,就将学习率乘以一个衰减系数:

def test_StepLR(): # 0.参数初始化 LR = 0.1 # 设置学习率初始化值为0.1 iteration = 10 max_epoch = 200 # 1 初始化参数 y_true = torch.tensor([0]) x = torch.tensor([1.0]) w = torch.tensor([1.0], requires_grad=True) # 2.优化器 optimizer = optim.SGD([w], lr=LR, momentum=0.9) # 3.设置学习率下降策略 scheduler_lr = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.5) # 4.获取学习率的值和当前的epoch lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) # 获取当前lr epoch_list.append(epoch) # 获取当前的epoch for i in range(iteration): # 遍历每一个batch数据 loss = ((w*x-y_true)**2)/2.0 # 目标函数 optimizer.zero_grad() # 反向传播 loss.backward() optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() # 5.绘制学习率变化的曲线 plt.plot(epoch_list, lr_list, label="Step LR Scheduler") plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()
6.4.2 指定间隔学习率衰减

在指定的轮次进行学习率衰减,更灵活:

def test_MultiStepLR(): torch.manual_seed(1) LR = 0.1 iteration = 10 max_epoch = 200 weights = torch.randn((1), requires_grad=True) target = torch.zeros((1)) print('weights--->', weights, 'target--->', target) optimizer = optim.SGD([weights], lr=LR, momentum=0.9) # 设定调整时刻数 milestones = [50, 125, 160] # 设置学习率下降策略 scheduler_lr = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.5) lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) epoch_list.append(epoch) for i in range(iteration): loss = torch.pow((weights - target), 2) optimizer.zero_grad() # 反向传播 loss.backward() # 参数更新 optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler\nmilestones:{}".format(milestones)) plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()
6.4.3 按指数学习率衰减

学习率按指数规律衰减:

def test_ExponentialLR(): # 0.参数初始化 LR = 0.1 # 设置学习率初始化值为0.1 iteration = 10 max_epoch = 200 # 1 初始化参数 y_true = torch.tensor([0]) x = torch.tensor([1.0]) w = torch.tensor([1.0], requires_grad=True) # 2.优化器 optimizer = optim.SGD([w], lr=LR, momentum=0.9) # 3.设置学习率下降策略 gamma = 0.95 scheduler_lr = optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma) # 4.获取学习率的值和当前的epoch lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) epoch_list.append(epoch) for i in range(iteration): # 遍历每一个batch数据 loss = ((w*x-y_true)**2)/2.0 optimizer.zero_grad() # 反向传播 loss.backward() optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() # 5.绘制学习率变化的曲线 plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler") plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()

优化方法对比辨析

优化算法核心特点适用场景
SGD简单的随机梯度下降,收敛慢,容易震荡简单任务,数据量小
SGD+Momentum加入动量,加速收敛,减少震荡大部分任务,稳定训练
AdaGrad自适应学习率,适合稀疏数据稀疏数据,NLP 任务
RMSProp解决 AdaGrad 学习率递减问题深度学习任务
Adam结合动量和自适应学习率,收敛快主流深度学习任务,默认首选

七、正则化方法

本章节学习目标:

  • 知道正则化的作用

  • 掌握随机失活 DropOut 策略

  • 知道 BN 层的作用

7.1 正则化的作用

在设计机器学习算法时希望在新样本上的泛化能力强。许多机器学习算法都采用相关的策略来减小测试误差,这些策略被统称为正则化。

神经网络强大的表示能力经常遇到过拟合,所以需要使用不同形式的正则化策略。

7.2 Dropout 正则化

在练神经网络中模型参数较多,在数据量不足的情况下,很容易过拟合。Dropout(随机失活)是一个简单有效的正则化方法。

在训练过程中,Dropout 的实现是让神经元以超参数 p 的概率停止工作或者激活被置为 0, 未被置为 0 的进行缩放,缩放比例为 1/(1-p)。训练过程可以认为是对完整的神经网络的一些子集进行训练,每次基于输入数据只更新子网络的参数。

在测试过程中,随机失活不起作用。

代码示例:

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)

输出结果:

未失活FC层的输出结果: tensor([[-0.8369, -5.5720, 0.2258, 3.4256, 2.1919]], grad_fn=<AddmmBackward0>) 失活后FC层的输出结果: tensor([[-1.3949, -9.2866, 0.3763, 0.0000, 3.6531]], grad_fn=<MulBackward0>)

7.3 批量归一化(Batch Normalization)

批量归一化是对数据标准化,再对数据重构(缩放 + 平移),如下所示:

其中:

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

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

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

  • Var (x) 表示变量的方差;

批量归一化层在计算机视觉领域使用较多,它可以:

  1. 缓解梯度消失 / 爆炸问题

  2. 允许使用更高的学习率

  3. 降低对初始化的敏感程度

  4. 有轻微的正则化效果

代码示例:

import torch import torch.nn as nn m = nn.BatchNorm2d(2, eps=1e-05, momentum=0.1, affine=True) # affine参数设为True表示weight和bias将被使用 input = torch.randn(1, 2, 3, 4) print("input-->", input) output = m(input) print("output-->", output) print(output.size()) print(m.weight) print(m.bias)

输出结果:

input--> tensor([[[[-0.2751, -1.2183, -0.5106, -0.1540], [-0.4585, -0.5989, -0.6063, 0.5986], [-0.4745, 0.1496, -1.1266, -1.2377]], [[ 0.2580, 1.2065, 1.4598, 0.8387], [-0.4586, 0.8938, -0.3328, 0.1192], [-0.3265, -0.6263, 0.0419, -1.2231]]]]) output--> tensor([[[[ 0.4164, -1.3889, -0.0343, 0.6484], [ 0.0655, -0.2032, -0.2175, 2.0889], [ 0.0349, 1.2294, -1.2134, -1.4262]], [[ 0.1340, 1.3582, 1.6853, 0.8835], [-0.7910, 0.9546, -0.6287, -0.0452], [-0.6205, -1.0075, -0.1449, -1.7779]]]], grad_fn=<NativeBatchNormBackward0>) torch.Size([1, 2, 3, 4]) Parameter containing: tensor([1., 1.], requires_grad=True) Parameter containing: tensor([0., 0.], requires_grad=True)

八、实战案例:手机价格分类

本章节学习目标:

  • 掌握构建分类模型的完整流程

  • 动手实践整个训练和评估过程

8.1 需求分析

小明创办了一家手机公司,他不知道如何估算手机产品的价格。为了解决这个问题,他收集了多家公司的手机销售数据。该数据为二手手机的各个性能的数据,最后根据这些性能得到 4 个价格区间,作为这些二手手机售出的价格区间。

我们需要帮助小明找出手机的功能(例如:RAM 等)与其售价之间的某种关系。我们可以使用机器学习的方法来解决这个问题,也可以构建一个全连接的网络。

需要注意的是:在这个问题中,我们不需要预测实际价格,而是一个价格范围,它的范围使用 0、1、2、3 来表示,所以该问题也是一个分类问题。

I在这里插入图片描述

8.2 完整流程

我们按照四个步骤来完成这个任务:

  1. 准备训练集数据

  2. 构建要使用的模型

  3. 模型训练

  4. 模型预测评估

8.2.1 导入工具包
# 导入相关模块 import torch from torch.utils.data import TensorDataset from torch.utils.data import DataLoader import torch.nn as nn import torch.optim as optim from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt import numpy as np import pandas as pd import time
8.2.2 构建数据集

数据共有 2000 条,其中 1600 条数据作为训练集,400 条数据用作测试集。 我们使用 sklearn 的数据集划分工作来完成。并使用 PyTorch 的 TensorDataset 来将数据集构建为 Dataset 对象,方便构造数据集加载对象。

# 构建数据集 def create_dataset(): # 使用pandas读取数据 data = pd.read_csv('data/手机价格预测.csv') # 特征值和目标值 x, y = data.iloc[:, :-1], data.iloc[:, -1] # 类型转换:特征值,目标值 x = x.astype(np.float32) y = y.astype(np.int64) # 数据集划分 x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88) # 构建数据集,转换为pytorch的形式 train_dataset = TensorDataset(torch.from_numpy(x_train.values), torch.tensor(y_train.values)) valid_dataset = TensorDataset(torch.from_numpy(x_valid.values), torch.tensor(y_valid.values)) # 返回结果 return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))

输出结果:

输入特征数: 20 分类个数: 4
8.2.3 构建分类网络模型

构建全连接神经网络来进行手机价格分类,该网络主要由三个线性层来构建,使用 relu 激活函数。

网络共有 3 个全连接层,具体信息如下:

  • 第一层:输入为维度为 20, 输出维度为: 128

  • 第二层:输入为维度为 128, 输出维度为: 256

  • 第三层:输入为维度为 256, 输出维度为: 4

# 构建网络模型 class PhonePriceModel(nn.Module): def __init__(self, input_dim, output_dim): super(PhonePriceModel, self).__init__() # 1. 第一层: 输入为维度为 20, 输出维度为: 128 self.linear1 = nn.Linear(input_dim, 128) # 2. 第二层: 输入为维度为 128, 输出维度为: 256 self.linear2 = nn.Linear(128, 256) # 3. 第三层: 输入为维度为 256, 输出维度为: 4 self.linear3 = nn.Linear(256, output_dim) def forward(self, x): # 前向传播过程 x = torch.relu(self.linear1(x)) x = torch.relu(self.linear2(x)) output = self.linear3(x) # 获取数据结果 return output

模型实例化的输出:

8.2.4 模型训练

网络编写完成之后,我们需要编写训练函数。所谓的训练函数,指的是输入数据读取、送入网络、计算损失、更新参数的流程,该流程较为固定。我们使用的是多分类交叉生损失函数、使用 SGD 优化方法。最终,将训练好的模型持久化到磁盘中。

# 模型训练过程 def train(train_dataset,input_dim,class_num,): # 固定随机数种子 torch.manual_seed(0) # 初始化数据加载器 dataloader = DataLoader(train_dataset, shuffle=True, batch_size=8) # 初始化模型 model = PhonePriceModel(input_dim, class_num) # 损失函数 criterion = nn.CrossEntropyLoss() # 优化方法 optimizer = optim.SGD(model.parameters(), lr=1e-3) # 训练轮数 num_epoch = 50 # 遍历每个轮次的数据 for epoch_idx in range(num_epoch): # 训练时间 start = time.time() # 计算损失 total_loss = 0.0 total_num = 0 # 遍历每个batch数据进行处理 for x, y in dataloader: # 将数据送入网络中进行预测 output = model(x) # 计算损失 loss = criterion(output, y) # 梯度清零 optimizer.zero_grad() # 反向传播 loss.backward() # 参数更新 optimizer.step() # 损失计算 total_num += 1 total_loss += loss.item() # 打印损失变换结果 print('epoch: %4s loss: %.2f, time: %.2fs' %(epoch_idx + 1, total_loss / total_num, time.time() - start)) # 模型保存 torch.save(model.state_dict(), 'model/phone.pth')

训练的输出结果:

8.2.5 模型评估

使用训练好的模型,对未知的样本的进行预测的过程。我们这里使用前面单独划分出来的验证集来进行评估。

def test(valid_dataset,input_dim,class_num): # 加载模型和训练好的网络参数 model = PhonePriceModel(input_dim, class_num) model.load_state_dict(torch.load('model/phone.pth')) # 构建加载器 dataloader = DataLoader(valid_dataset, batch_size=8, shuffle=False) # 评估测试集 correct = 0 # 遍历测试集中的数据 for x, y in dataloader: # 将其送入网络中 output = model(x) # 获取类别结果 y_pred = torch.argmax(output, dim=1) # 获取预测正确的个数 correct += (y_pred == y).sum() # 求预测精度 print('Acc: %.5f' % (correct.item() / len(valid_dataset)))

输出结果:

Acc: 0.64250

8.3 调优方向

我们前面的网络模型在测试集的准确率为: 0.64250, 我们可以通过以下方面进行调优:

  1. 优化方法由 SGD 调整为 Adam

  2. 学习率由 1e-3 调整为 1e-4

  3. 对数据进行标准化

  4. 增加网络深度,即:增加网络参数量

  5. 调整训练轮次


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 6:10:28

3分钟实现Figma中文界面:设计师必备的高效本地化工具

3分钟实现Figma中文界面&#xff1a;设计师必备的高效本地化工具 【免费下载链接】figmaCN 中文 Figma 插件&#xff0c;设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma的英文界面感到困扰吗&#xff1f;想要在创意设计时摆脱语言…

作者头像 李华
网站建设 2026/5/15 6:08:06

WorkBuddy清理Claw历史会话指南

&#x1f527; WorkBuddy 清理Claw历史会话指南「有些在Claw上用来做测试的对话一直存在&#xff0c;界面没有删除按钮&#xff0c;就算把文件夹删了&#xff0c;历史记录也还是在&#xff0c;强迫症都犯了&#xff01;&#xff01;&#xff01;」—— 来自一位真实网友的吐槽如…

作者头像 李华
网站建设 2026/5/15 6:05:17

Python ChatGPT SDK封装:简化OpenAI API集成,提升开发效率

1. 项目概述&#xff1a;一个面向开发者的ChatGPT SDK封装如果你正在开发一个需要集成AI对话能力的应用&#xff0c;无论是客服机器人、内容生成工具&#xff0c;还是智能助手&#xff0c;你大概率绕不开OpenAI的API。但直接调用原生的OpenAI API&#xff0c;尤其是在一个稍具规…

作者头像 李华
网站建设 2026/5/15 6:05:16

AirLift协处理器与Adafruit IO:简化物联网开发的硬件与云端组合

1. 项目概述&#xff1a;当微控制器遇上云端 如果你玩过Arduino或者CircuitPython&#xff0c;肯定遇到过这样的场景&#xff1a;想给温湿度传感器加个联网功能&#xff0c;实时把数据传到手机上看看&#xff0c;结果光是让ESP8266连上Wi-Fi就折腾了半天&#xff0c;更别提还要…

作者头像 李华