神经网络
神经网络(NN)是一种模拟人脑工作方式的数学模型或算法结构,它被广泛应用于人工智能、深度学习、语音识别、图像识别等领域。
它的目标是:模拟人脑中神经元如何接收、处理和传递信息,让计算机也能“像人脑一样学习”。
组成部分
人工神经网络通常由以下几个部分组成:
输入层(Input Layer)
- 就是接收原始数据的地方,比如图像的像素值、文本的编码等。
隐藏层(Hidden Layers)
类似“思考”的过程。输入经过加权处理 + 非线性变换,再传给下一层。
隐藏层的多寡、结构决定了模型的“智能程度”。
输出层(Output Layer)
- 最终给出预测结果,比如“是狗还是猫”、“分数是多少”等。
其中,每一层都有若干个神经元:

多个神经元来构建神经网络,相邻层之间的神经元相互连接:
- 同一层的神经元之间没有连接。
- 第 \(N\) 层的每个神经元和第 \(N-1\) 层的所有神经元相连(这就是full connectedi的含义),这就是全连接神经网络。
- 第 \(N-1\) 层神经元的输出就是第 \(N\) 层神经元的输入。
- 每个连接都有一个权重值 (\(w\) 系数和 \(b\) 系数)。
激活函数
激活函数用于对每层的输出数据进行变换,进而为整个网络注入了非线性因素。此时,神经网络就可以拟合各种曲线。
- 没有引入非线性因素的网络等价于使用一个线性模型来拟合。
- 通过给网络输出增加激活函数,实现引入非线性因素,使得网络模型可以逼近任意函数,提升网络对复杂问题的拟合能力。
导入需要的库
import matplotlib.pyplot as plt
# 首先导入所有必要的库
import numpy as np
# 设置样式
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号Sigmod 函数
函数定义:
导数:
图像:
# Sigmoid函数及其导数实现
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
s = sigmoid(x)
return s * (1 - s)
# 绘制图像
x = np.linspace(-5, 5, 500)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x, sigmoid(x), label='Sigmoid', color='blue')
plt.title('Sigmoid函数')
plt.xlabel('x')
plt.ylabel('σ(x)')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(x, sigmoid_derivative(x), label='Sigmoid导数', color='red')
plt.title('Sigmoid导数')
plt.xlabel('x')
plt.ylabel('σ\'(x)')
plt.legend()
plt.tight_layout()
plt.show()
特点:
- 将输入压缩到(0,1)区间,当输入的值大致在 <-6 或者 >6 时,意味着输入任何值得到的激活值都是差不多的,这样会丢失部分的信息
- 比如:输入100和输出10000经过sigmoid的激活值几乎都是等于1的,但是输入的数据之间相差100倍的信息就丢失了
分析原因:
# 标记信息丢失区间
plt.figure(figsize=(8, 5))
plt.plot(x, sigmoid_derivative(x), label='Sigmoid导数', color='red')
# 标记导数接近0的区域
plt.fill_between(x, sigmoid_derivative(x), where=(x>2.5)|(x<-2.5),
color='red', alpha=0.3, label='梯度消失区域')
plt.title('Sigmoid导数及信息丢失区间')
plt.xlabel('x')
plt.ylabel('σ\'(x)')
plt.legend()
plt.show()
由图像可知:
- 当|x| > 2.5 时,导数接近 0,梯度几乎消失
- 此时网络参数将更新极其缓慢,意味着在这些区域输入的变化几乎不会影响输出,网络难以学习
- 在深度网络中,多层小梯度相乘会导致梯度指数级减小
一般来说,sigmoid网络在5层之内就会产生梯度消失现象。而且,该激活函数并不是以0为中心的,所以在实践中这种激活函数使用的很少。sigmoid函数一般只用于二分类的输出层。
Tanh 函数
函数定义:
导数:
图像:
# Tanh函数及其导数实现
def tanh(x):
return np.tanh(x)
def tanh_derivative(x):
return 1 - np.tanh(x)**2
# 绘制图像
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x, tanh(x), label='Tanh', color='blue')
plt.title('Tanh函数')
plt.xlabel('x')
plt.ylabel('tanh(x)')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(x, tanh_derivative(x), label='Tanh导数', color='red')
plt.title('Tanh导数')
plt.xlabel('x')
plt.ylabel('tanh\'(x)')
plt.legend()
plt.tight_layout()
plt.show()
特点:
- 将输入压缩到(-1,1)区间,当输入在 <-3 或者 >3 时将被映射为 -1 或者 1。
- 与Sigmoid相比,它是以0为中心的,且梯度相对于sigmoid大,使得其收敛速度要比Sigmoid快,减少迭代次数。
分析原因:
plt.figure(figsize=(8, 5))
plt.plot(x, tanh_derivative(x), label='Tanh导数', color='red')
# 标记导数接近0的区域
plt.fill_between(x, tanh_derivative(x), where=(x>2.5)|(x<-2.5),
color='red', alpha=0.3, label='梯度消失区域')
plt.title('Tanh导数及信息丢失区间')
plt.xlabel('x')
plt.ylabel('tanh\'(x)')
plt.legend()
plt.show()
由图像可知:
- 当|x|>2.5时,导数接近0,存在梯度消失问题,但相比Sigmoid,梯度消失的程度较轻
- 其导数值范围是(0,1],而Sigmoid是(0,0.25]
若使用时可在隐藏层使用tanh函数,如RNN和LSTM网络中的隐藏层,在输出层使用sigmoid函数。
ReLU 函数
函数定义:
导数:
图像:
# ReLU函数及其导数实现
def relu(x):
return np.maximum(0, x)
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# 绘制图像
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x, relu(x), label='ReLU', color='blue')
plt.title('ReLU函数')
plt.xlabel('x')
plt.ylabel('ReLU(x)')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(x, relu_derivative(x), label='ReLU导数', color='red')
plt.title('ReLU导数')
plt.xlabel('x')
plt.ylabel('ReLU\'(x)')
plt.legend()
plt.tight_layout()
plt.show()
特点:
- ReLU 激活函数将小于 0 的值映射为 0,而大于 0 的值则保持不变,它更加重视正信号,而忽略负信号,这种激活函数运算更为简单,能够提高模型的训练效率。
- 当 x<0 时,ReLU 导数为 0,而当 x>0 时,则不存在饱和问题。所以,ReLU 能够在 x>0 时保持梯度不衰减,从而缓解梯度消失问题。
- 然而,随着训练的推进,部分输入会落入小于 0 区域,导致对应权重无法更新。这种现象被称为“神经元死亡”
采用 Sigmoid 函数,计算量大(指数运算),反向传播求误差梯度时,计算量相对大,而采用ReLU激活函数,整个过程的计算量节省很多。
Sigmoid 函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。ReLU 会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生,适用于CNN等计算密集型网络和需要快速收敛的场景。
Softmax 函数
Softmax函数将任意实值向量转换为概率分布
函数定义:
其中:
- \(\mathbf{x} = (x_1, x_2, ..., x_n)\) 是输入向量
- 输出向量的每个元素 \(\text{Softmax}(\mathbf{x})_i \in (0,1)\)
- 所有输出元素之和为 \(1\) :\(\sum_{i=1}^n \text{Softmax}(\mathbf{x})_i = 1\)
Softmax的导数是一个Jacobian矩阵:
图像:
# Softmax函数实现
def softmax(x):
e_x = np.exp(x - np.max(x)) # 数值稳定处理
return e_x / e_x.sum()
# 1D情况下的Softmax及其导数
x_softmax = np.linspace(-5, 5, 500)
y_softmax = softmax(x_softmax)
softmax_derivative_1d = y_softmax * (1 - y_softmax) # 1D特殊情况
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(x_softmax, y_softmax, label='Softmax', color='blue')
plt.title('Softmax函数(1D输入)')
plt.xlabel('x')
plt.ylabel('Softmax(x)')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(x_softmax, softmax_derivative_1d, label='Softmax导数(1D)', color='red')
plt.title('Softmax导数(1D情况)')
plt.xlabel('x')
plt.ylabel('Softmax\'(x)')
plt.legend()
plt.tight_layout()
plt.show()
特点:
- Softmax 就是将网络输出的 logits 通过 softmax函数,就映射成为(O,1)的值
- 这些值的累和为 1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。
分析原因:
plt.figure(figsize=(8, 5))
plt.plot(x_softmax, softmax_derivative_1d, label='Softmax导数', color='red')
# 标记导数接近0的区域
plt.fill_between(x_softmax, softmax_derivative_1d,
where=(x_softmax>2)|(x_softmax<-2),
color='red', alpha=0.3, label='梯度消失区域')
plt.title('Softmax导数及信息丢失区间(1D情况)')
plt.xlabel('x')
plt.ylabel('Softmax\'(x)')
plt.legend()
plt.show()
由图像可知:
- 当输入值差异很大时,输出接近one-hot编码,导数接近0
- 数值不稳定问题需要特殊处理(减去最大值)
- 多维情况下导数形成 Jacobian 矩阵,非对角线元素不为零
Softmax 不是严格意义上的信息“丢失”,而是信息“压缩”甚至“掩盖”,在数值差异过大的情况下可能带来梯度消失、训练不稳定等问题。尤其在深层网络或大类分类中更明显。
选择推荐
隐藏层:
| 激活函数 | 适用场景 | 注意事项 |
|---|---|---|
| Sigmoid | 1. 二分类输出层 2. 需要概率输出的场景 3. 早期神经网络(现较少用于隐藏层) | 1. 梯度消失问题严重 2. 输出非零中心(可能影响梯度下降效率) 3. 计算量较大(含指数运算) |
| Tanh | 1. 隐藏层激活(优于Sigmoid) 2. RNN/LSTM网络 3. 需要输出范围在[-1,1]的场景 | 1. 梯度消失问题仍存在(但比Sigmoid轻) 2. 零中心输出(梯度下降更稳定) |
| ReLU | 1. CNN和DNN的隐藏层(最常用) 2. 需要稀疏激活的场景 3. 深层网络 | 1. 死亡ReLU问题(负值区梯度为0) 2. 输出非零中心 3. 无上界(可能梯度爆炸) |
- 优先选择 ReLU 激活函数,如果 ReLu效果不好,那么尝试 Leaky ReLu
- 如果你使用了 ReLU,需要注意一下 Dead ReLU 问题,避免出现大的梯度从而导致过多的神经元死亡
- 少用使用 Sigmoid 激活函数,可以尝试使用 Tanh 激活函数
输出层:
| 激活函数 | 适用场景 | 注意事项 |
|---|---|---|
| Sigmoid | 1. 二分类任务(输出概率) 2. 多标签分类(每个标签独立概率) | 1. 搭配 Binary Cross-Entropy 损失函数2. 梯度消失严重,避免用于隐藏层 3. 输出非零中心化(0,1),可能影响训练收敛 |
| Tanh | 1. 有界回归任务(输出需归一化到 \([-1, 1]\)) 2. 生成模型(如GAN生成器输出) | 1. 输出零中心化,梯度比Sigmoid稳定 2. 仍存在梯度消失问题,不适合极深网络 |
| ReLU | 1. 计数型回归(如预测商品销量,输出非负) 2. 轻量级输出层(需快速计算) | 1. 输出无上界,可能不稳定 2. 需谨慎初始化(如He初始化) 3. 死亡神经元问题较少(因输出层无多次反向传播) |
| Softmax | 1. 单标签多分类任务(如分类10个类别) 2. 输出互斥概率分布(概率和为1) | 1. 搭配 Categorical Cross-Entropy 损失函数2. 计算时需数值稳定化(如 logits - max(logits))3. 类别过多时计算成本高(如超1000类) |
- 二分类问题选择 Sigmod激活函数
- 多分类问题选择 Softmax 激活函数
参数初始化
参数初始化的目的:
- 保证激活值在合理范围内分布(不过大/不过小)
- 保证反向传播时梯度不会消失或爆炸
零初始化
方法:
import torch.nn as nn
model = nn.Linear(10, 5)
nn.init.constant_(model.weight, 0.0)
nn.init.constant_(model.bias, 0.0)问题:每个神经元计算完全相同的结果(对称性),导致网络无法学习有用特征。
结论:不推荐
随机初始化
方法:高斯分布或均匀分布
nn.init.normal_(model.weight, mean=0.0, std=1.0)
nn.init.constant_(model.bias, 0.0)
nn.init.uniform(model.weight) # 均匀分布初始化问题:可能会导致不同层之间的数值范围差异过大,造成梯度爆炸或消失。
结论:不够稳定,需改进
Xavier 初始化
适用:Sigmoid / Tanh 激活函数
公式:
- 均匀分布版本:\[W \sim \mathcal{U}\left[-\frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}}, \frac{\sqrt{6}}{\sqrt{n_{\text{in}} + n_{\text{out}}}}\right] \]
nn.init.xavier_uniform_(model.weight)- 正态分布版本:\[W \sim \mathcal{N}\left(0, \frac{2}{n_{\text{in}} + n_{\text{out}}}\right) \]
nn.init.xavier_normal_(model.weight)优点:保持前向传播/反向传播中方差一致,避免梯度消失
He 初始化(Kaiming Initialization)
适用:ReLU / Leaky ReLU 激活函数
公式:
- 正态分布版本:\[W \sim \mathcal{N}\left(0, \frac{2}{n_{\text{in}}}\right) \]
nn.init.kaiming_uniform_(model.weight, mode='fan_in', nonlinearity='relu')- 均匀分布版本:\[W \sim \mathcal{U}\left[-\sqrt{\frac{6}{n_{\text{in}}}}, \sqrt{\frac{6}{n_{\text{in}}}}\right] \]
nn.init.kaiming_normal_(model.weight, mode='fan_in', nonlinearity='relu')优点:显著缓解ReLU网络的死亡神经元问题,稳定训练。
案例实战
一个简单的二分类任务前馈神经网络:
- 第1个隐藏层:权重初始化采用标准化的 Xavier 初始化激活函数使用 Sigmod
- 第2个隐藏层:权重初始化采用标准化的He初始化激活函数采用 ReLU
- 输出层:线性层,二分类,采用 Softmax 做数据归一化
import torch
import torch.nn as nn
from torchsummary import summary # 计算模型参数,查看模型结构
# 创建神经网络模型类
class Model(nn.Module):
# 初始化属性值
def __init__(self):
super(Model, self).__init__() # 调用父类的初始化属性值
# 创建第一个隐藏层模型,3个输入特征,3个输出特征
self.linear1 = nn.Linear(3, 3)
# 初始化权重 - 标准化的Xavier初始化
nn.init.xavier_normal_(self.linear1.weight)
# 创建第二个隐藏层模型,3个输入特征(上一层的输出特征),2个输出特征
self.linear2 = nn.Linear(3, 2)
# 初始化权重 - He初始化
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) # 注意:这里应该是torch.relu(x),原代码有拼写错误
# 数据经过输出层
x = self.out(x)
# 使用softmax激活函数
# dim=-1: 每一维度行数据相加为1
x = torch.softmax(x, dim=-1)
return x
if __name__ == '__main__':
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print('current device:', device)
# 实例化model对象
my_model = Model().to(device)
# 随机产生数据
my_data = torch.randn(5, 3).to(device) # 5个样本,每个样本3个特征
print("my_data shape:", my_data.shape)
# 数据经过神经网络模型训练
output = my_model(my_data)
print("output shape->", output.shape)
# 计算模型参数
# 计算每层每个神经元的w和b个数总和
summary(my_model, input_size=(3,), batch_size=5, device=str(device))
# 查看模型参数
print("查看模型参数w和b:")
for name, parameter in my_model.named_parameters():
print(name, parameter)模型设计:
| 层级 | 输入维度 | 输出维度 | 激活函数 | 初始化方法 |
|---|---|---|---|---|
| Linear1 | 3 | 3 | Sigmoid | Xavier(适用于Sigmoid) |
| Linear2 | 3 | 2 | ReLU | He(适用于ReLU) |
| Output Layer | 2 | 2 | Softmax | 默认初始化 |
为什么 x = torch.softmax(x, dim=-1):
dim决定了 在哪个维度上做归一化,也就是在这个维度上让所有值的和为 1。dim=-1是一种写法,表示 最后一个维度,无论输入是 2D、3D 还是更多维。dim=1和dim=-1在 2D 情况下是一样的,都是对“列”归一化。- 但在更高维(比如 3D、4D)中,
dim=-1可以自动适配最后一维,更通用和稳健。
模型的参数个数 = 权重数 + 偏置数 = (in_features × out_features) + out_features
计算该模型的参数量:
- 第一个隐藏层:nn.Linear(3, 3),共 3 * 3 + 3 = 12 个参数
- 第二个隐藏层:nn.Linear(3, 2),共 3 * 2 + 2 = 8 个参数
- 输出层:nn.Linear(2, 2),共 2 * 2 + 2 = 6 个参数
故一共有 26 个可训练参数
损失函数
在机器学习和深度学习中,损失函数是用来衡量模型参数的质量的函数,衡量的方式是比较网络输出和真实输出的差异:

多分类损失函数
公式定义
在多分类任务通常使用 Softmax 将 logits 转换为概率的形式,所以多分类的交叉熵损失也叫做 Softmax 损失,计算方式如下:
首先,将模型的原始输出(logits)通过 Softmax 函数转换为概率分布:
其中:
- \(z_i\) 是第 \(i\) 类的 logit 值,
- \(C\) 是类别总数,
- \(p_i\) 是第 \(i\) 类的预测概率。
然后,计算真实标签与预测概率之间的交叉熵损失:
其中:
- \(y_i\) 是第 \(i\) 类的真实标签(one-hot 编码形式,即真实类别为 1,其余为 0),
- \(p_i\) 是第 \(i\) 类的预测概率,
- \(C\) 是类别总数。
通常将 Softmax 和交叉熵合并表示为:
其中 \(z_k\) 是真实类别对应的 logit 值。
计算示例
假设:
模型输出的 logits(未归一化的分数):
\[\mathbf{z} = [2.0, 1.0, 0.1] \]真实标签(one-hot 编码):
\[\mathbf{y} = [0, 1, 0] \]
首先,将 logits 通过 Softmax 函数转换为概率分布:
得到:
所以 Softmax 概率为:
即:
由交叉熵损失公式:
由于真实标签是 one-hot 编码(\(y_2 = 1\),其余为 0),所以只需计算真实类别对应的 \(-\log(p_2)\):
代码实现:
import torch
import torch.nn as nn
# 定义 logits 和真实标签(注意:这里 y 是类别索引,不是 one-hot)
z = torch.tensor([[2.0, 1.0, 0.1]], dtype=torch.float32) # 必须是 2D (batch_size, num_classes)
y = torch.tensor([1], dtype=torch.long) # 类别索引(1 表示第2类)
# 使用 nn.CrossEntropyLoss(自动包含 Softmax + 交叉熵)
criterion = nn.CrossEntropyLoss()
loss = criterion(z, y)
print("Logits:", z)
print("真实类别:", y.item()) # 1
print("交叉熵损失:", loss.item()) # ≈1.418二分类损失函数
公式定义
在二分类任务中,通常使用 Sigmoid 函数将模型的原始输出(logit)转换为概率值,然后计算 二元交叉熵损失,具体步骤如下:
首先适用 Sigmoid 函数将 logit 转换为概率:
其中:
- \(z\) 是模型的原始输出(logit),
- \(p\) 是预测为正类(类别 1)的概率。
再计算元交叉熵损失:
其中:
- \(y\) 是真实标签(0 或 1),
- \(p\) 是预测为正类的概率。
特点:
- 当 \(y = 1\) 时,损失为 \(-\log(p)\)(希望 \(p\) 越大越好)。
- 当 \(y = 0\) 时,损失为 \(-\log(1 - p)\)(希望 \(p\) 越小越好)。
计算示例
假设:
- 模型输出的 logit:\[z = 0.8 \]
- 真实标签:\[y = 1 \]
- 计算 Sigmoid 概率:\[p = \frac{1}{1 + e^{-0.8}} \approx \frac{1}{1 + 0.449} \approx 0.690 \]
- 计算交叉熵损失:\[L = -\left[ 1 \cdot \log(0.690) + 0 \cdot \log(1 - 0.690) \right] \approx -\log(0.690) \approx 0.371 \]
代码实现:
import torch
import torch.nn as nn
# 定义 logit 和真实标签
z = torch.tensor([0.8], dtype=torch.float32) # 注意是 1D 或 2D
y = torch.tensor([1.0], dtype=torch.float32) # 二分类标签必须是浮点数
# 使用 nn.BCEWithLogitsLoss(内置 Sigmoid + 交叉熵)
criterion = nn.BCEWithLogitsLoss()
loss = criterion(z, y)
print("Logit:", z.item()) # 0.8
print("真实标签:", y.item()) # 1
print("Sigmoid 概率:", torch.sigmoid(z).item()) # ≈0.690
print("二元交叉熵损失:", loss.item()) # ≈0.371回归MAE损失函数
公式定义
平均绝对误差(Mean Absolute Error, MAE)是一种用于回归任务的损失函数,MAE也被称为 L1 Loss,计算预测值与真实值之间绝对差值的平均值。
公式如下:
其中:
- \(y_i\) 是第 \(i\) 个样本的真实值,
- \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,
- \(N\) 是样本数量。
特点:
- 鲁棒性:MAE 对异常值不敏感,因为它是绝对误差的平均值,不像均方误差(MSE)会放大异常值的影响。
- 梯度:MAE 的梯度是常数(±1),在优化时不会因为误差大小而改变步长,但在接近最小值时可能收敛较慢。
- 输出解释:MAE 的值与实际目标值的单位一致,直观易解释(例如,MAE=2 表示平均误差为 2 个单位)。
计算示例
假设:
- 真实值:\([3.0, -0.5, 2.0, 7.0]\)
- 预测值:\([2.5, 0.0, 2.0, 8.0]\)
计算绝对误差:
MAE 损失:
代码实现:
import torch
import torch.nn as nn
# 真实值和预测值(假设 batch_size=4)
y_true = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0])
# 使用 PyTorch 的 L1Loss(即 MAE)
criterion = nn.L1Loss()
loss = criterion(y_pred, y_true)
print("MAE 损失:", loss.item()) # 输出 0.5回归MSE损失函数
公式定义
均方误差(Mean Squared Error, MSE)是回归任务中最常用的损失函数之一,MSE也被称为L2 Loss,计算预测值与真实值之间平方差值的平均值。
公式如下:
其中:
- \(y_i\) 是第 \(i\) 个样本的真实值,
- \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,
- \(N\) 是样本数量。
特点:
- 对异常值敏感:MSE会放大较大误差的影响(因为平方操作),因此对异常值比MAE更敏感。
- 梯度特性:MSE的梯度与误差大小成正比(梯度为\(2(y_i - \hat{y}_i)\)),在误差较大时梯度也较大,有助于快速收敛,但在接近最小值时可能不稳定。
- 输出解释:MSE的单位是目标值的平方(例如,若目标单位是"米",MSE单位是"平方米"),因此需开平方(RMSE)才能与实际单位一致。
计算示例
假设:
- 真实值:\([3.0, -0.5, 2.0, 7.0]\)
- 预测值:\([2.5, 0.0, 2.0, 8.0]\)
计算平方误差:
MSE 损失:
代码实现:
import torch
import torch.nn as nn
# 真实值和预测值(假设 batch_size=4)
y_true = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0])
# 使用 PyTorch 的 MSELoss
criterion = nn.MSELoss()
loss = criterion(y_pred, y_true)
print("MSE 损失:", loss.item()) # 输出 0.375平滑L1损失函数
公式定义
平滑L1损失(Smooth L1 Loss)是L1 Loss(MAE)和L2 Loss(MSE)的结合体,由Ross Girshick在Fast R-CNN中提出。它在误差较小时使用平方项(类似MSE),在误差较大时使用线性项(类似MAE),兼具两者的优点。
公式如下:
其中:
特点:
- 鲁棒性:对异常值比MSE更鲁棒(大误差时退化为线性)
- 梯度平滑:在误差接近0时梯度逐渐减小(避免MSE在极小误差时的不稳定)
- 收敛性:比MAE在接近最优解时收敛更快(因小误差时使用二次项)
对比图像:

计算示例
假设:
- 真实值:\([1.2, -0.8, 3.0, -2.5]\)
- 预测值:\([0.9, -0.5, 2.6, -1.8]\)
- 阈值 \(\beta=1\)(默认值)
逐项计算:
\(|1.2-0.9|=0.3<1\) → \(0.5 \times 0.3^2 = 0.045\)
\(|-0.8-(-0.5)|=0.3<1\) → \(0.5 \times 0.3^2 = 0.045\)
\(|3.0-2.6|=0.4<1\) → \(0.5 \times 0.4^2 = 0.08\)
\(|-2.5-(-1.8)|=0.7<1\) → \(0.5 \times 0.7^2 = 0.245\)
总损失:
代码实现:
import torch
import torch.nn as nn
# 真实值和预测值
y_true = torch.tensor([1.2, -0.8, 3.0, -2.5])
y_pred = torch.tensor([0.9, -0.5, 2.6, -1.8])
# PyTorch的SmoothL1Loss(默认beta=1.0)
criterion = nn.SmoothL1Loss()
loss = criterion(y_pred, y_true)
print("Smooth L1 Loss:", loss.item()) # 输出 0.10375PyTorch中的 beta参数 控制二次项与线性项的分界点:
- 当
|y - ŷ| < beta时使用二次项 - 当
|y - ŷ| >= beta时使用线性项
不同beta值的影响:
# 示例:调整beta值
criterion_beta05 = nn.SmoothL1Loss(beta=0.5) # 更早切换到线性区域
criterion_beta2 = nn.SmoothL1Loss(beta=2.0) # 更晚切换到线性区域