拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深度学习模型选型指南:从DNN到CNN、RNN、LSTM、GCN、GAN

深度学习模型选型指南:从DNN到CNN、RNN、LSTM、GCN、GAN

做深度学习这些年,我把DNN、CNN、RNN、LSTM、GCN、GAN反复用在不同项目里,最深的感受是:模型本身不难,难的是知道什么时候该用哪一种,以及为什么用。很多人学完一轮以后,脑子里像存了一堆散装零件:卷积核、门控单元、图拉普拉斯、对抗损失,每个都听过,但真到项目里就不知道从哪下手。这篇总结想干的事很直接,把深度学习中几个最常见、最常被问到的模型串成一条线,从全连接网络讲到卷积网络,再讲到循环网络、长短期记忆网络、图卷积网络和生成对抗网络。它适合刚入门、正在做课程项目、准备面试八股、或者想复盘自己技术栈的人。你可以把它当成一份个人笔记,也可以当成一张选型地图。我不会只写公式,更多会写我踩过的坑、参数怎么定、训练为什么不稳、以及哪些地方最容易想当然。

1. 为什么把这些模型放在一张图里:从DNN到GCN/GAN的演进逻辑

1.1 先把DNN当底座:全连接到底在学什么

DNN,也就是深度神经网络,很多时候被当成最普通的基线。它的核心结构是全连接层:每个输入神经元和下一层每个神经元都相连。一层的前向传播可以写成z = Wx + b,然后接一个非线性激活函数,比如 ReLU、Sigmoid、Tanh。没有激活函数,再多层全连接叠起来也只是线性变换,深度就失去意义。反向传播则是链式法则的应用:损失函数对每一层参数的梯度,从后往前一层层算回去,再用梯度下降更新。

DNN最容易被低估的地方是它并不“笨”。在表格数据、特征工程已经做得不错的场景里,DNN往往能给出很强的基线。比如用户流失预测、房价回归、简单分类任务,把类别特征做Embedding、数值特征做标准化,几层全连接就能跑出不错的结果。但它的问题也很明显:参数量大、对输入顺序和空间结构不敏感、容易过拟合。图像里相邻像素的关系、文本里词序的关系、图结构里节点连接的关系,DNN默认全都不关心。这就是为什么后面会出现CNN、RNN、GCN。

我个人的经验是,DNN应该作为第一个基线,而不是最后一个方案。先跑通数据管道、损失函数、评估指标,再换更复杂的结构。很多项目失败不是因为模型不够深,而是因为数据泄漏、标签噪声、划分不合理。DNN足够简单,能帮你快速暴露这些问题。

1.2 结构归纳偏置决定模型:CNN、RNN、GCN的路线差异

机器学习里有个词叫归纳偏置,意思是模型对数据做了什么先验假设。CNN假设图像具有局部相关性和平移不变性,所以用局部卷积核和权值共享。RNN假设序列前后有依赖关系,所以用循环连接把历史信息传下去。LSTM是RNN的改进版,用门控机制缓解长依赖中的梯度消失。GCN假设数据存在于图结构中,节点特征和邻居特征应该互相影响,所以用邻接矩阵做消息传递。GAN则换了一个思路:不直接建模数据分布,而是让生成器和判别器互相博弈。

这些模型不是互相替代的关系,而是对应不同数据结构。你把图像拉平喂给DNN,理论上也能训练,但参数量会爆炸,而且丢掉了空间位置。你把长序列截断成固定窗口喂给DNN,短期预测也许能凑合,但一旦依赖跨过窗口边界就失效。你把图数据强行排成序列,邻居顺序就变成了人为设定,模型学到的关系可能完全错位。选型时先问数据长什么样,再问任务要输出什么,最后才问用哪个模型。

1.3 GAN是另一条线:从判别式到生成式

前面提到的DNN、CNN、RNN、LSTM、GCN,大多数时候做的是判别任务:输入一个样本,输出类别、数值或节点标签。GAN属于生成式模型,目标是学会生成逼真样本。它的训练过程像造假者和鉴定师的对抗:生成器尽量骗过判别器,判别器尽量区分真假。最终理想状态下,生成器能拟合真实数据分布,判别器无法区分。

GAN的难点不在结构,而在训练动态。判别器太强,生成器梯度消失;生成器太强,判别器学不到有效边界。很多新手第一次跑GAN,损失曲线看起来正常,但生成结果全是噪声。后面我会专门拆原始GAN公式、交叉熵负号问题、以及训练不稳定的排查清单。

2. DNN与CNN:把图像和表格数据吃透的关键细节

2.1 DNN的前向传播、反向传播与参数量计算

假设输入维度是784,第一层隐藏层512,第二层256,输出10类。第一层参数量是784*512 + 512 = 401920,第二层是512*256 + 256 = 131328,输出层是256*10 + 10 = 2570。总共五十多万参数。如果输入换成256乘256的彩色图像,拉平后是256*256*3 = 196608维,第一层如果还是512个神经元,仅这一层就有约一亿参数。参数量大不仅训练慢,还特别容易过拟合。

反向传播时,每一层的梯度依赖后一层的误差项。以ReLU为例,负半轴梯度为0,如果某层很多神经元输出为负,梯度就传不回去,出现“神经元死亡”。学习率太大、初始化太差、BatchNorm位置不对,都会加剧这个问题。我的习惯是先用较小学习率配Adam跑几百步,观察损失是否稳定下降,再决定是否调大。

DNN调参有几个实用顺序:先看数据标准化,再看初始化,再看学习率和BatchNorm,最后才动层数和宽度。很多新手一上来堆很深,结果训练集都拟合不了。如果训练集损失不降,优先怀疑学习率、标签处理、损失函数和梯度流动,而不是模型深度。

2.2 图像处理为啥用CNN不用前馈神经网络

这个问题几乎每次面试都会出现。核心原因有三个。第一,参数共享。一个3乘3卷积核在整张图上滑动,参数量只有9个权重加1个偏置,而全连接层每个像素位置都有独立权重。第二,局部连接。图像里边缘、角点、纹理都是局部模式,卷积核只看局部区域,符合视觉规律。第三,平移等变性。同一个物体出现在左上角还是右下角,卷积核都能检测到,全连接网络需要分别学习所有位置。

更准确地说,CNN并不是完全平移不变,而是通过卷积和池化获得一定的平移鲁棒性。池化层会丢弃部分位置信息,保留显著特征。深层卷积网络从浅层的边缘、颜色,到中层的纹理、部件,再到深层的物体语义,形成层级特征。这种层级抽象是前馈神经网络很难高效学到的。

还有一个工程原因:全连接网络对输入尺寸敏感。图像稍微裁剪或缩放,输入维度就变了,模型结构要改。CNN可以接受不同尺寸输入,只要最后用全局池化或自适应池化处理。这在真实项目里非常关键。

2.3 卷积核、池化、感受野和通道数怎么设计

卷积输出尺寸公式是(W - F + 2P) / S + 1,其中W是输入尺寸,F是卷积核大小,P是填充,S是步长。比如输入32,卷积核3,填充1,步长1,输出还是32。如果步长2,输出就是16。很多人写网络时通道数翻倍、尺寸减半,就是靠步长2或池化实现。

感受野是输出特征图上一个点对应输入图像的区域大小。两层3乘3卷积的感受野相当于一层5乘5,但参数更少、非线性更多。所以现代网络喜欢堆小卷积核。通道数从32、64、128、256往上加,但不要盲目翻倍。小数据集上通道数太大,训练集准确率很高,验证集很差。

池化层常用最大池化和平均池化。最大池化保留显著特征,适合分类;平均池化更平滑,适合某些分割或回归任务。全局平均池化可以直接把特征图变成向量,减少全连接层参数。我的经验是:分类任务最后用全局平均池化,比直接展平接大全连接更不容易过拟合。

2.4 实操要点与避坑

第一,输入归一化。图像一般除以255再减均值除标准差,不要直接把0到255丢进去。第二,数据增强要匹配任务。分类任务可以用随机裁剪、翻转、颜色抖动,但医学图像、工业缺陷检测不能随便翻转,因为方向可能有物理意义。第三,BatchNorm在卷积层后、激活函数前还是后,不同框架和网络有差异,但推理时要记得切换评估模式。第四,学习率不要照抄。别人用0.1是因为有学习率预热和衰减,你直接0.1可能直接发散。

常见坑是验证集准确率比训练集高。这通常不是模型太好,而是训练时做了强增强、验证时没有,或者训练时Dropout开启、验证时关闭。另一种情况是数据划分时同一类样本泄漏到训练和验证。遇到这种“反常好结果”,先查数据管道。

3. RNN与LSTM:序列建模的坑与时间序列预测实操

3.1 RNN的循环结构与梯度消失问题

RNN的公式可以简写为h_t = tanh(W_x x_t + W_h h_{t-1} + b),输出可以是每个时间步的h_t,也可以是最后一个时间步。它把历史信息压缩到隐藏状态里,理论上能建模任意长依赖。但标准RNN训练时用BPTT,也就是沿时间反向传播。梯度要连乘很多个雅可比矩阵,如果特征值小于1,梯度指数衰减;大于1,梯度爆炸。

梯度消失的表现是:模型只能记住最近几个时间步,长距离依赖学不到。梯度爆炸的表现是:损失变成NaN,参数飞掉。解决办法包括梯度裁剪、使用ReLU、换LSTM或GRU。但梯度裁剪只解决爆炸,不解决消失。真正让长依赖可学的,是LSTM的门控结构。

我试过在简单正弦序列上对比RNN和LSTM。序列长度20以内,两者差不多;长度到50以上,RNN预测开始滞后,LSTM还能跟上。这不是绝对规律,但能直观感受到门控的价值。

3.2 LSTM门控机制:遗忘门、输入门、输出门

LSTM的关键是细胞状态C_t和三个门。遗忘门决定旧信息丢多少:f_t = sigmoid(W_f [h_{t-1}, x_t] + b_f)。输入门决定新信息写多少:i_t = sigmoid(W_i [h_{t-1}, x_t] + b_i),候选状态C_tilde = tanh(W_c [h_{t-1}, x_t] + b_c)。然后更新细胞状态:C_t = f_t * C_{t-1} + i_t * C_tilde。输出门决定暴露多少:o_t = sigmoid(W_o [h_{t-1}, x_t] + b_o),h_t = o_t * tanh(C_t)。

这套结构让梯度可以沿细胞状态更稳定地流动。遗忘门接近1时,信息长期保留;接近0时,快速遗忘。很多人背公式但不会用。实际调参时,LSTM层数一般1到3层,隐藏维度64到512。层数太多不仅慢,还容易过拟合。双向LSTM适合文本分类,因为可以同时看左右上下文;但时间序列预测不能用双向,因为未来信息不能提前知道。

3.3 用LSTM做时间序列预测的完整流程

第一步,数据清洗。缺失值插值、异常值处理、重采样。第二步,归一化。用训练集统计量做MinMax或Z-score,不要用全量数据,否则验证集信息泄漏。第三步,构造滑动窗口。比如用过去30天预测第31天,窗口步长1。输入形状是(样本数, 时间步, 特征数)。第四步,划分训练、验证、测试。时间序列不能随机打乱,要按时间切分。第五步,搭模型。通常一层或两层LSTM,接Dropout,再接全连接输出。第六步,损失函数用MSE或MAE。第七步,训练时用早停,监控验证损失。第八步,预测后反归一化。

代码骨架大概这样:

import torch import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_size, hidden_size=64, num_layers=2, output_size=1): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=0.2 ) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): out, _ = self.lstm(x) last = out[:, -1, :] return self.fc(last)

训练时注意:Batch size不要太大,时间序列样本相关性高,太大容易陷入局部最优。学习率1e-3配Adam是常见起点。如果验证损失震荡,试试学习率衰减。如果预测曲线整体平移,检查归一化是否反错了。如果预测总是滞后,说明模型偏向复制上一时刻,可以加入差分特征或改用Seq2Seq。

3.4 文本情感分析中的LSTM注意事项

文本情感分析里,LSTM常和Embedding层搭配。流程是:分词、建词表、转ID、Padding、Embedding、LSTM、池化、分类。这里最大的坑是Padding的mask。如果直接对LSTM输出做平均池化,短句补的0也会被算进去,导致结果偏移。正确做法是用mask把Padding位置排除,或者用最后一个非Padding时间步的输出。另一个坑是词表太小导致大量OOV,太大导致Embedding矩阵过参数化。中文任务里,字级别和词级别都值得试,字级别对分词错误更鲁棒。

双向LSTM在情感分析里通常比单向好,因为情感词可能出现在句子后半段。但要注意,双向LSTM的输出维度是隐藏维度的两倍,后面接全连接时要对应。如果数据量小,先冻结Embedding,只训练LSTM和分类头,效果往往更稳。

4. GCN:图数据上的深度模型怎么理解

4.1 图、邻接矩阵、度矩阵与拉普拉斯

图由节点和边组成,记为G = (V, E)。邻接矩阵A描述节点之间是否相连,A_ij = 1表示节点i和j有边。度矩阵D是对角矩阵,D_ii等于节点i的度数。拉普拉斯矩阵L = D - A,归一化拉普拉斯是L_sym = I - D^{-1/2} A D^{-1/2}。这些符号第一次看很吓人,但本质是描述节点之间如何互相影响。

GCN的核心思想是消息传递:每个节点收集邻居节点的特征,做加权平均,再经过线性变换和非线性激活。这样一层之后,节点就融合了邻居信息。两层之后,融合了邻居的邻居。层数越多,感受野越大,但也越容易把不同社区的信息混在一起,出现过平滑。

图数据在现实里很常见:社交网络、引用网络、分子结构、推荐系统中的用户物品关系。GCN适合节点分类、链接预测、图分类。如果图特别大,全图训练不现实,要用邻居采样,比如GraphSAGE或Cluster-GCN。

4.2 GCN逐层传播公式与过平滑问题

经典GCN的逐层传播公式是:

H^{(l+1)} = sigma( D_hat^{-1/2} A_hat D_hat^{-1/2} H^{(l)} W^{(l)} )

其中A_hat = A + I,加I是为了让节点也保留自身信息。D_hat是A_hat的度矩阵。H^{(l)}是第l层节点特征,W^{(l)}是可学习权重。这个公式看起来简单,但调起来有几个关键点。第一,层数通常2到3层。太多层会让所有节点特征趋同,分类性能反而下降。第二,特征归一化很重要。第三,邻接矩阵要处理孤立节点和自环。

过平滑是GCN的经典问题。可以加残差连接,保留原始特征;也可以使用JKNet,把不同层输出拼接;还可以用DropEdge,训练时随机删边,减少过拟合。我的经验是,小图任务上2层GCN加Dropout和权重衰减已经很强,不要一上来堆深。

4.3 节点分类实操与调参

节点分类的流程:准备图数据,划分训练、验证、测试节点;构造归一化邻接矩阵;定义两层GCN;训练时只计算训练节点损失;评估验证节点,早停;最后测试。损失函数用交叉熵。优化器用Adam,学习率0.01左右,权重衰减5e-4。隐藏维度16到128。Dropout 0.5。

常见问题:第一,验证集准确率波动大,因为节点划分不同影响大,要多跑几个随机种子。第二,类别不平衡,可以用加权交叉熵。第三,特征维度太高,先做PCA或特征选择。第四,图太大,显存爆了,就用邻居采样。第五,如果GCN效果不如MLP,检查邻接矩阵是否真的有用,有些数据集节点特征本身已经很强,图结构贡献有限。

5. GAN:生成模型的核心公式与训练技巧

5.1 原始GAN的极小极大博弈与交叉熵负号问题

原始GAN的目标函数是:

min_G max_D V(D, G) = E_{x~p_data}[log D(x)] + E_{z~p_z}[log(1 - D(G(z)))]

判别器D要最大化这个值,生成器G要最小化这个值。很多人第一次看会问:交叉熵公式不是有负号吗,为什么这里没有?原因在于优化方向不同。二分类交叉熵通常写成L = -[y log p + (1-y) log(1-p)],训练时最小化L。对于判别器,真实样本标签y=1,希望D(x)越大越好,所以-log D(x)越小越好;生成样本标签y=0,希望D(G(z))越小越好,所以-log(1-D(G(z)))越小越好。把这两项合起来,判别器的损失是-[log D(x) + log(1-D(G(z)))]。原始公式写的是最大化log D(x) + log(1-D(G(z))),等价于最小化带负号的交叉熵。所以不是没有负号,而是负号被并入了“最大化”这个方向。

实际训练生成器时,常用非饱和损失min -log D(G(z)),而不是min log(1-D(G(z)))。因为训练初期D很容易把假样本判成0,log(1-D(G(z)))梯度接近0,生成器学不动。换成-log D(G(z))梯度更健康。

5.2 生成器与判别器结构设计

生成器输入随机噪声z,通常从正态分布采样,维度64到128。然后通过全连接或转置卷积逐步放大。DCGAN的经典结构是:全连接投影到4x4x1024,然后转置卷积到8x8、16x16、32x32,最后输出64x64。判别器反过来,用卷积层下采样,最后输出一个概率。注意判别器不要用MaxPool,容易丢失信息,用步长卷积代替。激活函数生成器中间用ReLU,最后一层用Tanh;判别器用LeakyReLU,斜率0.2。

条件GAN会把标签或属性拼接到噪声和判别器输入里。比如生成指定数字的手写体,就把类别Embedding后和z拼接。这样能控制生成类别。项目里如果只是生成随机样本,普通GAN就够;如果需要可控生成,条件GAN更实用。

5.3 训练不稳定的排查清单

GAN训练不稳定是常态。我的排查顺序是:第一,检查数据归一化。生成器最后一层Tanh输出-1到1,真实图像也要归一化到-1到1。第二,判别器不要训练太强。可以降低判别器学习率,或者每训练一次判别器就训练一次生成器,甚至训练两次生成器。第三,用标签平滑。真实样本标签用0.9而不是1.0,减少判别器过度自信。第四,使用Adam,beta1设0.5,学习率2e-4,这是DCGAN的经验配置。第五,加入谱归一化或梯度惩罚。第六,监控判别器输出。如果D(x)很快接近1,D(G(z))接近0,说明判别器太强,生成器梯度消失。

还有一个常见坑:把生成器和判别器的损失曲线当成绝对指标。GAN的损失没有明确收敛值,生成质量要看样本。最好每隔几个epoch保存生成样本,肉眼观察。如果生成样本多样性差,可能是模式崩溃,可以增加噪声维度、使用小批量判别、或改用WGAN-GP。

5.4 从DCGAN到条件GAN的扩展

DCGAN给了一套稳定的卷积GAN结构规范。WGAN用Wasserstein距离替代JS散度,理论上缓解梯度消失,但需要权重裁剪或梯度惩罚。StyleGAN在生成高质量人脸方面很强,但结构复杂,不适合入门。条件GAN适合带标签的数据集。CycleGAN适合无配对图像转换。选择时先明确任务:无条件生成、条件生成、图像翻译、还是超分辨率。不同任务对应不同损失和结构,不要拿一个GAN套所有场景。

我实际做小规模生成任务时,通常先用DCGAN跑通,再考虑加条件。如果生成64乘64图像,Batch size 64,学习率2e-4,训练几十个epoch就能看到轮廓。如果生成256乘256以上,需要更多显存和更精细的架构,入门阶段不建议直接挑战。

6. 个人复盘:模型选型、训练流程与常见问题速查

6.1 选型决策表:什么数据用什么模型

数据类型推荐模型原因注意事项
表格特征DNN、树模型特征独立,全连接易用标准化、类别Embedding
图像CNN局部相关、平移等变数据增强、归一化
文本序列RNN、LSTM、Transformer顺序依赖Padding mask、词表
时间序列LSTM、TCN、Transformer长短期依赖按时间划分、反归一化
图结构GCN、GraphSAGE邻居消息传递层数2到3层、过平滑
生成任务GAN、VAE、扩散模型学习数据分布训练不稳定、看样本

选型不是非此即彼。图像加文本可以用CNN加LSTM,图加序列可以用时空图网络。关键是先明确数据结构,再明确输出形式。

6.2 训练流程与epoch、batch、学习率经验

我通常按这个顺序:数据划分、基线模型、过拟合小样本、再扩大。先拿几十个样本训练,确保模型能过拟合,说明网络和损失没问题。然后上全量数据,加正则化。Batch size从32或64开始,显存不够就减。学习率分类任务常用1e-3到1e-4,生成任务2e-4,GCN 0.01。Epoch不是越多越好,早停看验证损失。

学习率调度方面,ReduceLROnPlateau适合验证损失停滞时降学习率,CosineAnnealing适合长训练。warmup对Transformer和大Batch很重要。梯度裁剪对RNN和GAN很有用,阈值通常1.0或5.0。

6.3 常见问题速查表

现象可能原因排查动作
训练损失不降学习率、标签、损失函数小样本过拟合、检查标签
验证损失上升过拟合加Dropout、权重衰减、早停
损失NaN梯度爆炸、学习率太大梯度裁剪、降学习率
CNN验证准确率异常高数据泄漏、增强不一致检查划分和预处理
LSTM预测滞后模型复制上一时刻加差分特征、Seq2Seq
GCN效果差图结构无用、过平滑减少层数、残差连接
GAN生成噪声判别器太强调学习率、标签平滑
GAN模式崩溃生成器多样性差加噪声、WGAN-GP

6.4 环境与工具链的取舍

深度学习环境配置是很多人的第一道坎。Python、PyTorch或TensorFlow、CUDA、cuDNN,版本匹配很关键。新手建议用Anaconda建虚拟环境,按官方命令安装。如果只是做课程作业,Google Colab或国内云平台也能快速起步。MATLAB的Deep Learning Toolbox适合教学和快速验证,但自定义模型和部署生态不如PyTorch灵活。HALCON这类机器视觉工具在工业检测里很强,但深度学习模块通常和传统视觉算子配合使用,学习路径不同。

硬件方面,消费级显卡、专业卡、国产加速卡都要看框架适配和算子覆盖。不要只看算力参数,要看你要用的模型有没有优化。显存不够时,梯度累积可以模拟大Batch,混合精度可以省显存。数据管道用DataLoader多进程加载,别让GPU等CPU。

我个人在后续项目里越来越重视两件事:一是把数据管道和评估做扎实,二是给每个模型准备一个最简单的基线。DNN、CNN、RNN、LSTM、GCN、GAN这些名字背后,真正决定结果的往往不是模型有多新,而是你有没有理解数据、有没有控制变量、有没有耐心看样本。很多时候,一个调好的小模型比一个没调好的大模型更值得信任。

返回列表