概述

多层感知机(Multi-Layer Perceptron, MLP)是最基础的深度神经网络形式。尽管近年来 Transformer 和混合架构占据主流,MLP 的理论分析为理解深度学习的本质提供了重要洞见。

本文件系统介绍 MLP 的表达能力理论,包括通用逼近定理、宽度-深度权衡、以及最新的构造性逼近结果。1


通用逼近定理

历史背景

1989 年:Cybenko 和 Hornik 分别独立证明了 MLP 的通用逼近能力。

Cybenko 的经典结果

定理(Cybenko, 1989):

是非常量有界连续函数, 上的有限 Borel 测度。则对于任意 和紧致集 ,存在整数 和参数 使得:

直观理解

任意连续函数都可以用有限个 Sigmoid 函数的线性组合任意精确地逼近。

激活函数的条件

条件需要的激活函数
有界连续函数Sigmoid, Tanh
局部有界连续ReLU, Leaky ReLU
有界Hard-sigmoid

关键洞察:ReLU 虽然不可导,但仍然满足通用逼近条件!

通用逼近定理的局限性

  1. 只保证存在性:没有给出网络规模的上界
  2. 不保证可学习性:优化算法能否找到这些参数?
  3. 不涉及深度:单层网络理论上就够用

深度 vs 宽度

为什么需要深度?

直觉:深层网络能够高效地表示某些函数,而浅层网络需要指数级更多的神经元。

深度指数优势

Telgarsky 定理(2016)

存在一列函数 ,使得:

  • 深度 的 ReLU 网络以 参数表示
  • 任何宽度为 的浅层网络需要 参数表示
# Telgarsky 的三角形函数
def telgarsky_function(x, n):
    """
    Telgarsky (2016) 构造的函数
    
    该函数需要指数级参数用浅层网络逼近
    """
    f = x
    for _ in range(n):
        # ReLU 网络中的"锯齿"结构
        f = 2 * torch.relu(2 * f) - 1
    return f

多项式深度分离

Cohen 等人(2020)

存在多项式函数族,深度 的网络可以精确表示,但宽度为 的浅层网络需要 宽度。


宽度-深度权衡定理

主要定理(Salinas et al., 2024)

定理:设数据集 包含 个点,

则存在一个宽度为 2深度为 的 ReLU 网络能够精确分类这个数据集。

构造性证明概述

步骤 1:单点精确激活

对于每个输入点 ,构造一个子网络使得:

使用 ReLU 的尖锐性:

def hard_rect(x, a, b):
    """
    构造 [a,b] 区间的指示函数
    
    ReLU([0, x-a]) - ReLU([0, x-b])
    """
    return torch.relu(x - a) - torch.relu(x - b)
 
def point_indicator(x, x_i, epsilon=0.1):
    """
    构造点的指示函数
    
    使用 ReLU 的相交特性
    """
    # 创建包围 x_i 的区间的指示函数
    # 当且仅当 x 接近 x_i 时输出较大值
    distance = torch.abs(x - x_i).max(dim=-1, keepdim=True)[0]
    return torch.relu(1 - distance / epsilon)

步骤 2:分类组合

将单点激活组合为类别标签:

步骤 3:深度估计

每个点的指示函数需要 深度的网络( 维超立方体的划分)。

定理的实践意义

方面含义
宽度下界宽度 1 不够,至少需要宽度 2
深度需求深度 对于精确记忆
表达能力窄而深的网络可以精确记忆任意数据

神经网络的曲面积分

深度网络的几何性质

Lu 等人(2017)

ReLU 网络将输入空间划分为多个线性区域,数量满足:

其中 是神经元数量, 是输入维度。

区域数量的深度效应

def count_regions(depth, width, input_dim):
    """
    估计 ReLU 网络的线性区域数量
    
    对于深度 L、宽度 W 的网络:
    Regions ≈ W^L
    """
    return width ** depth
 
# 示例
for depth in [1, 2, 4, 8, 12]:
    print(f"Depth {depth:2d}: ~ {count_regions(depth, 10, 784):.2e} regions")

输出

Depth  1: ~ 1.00e+01 regions
Depth  2: ~ 1.00e+02 regions
Depth  4: ~ 1.00e+04 regions
Depth  8: ~ 1.00e+08 regions
Depth 12: ~ 1.00e+12 regions

宽度-深度权衡的量化分析

Expressivity vs Efficiency

架构表达能力参数效率计算效率
宽而浅高(指数区域)高(可并行)
窄而深指数区域低(顺序计算)
适度平衡足够高平衡平衡

最优宽度

问题:给定参数量 ,深度 应该是多少?

定理:对于在 上逼近 -阶多项式,最优深度满足:


超越宽度定理:更强的构造

带残差连接的构造

定理:带跳跃连接的 ResNet 可以用更少的层数达到相同的表达能力。

理论 vs 实践

理论结果实际观察
单层可逼近任意函数但需要指数多神经元
深层可指数压缩但训练困难
宽度 2 足够精确记忆但泛化能力差

MLP 的参数初始化

Xavier 初始化

def xavier_init(module):
    """Xavier/Glorot 初始化"""
    for name, param in module.named_parameters():
        if 'weight' in name:
            fan_in, fan_out = param.shape
            std = math.sqrt(2.0 / (fan_in + fan_out))
            nn.init.uniform_(param, -std, std)
        elif 'bias' in name:
            nn.init.zeros_(param)

推导

对于线性网络,假设权重方差为 ,则:

为保持方差,需要

He 初始化

def he_init(module):
    """He 初始化(适合 ReLU)"""
    for name, param in module.named_parameters():
        if 'weight' in name:
            fan_in = param.shape[0]
            std = math.sqrt(2.0 / fan_in)
            nn.init.normal_(param, 0, std)
        elif 'bias' in name:
            nn.init.zeros_(param)

正交初始化

def orthogonal_init(module, gain=1.0):
    """正交初始化"""
    for name, param in module.named_parameters():
        if 'weight' in name:
            nn.init.orthogonal_(param, gain=gain)
        elif 'bias' in name:
            nn.init.zeros_(param)

MLP 的正则化

1. Dropout

class MLPWithDropout(nn.Module):
    def __init__(self, sizes, p=0.5):
        super().__init__()
        self.dropout = nn.Dropout(p)
        self.layers = nn.ModuleList([
            nn.Linear(sizes[i], sizes[i+1]) 
            for i in range(len(sizes)-1)
        ])
    
    def forward(self, x):
        for i, layer in enumerate(self.layers):
            x = layer(x)
            if i < len(self.layers) - 1:  # 除了最后一层
                x = F.relu(x)
                x = self.dropout(x)
        return x

2. 权重衰减

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)

3. 谱归一化

class SNMLP(nn.Module):
    def __init__(self, sizes):
        super().__init__()
        self.layers = nn.ModuleList([
            nn.utils.spectral_norm(nn.Linear(sizes[i], sizes[i+1]))
            for i in range(len(sizes)-1)
        ])

MLP 的现代应用

1. 特征提取器

class MLPFeatureExtractor(nn.Module):
    """MLP 作为特征提取器"""
    def __init__(self, input_dim, hidden_dims, output_dim):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Linear(input_dim, hidden_dims[0]),
            nn.ReLU(),
            nn.BatchNorm1d(hidden_dims[0]),
            nn.Linear(hidden_dims[0], hidden_dims[1]),
            nn.ReLU(),
            nn.BatchNorm1d(hidden_dims[1]),
        )
        self.head = nn.Linear(hidden_dims[1], output_dim)
    
    def forward(self, x):
        features = self.encoder(x)
        return self.head(features)

2. Tabular Data 建模

class TabularMLP(nn.Module):
    """用于表格数据的 MLP"""
    def __init__(self, num_features, hidden_dim=256, num_classes=1):
        super().__init__()
        self.embeddings = nn.ModuleList([
            nn.Embedding(num_categories, embedding_dim) 
            for num_categories in num_categories_list
        ])
        
        total_dim = embedding_dim * len(num_categories_list) + num_continuous
        
        self.mlp = nn.Sequential(
            nn.Linear(total_dim, hidden_dim),
            nn.BatchNorm1d(hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, hidden_dim),
            nn.BatchNorm1d(hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, num_classes)
        )

3. 分类头

class ClassificationHead(nn.Module):
    """标准的分类 MLP"""
    def __init__(self, d_model, num_classes, hidden_dim=None):
        super().__init__()
        if hidden_dim is None:
            hidden_dim = d_model
        self.mlp = nn.Sequential(
            nn.Linear(d_model, hidden_dim),
            nn.GELU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.GELU(),
            nn.Linear(hidden_dim, num_classes)
        )
    
    def forward(self, x):
        return self.mlp(x)

梯度流分析

深度网络的梯度消失

def analyze_gradients(model, x, y):
    """分析 MLP 的梯度流"""
    output = model(x)
    loss = F.cross_entropy(output, y)
    loss.backward()
    
    results = {
        'layer_grad_norms': [],
        'param_grad_norms': []
    }
    
    for name, param in model.named_parameters():
        if param.grad is not None:
            results['param_grad_norms'].append((
                name, 
                param.grad.norm().item()
            ))
    
    return results

诊断与可视化

import matplotlib.pyplot as plt
 
def plot_gradient_flow(results):
    """可视化梯度流"""
    names = [r[0] for r in results['param_grad_norms']]
    norms = [r[1] for r in results['param_grad_norms']]
    
    plt.figure(figsize=(12, 6))
    plt.barh(names, norms)
    plt.xlabel('Gradient Norm (log scale)')
    plt.xscale('log')
    plt.title('Gradient Flow in MLP')
    plt.tight_layout()
    plt.show()

训练技巧

1. 学习率调度

# 余弦退火
scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(
    optimizer, T_0=10, T_mult=2
)
 
# 阶梯衰减
scheduler = torch.optim.lr_scheduler.StepLR(
    optimizer, step_size=30, gamma=0.1
)

2. 批归一化

class BatchNormMLP(nn.Module):
    def __init__(self, input_dim, hidden_dims, num_classes):
        super().__init__()
        layers = []
        prev_dim = input_dim
        
        for hidden_dim in hidden_dims:
            layers.extend([
                nn.Linear(prev_dim, hidden_dim),
                nn.BatchNorm1d(hidden_dim),
                nn.ReLU(),
            ])
            prev_dim = hidden_dim
        
        layers.append(nn.Linear(prev_dim, num_classes))
        self.mlp = nn.Sequential(*layers)

3. 残差连接

class ResidualMLPBlock(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.mlp = nn.Sequential(
            nn.Linear(dim, dim),
            nn.GELU(),
            nn.Linear(dim, dim)
        )
        self.norm = nn.LayerNorm(dim)
    
    def forward(self, x):
        return x + self.mlp(self.norm(x))

参考


相关阅读

Footnotes

  1. Salinas, M. H., et al. (2024). “Constructive Universal Approximation and Finite Sample Memorization by Narrow Deep ReLU Networks”. arXiv:2409.06555.