欢迎光临
我们一直在努力

何恺明算法与论文深度解析:从暗通道先验到生成式模型的十年技术版图

在这里插入图片描述

一句话总结:何恺明用「简洁到令人发指」的结构设计,三次改写计算机视觉的历史走向——Dark Channel Prior 让单张图像去雾成为可能,ResNet 让深度网络真正变深,MAE 让视觉自监督学习走向可扩展。本文从原理、公式、源码到效果对比,系统拆解他的六大核心贡献。


一、何恺明是谁?——从高考满分到 CV 界天花板

先简单交代背景,方便理解他每一步研究的内在逻辑。

学术轨迹:

  • 2003年:广东省高考满分状元(标准分900),进入清华大学基础科学班
  • 2007-2011年:香港中文大学博士,师从汤晓鸥教授
  • 2011-2016年:微软亚洲研究院(MSRA)研究员 → ResNet 诞生于此
  • 2016-2024年:Meta FAIR(Facebook AI研究院)核心科学家 → MoCo、MAE 诞生于此
  • 2024年至今:MIT EECS 副教授(一年拿到终身教职)
  • 2025年起:兼任 Google DeepMind 杰出科学家

核心荣誉:两次 CVPR Best Paper(2009 去雾、2016 ResNet)、ICCV Best Paper(2017 Mask R-CNN)、PAMI 青年研究者奖、2023 未来科学大奖、论文总引用超 71 万次,ResNet 单篇引用超 28 万次,是 21 世纪引用最高的论文。

研究风格:追求极简结构解决核心难题。论文逻辑清晰、公式简洁,仅凭示意图就能看懂核心思想。每次都精准踩在最关键的问题上。

下面按照时间顺序,逐一拆解他的六大核心贡献。


二、Dark Channel Prior(暗通道先验)—— 2009 CVPR Best Paper

2.1 介绍

这是何恺明的第一篇论文,发表时他只有 25 岁,还是 MSRA 的实习生。论文标题是《Single Image Haze Removal Using Dark Channel Prior》。

问题背景:雾霾天气下拍摄的图像,对比度低、颜色偏灰白、细节丢失。传统去雾方法要么需要多张图像、要么需要深度信息,单张图像去雾一直是个难题。

何恺明的洞察简单到令人拍大腿:在无雾的自然图像中,每个局部区域里,RGB 三个通道中至少有一个通道的某些像素值非常低(接近0)。他把这个统计规律叫做"暗通道先验"。

2.2 原理

暗通道先验的理论基础是大气散射模型(Atmospheric Scattering Model)。有雾图像可以表示为:

I

(

x

)

=

J

(

x

)

t

(

x

)

+

A

(

1

t

(

x

)

)

I(x) = J(x) \\cdot t(x) + A \\cdot (1 – t(x))

I(x)=J(x)t(x)+A(1t(x))

其中:

  • I

    (

    x

    )

    I(x)

    I(x):观察到的有雾图像

  • J

    (

    x

    )

    J(x)

    J(x):无雾的真实场景辐射(待恢复)

  • t

    (

    x

    )

    t(x)

    t(x):透射率,反映光线穿透雾的能力

  • A

    A

    A:全局大气光值

  • J

    (

    x

    )

    t

    (

    x

    )

    J(x) \\cdot t(x)

    J(x)t(x) 称为直接衰减

  • A

    (

    1

    t

    (

    x

    )

    )

    A \\cdot (1-t(x))

    A(1t(x)) 称为大气光分量

去雾的核心就是:从

I

(

x

)

I(x)

I(x) 估计

t

(

x

)

t(x)

t(x)

A

A

A,然后反推

J

(

x

)

J(x)

J(x)

暗通道的定义:对无雾图像

J

J

J,其暗通道定义为:

J

d

a

r

k

(

x

)

=

min

c

{

r

,

g

,

b

}

(

min

y

Ω

(

x

)

J

c

(

y

)

)

J^{dark}(x) = \\min_{c \\in \\{r,g,b\\}} \\left( \\min_{y \\in \\Omega(x)} J^c(y) \\right)

Jdark(x)=c{r,g,b}min(yΩ(x)minJc(y))

其中

Ω

(

x

)

\\Omega(x)

Ω(x) 是以像素

x

x

x 为中心的局部窗口。对于无雾的自然图像(非天空区域),

J

d

a

r

k

(

x

)

0

J^{dark}(x) \\to 0

Jdark(x)0

为什么?因为自然图像的局部区域中,通常存在阴影、彩色物体或暗色物体,使得至少一个通道的值很低。但雾的存在会"照亮"这些暗值,让暗通道不再趋近于0——这就是去雾的突破口。

2.3 公式推导

Step 1:估计透射率

对大气散射模型两边除以

A

c

A^c

Ac

I

c

(

x

)

A

c

=

J

c

(

x

)

A

c

t

(

x

)

+

(

1

t

(

x

)

)

\\frac{I^c(x)}{A^c} = \\frac{J^c(x)}{A^c} t(x) + (1 – t(x))

AcIc(x)=AcJc(x)t(x)+(1t(x))

对每个通道取局部最小值:

min

y

Ω

(

x

)

I

c

(

y

)

A

c

=

t

~

(

x

)

min

y

Ω

(

x

)

J

c

(

y

)

A

c

+

(

1

t

~

(

x

)

)

\\min_{y \\in \\Omega(x)} \\frac{I^c(y)}{A^c} = \\tilde{t}(x) \\min_{y \\in \\Omega(x)} \\frac{J^c(y)}{A^c} + (1 – \\tilde{t}(x))

yΩ(x)minAcIc(y)=t~(x)yΩ(x)minAcJc(y)+(1t~(x))

再对三个通道取最小值,根据暗通道先验

min

c

min

y

Ω

(

x

)

J

c

(

y

)

A

c

0

\\min_c \\min_{y \\in \\Omega(x)} \\frac{J^c(y)}{A^c} \\to 0

mincminyΩ(x)AcJc(y)0

t

~

(

x

)

=

1

min

c

min

y

Ω

(

x

)

I

c

(

y

)

A

c

\\tilde{t}(x) = 1 – \\min_{c} \\min_{y \\in \\Omega(x)} \\frac{I^c(y)}{A^c}

t~(x)=1cminyΩ(x)minAcIc(y)

引入去雾保留系数

ω

\\omega

ω(通常取 0.95,保留少量雾使图像更自然):

t

~

(

x

)

=

1

ω

min

c

min

y

Ω

(

x

)

I

c

(

y

)

A

c

\\tilde{t}(x) = 1 – \\omega \\cdot \\min_{c} \\min_{y \\in \\Omega(x)} \\frac{I^c(y)}{A^c}

t~(x)=1ωcminyΩ(x)minAcIc(y)

Step 2:估计大气光

A

A

A

在暗通道图中,选取亮度最高的前 0.1% 像素(对应雾最浓的区域),在原图中这些位置取各通道最大值作为

A

A

A

Step 3:恢复无雾图像

J

(

x

)

=

I

(

x

)

A

t

(

x

)

+

A

J(x) = \\frac{I(x) – A}{t(x)} + A

J(x)=t(x)I(x)A+A

为避免

t

(

x

)

t(x)

t(x) 过小导致噪声放大,设置下限

t

0

=

0.1

t_0 = 0.1

t0=0.1

J

(

x

)

=

I

(

x

)

A

max

(

t

(

x

)

,

t

0

)

+

A

J(x) = \\frac{I(x) – A}{\\max(t(x), t_0)} + A

J(x)=max(t(x),t0)I(x)A+A

2.4 算法流程图示

输入有雾图像 I(x)


计算暗通道 I^dark(x) ──── 取 RGB 最小值 + 局部最小值滤波

├──► 从暗通道最亮的前 0.1% 像素估计大气光 A

└──► 估计粗透射率 t̃(x) = 1 – ω·min_c(min_{Ω(x)} I^c/A^c)


导向滤波细化透射率 ──► 消除块效应/halo


J(x) = (I(x) – A) / max(t(x), t0) + A


输出无雾图像 J(x)

2.5 踩坑点

  • 天空区域失效:暗通道先验假设对天空区域不成立——天空的暗通道值远大于0,导致透射率被低估,去雾后天空区域出现严重色偏和噪声。后续改进方案通常对天空区域单独处理(如用 CLAHE 增强代替 DCP)。

  • 软抠图太慢:原论文用软抠图(Soft Matting)细化透射率,计算复杂度极高

    O

    (

    N

    1.2

    )

    O(N^{1.2})

    O(N1.2),处理一张图要好几秒。后来何恺明自己又提出了导向滤波(Guided Filter),把复杂度降到

    O

    (

    N

    )

    O(N)

    O(N)

  • 窗口大小敏感:局部窗口

    Ω

    (

    x

    )

    \\Omega(x)

    Ω(x) 的选取直接影响效果。窗口太小,去雾不彻底;窗口太大,边缘处出现 halo 效应。一般取 15×15 是经验最优值。

  • 不适用于夜间图像:暗通道先验基于白天自然光的统计规律,对夜间低照度图像效果很差。

  • 2.6 效果对比

    指标去雾前DCP 去雾后
    对比度 显著提升
    颜色饱和度 偏灰白 恢复正常
    暗通道值 偏高 趋近0
    天空区域 正常 色偏/噪声
    处理速度 导向滤波后 ~0.1s/张

    这篇论文的意义远超去雾本身——它展现了何恺明终其一生的研究哲学:用最简单的统计观察,解决最本质的问题。


    三、He 初始化(Kaiming 初始化)与 PReLU —— 2015 ICCV

    3.1 介绍

    论文标题:《Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》。

    背景:深度神经网络的权重初始化至关重要——初始化不好,网络要么梯度消失要么梯度爆炸。2010年 Xavier Glorot 和 Yoshua Bengio 提出了 Xavier 初始化,但它针对的是 tanh/sigmoid 激活函数。随着 ReLU 的流行,Xavier 初始化在深层 ReLU 网络中表现不佳——30层 CNN 直接停止学习。

    何恺明从方差分析的角度,推出了专门针对 ReLU 的初始化方案,并在 ImageNet-2012 上取得 4.94% top-5 错误率,首次超越人类水平(5.1%)。

    3.2 原理

    核心思想:让每一层输出的方差与输入方差保持一致。

    对于 ReLU 激活函数

    f

    (

    x

    )

    =

    max

    (

    0

    ,

    x

    )

    f(x) = \\max(0, x)

    f(x)=max(0,x),它会把一半的输入置零。如果输入方差为

    Var

    (

    x

    )

    \\text{Var}(x)

    Var(x),经过 ReLU 后输出方差约为

    1

    2

    Var

    (

    x

    )

    \\frac{1}{2} \\text{Var}(x)

    21Var(x)

    假设权重

    W

    W

    W 服从均值为0的分布,且与输入独立,那么第

    l

    l

    l 层输出的方差为:

    Var

    (

    y

    l

    )

    =

    n

    l

    Var

    (

    W

    l

    )

    1

    2

    Var

    (

    x

    l

    )

    \\text{Var}(y_l) = n_l \\cdot \\text{Var}(W_l) \\cdot \\frac{1}{2} \\text{Var}(x_l)

    Var(yl)=nlVar(Wl)21Var(xl)

    其中

    n

    l

    n_l

    nl 是第

    l

    l

    l 层的 fan-in(输入神经元数),

    1

    2

    \\frac{1}{2}

    21 来自 ReLU 的截断。

    要使

    Var

    (

    y

    l

    )

    =

    Var

    (

    x

    l

    )

    \\text{Var}(y_l) = \\text{Var}(x_l)

    Var(yl)=Var(xl) 对所有层成立,需要:

    n

    l

    Var

    (

    W

    l

    )

    1

    2

    =

    1

    n_l \\cdot \\text{Var}(W_l) \\cdot \\frac{1}{2} = 1

    nlVar(Wl)21=1

    Var

    (

    W

    l

    )

    =

    2

    n

    l

    \\text{Var}(W_l) = \\frac{2}{n_l}

    Var(Wl)=nl2

    3.3 公式

    He 初始化:权重从均值为0、方差为

    2

    n

    \\frac{2}{n}

    n2 的正态分布中采样:

    W

    N

    (

    0

    ,

    2

    n

    )

    W \\sim \\mathcal{N}\\left(0, \\frac{2}{n}\\right)

    WN(0,n2)

    或者等价的均匀分布版本:

    W

    U

    (

    6

    n

    ,

    6

    n

    )

    W \\sim \\mathcal{U}\\left(-\\sqrt{\\frac{6}{n}}, \\sqrt{\\frac{6}{n}}\\right)

    WU(n6

    ,n6

    )

    其中

    n

    n

    n 是 fan-in(当前层的输入通道数 × 卷积核高度 × 卷积核宽度)。

    对比 Xavier 初始化:

    Var

    (

    W

    )

    =

    1

    n

    \\text{Var}(W) = \\frac{1}{n}

    Var(W)=n1,He 初始化的方差恰好是 Xavier 的两倍——多出来的那个因子 2 正是补偿 ReLU 截断造成的方差减半。

    PReLU(Parametric ReLU):论文同时提出了参数化 ReLU,让负半轴的斜率

    α

    \\alpha

    α 可学习:

    f

    (

    x

    )

    =

    {

    x

    ,

    x

    >

    0

    α

    x

    ,

    x

    0

    f(x) = \\begin{cases} x, & x > 0 \\\\ \\alpha x, & x \\leq 0 \\end{cases}

    f(x)={x,αx,x>0x0

    PReLU 的初始化需要考虑

    α

    \\alpha

    α 的影响:

    Var

    (

    W

    )

    =

    2

    (

    1

    +

    α

    2

    )

    n

    \\text{Var}(W) = \\frac{2}{(1 + \\alpha^2) \\cdot n}

    Var(W)=(1+α2)n2

    3.4 踩坑点

  • 别用错激活函数:He 初始化是给 ReLU 系列用的。如果你的网络用 tanh 或 sigmoid,用 Xavier 初始化反而更好。PyTorch 中 nn.init.kaiming_normal_ 默认是针对 ReLU 的(mode='fan_in', nonlinearity='leaky_relu'),但很多人传参时搞混了 nonlinearity 参数。

  • fan_in vs fan_out:He 初始化默认用 fan_in(输入维度),但在某些场景下(如转置卷积、某些 GAN 结构)应该用 fan_out。何恺明论文中指出对于前向传播用 fan_in,对于反向传播用 fan_out 更合适。

  • PReLU 的过拟合:PReLU 虽然引入的额外参数很少,但在小数据集上仍然可能过拟合。实际应用中 ReLU 已经够用,PReLU 更多是用在大数据集 + 大模型上。

  • 3.5 源码拆解(PyTorch 风格)

    import torch
    import torch.nn as nn

    # He 正态初始化(手动实现)
    def kaiming_normal_(tensor, fan_in=None, a=0):
    if fan_in is None:
    fan_in = tensor.shape[1] * tensor.shape[2] * tensor.shape[3]
    # a=0 对应标准 ReLU;若 PReLU,a 就是负半轴斜率
    gain = torch.sqrt(torch.tensor(2.0 / (1 + a ** 2)))
    std = gain / torch.sqrt(torch.tensor(fan_in * 1.0))
    with torch.no_grad():
    tensor.normal_(0, std)
    return tensor

    # PyTorch 内置版本
    conv = nn.Conv2d(64, 128, 3, padding=1)
    nn.init.kaiming_normal_(conv.weight, mode='fan_in', nonlinearity='relu')
    nn.init.zeros_(conv.bias) # 偏置初始化为0

    3.6 效果对比

    初始化方法30层 CNN 收敛100层 ReLU 网络ImageNet top-5 error
    随机初始化(标准正态) 不收敛 爆炸
    Xavier 初始化 30层停止学习 梯度消失 ~10%
    He 初始化 正常收敛 稳定 4.94%

    四、ResNet(深度残差网络)—— 2016 CVPR Best Paper

    4.1 介绍

    论文标题:《Deep Residual Learning for Image Recognition》。

    这篇论文不需要太多介绍——它是 21 世纪引用最高的论文,没有之一。但它解决的问题其实非常具体:网络更深反而更差。

    在 ResNet 之前,VGG 能训 19 层,GoogleNet 能训 22 层。人们天真地以为加更多层会更好,结果发现:34层网络比18层网络训练误差更高。这不是过拟合(因为训练集也变差了),而是退化问题(degradation problem)——深层网络更难优化。

    何恺明的解决方案异常简洁:与其让网络学

    H

    (

    x

    )

    H(x)

    H(x),不如让网络学

    F

    (

    x

    )

    =

    H

    (

    x

    )

    x

    F(x) = H(x) – x

    F(x)=H(x)x,即残差。

    4.2 原理

    核心假设:如果恒等映射(identity mapping)是最优的,那么让非线性层拟合恒等映射很难,但让残差

    F

    (

    x

    )

    0

    F(x) \\to 0

    F(x)0 很容易——只要权重趋近于0即可。

    换句话说:残差学习保证深层网络至少不会比浅层网络差。最坏情况下

    F

    (

    x

    )

    =

    0

    F(x) = 0

    F(x)=0,输出就是输入

    x

    x

    x,相当于跳过了这几层。

    4.3 公式

    残差块:

    y

    =

    F

    (

    x

    ,

    {

    W

    i

    }

    )

    +

    x

    y = F(x, \\{W_i\\}) + x

    y=F(x,{Wi})+x

    其中

    F

    F

    F 是残差映射,

    x

    x

    x 是恒等跳连。对于两层卷积:

    F

    =

    W

    2

    σ

    (

    W

    1

    x

    )

    F = W_2 \\sigma(W_1 x)

    F=W2σ(W1x)

    其中

    σ

    \\sigma

    σ 是 ReLU。

    当维度不匹配时(如通道数变化),用

    1

    ×

    1

    1 \\times 1

    1×1 卷积做投影:

    y

    =

    F

    (

    x

    ,

    {

    W

    i

    }

    )

    +

    W

    s

    x

    y = F(x, \\{W_i\\}) + W_s x

    y=F(x,{Wi})+Wsx

    Bottleneck 结构(ResNet-50/101/152 使用):

    F

    (

    x

    )

    =

    W

    3

    σ

    (

    W

    2

    σ

    (

    W

    1

    x

    )

    )

    F(x) = W_3 \\sigma(W_2 \\sigma(W_1 x))

    F(x)=W3σ(W2σ(W1x))

    其中

    W

    1

    W_1

    W1

    1

    ×

    1

    1 \\times 1

    1×1 降维、

    W

    2

    W_2

    W2

    3

    ×

    3

    3 \\times 3

    3×3 卷积、

    W

    3

    W_3

    W3

    1

    ×

    1

    1 \\times 1

    1×1 升维,用瓶颈结构减少计算量。

    4.4 图示

    Plain Network Residual Network

    Input Input ─────────────┐
    │ │ │ (identity shortcut)
    ▼ ▼ │
    Conv 3×3, 64 Conv 3×3, 64 │
    │ │ │
    ▼ ▼ │
    Conv 3×3, 64 Conv 3×3, 64 │
    │ │ │
    ▼ ▼ │
    Conv 3×3, 64 Conv 3×3, 64 │
    │ │ + <──────────┘
    ▼ ▼
    … ReLU
    │ │
    ▼ ▼
    Output Output

    34层:训练误差 > 18层 34层:训练误差 < 18层 ✓

    4.5 踩坑点

  • BN 层的位置很关键:ResNet 把 BN 放在卷积后、ReLU 前。后来何恺明自己在《Identity Mappings in Deep Residual Networks》(ResNet v2)中发现,pre-activation 结构(BN → ReLU → Conv)效果更好。但 PyTorch 自带的 torchvision.models.resnet50 用的是原始结构,很多人不知道这俩的区别。

  • 下采样时的投影短连:当 stride=2 且通道数翻倍时,短连必须用

    1

    ×

    1

    1 \\times 1

    1×1 卷积投影。但原始论文发现,用零填充短连(Option A)也能work,只是不如投影短连(Option B)。零填充不引入额外参数,但精度略低。

  • 深度不是越深越好:ResNet-1202 在 CIFAR-10 上虽然能训练,但精度不如 ResNet-110。过深的网络会过拟合。真正实用的深度上限大约在 152 层(ImageNet)和 110 层(CIFAR)。

  • 残差的数学直觉:很多人误以为残差连接解决了梯度消失。严格来说,梯度消失已经被 He 初始化和 BN 基本解决了。ResNet 解决的是优化困难——让深层网络的损失函数地形更平缓,更容易找到好的极小值。

  • 4.6 源码拆解

    import torch
    import torch.nn as nn

    class BasicBlock(nn.Module):
    """ResNet-18/34 的基本残差块"""
    expansion = 1

    def __init__(self, in_channels, out_channels, stride=1, downsample=None):
    super().__init__()
    self.conv1 = nn.Conv2d(in_channels, out_channels,
    kernel_size=3, stride=stride, padding=1, bias=False)
    self.bn1 = nn.BatchNorm2d(out_channels)
    self.conv2 = nn.Conv2d(out_channels, out_channels,
    kernel_size=3, stride=1, padding=1, bias=False)
    self.bn2 = nn.BatchNorm2d(out_channels)
    self.relu = nn.ReLU(inplace=True)
    self.downsample = downsample # 维度不匹配时的投影短连

    def forward(self, x):
    identity = x # 恒等映射

    out = self.conv1(x)
    out = self.bn1(out)
    out = self.relu(out)

    out = self.conv2(out)
    out = self.bn2(out)

    if self.downsample is not None:
    identity = self.downsample(x) # 1×1 卷积投影

    out += identity # ← 这就是残差连接的核心:F(x) + x
    out = self.relu(out)

    return out

    class Bottleneck(nn.Module):
    """ResNet-50/101/152 的瓶颈残差块"""
    expansion = 4

    def __init__(self, in_channels, mid_channels, stride=1, downsample=None):
    super().__init__()
    # 1×1 降维
    self.conv1 = nn.Conv2d(in_channels, mid_channels, 1, bias=False)
    self.bn1 = nn.BatchNorm2d(mid_channels)
    # 3×3 卷积(瓶颈)
    self.conv2 = nn.Conv2d(mid_channels, mid_channels, 3,
    stride=stride, padding=1, bias=False)
    self.bn2 = nn.BatchNorm2d(mid_channels)
    # 1×1 升维
    self.conv3 = nn.Conv2d(mid_channels, mid_channels * self.expansion, 1, bias=False)
    self.bn3 = nn.BatchNorm2d(mid_channels * self.expansion)
    self.relu = nn.ReLU(inplace=True)
    self.downsample = downsample

    def forward(self, x):
    identity = x

    out = self.relu(self.bn1(self.conv1(x))) # 降维
    out = self.relu(self.bn2(self.conv2(out))) # 3×3
    out = self.bn3(self.conv3(out)) # 升维

    if self.downsample is not None:
    identity = self.downsample(x)

    out += identity
    out = self.relu(out)
    return out

    4.7 效果对比

    模型层数ImageNet top-1 errortop-5 error
    VGG-16 16 28.1% 9.9%
    GoogLeNet 22 9.2%
    Plain-34 34 比Plain-18更高 退化
    ResNet-34 34 21.4% 5.7%
    ResNet-50 50 20.4% 4.5%
    ResNet-101 101 19.6% 4.1%
    ResNet-152 152 19.4% 3.57%

    ResNet-152 在 ILSVRC 2015 上横扫五项冠军:分类、检测、定位、COCO 检测和分割。

    影响力:AlphaGo、AlphaFold、ViT、GPT、自动驾驶感知——几乎所有现代 AI 系统的视觉模块都建立在 ResNet 之上。2023年,何恺明、孙剑、任少卿、张祥雨凭此论文共同斩获未来科学大奖。


    五、Mask R-CNN —— 2017 ICCV Best Paper

    5.1 介绍

    论文标题:《Mask R-CNN》。

    在 ResNet 之后,何恺明把目光转向了目标检测。2015年的 Faster R-CNN 已经能做目标检测(画框 + 分类),但实例分割(pixel-level mask)还很难。Mask R-CNN 在 Faster R-CNN 基础上增加了一个掩码预测分支,同时用 RoIAlign 替换了 RoIPooling,让检测精度大幅提升。

    5.2 原理

    Mask R-CNN 的核心改进有两点:

    改进1:增加 Mask 分支

    Faster R-CNN 对每个 RoI 输出两样东西:类别标签和边界框回归参数。Mask R-CNN 增加了第三个输出:一个 FCN(全卷积网络),为每个 RoI 生成

    m

    ×

    m

    m \\times m

    m×m 的二值分割掩码。

    关键设计:类别与掩码解耦。不对所有类别做 softmax,而是对每个类别独立预测一个二值 mask。分类分支决定"是什么",掩码分支决定"长什么样",两者并行且互不干扰。

    改进2:RoIAlign 替换 RoIPool

    RoIPool 的问题:它把浮点坐标量化为整数(截断),然后又在 bin 级别再次量化。两次量化导致特征与 RoI 区域不对齐——对分类影响不大,但对像素级分割是致命的。

    RoIAlign 的解决方案:完全不做量化,用双线性插值在 RoI 的每个 bin 内采样 4 个等距点,然后取最大值或平均值。这保留了精确的空间对齐。

    5.3 公式

    Mask R-CNN 的多任务损失:

    L

    =

    L

    c

    l

    s

    +

    L

    b

    o

    x

    +

    L

    m

    a

    s

    k

    L = L_{cls} + L_{box} + L_{mask}

    L=Lcls+Lbox+Lmask

    其中:

    • L

      c

      l

      s

      L_{cls}

      Lcls:分类损失(交叉熵)

    • L

      b

      o

      x

      L_{box}

      Lbox:边界框回归损失(Smooth L1)

    • L

      m

      a

      s

      k

      L_{mask}

      Lmask:每个类别的二值掩码损失(对每个像素做 sigmoid 二分类交叉熵)

    L

    m

    a

    s

    k

    =

    1

    m

    2

    i

    ,

    j

    [

    y

    i

    j

    log

    y

    ^

    i

    j

    +

    (

    1

    y

    i

    j

    )

    log

    (

    1

    y

    ^

    i

    j

    )

    ]

    L_{mask} = -\\frac{1}{m^2} \\sum_{i,j} [y_{ij} \\log \\hat{y}_{ij} + (1-y_{ij}) \\log(1-\\hat{y}_{ij})]

    Lmask=m21i,j[yijlogy^ij+(1yij)log(1y^ij)]

    注意:

    L

    m

    a

    s

    k

    L_{mask}

    Lmask 只对正确类别对应的 mask 计算,不对所有类别都算。

    5.4 图示

    输入图像


    ┌──────────────────┐
    │ Backbone │ ResNet-50/101 + FPN
    │ (特征提取) │
    └──────────────────┘


    ┌──────────────────┐
    │ RPN │ 生成候选区域 (proposals)
    │ (区域提议网络) │
    └──────────────────┘


    ┌──────────────────┐
    │ RoIAlign │ ← 替换 RoIPool,用双线性插值,不做量化
    └──────────────────┘

    ┌──────────┼──────────┐
    ▼ ▼ ▼
    ┌────────┐ ┌────────┐ ┌──────────┐
    │ 分类 │ │ 边框 │ │ Mask 分支│
    │ branch │ │ 回归 │ │ (FCN) │
    │(softmax)│ │branch │ │ │
    └────────┘ └────────┘ └──────────┘


    每类一个 m×m 二值掩码
    取正确类别的mask输出

    5.5 踩坑点

  • RoIAlign 的采样点数:原始论文在每个 bin 内采样 4 个点,但实际发现采样点数对精度影响不大,2 个点就够了。PyTorch 的 RoIAlign 默认 sampling_ratio=2。

  • 别用 softmax mask:很多人用 softmax 给 mask 分类,结果性能下降。原因:softmax 会让不同类别之间竞争,而 sigmoid 允许一个像素同时属于多个类别(虽然有实例分割的语义约束,但 sigmoid 更灵活)。

  • mask 分辨率:论文用

    14

    ×

    14

    14 \\times 14

    14×14

    28

    ×

    28

    28 \\times 28

    28×28 的 mask,然后上采样到原图大小。更大的 mask 分辨率(如

    56

    ×

    56

    56 \\times 56

    56×56)效果略好但计算量翻倍,性价比不高。

  • FPN 很重要:Mask R-CNN + ResNet-C4 的效果不如 Mask R-CNN + ResNet-FPN,因为 FPN 提供了多尺度特征。别省这个。

  • 5.6 源码拆解(核心结构)

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

    class MaskRCNNHead(nn.Module):
    """Mask R-CNN 的掩码预测分支"""
    def __init__(self, in_channels, num_classes, mask_resolution=28):
    super().__init__()
    # 4 层 3×3 卷积
    layers = []
    for _ in range(4):
    layers.append(nn.Conv2d(in_channels, in_channels, 3, padding=1))
    layers.append(nn.ReLU(inplace=True))
    self.convs = nn.Sequential(*layers)
    # 反卷积上采样
    self.upsample = nn.ConvTranspose2d(in_channels, in_channels, 2, stride=2)
    # 1×1 卷积输出每类一个 mask
    self.predictor = nn.Conv2d(in_channels, num_classes, 1)
    self.resolution = mask_resolution

    def forward(self, x):
    x = self.convs(x)
    x = F.relu(self.upsample(x))
    x = self.predictor(x) # [N, num_classes, 2*res, 2*res]
    # 注意:这里不接 sigmoid,在计算 loss 时再做
    return x

    class RoIAlign(nn.Module):
    """简化版 RoIAlign:不做量化,用双线性插值"""
    def __init__(self, output_size=(7, 7), sampling_ratio=2):
    super().__init__()
    self.output_size = output_size
    self.sampling_ratio = sampling_ratio

    def forward(self, features, rois):
    """
    features: [B, C, H, W] 特征图
    rois: [N, 5] (batch_idx, x1, y1, x2, y2) 浮点坐标
    """

    # 对每个 RoI,在 output_size × output_size 的网格上
    # 每个网格内取 sampling_ratio² 个等距点
    # 用双线性插值计算这些点的值
    # 然后取平均(或最大值)作为该网格的输出
    # 实际实现用 torchvision.ops.roi_align
    from torchvision.ops import roi_align
    return roi_align(features, rois, self.output_size,
    sampling_ratio=self.sampling_ratio,
    aligned=True) # aligned=True 是关键!

    5.7 效果对比

    方法COCO AP(box)COCO AP(mask)速度
    Faster R-CNN (ResNet-101-FPN) 36.2 5 fps
    FCIS (2016冠军方案) 37.0 34.7 复杂
    Mask R-CNN (ResNet-101-FPN) 38.2 35.7 5 fps
    Mask R-CNN (ResNeXt-101-FPN) 39.8 36.7 4 fps

    Mask R-CNN 在只增加约 20% 计算量的情况下,超越了之前所有实例分割方法。


    六、MoCo(动量对比学习)—— 2020 CVPR

    6.1 介绍

    论文标题:《Momentum Contrast for Unsupervised Visual Representation Learning》。

    在监督学习已经快被"标签用完"的时代,自监督学习成为热门方向。对比学习(Contrastive Learning)的核心思想是:让正样本对的特征更接近,负样本对的特征更远。

    但对比学习有个瓶颈:负样本越多效果越好,但 GPU 显存有限,batch size 上不去。SimCLR 需要 4096 的 batch size 才有好效果,一般人玩不起。

    何恺明把对比学习重新定义为字典查询问题,用队列 + 动量编码器巧妙地解耦了 batch size 和字典大小。

    6.2 原理

    MoCo 把对比学习看成字典查询:

    • Query:对输入图像做数据增强得到

      x

      q

      x_q

      xq,通过编码器

      f

      q

      f_q

      fq 得到特征

      q

      =

      f

      q

      (

      x

      q

      )

      q = f_q(x_q)

      q=fq(xq)

    • Key:对同一图像做不同增强得到

      x

      k

      x_k

      xk,通过编码器

      f

      k

      f_k

      fk 得到特征

      k

      =

      f

      k

      (

      x

      k

      )

      k = f_k(x_k)

      k=fk(xk)

    • 正样本:

      x

      q

      x_q

      xq

      x

      k

      x_k

      xk 来自同一图像

    • 负样本:字典中其他 key

    创新1:队列(Queue)

    字典不限于当前 batch,而是维护一个 FIFO 队列。每次把当前 batch 的 key 入队,最老的 key 出队。队列大小可以远大于 batch size(通常 65536)。

    创新2:动量编码器(Momentum Encoder)

    队列中的 key 是历史 batch 生成的,如果编码器

    f

    k

    f_k

    fk 更新太快,队列中不同时刻的 key 不一致,训练不稳定。

    解决方案:

    f

    k

    f_k

    fk 不通过梯度更新,而是用动量方式跟随

    f

    q

    f_q

    fq

    θ

    k

    m

    θ

    k

    +

    (

    1

    m

    )

    θ

    q

    \\theta_k \\leftarrow m \\cdot \\theta_k + (1 – m) \\cdot \\theta_q

    θkmθk+(1m)θq

    其中

    m

    [

    0

    ,

    1

    )

    m \\in [0, 1)

    m[0,1),通常取 0.999。这让

    f

    k

    f_k

    fk 更新非常缓慢,保证字典中 key 的一致性。

    6.3 公式

    InfoNCE 对比损失:

    L

    MoCo

    =

    log

    exp

    (

    q

    k

    +

    /

    τ

    )

    i

    =

    0

    N

    exp

    (

    q

    k

    i

    /

    τ

    )

    \\mathcal{L}_{\\text{MoCo}} = -\\log \\frac{\\exp(q \\cdot k^+ / \\tau)}{\\sum_{i=0}^{N} \\exp(q \\cdot k_i / \\tau)}

    LMoCo=logi=0Nexp(qki/τ)exp(qk+/τ)

    其中:

    • q

      q

      q:查询特征

    • k

      +

      k^+

      k+:正样本 key 特征

    • k

      i

      k_i

      ki:字典中第

      i

      i

      i 个 key

    • N

      N

      N:字典大小

    • τ

      \\tau

      τ:温度超参数(通常 0.07)

    动量更新:

    θ

    k

    m

    θ

    k

    +

    (

    1

    m

    )

    θ

    q

    ,

    m

    =

    0.999

    \\theta_k \\leftarrow m \\cdot \\theta_k + (1 – m) \\cdot \\theta_q, \\quad m = 0.999

    θkmθk+(1m)θq,m=0.999

    注意:

    θ

    q

    \\theta_q

    θq 通过反向传播更新(有梯度),

    θ

    k

    \\theta_k

    θk 只通过动量更新(无梯度)。

    6.4 图示

    x ──── 数据增强1 ──► xq ──► [fq] ──► q (query)
    │ │
    │ 计算 q·k 的相似度
    │ │
    └── 数据增强2 ──► xk ──► [fk] ──► k+ (positive key)
    │ │
    动量更新 θk 入队
    │ │
    ▼ ▼
    ┌─────────────────────────┐
    │ 字典队列 (Queue) │
    │ [k1, k2, …, kN] │ ← 负样本
    │ (N=65536) │
    └─────────────────────────┘


    InfoNCE Loss: L = -log(exp(q·k+/τ) / Σexp(q·ki/τ))


    反向传播更新 θq(只更新query编码器)

    动量更新 θk = 0.999·θk + 0.001·θq

    6.5 踩坑点

  • 动量系数

    m

    m

    m 太小会崩:

    m

    =

    0.9

    m=0.9

    m=0.9 时训练不稳定,因为

    f

    k

    f_k

    fk 更新太快,字典中 key 不一致。

    m

    =

    0.999

    m=0.999

    m=0.999 是经验最优值,

    m

    =

    0.9999

    m=0.9999

    m=0.9999 也行但收敛慢。

  • 队列大小不是越大越好:增大队列在初期提升明显,但超过 65536 后收益递减。更大的队列还会让 stale key(过期 key)的比例增大,反而有害。

  • MoCo v2 的改进别忽略:MoCo v2 加入了 MLP projection head 和更强的数据增强(高斯模糊、颜色抖动),效果大幅提升。用 MoCo 就直接上 v2。

  • 温度参数

    τ

    \\tau

    τ 敏感:

    τ

    \\tau

    τ 太大,损失对负样本的区分度下降;

    τ

    \\tau

    τ 太小,梯度容易消失。0.07 是经验值。

  • 6.6 源码拆解

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

    class MoCo(nn.Module):
    def __init__(self, base_encoder, dim=128, K=65536, m=0.999, T=0.07):
    super().__init__()
    self.K = K # 队列大小
    self.m = m # 动量系数
    self.T = T # 温度

    # query 编码器(通过梯度更新)
    self.encoder_q = base_encoder()
    # key 编码器(通过动量更新,不通过梯度)
    self.encoder_k = base_encoder()

    # 初始化:两个编码器参数相同
    for param_q, param_k in zip(
    self.encoder_q.parameters(), self.encoder_k.parameters()):
    param_k.data.copy_(param_q.data)
    param_k.requires_grad = False # key 编码器不需要梯度

    # 维护的负样本队列
    self.register_buffer("queue", torch.randn(dim, K))
    self.queue = F.normalize(self.queue, dim=0)
    self.register_buffer("queue_ptr", torch.zeros(1, dtype=torch.long))

    @torch.no_grad()
    def _momentum_update_key_encoder(self):
    """动量更新 key 编码器"""
    for param_q, param_k in zip(
    self.encoder_q.parameters(), self.encoder_k.parameters()):
    param_k.data = param_k.data * self.m + param_q.data * (1.0 self.m)

    @torch.no_grad()
    def _dequeue_and_enqueue(self, keys):
    """出队 + 入队"""
    batch_size = keys.shape[0]
    ptr = int(self.queue_ptr)

    # 替换队列中对应位置
    self.queue[:, ptr:ptr + batch_size] = keys.T
    ptr = (ptr + batch_size) % self.K
    self.queue_ptr[0] = ptr

    def forward(self, im_q, im_k):
    # 计算 query 特征
    q = self.encoder_q(im_q) # [B, C]
    q = F.normalize(q, dim=1)

    # 计算 key 特征(no_grad,不反向传播)
    with torch.no_grad():
    self._momentum_update_key_encoder() # 动量更新
    k = self.encoder_k(im_k) # [B, C]
    k = F.normalize(k, dim=1)

    # InfoNCE 损失
    # 正样本:q · k^+
    l_pos = torch.einsum('nc,nc->n', [q, k]).unsqueeze(1) # [B, 1]
    # 负样本:q · queue 中的所有 key
    l_neg = torch.einsum('nc,ck->nk', [q, self.queue.clone()]) # [B, K]

    # 拼接后计算 InfoNCE
    logits = torch.cat([l_pos, l_neg], dim=1) # [B, 1+K]
    logits /= self.T # 温度缩放

    labels = torch.zeros(logits.shape[0], dtype=torch.long).cuda()
    # 第 0 个位置是正样本

    loss = F.cross_entropy(logits, labels)

    # 入队
    self._dequeue_and_enqueue(k)

    return loss

    6.7 效果对比

    方法batch sizeImageNet Linear Eval (top-1)
    Supervised ResNet-50 76.1%
    SimCLR (ResNet-50) 4096 69.3%
    MoCo (ResNet-50) 256 60.6%
    MoCo v2 (ResNet-50) 256 71.1%
    MoCo v3 (ViT-B) 4096 76.7%

    MoCo 的最大优势:只需要普通 batch size(256)就能达到接近 SimCLR 大 batch 的效果,让对比学习真正变得"买得起"。


    七、MAE(掩码自编码器)—— 2022 CVPR

    7.1 介绍

    论文标题:《Masked Autoencoders Are Scalable Vision Learners》。

    NLP 早已用 BERT 的 “mask and predict” 范式做自监督预训练,但视觉领域一直跟不上。原因有二:

  • CNN 不适合 mask token:CNN 操作在规则网格上,mask token 打破了空间结构。ViT 出现后这个问题才解决。
  • 图像信息冗余:语言是高度语义化的,缺一个词很难猜。但图像空间冗余极大——缺一小块像素,看看邻居就能插值出来。
  • 何恺明的解决方案简洁到令人叹服:既然图像冗余,那就大刀阔斧地 mask 掉 75%。只保留 25% 的 patch 输入编码器,逼模型学真正的高层语义,而不是局部插值。

    7.2 原理

    MAE 的核心是非对称编码器-解码器架构:

    • 编码器(重):只处理可见的 25% patch,不处理 mask token。这大幅减少了计算量(约 4× 加速)。
    • 解码器(轻):接收编码器的可见 patch 表示 + mask token,重建被 mask 掉的像素值。

    训练目标极其简单:用 MSE 损失重建被 mask 掉的 patch 的原始像素值。

    为什么 mask 75%? 论文发现,mask 比例在 75% 时效果最佳。低于这个比例,模型可以靠局部插值"作弊",学不到高层语义。高于 75%,任务太难,信息不足。

    7.3 公式

    MSE 重建损失(只在被 mask 的位置计算):

    L

    =

    1

    M

    i

    M

    x

    i

    x

    ^

    i

    2

    2

    \\mathcal{L} = \\frac{1}{|\\mathcal{M}|} \\sum_{i \\in \\mathcal{M}} \\| x_i – \\hat{x}_i \\|_2^2

    L=M1iMxix^i22

    其中

    M

    \\mathcal{M}

    M 是被 mask 的 patch 索引集合,

    x

    i

    x_i

    xi 是原始像素值(经过 per-patch 归一化),

    x

    ^

    i

    \\hat{x}_i

    x^i 是重建值。

    注意:只对被 mask 的 patch 计算 loss,不对可见 patch 计算——否则模型直接复制就行了。

    7.4 图示

    原始图像 (224×224)
    ┌───────────────────────┐
    │ ▓ ▓ ░ ░ ▓ ░ ░ ░ ░ ▓ │ ▓ = 可见 patch (25%)
    │ ░ ▓ ░ ░ ░ ▓ ░ ░ ░ ░ │ ░ = 被 mask 的 patch (75%)
    │ ░ ░ ▓ ░ ░ ░ ░ ▓ ░ ░ │
    │ ░ ░ ░ ░ ▓ ░ ░ ░ ▓ ░ │
    └───────────────────────┘

    ▼ 只取可见 patch
    ┌───────────────┐
    │ Encoder │ ViT-Huge(只处理 49 个 patch,而非 196 个)
    │ (Heavy) │ → 3×~4× 加速
    └───────────────┘

    ▼ 可见 patch 的 latent representation
    ┌───────────────────────────┐
    │ Decoder (Lightweight) │
    │ │
    │ 输入:可见 patch 编码 │
    │ + mask token 填充 │
    │ + 位置编码 │
    │ │
    │ 输出:所有 patch 的重建 │
    └───────────────────────────┘


    MSE Loss(只计算被 mask 位置的重建误差)


    训练完成后:丢弃 Decoder,只保留 Encoder 做下游任务

    7.5 踩坑点

  • 解码器深度很关键:太浅的解码器会让编码器被迫学习低层像素特征(因为得自己重建)。论文发现解码器至少 4-8 层 Transformer block 才好。编码器越深,解码器可以越浅——但别太极端。

  • 归一化方式影响效果:对每个 patch 做 per-patch 归一化(减均值除标准差)比直接重建原始像素略好。这是一个容易忽略的细节。

  • MAE 特征的线性可分性不如对比学习:MoCo 的特征直接加个线性分类器就有不错效果,但 MAE 需要端到端 fine-tune 才发挥实力。如果你的下游任务只能做 linear probe,别用 MAE;如果能 fine-tune,MAE 优于对比学习。

  • 别用离散 tokenizer:BEiT 用 dVAE 把图像 patch 变成离散 token 再预测,比 MAE 复杂得多但效果不一定好。何恺明证明了:直接重建像素就够了。

  • mask 策略很重要:随机均匀 mask 是最好的。有人试过 block-wise mask(整块遮挡),效果反而差——因为破坏了全局信息流。

  • 7.6 源码拆解

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    from timm.models.vision_transformer import PatchEmbed, Block

    class MaskedAutoencoderViT(nn.Module):
    """简化版 MAE"""
    def __init__(self, img_size=224, patch_size=16, in_chans=3,
    embed_dim=1024, depth=24, num_heads=16,
    decoder_embed_dim=512, decoder_depth=8,
    mlp_ratio=4., norm_pix_loss=True):
    super().__init__()
    self.patch_embed = PatchEmbed(img_size, patch_size, in_chans, embed_dim)
    num_patches = self.patch_embed.num_patches # 14×14=196

    # 分类 token 和位置编码
    self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
    self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim))

    # 编码器(重)
    self.blocks = nn.ModuleList([
    Block(embed_dim, num_heads, mlp_ratio)
    for _ in range(depth)])

    # 解码器的嵌入层(把编码器输出映射到解码器维度)
    self.decoder_embed = nn.Linear(embed_dim, decoder_embed_dim)

    # mask token(可学习的占位符)
    self.mask_token = nn.Parameter(torch.zeros(1, 1, decoder_embed_dim))

    # 解码器位置编码
    self.decoder_pos_embed = nn.Parameter(
    torch.zeros(1, num_patches + 1, decoder_embed_dim))

    # 解码器(轻)
    self.decoder_blocks = nn.ModuleList([
    Block(decoder_embed_dim, num_heads, mlp_ratio)
    for _ in range(decoder_depth)])

    # 重建预测头:输出每个 patch 的像素值
    self.decoder_pred = nn.Linear(decoder_embed_dim,
    patch_size ** 2 * in_chans)

    self.norm_pix_loss = norm_pix_loss

    def patchify(self, imgs):
    """把图像切分成 patch"""
    p = self.patch_embed.patch_size[0]
    h = w = imgs.shape[2] // p
    x = imgs.unfold(2, p, p).unfold(3, p, p) # [B, C, h, w, p, p]
    x = x.contiguous().view(x.shape[0], x.shape[1], 1, p*p)
    x = x.permute(0, 2, 1, 3) # [B, num_patches, C, p*p]
    x = x.flatten(1) if False else x.flatten(2) # [B, num_patches, C*p*p]
    return x

    def forward(self, imgs, mask_ratio=0.75):
    # 1. Patchify
    x = self.patch_embed(imgs) # [B, N, D]

    # 2. 随机 mask(保留 25%)
    x, mask, ids_restore = self.random_masking(x, mask_ratio)

    # 3. 编码器只处理可见 patch
    x = torch.cat([self.cls_token.expand(x.shape[0], 1, 1), x], dim=1)
    x = x + self.pos_embed
    for blk in self.blocks:
    x = blk(x)
    x = x[:, 1:, :] # 去掉 cls token

    # 4. 解码器:插入 mask token
    x = self.decoder_embed(x)
    # 在被 mask 的位置插入 mask token
    mask_tokens = self.mask_token.expand(
    x.shape[0], ids_restore.shape[1] x.shape[1], 1)
    x = torch.cat([x, mask_tokens], dim=1)
    x = torch.gather(x, dim=1,
    index=ids_restore.unsqueeze(1).repeat(1, 1, x.shape[2]))
    x = torch.cat([self.cls_token.expand(x.shape[0], 1, 1), x], dim=1)
    x = x + self.decoder_pos_embed
    for blk in self.decoder_blocks:
    x = blk(x)
    x = x[:, 1:, :] # 去掉 cls token

    # 5. 重建预测
    pred = self.decoder_pred(x) # [B, N, p*p*3]

    # 6. 计算 MSE loss(只在 mask 位置)
    target = self.patchify(imgs)
    if self.norm_pix_loss:
    mean = target.mean(dim=1, keepdim=True)
    var = target.var(dim=1, keepdim=True)
    target = (target mean) / (var + 1.e-6).sqrt()

    loss = (pred target) ** 2
    loss = loss.mean(dim=1) # 每个 patch 的 loss
    loss = (loss * mask).sum() / mask.sum() # 只算 mask 位置

    return loss

    def random_masking(self, x, mask_ratio):
    """随机 mask:保留 (1-mask_ratio) 的 patch"""
    N, L, D = x.shape
    len_keep = int(L * (1 mask_ratio))

    noise = torch.rand(N, L, device=x.device)
    ids_shuffle = torch.argsort(noise, dim=1)
    ids_restore = torch.argsort(ids_shuffle, dim=1)

    x_keep = torch.gather(x, dim=1,
    index=ids_shuffle[:, :len_keep].unsqueeze(1)
    .repeat(1, 1, D))

    mask = torch.ones([N, L], device=x.device)
    mask[:, :len_keep] = 0
    mask = torch.gather(mask, dim=1, index=ids_restore)

    return x_keep, mask, ids_restore

    7.7 效果对比

    方法架构ImageNet top-1 (fine-tune)预训练数据
    Supervised ViT-Huge ImageNet-1K (标签)
    DINO ViT-B/16 77.4 ImageNet-1K
    BEiT ViT-B/16 77.9 ImageNet-1K
    MAE ViT-B/16 83.6 ImageNet-1K (无标签)
    MAE ViT-L/16 85.9 ImageNet-1K (无标签)
    MAE ViT-H/16 86.9 ImageNet-1K (无标签)
    MAE (长训练) ViT-H/16 87.8 ImageNet-1K (无标签)

    ViT-Huge + MAE 是当时在仅用 ImageNet-1K 数据下取得的最佳精度。


    八、最新研究方向(2024-2025)—— 生成式模型

    何恺明在 2024 年加入 MIT 后,研究重心明显转向了生成式模型,尤其是扩散模型(Diffusion Models)的高效推理。

    8.1 核心论文

  • 《Is Noise Conditioning Necessary for Denoising Generative Models?》(ICML 2025):质疑扩散模型中噪声条件是否必需。结论:去掉噪声条件后模型仍能生成高质量样本,简化了训练流程。

  • 《Mean Flows for One-Step Generative Modeling》(NeurIPS 2025):提出 Mean Flow 框架,用均值流替代常微分方程(ODE)求解,将多步扩散模型压缩到一步推理,大幅缩小了与多步模型的差距。

  • 《Deconstructing Denoising Diffusion Models for Self-Supervised Learning》(ICLR 2025):拆解扩散模型的内部表示,将其用于自监督学习,建立了生成模型与表示学习的桥梁。

  • 《Transformers without Normalization》(CVPR 2025):与 Yann LeCun 合作,证明 Transformer 可以不需要归一化层(如 LayerNorm),用简单的仿射变换替代,挑战了"归一化不可或缺"的共识。

  • 《Fractal Generative Models》(TMLR 2025):提出分形生成模型——将生成模型抽象为原子模块,递归调用形成自相似的分形结构,为生成模型的可扩展性提供了新范式。

  • 《Autoregressive Image Generation without Vector Quantization》(NeurIPS 2024):在连续值空间做自回归图像生成,用扩散过程建模每个 token 的概率分布,避免了向量量化的信息损失。

  • 8.2 研究风格的一以贯之

    纵观这些最新工作,何恺明的研究风格依然鲜明:

    • 质疑共识:噪声条件真的必要吗?归一化层真的不可或缺吗?
    • 追求简洁:去掉一个模块、减少一个步骤,看效果会不会变差
    • 从表示学习到生成:自监督学习的特征能否反过来指导生成?生成模型能否用于表示学习?

    这种"用最简单的方法挑战最核心假设"的风格,从 2009 年的暗通道先验到 2025 年的无归一化 Transformer,一脉相承。


    九、总结:何恺明的方法论

    回顾何恺明从 2009 到 2025 的研究轨迹,可以提炼出几个核心方法论:

    9.1 简洁至上

    他的每一篇代表作都有一个共同特征:核心思想简单到可以用一句话讲清楚。

    • Dark Channel Prior:无雾图像的暗通道趋近于0
    • He 初始化:ReLU 的方差补偿因子就是

      2

      \\sqrt{2}

      2

    • ResNet:学残差

      F

      (

      x

      )

      F(x)

      F(x) 而不是

      H

      (

      x

      )

      H(x)

      H(x)

    • MoCo:用队列和动量解耦 batch size 和字典大小
    • MAE:mask 掉 75%,只处理 25%

    这种简洁不是偷懒,而是对问题本质的深刻洞察——当你真正理解了问题的核心,复杂的方案往往是多余的。

    9.2 从观察出发

    何恺明不追求数学上的花哨。他的每一步都是从经验观察出发:

    • Dark Channel Prior 来自对自然图像的统计观察
    • ResNet 来自对"深层网络退化"这一现象的观察
    • MAE 来自对"图像空间冗余"的认知

    先观察现象,理解本质,再设计方案。而不是先有方案,再找问题来套。

    9.3 可扩展性思维

    从 ResNet 到 MoCo 到 MAE,他始终在思考一个问题:这个方法能不能 scale?

    • ResNet:从 18 层到 152 层到 1202 层
    • MoCo:字典大小从 batch 级到 65536
    • MAE:模型从 ViT-B 到 ViT-H,训练加速 3-4×

    2025 年的分形生成模型更是把"可扩展"推向了新的维度:递归调用原子模块,形成分形结构。

    9.4 学术与产业的双轨并行

    何恺明是罕见的在学术界和工业界都达到顶尖水平的研究者。他的研究既有理论深度(ResNet 的残差学习理论),又有直接的应用价值(Mask R-CNN 的工业落地、MoCo/MAE 的大模型预训练)。

    2024年加入 MIT、2025年兼任 Google DeepMind,这种"学界 + 业界双轨并行"的模式,让他始终保持对最前沿问题的敏锐嗅觉。


    十、何恺明核心论文时间线

    年份论文会议核心贡献
    2009 Single Image Haze Removal Using Dark Channel Prior CVPR Best Paper 暗通道先验去雾
    2015 Delving Deep into Rectifiers ICCV He初始化 + PReLU,首次超越人类
    2016 Deep Residual Learning for Image Recognition CVPR Best Paper 残差连接,训练152层网络
    2017 Mask R-CNN ICCV Best Paper 实例分割 + RoIAlign
    2017 Focal Loss for Dense Object Detection ICCV Best Student Paper Focal Loss 解决类别不平衡
    2018 Non-local Neural Networks CVPR 视觉自注意力机制
    2020 Momentum Contrast (MoCo) CVPR 动量对比学习
    2021 Exploring Simple Siamese (SimSiam) CVPR 无负样本的对比学习
    2022 Masked Autoencoders (MAE) CVPR 掩码自编码器自监督学习
    2024 Autoregressive Image Generation w/o VQ NeurIPS 连续值自回归生成
    2025 Transformers without Normalization CVPR 无归一化Transformer
    2025 Mean Flows for One-Step Generative Modeling NeurIPS 一步生成模型
    2025 Fractal Generative Models TMLR 分形生成模型

    结语

    何恺明的学术生涯,是一部"如何用最简洁的方案解决最核心问题"的教科书。从 25 岁的暗通道先验到 40 岁的分形生成模型,他的研究始终围绕同一个底层信念:简单,但深刻。

    对每一个做研究或做工程的人来说,何恺明的方法论有着朴素的启示——

    与其追求复杂,不如理解本质。与其增加模块,不如减少假设。与其追赶热点,不如回到最基本的问题:什么才是真正重要的?

    这篇博客拆解了何恺明六大核心贡献的原理、公式、源码和效果对比,希望能为你自己的研究和工程实践提供参考。如果你对其中某个方向有更深入的兴趣,建议直接精读原始论文——何恺明的论文本身就是最好的教程,简洁、清晰、扎实。


    本文基于何恺明 2009-2025 年的核心论文和公开资料整理撰写。论文链接可在 Google Scholar 或 何恺明个人主页 查阅。

    赞(0)
    未经允许不得转载:171主机测评 » 何恺明算法与论文深度解析:从暗通道先验到生成式模型的十年技术版图
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址