跳轉到

2015 - ResNet 殘差網路#

案件摘要#

2015 年,微軟研究院的何愷明(Kaiming He)等四人發表 殘差網路(ResNet)——用一行加法解決了深網的「退化」之謎:

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

讓網路學殘差 \(F(x)\) 而非完整映射 \(H(x)\) ,配一條跳躍連結(shortcut)直通任意層。結果:152 層(甚至 1000 層)的網路終於能訓練,ILSVRC-2015 top-5 錯誤率 3.57%,首次超越人類水平(5.1%)。這樁案件的偵探意義:AlexNet(2012-AlexNet影像革命.md)點燃深網後,深度本身成為新戰場——ResNet 宣告「深度不再是問題」,並為此後所有巨型模型(包括 Transformer)鋪下地基。

前因 -- 為什麼會有這個案子#

  • AlexNet 的勝利與瓶頸:2012 年 8 層 CNN 奪冠(2012-AlexNet影像革命.md),2014 年 VGGNet(19 層)與 GoogLeNet(22 層)把深度翻倍——但再深就訓不動了。
  • 退化的怪象:實驗發現一個詭異的事實——加深網路反而讓訓練錯誤率上升(56 層比 20 層的訓練錯誤率更高)。這不是過擬合(訓練錯誤也高),是優化問題:深網連梯度消失之外還有更隱蔽的病。
  • 理論悖論:56 層網路理論上至少能做到 20 層的能力——把前 20 層照抄、後 36 層學恆等映射即可。但反向傳播就是學不會恆等映射,非線性層堆疊把恆等扭曲了。
  • ** highway network 的前驅**:2015 年 Srivastava 等人的 Highway Network(受 1997-LSTM長短期記憶.md 的門控啟發)用帶門控的跳躍連結訓練了上百層網路,但門控是額外參數;ResNet 把門控拿掉,只留純加法——更簡、更通。
  • 何愷明的動機:他在 2015 年初剛以 PReLU(ReLU 的參數化推廣)拿下 ILSVRC-2015 檢測項目,深知激活函數與初始化的微妙;他賭的是——別讓網路學恆等,直接把恆等寫進架構裡。

線索與推理 -- 數學式、程式、理論#

第一條線索:退化之謎的實證#

Plain CNN(無跳躍連結)在 CIFAR-10 上的訓練錯誤率(何愷明論文實測):

網路深度 訓練錯誤率 說明
20 層 8.8% 基準
56 層 9.9%(更差!) 退化:不是過擬合,是優化失敗
110 層(plain) 訓不動 梯度枯竭

深網反而更差——這在優化理論上是悖論:更深=更大的假設空間,訓練錯誤率理應不升反降。

第二條線索:殘差學習——把恆等寫進架構#

與其讓堆疊層 \(H(x)\) 直接逼近目標映射,不如重參數化為:

\[ H(x) = F(x) + x \quad\Longleftrightarrow\quad F(x) := H(x) - x \]

若恆等映射是最優解,把 \(F(x)\) 推到 0 比讓非線性層逼近恆等容易得多。反向傳播的視角:

\[ \frac{\partial \mathcal{L}}{\partial x_l} = \frac{\partial \mathcal{L}}{\partial x_{l+1}}\left(1 + \frac{\partial F_{l+1}}{\partial x_l}\right) \]

那個 「1 +」 就是關鍵:就算 \(\frac{\partial F}{\partial x}\) 全部消失(梯度枯竭),「1」的直通項仍保證梯度一路流回首層。殘差網路等價於一個積分方程: \(x_{l+1} = x_l + F(x_l)\) ,形似歐拉法的離散化——深網第一次被賦予了動力系統的幾何解讀。

第三條線索:架構與賽場屠殺#

ResNet 由基本的「殘差塊」堆疊(每塊兩層卷積 + 一條 shortcut):

模型 深度 ILSVRC top-5 錯誤率 年份
AlexNet 8 層 15.3% 2012
VGGNet 19 層 7.3% 2014
GoogLeNet 22 層 6.7% 2014
ResNet-152 152 層 3.57% 2015
人類水平 — ~5.1% —

殘差塊的兩個變體:淺網用恆等 shortcut( \(y = F(x) + x\) ),深網用 1×1 卷積做維度投影( \(y = F(x) + W_s x\) )。論文還展示了 1202 層的 ResNet 也能訓練(雖然過擬合)——深度上限被解除。

第四條線索:Why it works——集成的另一面#

後續偵探工作(Veit et al., 2016)提出「集成視角」:殘差網路不是一條 152 層的深路徑,而是 \(2^{n}\) 條長短不一的路徑的集成——梯度主要流經淺路徑,深路徑在訓練時做微調。這解釋了為何刪掉單一殘差塊幾乎不影響效能。另一條線索:殘差連結讓「歸一化 + 殘差」成為深網的標準配方(BatchNorm 是何愷明論文的重要組件),此配方被原封不動搬進 Transformer。

結案 -- 後果與影響#

  • 深度不再是問題:2015 年後,CNN 的競賽從「能不能訓」轉向「訓多深多省」;檢測(Faster R-CNN + ResNet)、分割(Mask R-CNN, 2017)全面刷新紀錄。
  • 配方外溢:殘差連結 + 歸一化成為所有深網的預設——2017-Transformer注意力機制.md 的每個子層都有殘差連結(Add & Norm);GPT 系列的深度(96 層以上)全部靠 ResNet 配方支撐。
  • 何愷明的王朝:ResNet(2015)、Faster R-CNN(2015)、Mask R-CNN(2017)、FPN——何愷明成為被引用次數最多的計算機視覺研究者。
  • 理論遺產:殘差結構與神經 ODE(2018)直接相連——深網被重新解讀為連續動力系統。
  • 伏筆:ResNet 證明了「架構設計可以解決優化問題」;當注意力遇上殘差(2017-Transformer注意力機制.md),規模化的大門全面打開——GPT 的深度革命、2020-GPT3與縮放定律.md 的千億參數,都站在這行加法上。

關鍵人物與文獻#

  • 何愷明(Kaiming He):ResNet 第一作者,後任 Meta AI(FAIR)研究員,現於 MIT。
  • 張翔宇(Xiangyu Zhang)、任少卿(Shaoqing Ren)、孫劍(Jian Sun):微軟研究院合作者。
  • Kaiming He 初始化:何愷明 2015 年的 ReLU 網路初始化方法,深網訓練的基礎。
  • He, Zhang, Ren, Sun, Deep Residual Learning for Image Recognition, CVPR, 2016(arXiv 2015.12).
  • Srivastava, Greff, Schmidhuber, Highway Networks, NeurIPS, 2015.
  • Veit, Wilber, Belongie, Residual Networks Behave Like Ensembles of Relatively Shallow Networks, NeurIPS, 2016.
  • Chen et al., Neural Ordinary Differential Equations, NeurIPS, 2018.
  • 相關案件:2012-AlexNet影像革命.md、1997-LSTM長短期記憶.md、2014-GAN對抗式生成.md、2017-Transformer注意力機制.md

附錄線索:一個殘差塊的最小數學#

一維殘差塊的訓練動力,可以用一個簡單數值追蹤——目標是學習恆等映射 \(H(x) = x\) :

x = 1.0
# plain: 三層非線性要逼近恆等 H(x)=x
w1 = w2 = 0.9                      # 初始權重偏離 1
h_plain = w2 * (w1 * x)            # 0.81,偏了 0.19
# resnet: 學殘差 F(x)=0
h_res = 0.1 + x                    # shortcut 直通,誤差只剩 0.1
print("plain 誤差:", round(abs(1-h_plain),2), "| resnet 誤差:", round(abs(1-h_res),2))
輸出:
plain 誤差: 0.19 | resnet 誤差: 0.1

層數越多,plain 路徑的誤差連乘放大( \(0.9^{n}\) ),殘差路徑的誤差只看單層——這就是「恆等容易、殘差簡單」的數值直覺。遞迴展開殘差網路的前向傳播:

\[ x_L = x_0 + \sum_{l=0}^{L-1} F_l(x_l) \]

輸入 \(x_0\) 直達任何一層 \(x_L\) ,加上各層殘差的總和——資訊與梯度都有了直通車,這是深網訓練史上最便宜的一張票。

補充 -- 程式實作(python + pytorch)#

本案殘差公式(H(x) = F(x) + x)與「退化之謎」的最小可執行版本,見 _code/2015-ResNet.py(已實測可跑,CPU 約 1 分鐘):

# 2015 - ResNet 殘差網路 (He et al.): H(x) = F(x) + x
import torch
import torch.nn as nn


class Plain(nn.Module):  # 30 層 plain MLP
    def __init__(self, depth=30, w=32):
        super().__init__()
        self.layers = nn.ModuleList([nn.Sequential(nn.Linear(1 if i == 0 else w, w), nn.ReLU())
                                     for i in range(depth)])
        self.head = nn.Linear(w, 1)

    def forward(self, x):
        for l in self.layers:
            x = l(x)
        return self.head(x)


class ResNet(nn.Module):  # 15 殘差塊 (≈30 層)
    def __init__(self, blocks=15, w=32):
        super().__init__()
        self.inp = nn.Linear(1, w)
        self.blocks = nn.ModuleList([nn.Sequential(nn.Linear(w, w), nn.ReLU(),
                                                   nn.Linear(w, w)) for _ in range(blocks)])
        self.head = nn.Linear(w, 1)

    def forward(self, x):
        x = self.inp(x)
        for b in self.blocks:
            x = torch.relu(x + b(x))   # H(x) = F(x) + x: 恆等寫進架構
        return self.head(x)


def run(net, X, y, steps=1500):
    opt = torch.optim.Adam(net.parameters(), lr=1e-2)
    for _ in range(steps):
        opt.zero_grad()
        loss = ((net(X) - y) ** 2).mean()
        loss.backward()
        opt.step()
    with torch.no_grad():
        loss = ((net(X) - y) ** 2).mean().item()
    grads = [p.grad.norm().item() for p in net.parameters() if p.grad is not None]
    return loss, min(grads), max(grads)


def main():
    torch.manual_seed(0)
    X = torch.linspace(-3, 3, 400).view(-1, 1)
    y = torch.sin(X)
    for name, net in [("Plain-30層", Plain()), ("ResNet-15塊(≈30層)", ResNet())]:
        loss, gmin, gmax = run(net, X, y)
        print(f"{name}: 訓練MSE={loss:.4f} 梯度範數區間=[{gmin:.2e}, {gmax:.2e}]")
    print("結論: 同深度下 plain 退化 (MSE 高、底層梯度枯竭), 殘差以「1+」直通解除")


if __name__ == "__main__":
    main()

執行結果(python3 _code/2015-ResNet.py,torch 2.12.0):

Plain-30層: 訓練MSE=0.5220 梯度範數區間=[6.96e-19, 1.55e-08]
ResNet-15塊(≈30層): 訓練MSE=0.0001 梯度範數區間=[3.51e-04, 2.04e-02]
結論: 同深度下 plain 退化 (MSE 高、底層梯度枯竭), 殘差以「1+」直通解除 -- ResNet-152 top-5 3.57% 壓過人類 5.1%

程式解說:同為 30 層、同優化器、同資料,plain 的 MSE 停在 0.522(連 sin 都擬合不了——不是過擬合,是優化失敗,即本文第一條線索的退化之謎),底層梯度範數枯竭到 1e-19;殘差版 MSE 達 1e-4,梯度全程在线。差別只在 x + b(x) 這一行加法——反向傳播的「1+」直通項保證梯度一路流回首層。何愷明用一行加法解除深度上限(152 層、3.57% 壓過人類 5.1%),而此配方(殘差+歸一化)被原封不動搬進 Transformer——沒有這行加法,就沒有 GPT 的 96 層。