中文

深度学习的本质:从特征工程到表示学习

一、深度学习的本质——从特征工程到表示学习

在回归模型一章中,我们反复强调了特征工程的重要性:将材料的成分、晶粒尺寸、热处理温度等原始数据,通过对数变换、幂律、交叉项等方式,变换成对模型更友好的形式。这种人工设计特征的思路在样本量有限、领域知识丰富时表现极好。然而,当数据的维度极高、内在结构极复杂——一张细胞显微图像包含数百万像素,一段蛋白质序列蕴含多层次的折叠信息,一个有限元网格数据包含数十万节点的位移场——人工设计特征便面临根本性的瓶颈:不是研究者不努力,而是问题的复杂性超过了人类单凭先验知识提取有用信息的能力上限。

深度学习(Deep Learning,DL)的核心突破在于将"特征提取"这件事本身也纳入学习过程。它不要求研究者先提取好特征再交给模型,而是让模型从原始输入数据出发,通过多个层次的变换,自动学习对最终任务最有用的中间表示(Representation)。这个思想被称为表示学习(Representation Learning),是深度学习与传统机器学习最根本的哲学分野。

用一个直觉性的例子说明。假设你的任务是预测骨小梁(Trabecular Bone)的弹性模量,输入数据是微型CT(micro-CT)扫描得到的三维图像。传统方法要求先提取几何描述符(BV/TV 骨体积分数、Tb.Th 骨小梁厚度、各向异性度 DA 等),再用线性回归或随机森林建立从描述符到弹性模量的映射——每个描述符的物理意义清晰,模型参数直接可解释。但如果骨骼微结构中存在你未曾量化的三阶段复杂拓扑特征,这些手工描述符就会错失关键信息。深度学习(具体说是三维卷积神经网络)直接把三维体素阵列送入网络,让网络自己学习"什么样的局部结构特征与弹性模量最相关"——从最底层的边缘和纹理,到中层的骨小梁连通模式,再到高层的拓扑结构——这就是"层次特征提取"。

"深度"这个词的含义就是层次的深度。理论上,浅层网络(1~2 层)也可以近似任意函数(通用近似定理),但所需的神经元数量随目标函数复杂度指数增长;深层网络可以用指数少的神经元表示同样复杂的函数,前提是函数具有层次可分解结构(Compositional Structure)——而自然界中的物理系统(材料微结构、生物形态、流体湍流)往往恰好具有这种特性,这是深度学习在科学计算中有效的深层原因。

然而,有一个极其重要的前提条件需要在一开始就澄清:深度学习不是免费的午餐,更不是万能的替代品。它带来强大表达能力的代价是:需要大量训练数据(通常数千到数百万样本)、更高的计算资源(GPU训练往往以小时甚至天计)、更多的超参数调优工作,以及相对更差的可解释性。在样本量少(N < 500)、特征有明确物理含义、需要系数层面解释的科研场景中,岭回归、Lasso、随机森林往往比深度网络更可靠、更高效、更可信。深度学习真正的适用场景是:输入为高维非结构化数据(图像、音频、序列、图结构)、样本量充足、且特征工程几乎无法穷举所有有用信息的情况。明确这一边界,比学会搭建一个神经网络更重要。


二、神经网络的基本构件——神经元、层与激活函数

神经网络这个名字来自对生物神经系统的粗糙类比:大脑神经元接收来自树突的化学信号,在细胞体中积累,当超过阈值时通过轴突发放电信号给下一个神经元。数学上,一个人工神经元接收来自前一层的输出向量 \(\mathbf{x} \in \mathbb{R}^d\),计算加权和,加上偏置项,再通过一个非线性函数(激活函数)后输出:

$$z = \mathbf{w}^T \mathbf{x} + b, \qquad a = \sigma(z)$$

其中 \(\mathbf{w} \in \mathbb{R}^d\) 是权重向量,\(b \in \mathbb{R}\) 是偏置,\(\sigma(\cdot)\) 是激活函数,\(a\) 是这个神经元的激活值。将 \(n_l\) 个神经元并列,用矩阵运算一次计算整层:

$$\mathbf{a}^{(l)} = \sigma\!\left(\mathbf{W}^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}\right)$$

其中 \(\mathbf{W}^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}}\) 是第 l 层的权重矩阵。把多层这样的变换串联起来,就构成了全连接(Fully Connected)前馈神经网络,又称多层感知机(MLP)。

激活函数的非线性至关重要。如果没有激活函数,多层网络的复合等价于一个单层线性变换,深层结构毫无意义——\(\mathbf{W}^{(L)} \cdots \mathbf{W}^{(1)} \mathbf{x}\) 仍然是 \(\mathbf{x}\) 的线性函数。非线性激活函数使得每一层真正在前一层输出的基础上引入新的表示能力。

常用的激活函数各有其物理直觉和适用场景。Sigmoid 函数 \(\sigma(z) = 1/(1 + e^{-z})\) 将实数压缩到 \((0,1)\),常用于二分类输出层,但在深层网络中存在梯度消失问题(导数最大仅 0.25,深层梯度指数衰减)。Tanh 函数输出在 \((-1,1)\),比 Sigmoid 均值更接近零,在 RNN 的隐藏层中常用。ReLU(Rectified Linear Unit)\(\text{ReLU}(z) = \max(0, z)\) 是当前深度网络最常用的隐藏层激活函数:计算极简单,梯度在正半轴为常数 1,有效缓解梯度消失,但存在"死亡 ReLU"问题——若某神经元的输入始终为负,梯度永远为零,该神经元永久失效。Leaky ReLU、ELU、GELU(Gaussian Error Linear Unit)是 ReLU 的改进版本,其中 GELU 是当前 Transformer 架构的标配激活函数。

在材料科学和力学语境中,选择激活函数有时有物理约束的考量。若物理量要求严格非负(如孔隙率、密度),输出层可用 Softplus \(\ln(1 + e^z)\);若系统有已知对称性,网络架构本身应体现这种对称性(这正是等变神经网络和物理启发神经网络 PINN 的设计出发点)。


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

# Visualize common activation functions and their gradients
x = torch.linspace(-4, 4, 400, requires_grad=False)
activations = {
    'ReLU':       nn.ReLU(),
    'Leaky ReLU': nn.LeakyReLU(0.1),
    'GELU':       nn.GELU(),
    'Tanh':       nn.Tanh(),
    'Sigmoid':    nn.Sigmoid(),
}

fig, axes = plt.subplots(1, 2, figsize=(12, 4))
for name, fn in activations.items():
    xv = x.clone().requires_grad_(True)
    y  = fn(xv)
    y.sum().backward()
    axes[0].plot(x.detach(), y.detach().numpy(), label=name)
    axes[1].plot(x.detach(), xv.grad.numpy(), label=name)

for ax, title in zip(axes, ['Activation f(z)', 'Gradient df/dz']):
    ax.set_xlabel('z'); ax.set_title(title)
    ax.axhline(0, color='k', lw=0.5, ls='--')
    ax.legend(fontsize=8)
plt.tight_layout(); plt.show()
# Note: Sigmoid gradient is <0.25 everywhere — the root cause of vanishing gradients

三、前向传播与损失函数——从输入到预测的旅程

理解了单层的运算之后,前向传播(Forward Propagation)就是将输入数据从第一层逐层向后传递,直到输出层给出预测的过程。对于一个 L 层的全连接网络,前向传播可以写成递推形式:

$$\mathbf{a}^{(0)} = \mathbf{x} \quad (\text{输入层})$$

$$\mathbf{z}^{(l)} = \mathbf{W}^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)}, \quad \mathbf{a}^{(l)} = \sigma^{(l)}(\mathbf{z}^{(l)}), \quad l = 1, \ldots, L-1$$

$$\hat{y} = \mathbf{a}^{(L)} \quad (\text{输出层,根据任务选择激活函数})$$

在回归任务中,输出层通常不加激活函数(线性输出);在二分类中,输出层加 Sigmoid;在多分类中,输出层加 Softmax,将原始分值归一化为概率分布:

$$\text{Softmax}(\mathbf{z})_k = \frac{e^{z_k}}{\sum_{j=1}^K e^{z_j}}$$

预测完成之后,需要量化预测值与真实标签的差距——这个量化函数叫做损失函数(Loss Function)。对回归任务,常用均方误差(MSE):

$$\mathcal{L}_{\text{MSE}} = \frac{1}{N}\sum_{i=1}^N (y_i - \hat{y}_i)^2$$

对分类任务,常用交叉熵损失(Cross-Entropy):

$$\mathcal{L}_{\text{CE}} = -\frac{1}{N}\sum_{i=1}^N \sum_{k=1}^K y_{ik} \log \hat{y}_{ik}$$

交叉熵损失的直觉来自信息论:它度量预测概率分布与真实标签分布之间的 KL 散度。当预测概率接近真实标签时,损失接近零;若预测与真实完全相反,损失趋向无穷——这是一个远比 MSE 对分类错误更敏感的惩罚机制。

在力学仿真加速的应用中(如用神经网络替代有限元求解器预测全场位移或应力),损失函数可以直接包含物理残差:网络输出被代入控制方程(如纳维-斯托克斯方程、弹性力学平衡方程)后的残余误差同时作为约束,使方程的满足程度本身成为训练信号之一。这就是物理信息神经网络(Physics-Informed Neural Networks,PINNs)的基本思想,将损失函数定制化为物理约束的度量,是深度学习在科学计算中最有特色的一条技术路线。

一个容易被忽略的实践细节:训练时最小化的是经验损失(训练集上的平均损失),而真正关心的是期望损失(模型在未知数据分布上的真实误差)。深度网络参数量往往远超样本量(过参数化),使得泛化分析极其复杂——这是深度学习理论的前沿问题,双重下降现象(Double Descent)等反直觉规律的发现,使传统统计学的过拟合理论需要实质性修正。


四、反向传播与梯度下降——网络如何从错误中学习

前向传播给出预测,损失函数给出误差的度量,接下来的问题是:如何调整网络中数以百万计的参数,使损失函数尽可能小?这个问题的答案是反向传播(Backpropagation)算法——它是深度学习整个工程体系得以运转的核心引擎,由 Rumelhart、Hinton 和 Williams 在 1986 年发表于 Nature 的工作正式奠定了其地位。

直觉上,若能计算出损失函数对每个参数的偏导数(梯度),就知道"把这个参数往哪个方向稍微挪动一点,损失会减小",沿梯度的反方向更新参数,这就是梯度下降(Gradient Descent)。对所有参数的集合 \(\theta\),更新规则为:

$$\theta \leftarrow \theta - \eta \cdot \nabla_\theta \mathcal{L}$$

其中 \(\eta\) 是学习率(Learning Rate),控制每次迭代的步长。学习率是深度学习中最敏感的超参数:太大导致训练不稳定(在损失函数的碗底震荡甚至发散);太小导致收敛极慢,且容易陷入尖锐的局部极小值。

反向传播算法的本质是链式法则(Chain Rule)的高效实现。对于复合函数 \(\mathcal{L} = f(g(h(\mathbf{x})))\),链式法则给出:

$$\frac{\partial \mathcal{L}}{\partial \mathbf{x}} = \frac{\partial \mathcal{L}}{\partial f} \cdot \frac{\partial f}{\partial g} \cdot \frac{\partial g}{\partial h} \cdot \frac{\partial h}{\partial \mathbf{x}}$$

对 L 层网络,从输出层开始逐层反向计算中间梯度(误差信号 \(\delta^{(l)}\)),每层的权重梯度可以通过该层的输入激活和误差信号直接得到,而不需要对每个参数单独做数值差分。关键在于:前向传播中计算过的每一个中间值 \(\mathbf{z}^{(l)}\) 和 \(\mathbf{a}^{(l)}\) 都需要缓存,供反向传播时计算梯度使用——这正是深度学习训练占用大量 GPU 显存的根本原因,也是为什么 batch size 受显存容量限制。

实践中采用随机梯度下降(SGD)的 mini-batch 变体:从训练集中随机抽取小批量样本估计梯度,然后更新。Mini-batch 带来的梯度噪声实际上有正则化效果——它使网络倾向于收敛到更"平坦"的极小值(Flat Minima),平坦极小值通常对应更好的泛化性能(对参数的小扰动不会显著改变预测)。在现代深度学习优化器中,Adam(Adaptive Moment Estimation)为每个参数维护一阶动量(梯度的指数移动平均)和二阶动量(梯度平方的指数移动平均),在训练早期大步前进、在曲率大的方向自动缩小步长,对超参数的鲁棒性远优于基础 SGD,是大多数任务的合理起点。


import torch, torch.nn as nn, torch.optim as optim
import numpy as np
from torch.utils.data import DataLoader, TensorDataset

torch.manual_seed(42); np.random.seed(42)

# ── Synthetic dataset: material-like features → target property ──
N, d = 2000, 10
X = torch.randn(N, d)
y = (X[:, 0]**2 + X[:, 1]*X[:, 2] - X[:, 3]
     + 0.3*torch.randn(N)).unsqueeze(1)

X_tr, X_val = X[:1600], X[1600:]
y_tr, y_val = y[:1600], y[1600:]
loader = DataLoader(TensorDataset(X_tr, y_tr), batch_size=64, shuffle=True)

# ── 3-layer MLP ──
class MLP(nn.Module):
    def __init__(self, in_dim, hidden=64, out_dim=1):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(in_dim, hidden), nn.ReLU(),
            nn.Linear(hidden, hidden), nn.ReLU(),
            nn.Linear(hidden, out_dim)   # Linear output — regression
        )
    def forward(self, x):
        return self.net(x)

model     = MLP(d)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)
# weight_decay adds L2 penalty: equivalent to Ridge regression

history = {'train': [], 'val': []}
for epoch in range(200):
    model.train()
    epoch_loss = 0.0
    for xb, yb in loader:
        optimizer.zero_grad()    # Clear accumulated gradients
        pred = model(xb)
        loss = criterion(pred, yb)
        loss.backward()          # Backpropagation: compute all gradients
        optimizer.step()         # Update parameters
        epoch_loss += loss.item() * len(xb)
    history['train'].append(epoch_loss / len(X_tr))

    model.eval()
    with torch.no_grad():        # Disable gradient tracking for validation
        val_loss = criterion(model(X_val), y_val).item()
    history['val'].append(val_loss)

print(f"Final train MSE : {history['train'][-1]:.4f}")
print(f"Final val   MSE : {history['val'][-1]:.4f}")

在结构力学的代理模型(Surrogate Model)研究中,训练循环的逻辑与上面完全一致——只是 X 换成了几何参数或载荷边界条件,y 换成了关键位移或应力响应峰值。正是这种高度的模块化使 PyTorch 成为跨学科计算研究的通用工具:力学研究者不需要重新实现反向传播,只需要定义前向传播(模型结构和物理约束),其余由自动微分(Autograd)引擎处理。


五、卷积神经网络——空间结构的特征提取

全连接网络有一个根本缺陷:对输入的空间结构一无所知。把一张 224×224 的显微图像展平成 150,528 维向量送入 MLP,网络需要从第一层的全连接参数中自己发现"相邻像素比远处像素更相关"这件事——这不仅计算量极大(第一层就有数亿参数),还极度数据低效。卷积神经网络(CNN)通过三个结构性归纳偏置(Inductive Bias)从根本上解决了这一问题:局部连接、权重共享和平移不变性。

卷积层是 CNN 的核心。一个二维卷积核(Kernel)\(\mathbf{K} \in \mathbb{R}^{k \times k}\) 在输入特征图上滑动,每次对 \(k \times k\) 的局部区域做内积:

$$(\mathbf{X} * \mathbf{K})_{ij} = \sum_{m=0}^{k-1}\sum_{n=0}^{k-1} \mathbf{X}_{i+m,\,j+n} \cdot \mathbf{K}_{m,n}$$

关键点:这个卷积核在整幅图上共享——无论滑动到哪个位置,使用的是同一组权重。这使得 CNN 能够检测平移不变的局部特征(边缘、纹理、重复单元),且参数数量与图像尺寸解耦,仅取决于核大小和通道数。在材料科学中,这种设计天然适合检测显微图像中的重复性微结构特征(晶粒边界、裂纹尖端、纤维取向),无论这些特征出现在图像的哪个位置。

池化层(Pooling)通常跟在卷积层之后,对特征图做空间下采样(Max Pooling 取局部区域最大值),压缩特征图尺寸,增强对微小平移和形变的鲁棒性。经过多个"卷积+池化"模块堆叠,特征图的空间分辨率逐层降低,而通道数(特征深度)逐层增加——低层通道学习到边缘/纹理等简单特征,高层通道学习到语义级的抽象特征(如细胞核形态、骨小梁连通性)。

He et al. 在 2016 年提出的残差网络(ResNet)是 CNN 发展史上的里程碑。它引入跳跃连接(Skip Connection/Residual Connection),让每个模块学习残差映射而非直接映射:

$$\mathbf{a}^{(l+1)} = \mathcal{F}(\mathbf{a}^{(l)}, \mathbf{W}^{(l)}) + \mathbf{a}^{(l)}$$

跳跃连接为梯度提供了直接的反向传播通路(绕过卷积层),使得训练极深的网络(>100 层)成为可能。从结构力学的角度类比,跳跃连接就像给信号传递安装了一条"旁路管道",防止信息在深层传递中耗散殆尽。

批归一化(Batch Normalization, BN)是现代 CNN 的标配:对每一层的输出在 mini-batch 维度上归一化(均值为0、方差为1),然后经过可学习的缩放和平移。BN 显著加速收敛,允许使用更大学习率。在力学场预测任务中,若 batch 内数据分布差异极大(不同量级的载荷仿真),BN 可能引入不希望的统计耦合,此时 Layer Normalization 是更稳健的替代。


import torch
import torch.nn as nn

class MicrostructureCNN(nn.Module):
    """
    CNN for image-based material property prediction.
    Input : grayscale micrograph  [B, 1, 64, 64]
    Output: scalar property       [B, 1]
    """
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            # Block 1: 1 → 32 ch, 64×64 → 32×32
            nn.Conv2d(1, 32, 3, padding=1), nn.BatchNorm2d(32),
            nn.ReLU(inplace=True), nn.MaxPool2d(2),
            # Block 2: 32 → 64 ch, 32×32 → 16×16
            nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64),
            nn.ReLU(inplace=True), nn.MaxPool2d(2),
            # Block 3: 64 → 128 ch, 16×16 → 8×8
            nn.Conv2d(64, 128, 3, padding=1), nn.BatchNorm2d(128),
            nn.ReLU(inplace=True), nn.MaxPool2d(2),
        )
        self.regressor = nn.Sequential(
            nn.Flatten(),                   # 128 × 8 × 8 = 8192
            nn.Linear(128 * 8 * 8, 256), nn.ReLU(inplace=True),
            nn.Dropout(0.4),
            nn.Linear(256, 1)              # Predicted property (e.g., E-modulus)
        )

    def forward(self, x):
        return self.regressor(self.features(x))

# Manual residual block for illustration
class ResBlock(nn.Module):
    """Basic residual block: F(x) + x."""
    def __init__(self, channels):
        super().__init__()
        self.block = nn.Sequential(
            nn.Conv2d(channels, channels, 3, padding=1),
            nn.BatchNorm2d(channels), nn.ReLU(inplace=True),
            nn.Conv2d(channels, channels, 3, padding=1),
            nn.BatchNorm2d(channels),
        )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        return self.relu(self.block(x) + x)   # Skip connection

model   = MicrostructureCNN()
x_dummy = torch.randn(8, 1, 64, 64)
print("Output shape:", model(x_dummy).shape)  # → [8, 1]
n_params = sum(p.numel() for p in model.parameters())
print(f"Parameters  : {n_params:,}")

在科研中常见的 CNN 应用场景包括:断层扫描图像的孔隙分割(材料科学)、组织切片中的细胞类型识别(病理学/生物力学)、有限元网格节点场的局部特征提取(结构力学代理模型)、以及复合材料层合板损伤的超声 C 扫描图像分类。


六、序列模型与注意力机制——LSTM 与 Transformer

生物、材料与力学领域中有大量序列数据:蛋白质氨基酸序列(折叠与配体结合预测)、基因序列、传感器采集的疲劳载荷时间序列、有限元仿真的步进时间序列(结构动力学响应)、材料制备的多步工艺序列。这类数据的特点是:当前时刻的输出依赖于历史输入,且依赖的历史长度未知。全连接网络和 CNN 对序列时序依赖的建模能力有根本局限;循环神经网络(RNN)及其变体专门为此设计。

基础 RNN 在每个时间步维护一个隐藏状态 \(\mathbf{h}_t\),充当"记忆":

$$\mathbf{h}_t = \tanh\!\left(\mathbf{W}_h \mathbf{h}_{t-1} + \mathbf{W}_x \mathbf{x}_t + \mathbf{b}\right)$$

然而,基础 RNN 存在著名的梯度消失/爆炸问题:当序列很长时,梯度通过时间的反向传播(BPTT)需要乘以大量 \(\mathbf{W}_h\) 矩阵的导数,若矩阵主特征值小于1,梯度指数衰减,遥远的历史信息对当前参数更新贡献几乎为零。在疲劳裂纹扩展预测中,若裂纹速率依赖于几十个循环前的损伤积累历史,基础 RNN 完全无法捕捉这种长程依赖。

长短期记忆网络(LSTM)由 Hochreiter 和 Schmidhuber 在 1997 年提出,通过引入门控机制细胞状态(Cell State)解决这一问题。LSTM 有三个门(均为 Sigmoid 激活的软开关):遗忘门(决定保留多少历史记忆)、输入门(决定写入多少新信息)、输出门(决定从细胞状态中读取多少)。细胞状态像一条"传送带",信息沿序列以加法形式(而非乘法)传递,梯度流经细胞状态时几乎不衰减——这是 LSTM 能学习长距离依赖的根本机制。

2017年,Vaswani 等人在 NeurIPS 上提出 Transformer 架构,彻底重塑了序列建模的格局。Transformer 完全摒弃循环结构,改用自注意力机制(Self-Attention):序列中每个位置的输出由序列中所有位置的加权组合决定,权重(注意力分数)由查询(Query)与键(Key)的相似度计算得到:

$$\text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{Softmax}\!\left(\frac{\mathbf{Q}\mathbf{K}^T}{\sqrt{d_k}}\right)\mathbf{V}$$

其中 \(\mathbf{Q} = \mathbf{X}\mathbf{W}_Q\),\(\mathbf{K} = \mathbf{X}\mathbf{W}_K\),\(\mathbf{V} = \mathbf{X}\mathbf{W}_V\),\(\sqrt{d_k}\) 用于数值稳定性缩放。自注意力的核心优势在于:序列中任意两个位置之间的依赖可以用一步计算捕捉,而 LSTM 中相距 T 步的信息需经过 T 次循环才能相互"接触"——这使 Transformer 对长程依赖的建模效率远超 LSTM,且天然支持 GPU 的并行计算。Transformer 在蛋白质序列建模(AlphaFold 的 Evoformer 模块)、材料晶体结构预测(Crystal Transformer)、结构健康监测时序异常检测等领域正在产生重要影响。


import torch
import torch.nn as nn

# ── LSTM: fatigue crack growth rate prediction from load history ──
class CrackGrowthLSTM(nn.Module):
    """
    LSTM-based surrogate for fatigue crack growth rate prediction.
    Input : load history sequence  [B, T, input_size]
    Output: crack growth rate da/dN [B, 1]
    """
    def __init__(self, input_size=3, hidden_size=64, num_layers=2, dropout=0.2):
        super().__init__()
        self.lstm = nn.LSTM(
            input_size, hidden_size, num_layers,
            batch_first=True, dropout=dropout
        )
        self.head = nn.Linear(hidden_size, 1)

    def forward(self, x):
        out, (h_n, _) = self.lstm(x)   # out: [B, T, hidden]
        return self.head(h_n[-1])      # Use last hidden state

# ── Minimal single-head self-attention for illustration ──
class SingleHeadAttention(nn.Module):
    def __init__(self, d_model=64):
        super().__init__()
        self.d_k = d_model
        self.W_q = nn.Linear(d_model, d_model, bias=False)
        self.W_k = nn.Linear(d_model, d_model, bias=False)
        self.W_v = nn.Linear(d_model, d_model, bias=False)

    def forward(self, x):
        Q, K, V = self.W_q(x), self.W_k(x), self.W_v(x)
        scores   = (Q @ K.transpose(-2, -1)) / (self.d_k ** 0.5)
        weights  = torch.softmax(scores, dim=-1)   # Attention distribution
        return weights @ V                         # Weighted value aggregation

# Quick shape test
B, T, d = 4, 20, 3
lstm_model = CrackGrowthLSTM(input_size=d)
seq        = torch.randn(B, T, d)
print("LSTM output:", lstm_model(seq).shape)  # → [4, 1]

attn = SingleHeadAttention(d_model=64)
x64  = torch.randn(B, T, 64)
print("Attn output:", attn(x64).shape)        # → [4, 20, 64]

七、正则化与训练技巧——从过拟合到泛化

深度网络的参数量往往比训练样本量大几个数量级,理论上可以完美记住所有训练数据(包括噪声),即极度过拟合。防止过拟合、使模型真正学到泛化性规律,是深度学习实践的核心挑战。

Dropout(Srivastava et al., 2014)是最著名的正则化技术之一。训练时,每个 mini-batch 独立随机地将网络中一定比例(通常 0.2~0.5)的神经元临时"关掉"(输出置零),使网络不能依赖任何单一神经元,被迫学习冗余且鲁棒的表示。推理时,关闭 Dropout 并将权重乘以 \((1 - p)\) 的缩放因子,确保期望激活值不变。Dropout 的一个有趣解释是:训练过程相当于在指数级数量的子网络上做集成,这与随机森林的集成策略在哲学上高度一致。

数据增强(Data Augmentation)在样本量不足时是不花额外实验代价扩充数据集的利器。对于材料显微图像,若晶粒分布具有各向同性,对图像进行随机旋转(0°/90°/180°/270°)是严格合理的物理操作;若各向异性是关键特征(如纤维增强复合材料的纤维取向),旋转增强必须谨慎,只能在保持物理合理性的范围内进行。不加分辨地使用数据增强是一种容易被忽略的错误,可能引入与物理先验矛盾的训练信号。

早停(Early Stopping)是最简洁有效的防过拟合手段:用独立的验证集监控验证损失,当验证损失连续若干个 epoch 不再下降时停止训练,保存验证损失最低时的模型权重。学习率调度对最终性能有显著影响:余弦退火(Cosine Annealing)让学习率从初始值按余弦函数平滑衰减到接近零;学习率预热(Warmup)在训练初期线性从零升到目标学习率,防止随机初始化的不稳定梯度导致早期发散,是 Transformer 类模型的标准配置。

迁移学习(Transfer Learning)是在目标域数据量有限时的强力手段。将在大规模数据集(如 ImageNet)上预训练的 CNN 的特征提取部分迁移到新任务,仅微调(Fine-tune)最后几层,往往比从随机初始化开始效果更好——即使目标域(材料显微图像)与源域(自然图像)视觉内容差异巨大,低层卷积层学到的边缘/纹理检测器仍具有通用性。在实验室小样本数据集(N < 1000 张图像)上使用深度学习时,迁移学习几乎是必须采用的策略,而不是可选项。


import torch, torch.nn as nn, torch.optim as optim

# ── Regularized MLP with Dropout + LayerNorm ──
class RegularizedMLP(nn.Module):
    def __init__(self, in_dim, hidden=128, p_drop=0.3):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(in_dim, hidden), nn.LayerNorm(hidden),
            nn.ReLU(), nn.Dropout(p_drop),
            nn.Linear(hidden, hidden // 2), nn.ReLU(),
            nn.Dropout(p_drop),
            nn.Linear(hidden // 2, 1)
        )
    def forward(self, x):
        return self.net(x)

model     = RegularizedMLP(in_dim=10)
optimizer = optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100, eta_min=1e-6)

# ── Early stopping (manual) ──
best_val_loss = float('inf')
patience, no_improve, best_w = 20, 0, None

for epoch in range(300):
    model.train()
    # ... mini-batch training loop (see Section 4) ...

    model.eval()
    with torch.no_grad():
        val_loss = 0.0   # Replace with: criterion(model(X_val), y_val).item()

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        best_w        = {k: v.clone() for k, v in model.state_dict().items()}
        no_improve    = 0
    else:
        no_improve += 1
        if no_improve >= patience:
            print(f"  Early stopping at epoch {epoch}"); break

    scheduler.step()

if best_w:
    model.load_state_dict(best_w)  # Restore best-performing weights

# ── Transfer learning: fine-tune a pretrained ResNet for micrograph regression ──
import torchvision.models as tv_models

def build_transfer_model(n_outputs=1, freeze_backbone=True):
    """Load pretrained ResNet18, replace classifier head."""
    backbone = tv_models.resnet18(weights='IMAGENET1K_V1')
    if freeze_backbone:
        for param in backbone.parameters():
            param.requires_grad = False          # Freeze pretrained weights
    # Replace final FC layer: 512 → n_outputs
    backbone.fc = nn.Sequential(
        nn.Linear(512, 128), nn.ReLU(),
        nn.Dropout(0.3),
        nn.Linear(128, n_outputs)
    )
    return backbone

transfer_model = build_transfer_model(n_outputs=1)
trainable      = sum(p.numel() for p in transfer_model.parameters() if p.requires_grad)
total          = sum(p.numel() for p in transfer_model.parameters())
print(f"Trainable / Total params: {trainable:,} / {total:,}")
# Trainable << Total: only the new head is updated — data-efficient fine-tuning

八、深度学习的适用边界——与传统机器学习的关系

在学完深度学习的各个技术细节之后,最重要的一个问题其实应该在最开始就被问出来:这道题真的需要深度学习吗? 在科研环境中,盲目使用深度网络——因为它"先进"、因为论文需要"方法足够复杂"——是对计算资源、时间和科学严谨性的双重浪费。下面从几个核心维度给出清晰的决策框架。

输入数据的结构是第一判断维度。若输入是表格型数据(若干列特征,每个特征有明确物理含义),样本量在数百到数千,梯度提升(XGBoost/LightGBM)几乎总是比深度网络表现更好,且调参更容易、训练更快、可解释性更强。Nature Materials、Acta Materialia 上大量高影响力材料基因组学工作用的正是随机森林或梯度提升,而非深度网络。只有当输入是高维非结构化数据(显微图像、蛋白质序列、有限元位移场快照),或数据量达到数万以上时,深度学习的优势才真正显现。

样本量是第二判断维度。深度学习模型参数量动辄百万,需要大量数据才能可靠估计。对于典型材料科学实验——每个样本的制备需要数天到数周,数据集规模往往 N < 500——深度学习面临严峻的数据效率问题;此时物理启发的参数化模型或带正则化的线性/非线性模型几乎总是更合理的选择。迁移学习和数据增强可以一定程度缓解数据稀缺问题,但不是万能药,且需要目标域与源域之间存在可迁移的表示。

可解释性要求是第三判断维度。若研究目标是理解"哪些材料成分组合控制断裂韧性"(特征归因分析),Lasso 或随机森林+SHAP 提供的解释比深度网络的 SHAP/GradCAM 更清晰、更稳定,且不依赖近似方法。若研究目标是精准预测而解释性不是首要需求(如用网络替代昂贵的有限元求解器做快速结构拓扑优化,计算效率提升数百倍),深度网络的黑盒性可以接受。

计算约束是第四判断维度。没有 GPU 资源时,在 CPU 上训练一个中等规模的 CNN 需要数小时甚至数天,而同等任务的梯度提升在 CPU 上数分钟内完成。在无法稳定获取 GPU 计算资源的实验室环境中,深度学习的实用性大打折扣。在算力有限的情境下,选择计算高效的传统机器学习方法,然后系统进行特征工程,往往能以极低的代价达到足够好的预测性能。

外推能力是第五判断维度,在材料设计和逆向工程中尤为关键。深度网络(包括 CNN、LSTM)和树模型一样,在训练集覆盖范围之外的外推是根本不可靠的——它们是数据驱动的插值器,而非物理定律的泛化模型。在需要预测训练集之外的材料性能(如极端温度、未见成分体系)时,将物理约束显式嵌入网络(PINN)或使用不确定性感知的贝叶斯方法,是比单纯增大网络规模更正确的方向。

将上述判断标准总结为决策参考表:

场景数据类型样本量推荐方向
------------
材料成分→性能映射表格< 500岭 / Lasso / 弹性网
多参数工艺优化表格500~10,000梯度提升(XGBoost/LGB)
显微图像→性能图像> 1,000CNN(小样本配合迁移学习)
疲劳载荷时序→寿命时间序列> 500LSTM / Transformer
分子/晶体性质预测图结构任意图神经网络(GNN)
PDE 求解/力学场预测坐标+边界无标签Physics-Informed NN
需高度解释,小样本任意< 200线性模型 + SHAP

深度学习与传统机器学习不是竞争对手,而是互补的工具库。最优秀的计算科学家的标志不是"我只用深度学习"或"我只用传统 ML",而是能够根据问题特性、数据规模、解释需求和计算约束做出有根据的方法选择。在材料科学、结构力学和生物力学领域,理解"什么时候不用深度学习"往往比知道如何搭建一个 Transformer 更有价值。这种判断力本身,才是超越任何单一算法的核心竞争力。


参考文献

  1. LeCun, Y., Bengio, Y., & Hinton, G. (2015). Deep learning. Nature, 521(7553), 436–444. https://doi.org/10.1038/nature14539
  2. Rumelhart, D. E., Hinton, G. E., & Williams, R. J. (1986). Learning representations by back-propagating errors. Nature, 323(6088), 533–536. https://doi.org/10.1038/323533a0
  3. Hochreiter, S., & Schmidhuber, J. (1997). Long short-term memory. Neural Computation, 9(8), 1735–1780. https://doi.org/10.1162/neco.1997.9.8.1735
  4. Vaswani, A., Shazeer, N., Parmar, N., et al. (2017). Attention is all you need. Advances in Neural Information Processing Systems, 30, 5998–6008.
  5. He, K., Zhang, X., Ren, S., & Sun, J. (2016). Deep residual learning for image recognition. Proceedings of the IEEE CVPR, 770–778. https://doi.org/10.1109/CVPR.2016.90
  6. Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: A simple way to prevent neural networks from overfitting. Journal of Machine Learning Research, 15(1), 1929–1958.
  7. Raissi, M., Perdikaris, P., & Karniadakis, G. E. (2019). Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear PDEs. Journal of Computational Physics, 378, 686–707. https://doi.org/10.1016/j.jcp.2018.10.045
  8. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press. https://www.deeplearningbook.org/

💬 Questions or Feedback?

This blog is actively maintained by a PhD researcher. Reach out on GitHub for collaborations or corrections.

View on GitHub

Comments