环境声明

  • Python版本:Python 3.10+
  • PyTorch版本:PyTorch 2.0+
  • NumPy版本:NumPy 1.24+
  • Matplotlib版本:Matplotlib 3.7+
  • 开发工具:PyCharm / VS Code / Jupyter Notebook
  • 操作系统:Windows / macOS / Linux(通用)
  • GPU支持:CUDA 11.8+(可选,但推荐)

学习目标

通过本章学习,你将掌握:

  1. 理解激活函数在神经网络中的核心作用和选择原则
  2. 深入掌握Sigmoid、Tanh等经典激活函数的原理与适用场景
  3. 全面理解ReLU家族(ReLU、LeakyReLU、PReLU、ELU、GELU)的特点
  4. 掌握Swish、Mish、SwiGLU等现代激活函数的设计思想
  5. 理解批归一化(BatchNorm)的原理与PyTorch实现
  6. 掌握层归一化、实例归一化、组归一化的区别与选择
  7. 能够根据任务类型选择合适的激活函数和归一化方法

内容摘要

激活函数是神经网络的核心组件,它赋予网络非线性表达能力。从早期的Sigmoid到现代的SwiGLU,激活函数经历了长足发展。本章将系统讲解各类激活函数的原理、优缺点和适用场景,同时深入剖析归一化技术(BatchNorm、LayerNorm等)如何稳定训练过程、加速收敛。通过可视化对比和实战代码,帮助你建立完整的知识体系。


1. 激活函数的作用与选择原则

1.1 为什么需要激活函数

想象一个没有激活函数的神经网络:无论网络有多少层,它本质上只是在进行线性变换的叠加。数学上,多个线性变换的组合仍然是一个线性变换,这意味着网络的表达能力极其有限,无法学习复杂的非线性模式。

激活函数的作用可以类比为:

  • 神经元的开关:决定信号是否传递、传递多少
  • 非线性映射:将线性输出转换为非线性空间
  • 特征筛选:增强有用特征,抑制噪声

补充:没有激活函数的神经网络等价于单层感知机,这被称为"多层网络的线性退化问题"。

1.2 激活函数的选择原则

选择激活函数时需要考虑以下因素:

考量因素 说明 影响
非线性程度 函数的非线性表达能力 决定网络能学习的模式复杂度
梯度特性 导数是否平滑、是否存在梯度消失 影响训练稳定性和收敛速度
计算效率 前向和反向传播的计算开销 影响模型训练和推理速度
输出范围 输出值是否有界 影响网络层间的信号传递
平滑性 函数是否处处可导 影响优化过程的稳定性

2. 经典激活函数详解

2.1 Sigmoid函数

Sigmoid函数是最早被广泛使用的激活函数之一,它将任意实数映射到(0, 1)区间。

数学公式

σ(x) = 1 / (1 + e^(-x))

Python实现

import numpy as np
import torch
import torch.nn as nn

def sigmoid(x):
    """Sigmoid激活函数的实现"""
    return 1 / (1 + np.exp(-x))

# PyTorch内置实现
sigmoid_torch = nn.Sigmoid()

特性分析

  • 优点:输出范围(0,1),适合概率解释;处处可导且导数平滑
  • 缺点
    • 梯度消失问题:当|x|较大时,梯度趋近于0
    • 输出非零中心化:导致梯度更新效率低
    • 计算涉及指数运算,相对较慢

适用场景

  • 二分类问题的输出层
  • 门控机制(如LSTM的遗忘门、输入门)
  • 需要概率解释的场景

2.2 Tanh函数

Tanh(双曲正切)函数是Sigmoid的改进版本,输出范围扩展到(-1, 1)。

数学公式

tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x)) = 2σ(2x) - 1

Python实现

def tanh(x):
    """Tanh激活函数的实现"""
    return np.tanh(x)

# PyTorch内置实现
tanh_torch = nn.Tanh()

特性分析

  • 优点
    • 零中心化输出:有助于梯度下降收敛
    • 梯度比Sigmoid更强:导数范围(0,1] vs (0,0.25]
  • 缺点
    • 仍然存在梯度消失问题
    • 计算开销与Sigmoid相当

适用场景

  • 循环神经网络(RNN、LSTM)的隐藏层
  • 需要零中心化输出的中间层

3. ReLU家族详解

3.1 ReLU(Rectified Linear Unit)

ReLU是深度学习领域最重要的突破之一,它简单却极其有效。

数学公式

ReLU(x) = max(0, x)

Python实现

def relu(x):
    """ReLU激活函数的实现"""
    return np.maximum(0, x)

# PyTorch内置实现
relu_torch = nn.ReLU()

特性分析

  • 优点
    • 计算简单:仅需比较操作
      无梯度消失问题(正区间梯度恒为1)
    • 引入稀疏性:约50%神经元输出为0
  • 缺点
    • 死亡ReLU问题:负区间梯度为0,神经元可能永久失活
    • 输出非零中心化

适用场景

  • 卷积神经网络(CNN)的隐藏层
  • 全连接网络的中间层
  • 现代深度学习模型的默认选择

3.2 LeakyReLU

为解决死亡ReLU问题,LeakyReLU在负区间引入小的斜率。

数学公式

LeakyReLU(x) = max(αx, x),其中α通常取0.01

Python实现

def leaky_relu(x, alpha=0.01):
    """LeakyReLU激活函数的实现"""
    return np.where(x > 0, x, alpha * x)

# PyTorch内置实现
leaky_relu_torch = nn.LeakyReLU(negative_slope=0.01)

特性分析

  • 保留ReLU的优点
  • 负区间有微小梯度,避免神经元死亡
  • 超参数α需要调优

3.3 PReLU(Parametric ReLU)

PReLU将LeakyReLU的斜率参数α变为可学习参数。

Python实现

# PyTorch内置实现
prelu_torch = nn.PReLU()  # α作为可学习参数

特性分析

  • 自适应学习负区间的最佳斜率
  • 增加少量参数,提升模型灵活性
  • 可能增加过拟合风险

3.4 ELU(Exponential Linear Unit)

ELU在负区间使用指数函数,使输出更接近零均值。

数学公式

ELU(x) = x, if x > 0
ELU(x) = α(e^x - 1), if x ≤ 0

Python实现

def elu(x, alpha=1.0):
    """ELU激活函数的实现"""
    return np.where(x > 0, x, alpha * (np.exp(x) - 1))

# PyTorch内置实现
elu_torch = nn.ELU(alpha=1.0)

特性分析

  • 负区间平滑,输出接近零均值
  • 缓解死亡ReLU问题
  • 负区间计算涉及指数,稍慢于ReLU

3.5 GELU(Gaussian Error Linear Unit)

GELU是Transformer架构的核心激活函数,在BERT、GPT等模型中广泛使用。

数学公式

GELU(x) = x * Φ(x) = x * 0.5 * (1 + erf(x / √2))

其中Φ(x)是标准正态分布的累积分布函数。

近似实现

GELU(x) ≈ 0.5x * (1 + tanh[√(2/π) * (x + 0.044715x³)])

Python实现

def gelu(x):
    """GELU激活函数的近似实现"""
    return 0.5 * x * (1 + np.tanh(
        np.sqrt(2 / np.pi) * (x + 0.044715 * np.power(x, 3))
    ))

# PyTorch内置实现
gelu_torch = nn.GELU()

特性分析

  • 平滑可导,处处非零梯度
  • 自门控机制:输出与输入的概率分布相关
  • 在NLP任务中表现优异

4. 现代激活函数

4.1 Swish激活函数

Swish是Google Brain提出的自门控激活函数,在深层网络中表现优异。

数学公式

Swish(x) = x * σ(x) = x / (1 + e^(-x))

Python实现

def swish(x):
    """Swish激活函数的实现"""
    return x * sigmoid(x)

# PyTorch实现
class Swish(nn.Module):
    def forward(self, x):
        return x * torch.sigmoid(x)

特性分析

  • 非单调函数:在x<0时先下降后上升
  • 自门控机制:输出由输入自身控制
  • 深层网络中性能优于ReLU

4.2 Mish激活函数

Mish是2019年提出的激活函数,结合了Swish的优点并进一步优化。

数学公式

Mish(x) = x * tanh(softplus(x)) = x * tanh(ln(1 + e^x))

Python实现

def mish(x):
    """Mish激活函数的实现"""
    return x * np.tanh(np.log(1 + np.exp(x)))

# PyTorch实现
class Mish(nn.Module):
    def forward(self, x):
        return x * torch.tanh(torch.nn.functional.softplus(x))

特性分析

  • 自正则化特性:输出范围有限,有助于稳定训练
  • 平滑无界:避免硬阈值带来的信息损失
  • 在目标检测等任务中表现突出

4.3 SwiGLU激活函数

SwiGLU是当前大语言模型(如LLaMA、PaLM)广泛使用的激活函数,结合了Swish和GLU(门控线性单元)的优点。

数学公式

SwiGLU(x, W, V, b, c) = Swish(xW + b) ⊗ (xV + c)

其中⊗表示逐元素乘法。

Python实现

class SwiGLU(nn.Module):
    """SwiGLU激活函数实现"""
    def __init__(self, dim_in, dim_out):
        super().__init__()
        self.w1 = nn.Linear(dim_in, dim_out)
        self.w2 = nn.Linear(dim_in, dim_out)
        self.w3 = nn.Linear(dim_out, dim_out)
    
    def forward(self, x):
        # SwiGLU: Swish(xW) * (xV)
        return self.w3(nn.functional.silu(self.w1(x)) * self.w2(x))

# 简化的SwiGLU实现(常用于Transformer FFN)
class SwiGLUFFN(nn.Module):
    def __init__(self, dim, hidden_dim=None, dropout=0.0):
        super().__init__()
        if hidden_dim is None:
            # SwiGLU通常使用更大的中间维度
            hidden_dim = int(dim * 8 / 3)
        
        self.w1 = nn.Linear(dim, hidden_dim)
        self.w2 = nn.Linear(dim, hidden_dim)
        self.w3 = nn.Linear(hidden_dim, dim)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        # SiLU即Swish激活函数
        hidden = nn.functional.silu(self.w1(x)) * self.w2(x)
        return self.dropout(self.w3(hidden))

特性分析

  • 门控机制:选择性传递信息,增强表达能力
  • 在现代LLM中成为标配
  • 计算效率与表达能力的最优平衡

5. 激活函数可视化对比

下面通过代码可视化各类激活函数的特性和差异:

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

def plot_activation_functions():
    """可视化各类激活函数及其导数"""
    x = np.linspace(-5, 5, 1000)
    
    # 定义激活函数
    activations = {
        'Sigmoid': lambda x: 1 / (1 + np.exp(-x)),
        'Tanh': lambda x: np.tanh(x),
        'ReLU': lambda x: np.maximum(0, x),
        'LeakyReLU(0.1)': lambda x: np.where(x > 0, x, 0.1 * x),
        'ELU': lambda x: np.where(x > 0, x, np.exp(x) - 1),
        'Swish': lambda x: x / (1 + np.exp(-x)),
        'Mish': lambda x: x * np.tanh(np.log(1 + np.exp(x))),
        'GELU': lambda x: 0.5 * x * (1 + np.tanh(
            np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)
        ))
    }
    
    # 计算导数(数值微分)
    def derivative(func, x, h=1e-5):
        return (func(x + h) - func(x - h)) / (2 * h)
    
    # 创建子图
    fig, axes = plt.subplots(2, 4, figsize=(16, 8))
    axes = axes.flatten()
    
    for idx, (name, func) in enumerate(activations.items()):
        ax = axes[idx]
        y = func(x)
        dy = derivative(func, x)
        
        ax.plot(x, y, 'b-', linewidth=2, label=f'{name}')
        ax.plot(x, dy, 'r--', linewidth=2, label=f"{name}'")
        ax.axhline(y=0, color='k', linestyle='-', linewidth=0.5)
        ax.axvline(x=0, color='k', linestyle='-', linewidth=0.5)
        ax.set_title(name, fontsize=12, fontweight='bold')
        ax.set_xlabel('x')
        ax.set_ylabel('y / dy')
        ax.legend(loc='best')
        ax.grid(True, alpha=0.3)
        ax.set_xlim(-5, 5)
        ax.set_ylim(-2, 5)
    
    plt.tight_layout()
    plt.savefig('activation_functions_comparison.png', dpi=150, bbox_inches='tight')
    plt.show()

# 运行可视化
plot_activation_functions()

激活函数综合对比表

激活函数 公式 输出范围 梯度消失 计算复杂度 推荐场景
Sigmoid 1/(1+e^(-x)) (0, 1) 严重 二分类输出、门控
Tanh (ex-e(-x))/(ex+e(-x)) (-1, 1) 严重 RNN隐藏层
ReLU max(0, x) [0, +inf) 无(正区间) 极低 CNN、默认选择
LeakyReLU max(αx, x) (-inf, +inf) 避免死亡ReLU
PReLU max(αx, x) (-inf, +inf) 需要自适应斜率
ELU x或α(e^x-1) (-α, +inf) 需要零均值输出
GELU xΦ(x) (-inf, +inf) Transformer、NLP
Swish xσ(x) (-inf, +inf) 深层网络
Mish x·tanh(softplus(x)) (-inf, +inf) 目标检测
SwiGLU Swish(xW)·(xV) (-inf, +inf) 大语言模型

6. 归一化技术详解

6.1 为什么需要归一化

深度神经网络训练过程中,随着网络层数增加,会出现以下问题:

  • 内部协变量偏移(Internal Covariate Shift):每层输入分布随训练变化
  • 梯度消失/爆炸:信号在网络中传播时衰减或放大
  • 训练不稳定:学习率难以调节,收敛困难

归一化技术通过对层输入进行标准化,解决上述问题。

6.2 批归一化(Batch Normalization)

BatchNorm是2015年提出的里程碑技术,极大加速了深度网络训练。

原理
对于一个小批量数据,对每个特征维度进行标准化:

μ_B = (1/m) Σ x_i        # 批量均值
σ²_B = (1/m) Σ (x_i - μ_B)²   # 批量方差
x̂_i = (x_i - μ_B) / √(σ²_B + ε)   # 标准化
y_i = γx̂_i + β           # 缩放和平移

Python实现

import torch
import torch.nn as nn

# PyTorch内置BatchNorm
batch_norm_1d = nn.BatchNorm1d(num_features=64)  # 全连接层
batch_norm_2d = nn.BatchNorm2d(num_features=64)  # 卷积层

# 手动实现BatchNorm
class BatchNormManual(nn.Module):
    def __init__(self, num_features, eps=1e-5, momentum=0.1):
        super().__init__()
        self.eps = eps
        self.momentum = momentum
        
        # 可学习参数
        self.gamma = nn.Parameter(torch.ones(num_features))
        self.beta = nn.Parameter(torch.zeros(num_features))
        
        # 运行时统计量
        self.register_buffer('running_mean', torch.zeros(num_features))
        self.register_buffer('running_var', torch.ones(num_features))
    
    def forward(self, x):
        if self.training:
            # 训练模式:使用当前批次统计量
            mean = x.mean(dim=0)
            var = x.var(dim=0, unbiased=False)
            
            # 更新运行时统计量
            self.running_mean = (1 - self.momentum) * self.running_mean + \
                               self.momentum * mean
            self.running_var = (1 - self.momentum) * self.running_var + \
                              self.momentum * var
        else:
            # 推理模式:使用运行时统计量
            mean = self.running_mean
            var = self.running_var
        
        # 标准化
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        # 缩放和平移
        return self.gamma * x_norm + self.beta

特性分析

  • 优点
    • 加速训练收敛(允许更大学习率)
    • 减少对初始化的敏感性
    • 具有正则化效果
  • 缺点
    • 依赖批量大小,小批量时效果差
    • 训练和推理行为不一致
    • 对序列数据不友好

适用场景

  • 卷积神经网络(CNN)
  • 大批量训练的全连接网络
  • 不适用于RNN和Transformer

6.3 层归一化(Layer Normalization)

LayerNorm对每个样本的所有特征进行归一化,不依赖批量维度。

原理

μ = (1/H) Σ x_i        # 样本内均值
σ² = (1/H) Σ (x_i - μ)²   # 样本内方差
y = (x - μ) / √(σ² + ε)   # 标准化

Python实现

# PyTorch内置LayerNorm
layer_norm = nn.LayerNorm(normalized_shape=64)

# 手动实现LayerNorm
class LayerNormManual(nn.Module):
    def __init__(self, num_features, eps=1e-5):
        super().__init__()
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(num_features))
        self.beta = nn.Parameter(torch.zeros(num_features))
    
    def forward(self, x):
        # 在最后一个维度上计算均值和方差
        mean = x.mean(dim=-1, keepdim=True)
        var = x.var(dim=-1, keepdim=True, unbiased=False)
        
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        return self.gamma * x_norm + self.beta

特性分析

  • 不依赖批量大小,适合小批量和单样本
  • 训练和推理行为一致
  • 是Transformer架构的标准配置

适用场景

  • Transformer模型
  • 循环神经网络(RNN、LSTM)
  • 小批量训练场景

6.4 实例归一化(Instance Normalization)

InstanceNorm对每个样本的每个通道单独归一化,常用于风格迁移。

Python实现

# PyTorch内置InstanceNorm
instance_norm = nn.InstanceNorm2d(num_features=64)

# 手动实现InstanceNorm(2D图像)
class InstanceNormManual(nn.Module):
    def __init__(self, num_features, eps=1e-5):
        super().__init__()
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(num_features))
        self.beta = nn.Parameter(torch.zeros(num_features))
    
    def forward(self, x):
        # x: (N, C, H, W)
        N, C, H, W = x.shape
        
        # 对每个样本的每个通道计算统计量
        x_reshaped = x.view(N, C, -1)  # (N, C, H*W)
        mean = x_reshaped.mean(dim=2, keepdim=True).view(N, C, 1, 1)
        var = x_reshaped.var(dim=2, keepdim=True).view(N, C, 1, 1)
        
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        return self.gamma.view(1, C, 1, 1) * x_norm + self.beta.view(1, C, 1, 1)

适用场景

  • 风格迁移(Style Transfer)
  • 图像生成任务
  • 需要保持通道独立性的场景

6.5 组归一化(Group Normalization)

GroupNorm将通道分组,在组内进行归一化,是BatchNorm和InstanceNorm的折中方案。

Python实现

# PyTorch内置GroupNorm
group_norm = nn.GroupNorm(num_groups=8, num_channels=64)

# 手动实现GroupNorm
class GroupNormManual(nn.Module):
    def __init__(self, num_groups, num_channels, eps=1e-5):
        super().__init__()
        self.num_groups = num_groups
        self.eps = eps
        self.gamma = nn.Parameter(torch.ones(num_channels))
        self.beta = nn.Parameter(torch.zeros(num_channels))
    
    def forward(self, x):
        # x: (N, C, H, W)
        N, C, H, W = x.shape
        G = self.num_groups
        
        # 将通道分组
        x = x.view(N, G, C // G, H, W)
        
        # 在组内计算统计量
        mean = x.mean(dim=[2, 3, 4], keepdim=True)
        var = x.var(dim=[2, 3, 4], keepdim=True, unbiased=False)
        
        x_norm = (x - mean) / torch.sqrt(var + self.eps)
        x_norm = x_norm.view(N, C, H, W)
        
        return self.gamma.view(1, C, 1, 1) * x_norm + self.beta.view(1, C, 1, 1)

适用场景

  • 小批量目标检测
  • 语义分割
  • 批量大小受限的场景

7. 归一化方法对比与选择指南

7.1 综合对比表

归一化方法 归一化维度 依赖批量 适用场景 典型应用
BatchNorm (N, H, W) CNN、大批量 ResNet、VGG
LayerNorm (C, H, W) RNN、Transformer BERT、GPT
InstanceNorm (H, W) 风格迁移 CycleGAN
GroupNorm (C/G, H, W) 小批量检测 Mask R-CNN

7.2 选择决策流程

开始
  │
  ▼
是否是序列数据(NLP、时间序列)?
  │
  ├── 是 ──> 使用 LayerNorm
  │
  └── 否
        │
        ▼
    批量大小是否足够大(>32)?
        │
        ├── 是 ──> 使用 BatchNorm
        │
        └── 否
              │
              ▼
          是否是风格迁移/生成任务?
              │
              ├── 是 ──> 使用 InstanceNorm
              │
              └── 否 ──> 使用 GroupNorm

7.3 归一化位置选择

在神经网络中,归一化可以放在不同位置:

Post-Normalization(后归一化)

x → Linear/Conv → Activation → Normalization → 输出

这是原始Transformer的设计,但在深层网络中可能导致梯度问题。

Pre-Normalization(前归一化)

x → Normalization → Linear/Conv → Activation → 输出

现代大模型(如GPT-3、LLaMA)采用此设计,训练更稳定。


8. 实战:构建带归一化的神经网络

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

class ResidualBlock(nn.Module):
    """带BatchNorm的残差块"""
    def __init__(self, in_channels, out_channels, stride=1):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, stride, 1, bias=False)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, 1, 1, bias=False)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, 1, stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )
    
    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class TransformerBlock(nn.Module):
    """带LayerNorm的Transformer块(Pre-Norm设计)"""
    def __init__(self, dim, num_heads, mlp_ratio=4, dropout=0.1):
        super().__init__()
        self.norm1 = nn.LayerNorm(dim)
        self.attn = nn.MultiheadAttention(dim, num_heads, dropout=dropout, batch_first=True)
        self.norm2 = nn.LayerNorm(dim)
        
        mlp_hidden_dim = int(dim * mlp_ratio)
        self.mlp = nn.Sequential(
            nn.Linear(dim, mlp_hidden_dim),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(mlp_hidden_dim, dim),
            nn.Dropout(dropout)
        )
    
    def forward(self, x):
        # Pre-Normalization
        x = x + self.attn(self.norm1(x), self.norm1(x), self.norm1(x))[0]
        x = x + self.mlp(self.norm2(x))
        return x

class ModernFFN(nn.Module):
    """使用SwiGLU激活函数的现代前馈网络"""
    def __init__(self, dim, hidden_dim=None, dropout=0.0):
        super().__init__()
        if hidden_dim is None:
            # SwiGLU通常使用2/3*dim*2的隐藏维度
            hidden_dim = int(2 * dim * 2 / 3)
        
        self.w1 = nn.Linear(dim, hidden_dim)
        self.w2 = nn.Linear(dim, hidden_dim)
        self.w3 = nn.Linear(hidden_dim, dim)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        # SwiGLU: SiLU(xW1) * (xW2)
        hidden = F.silu(self.w1(x)) * self.w2(x)
        return self.dropout(self.w3(hidden))

9. 避坑小贴士

9.1 激活函数选择避坑

  1. 不要在深层网络中使用Sigmoid/Tanh作为隐藏层激活函数

    • 原因:梯度消失问题严重,深层信号难以传播
    • 替代方案:使用ReLU或GELU
  2. 使用ReLU时注意学习率设置

    • 原因:过大的学习率可能导致大量神经元死亡
    • 建议:配合BatchNorm使用,或使用LeakyReLU
  3. Transformer中优先使用GELU而非ReLU

    • 原因:GELU的平滑特性更适合注意力机制
    • 注意:GELU计算开销稍大,但通常值得
  4. 大语言模型推荐使用SwiGLU

    • 原因:SwiGLU在LLaMA、PaLM等模型中验证有效
    • 注意:需要调整隐藏层维度(通常是8/3倍输入维度)

9.2 归一化使用避坑

  1. 不要在RNN中使用BatchNorm

    • 原因:时间步之间统计量不稳定
    • 替代方案:使用LayerNorm
  2. BatchNorm的momentum参数调优

    • 小批量(<32):减小momentum(如0.01)
    • 大批量(>256):增大momentum(如0.1)
  3. 训练和推理时归一化行为不一致

    • 原因:训练时用批次统计量,推理时用运行统计量
    • 解决:确保调用model.eval()切换模式
  4. GroupNorm的num_groups选择

    • 通道数需能被num_groups整除
    • 建议:32通道用8组,64通道用8或16组

10. 本章小结

10.1 核心知识点回顾

激活函数

  • 经典函数:Sigmoid、Tanh适用于特定场景,但深层网络受限
  • ReLU家族:ReLU是默认选择,变体解决死亡ReLU问题
  • 现代函数:GELU是Transformer标配,SwiGLU是大模型趋势

归一化技术

  • BatchNorm:CNN标配,依赖大批量
  • LayerNorm:序列模型标配,批量无关
  • InstanceNorm/GroupNorm:特定场景的有效补充

10.2 一句话总结

激活函数赋予神经网络非线性表达能力,归一化技术确保深层网络训练稳定;从Sigmoid到SwiGLU的演进,体现了深度学习领域对"平滑性"和"门控机制"的持续追求。

10.3 推荐实践

  1. CNN任务:Conv + BatchNorm + ReLU
  2. Transformer任务:LayerNorm + MultiHeadAttention + GELU/SwiGLU
  3. 小批量/目标检测:Conv + GroupNorm + ReLU
  4. 风格迁移:Conv + InstanceNorm + ReLU

文章标签:深度学习、激活函数、归一化、BatchNorm、LayerNorm、PyTorch、神经网络

建议阅读时间:45分钟

配套代码:本章所有代码可在Python 3.10+和PyTorch 2.0+环境下直接运行。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐