跳转至


课程  因子投资  机器学习  Python  Poetry  ppw  tools  programming  Numpy  Pandas  pandas  算法  hdbscan  聚类  选股  Algo  minimum  numpy  回测  数据标准化  algo  FFT  模式识别  配对交易  GBDT  LightGBM  XGBoost  statistics  CDF  KS-Test  monte-carlo  VaR  过拟合  algorithms  machine learning  strategy  python  sklearn  pdf  概率  数学  面试题  量化交易  策略分类  风险管理  Info  interview  career  强化学习  监督学习  AI量化  复权  数据  tushare  akshare  xgboost  PCA  wavelet  时序事件归因  SHAP  深度学习  归一化  BN  LN  WN  machine-learning  quant-trading  dropout  kaggle  boosting  决策树  泰勒展开  openclaw  resources  quant  deep-learning  tcn  time-series  causal-convolution  交易实战  避坑指南  Figures  Behavioral Economics  graduate  arma  garch  人物  职场  Quantopian  figure  Banz  金融行业  买方  卖方  story  量化传奇  rsi  zigzag  穹顶压力  因子  ESG  因子策略  投资  策略  pe  ORB  Xgboost  Alligator  Indicator  factor  alpha101  alpha  技术指标  wave  algorithm  pearson  spearman  套利  LOF  白银  因子分析  Alphalens  涨停板  herd-behaviour  momentum  因子评估  review  SMC  聪明钱  trade  history  indicators  zscore  波动率  lightgbm  顶背离  另类数据  freshman  others  AI  DeepSeek  network  量子计算  金融交易  IBM  weekly  进化论  logic-factor  Agent Skills  Skills Marketplace  VS Code  Tushare  XtQuant  BaoStock  A股  量化  neutralization  basics  LLT  backtest  backtrader  研报  papers  UBL  金融阅读  免费资源  华尔街日报  WSJ  量化学习  quantlib  jupyter-notebook  scikit-learn  pypinyin  qmt  xtquant  blog  static-site  duckdb  工具  colors  free resources  barra  world quant  Alpha  openbb  risk-management  llm  prompt  CANSLIM  Augment  arsenal  copilot  vscode  code  量化数据存储  hdf5  h5py  cursor  augment  trae  Jupyter  jupysql  pyarrow  parquet  数据源  quantstats  几何收益  实盘  clickhouse  polars  滑动窗口  notebook  sqlite  sqlite-utils  fastlite  大数据  PyArrow  UV  Pydantic  Engineering  redis  remote-agent  AI-tools  Moonshot  回测,研报,tushare  dividend 

残差连接:深度学习成功的关键技术

最后更新: 2026-03-24


残差连接:深度学习成功的关键技术

开篇先问一个问题

你有没有这种感觉:网络越深,效果反而越差?

直觉告诉我们,层数越多,表达能力越强,效果应该更好。但现实往往打脸——堆到几十层以后,训练反而变得困难,梯度开始消失,效果甚至不如浅层网络。

这件事当年挺让业界头疼的,直到有人提出了一个看似简单的想法。


抄书的故事

让我打个比方。

假设让你把一本书从第1页抄到第100页,你会有两种方法:

方法A是直接抄,第1页抄到第2页,第2页抄到第3页,一路到底。这种方式一旦中间抄错了一处,后面的就会跟着错,错误会累积。

方法B是每抄完一页,就对照原文检查一下,发现不对立刻纠正。这样错误就不会一路传下去。

残差连接做的事情,其实就是"对照原文检查"这个机制。它在层的输出和输入之间修了一条捷径,让原始信息可以直接传到后面去。


残差连接是什么

说白了,残差连接就是让网络多了一条"直通车"。

普通网络是 y = F(x),你给我一个输入,我经过一堆变换给你输出。残差网络则是 y = F(x) + x,输出不只是变换后的结果,还要把原始输入加回来。

这意味着什么?

假设你输入80分,理想输出是85分。普通网络需要学会 F(x) = 85,直接拟合这个绝对值。残差网络呢?只需要学 F(x) = 5——也就是85和80的差值——然后再加回输入本身得到85。

学一个小的变化量,比学一个绝对值容易太多了。这就像你偏离目标的时候纠偏容易,但如果你根本不知道目标在哪,从零开始摸索就难得多。


从梯度流动的角度看

为什么残差连接能帮助训练?

反向传播的时候,梯度要一层一层往下传。普通网络的梯度是逐层衰减的,层数一深,前面的层几乎收不到梯度的更新。

残差连接有意思的地方在于,因为它有 y = F(x) + x 这一项,求梯度的时候会出现一个 +1。这个 +1 就像是高速公路,不管前面的层有多深,梯度都能直接传过去,不用担心衰减。

另一个角度是恒等映射。假设最优的变换其实就是"什么都不变",普通网络要拟合 F(x) = x 这个恒等映射,但网络很难做到完美。残差网络呢?只需要让 F(x) = 0 就行了,因为 y = 0 + x = x。学"让输出变成零"比"让输出等于输入"容易得多——至少网络可以很轻松地输出接近零的值。


投影层:维度不匹配怎么办

残差连接有个现实问题:输入和输出的维度有时候不一样。

比如输入是64个特征,经过一层变换后输出变成128个特征了。这时候 x + F(x) 根本没法加,因为维度对不上。

解决方案是加一个投影矩阵。如果维度相同,直接相加;如果维度不同,就用 W_proj · x 把输入投影到和输出相同的维度,然后再加。

实际代码里通常这么写:

1
2
3
4
5
# 维度相同,不需要投影
self.shortcut = nn.Identity()

# 维度不同,用 1x1 卷积投影
self.shortcut = nn.Conv1d(in_channels, out_channels, kernel_size=1)

金融例子:预测股价时残差的价值

预测明天股价这件事,残差思维的优势体现得很明显。

如果用普通网络,你直接学:今天收盘100元,明天价格 = 105元。这要求网络记住"起点是100",还要预测绝对值,中间错一点最终结果就差很多。

用残差网络的思路呢?网络只学"变化多少",输出是 +5元。然后 100 + 5 = 105。网络不需要记住绝对价格,只需要关注变化本身。

更妙的是,当市场变化很小时——比如明天几乎不变,只比今天高0.5元——残差网络可以轻松输出 0.5,因为接近零的值本来就很容易学到。但普通网络要精确预测 100.5 这个绝对值,反而更难。


代码里怎么写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class SimpleResidualBlock(nn.Module):
    """
    简单的残差块
    """
    def __init__(self, channels):
        super().__init__()
        self.conv1 = nn.Conv1d(channels, channels, 3, padding=1)
        self.conv2 = nn.Conv1d(channels, channels, 3, padding=1)
        self.relu = nn.ReLU()

    def forward(self, x):
        residual = x  # 记住输入

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

        out = out + residual  # 残差连接
        out = self.relu(out)

        return out

TCN里用到的残差块稍微复杂一点,因为包含了因果卷积和 LayerNorm,但核心思想是一样的:记住原始输入,最后加回来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ResidualBlock(nn.Module):
    """
    TCN 的残差块,包含两个因果卷积 + LayerNorm + 残差连接
    """
    def __init__(self, in_channels, out_channels, kernel_size=3, dilation=1):
        super().__init__()

        self.conv1 = CausalConv1D(in_channels, out_channels, kernel_size, dilation)
        self.ln1 = nn.LayerNorm(out_channels)

        self.conv2 = CausalConv1D(out_channels, out_channels, kernel_size, dilation)
        self.ln2 = nn.LayerNorm(out_channels)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.2)

        if in_channels != out_channels:
            self.shortcut = nn.Conv1d(in_channels, out_channels, 1)
        else:
            self.shortcut = nn.Identity()

    def forward(self, x):
        residual = self.shortcut(x)

        out = self.conv1(x)
        out = out.transpose(1, 2)
        out = self.ln1(out)
        out = out.transpose(1, 2)
        out = self.relu(out)
        out = self.dropout(out)

        out = self.conv2(out)
        out = out.transpose(1, 2)
        out = self.ln2(out)
        out = out.transpose(1, 2)
        out = self.relu(out)
        out = self.dropout(out)

        out = out + residual

        return out

多层堆叠的时候,每一层都有残差连接,信息层层传递,不会丢失:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TCN(nn.Module):
    """
    多层 TCN,每层都有残差连接
    """
    def __init__(self, input_dim, num_channels, kernel_size=3):
        super().__init__()

        layers = []
        for i in range(len(num_channels)):
            in_ch = input_dim if i == 0 else num_channels[i-1]
            out_ch = num_channels[i]
            dilation = 2 ** i

            layers.append(
                ResidualBlock(in_ch, out_ch, kernel_size, dilation)
            )

        self.network = nn.ModuleList(layers)

    def forward(self, x):
        for block in self.network:
            x = block(x)
        return x

残差连接在 TCN 里为什么重要

TCN 的网络深度通常有4到10层,每层都是因果卷积。深度一深,梯度消失就容易成为问题。

残差连接在 TCN 里解决的是两个事:一是每层都有"捷径",梯度可以直接传回去,不用担心衰减;二是保留了浅层的信息。

感受野叠加的时候,残差的效果更明显。第一层看3个点加上原始输入,第二层看5个点加上第一层的输出,第三层看9个点加上第二层的输出......信息层层叠加,但原始信息始终保留在"直通车"里,不会丢失。


一句话理解残差连接

如果非要我一句话总结,我会这么说:

残差连接就是"搭便车"——让原始信息坐直通车传到后面,网络只需要专心学"还需要改变什么"。

公式很好记:y = F(x) + x,或者维度不匹配时 y = F(x) + W_proj · x。

输出等于网络学到的变化,加上原始输入。

这件事说起来简单,但正是这个简单的设计,让深度学习训练深网络成为可能。说它是深度学习成功的关键技术,一点都不夸张。