ARTICLE DETAIL

资讯详情

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

深度学习基础术语入门:沿数据流理解张量、梯度与epoch

深度学习基础术语入门:沿数据流理解张量、梯度与epoch 带过几届学生、也帮不少转行的朋友做过入门辅导之后我发现深度学习基础这块内容最大的坑不是难而是术语密度太高。任意一本教材翻到前三章就会往外扔几十个词张量、梯度、反向传播、epoch、batch、过拟合、泛化误差、卷积核、感受野……每个词单独看都认识连起来读就像被塞了一嘴棉花。我自己刚开始学的时候也走过弯路把术语表工工整整抄了三遍结果写第一版训练代码时还是分不清 iteration 和 epoch把 loss 打印出来也不知道它到底在往哪个方向走。这篇是我自己攒的一份术语整理不打算按字母表把所有词条铺一遍那样既不实用也记不住。我选择用一条主线把它们串起来数据怎么流进网络、网络怎么算出结果、参数怎么被更新、结果怎么被评价、工程上怎么落地。这四个问题回答清楚了八成的术语会自动归位。内容适合两类人一是刚接触深度学习、能把别人的代码跑通但说不清里面在干什么的朋友二是已经上手一段时间、想把这些零散概念重新对齐一遍的从业者。全文不依赖任何特定框架的版本细节示例代码用 Python 和 PyTorch 写理解逻辑之后换到别的框架是一样的。1. 术语不该按字母表背而要挂在一条数据主线上1.1 为什么术语表背得越熟写代码时越懵术语解释通常会给你一个抽象定义比如张量是多维数组。这句话没错但它没有告诉你张量在代码里长什么样、为什么会报形状不匹配的错。我见过太多这样的情况定义倒背如流一上手就卡在RuntimeError: mat1 and mat2 shapes cannot be multiplied上。原因在于术语本身是某个动作的名字而不是一个孤立的知识点。梯度是参数该往哪改的名字epoch 是整个数据集走几遍的名字反向传播是怎么把误差分摊回每一层的名字。你把动作和名字对上了术语自然就记住了只背名字就像背菜谱却从没进过厨房。所以我更喜欢用一条数据流把它们重新排一遍输入数据以某种形状进来经过若干层做加权求和和非线性变换输出一个预测值预测值和真实标签比一比算出损失损失对每个参数求导得到梯度优化器拿着梯度去改参数。这个循环重复很多次。所有术语都是这条链上某个环节的标签。1.2 把术语分成五类一次理清归属我自己的笔记里是这么分的你抄过去直接能用类别它回答的问题典型术语数据类东西长什么样、怎么批量送进去张量、形状、维度、批大小、通道、数据集划分结构类网络由什么堆起来全连接层、卷积层、池化层、激活函数、参数量、深度训练类参数怎么被改损失函数、梯度、反向传播、优化器、学习率、epoch、iteration评估类学得好还是学坏了训练误差、泛化误差、过拟合、欠拟合、验证集、准确率、F1工程类怎么跑得起来、跑得快算力设备、框架、显存、检查点、混合精度、分布式提示如果你是零基础优先把数据类和训练类吃透这两块占了调试时间的八成以上。结构类可以随着你换模型慢慢补。1.3 数学符号和代码变量是同一件事的两种写法学的时候最容易割裂的地方是教材用数学符号写代码用变量名写两边对不上。这里给一份粗糙但好用的对照能省掉你很多来回猜的时间数学里的 $x$在代码里通常是inputs形状(batch, features)数学里的 $W$ 和 $b$在代码里是nn.Linear内部维护的权重和偏置数学里的 $\hat{y}$代码里是outputs或logits数学里的 $L$ 或 $J$代码里是loss数学里的 $\partial L / \partial W$代码里是你根本不用手写、框架自动帮你算的那个东西理解到符号只是代号这一层看论文和看源码就不再是两件事了。2. 张量、形状和维度所有术语的物理载体2.1 从标量到张量只是轴的个数在变标量是一个数比如3.14零维向量是一串数比如[1, 2, 3]一维矩阵是数的表格二维再往上三维、四维、五维的数组统称张量。你不需要把张量想得多玄它就是一个可以带任意多个轴的数组容器只不过在深度学习里它比普通的 numpy 数组多了两个能力能被放到算力设备上并行计算能自动求导。轴axis这个词很关键中文里也叫维度dim但要注意维度有两套用法。说这是一个 784 维的向量指的是单个样本有多少个特征说这个张量是 4 维的指的是它有几个轴。这两个说法在中文语境里经常打架读文献时务必看上下文。我一般会在笔记里明确写成轴数和特征数避免自己把自己绕进去。2.2 形状是深度学习的语法错了就全错以图像分类为例一个最常见的输入张量形状是(N, C, H, W)N是批大小batch size一次送进去多少张图C是通道数灰度图是 1彩色图是 3H和W是高和宽这四个轴的位置在不同框架里有差异。PyTorch 习惯(N, C, H, W)TensorFlow 在早期版本里习惯(N, H, W, C)。这个差异是新手调库时最常见的翻车点之一模型在 PyTorch 上跑得好好的搬到另一套代码里就报形状不匹配往往只是通道轴的位置换了。我自己的习惯是每写一层网络就在注释里标一次形状变化。比如import torch import torch.nn as nn x torch.randn(32, 3, 224, 224) # (N, C, H, W) conv nn.Conv2d(3, 64, kernel_size3, padding1) x conv(x) # (32, 64, 224, 224) pool nn.MaxPool2d(2) x pool(x) # (32, 64, 112, 112) x x.flatten(1) # (32, 64*112*112) fc nn.Linear(64 * 112 * 112, 10) x fc(x) # (32, 10)这几行注释看起来啰嗦但在你排查问题时价值极高。报错信息里的形状是唯一不会骗你的线索能读懂它你就能自己修八成的结构错误。2.3 广播、拼接和打平三个高频小概念**打平flatten**是把多维张量摊成一维特征向量通常发生在卷积部分接全连接部分的交界处。上面代码里的x.flatten(1)就是从第 1 个轴开始摊平保留第 0 个轴的批维度。**广播broadcasting**是指形状不同的张量在运算时框架自动把小的一方扩展到能对齐的形状。比如形状(32, 10)除以形状(10,)框架会把后者当成(1, 10)再复制 32 份。广播很方便但也是隐藏 bug 的温床。我曾经在一个自定义损失里写错了一个轴的顺序广播没有报错训练照样跑只是模型永远学不好排查了两天才发现问题在形状上。所以广播能跑通不等于逻辑正确这一点要格外小心。**拼接concat和堆叠stack**的区别常被忽略拼接是把两个张量沿某个已有轴接长总轴数不变堆叠是新增一个轴。在特征融合类的模型里搞混这两个形状会瞬间乱掉。记住一句话拼接改变长度堆叠改变轴数。3. 从逻辑回归到全连接层模型内部到底在算什么3.1 一切从加权求和加偏置开始哪怕是最简单的逻辑回归它的核心也只有一个式子把输入特征各自乘上一个权重加起来再补一个偏置项。用文字写就是加权求和用代码写就是nn.Linear(in_features, out_features)。所谓全连接层Fully Connected Layer也叫线性层、稠密层就是一堆这样的式子并排摆开。这里的术语有三个要分清**权重weight是乘在特征上的系数偏置bias是不乘任何特征的常数项两者合起来叫参数parameter。模型学的过程数学上就是不停调整这些参数而超参数hyperparameter**是你在训练前自己定好的、模型不会去学的量比如学习率、批大小、层数。这个区分非常重要很多初学者调参时不知道该动哪个就是没分清这两类。参数量可以估算。一个输入 784 维、输出 128 维的全连接层参数量是 $784 \times 128 128 100{,}480$其中 128 是偏置。看到这个数字你就明白为什么早期模型动辄几百万参数也明白为什么显存会成为瓶颈。3.2 激活函数没有它堆多少层都等于一层如果网络里只有线性层无论堆多少层整体依然是个线性变换表达能力不会比单层强。所以每层线性变换之后要接一个激活函数把非线性塞进去。这就是术语里常说的非线性激活。几个必须认识的名字Sigmoid把输出压到 0 到 1 之间二分类输出概率时常用。缺点是两端梯度很小层数一多容易梯度消失。Tanh压到 -1 到 1比 Sigmoid 更对称但同样有饱和问题。ReLU小于 0 变 0大于 0 原样输出。计算极快长期是卷积网络里的默认选择。缺点是负半轴梯度为 0可能出现神经元死掉。LeakyReLU / GELU / SiLU针对方程上那些缺点的改良版本Transformer 类结构里 GELU 出场率很高。这套术语背后的逻辑其实一句话激活函数决定了一个神经元要不要被激活、以多强的程度激活。你把某个激活函数的曲线画出来就明白它适合什么场景了。3.3 损失函数给模型打分的那把尺子模型输出预测值之后需要一个数字来衡量错得有多离谱这个数字就是损失loss算它的函数叫损失函数loss function有时也叫目标函数、代价函数。名字不同指的是同一件事这点先说明免得你以为是新概念。常见的有两类交叉熵Cross Entropy分类任务的主力。它衡量的是预测出来的概率分布和真实分布差多远。多分类时框架里通常用CrossEntropyLoss注意它内部已经包含了 Softmax 操作你要是自己再手动加一层 Softmax就重复了这是新手非常高频的错误。均方误差MSE回归任务的主力就是预测值和真实值差值的平方求平均。选损失函数的依据其实很朴素你的任务要输出什么类型的东西。输出属于哪一类用交叉熵输出一个连续数值用均方误差。选错了训练再久也没用。3.4 前向传播从输入算到损失的那一趟上面这一整套输入 → 线性变换 → 激活 → 再变换 → 输出 → 算损失的过程统称前向传播forward propagation。它对应代码里的forward方法。很多人不理解为什么框架要求你把网络结构写在forward里其实就是为了让框架知道数据该怎么流好在反向的时候按相反的路径把梯度传回去。4. 训练循环里的黑话梯度、反向传播、epoch 与优化器4.1 梯度就是参数往哪改、改多少的方向标想象你站在一座山上四周是雾你要下到谷底只能靠脚下的坡度判断方向。这个坡度就是梯度。损失函数是那座山的高度参数是你站的位置梯度告诉你往哪个方向迈一步能让高度下降得最快。数学上梯度是一个和参数同形状的向量每一位是对应参数求偏导的结果。代码里你几乎不会手算它但一定要理解一件事梯度是有形状的它和参数一一对应。所以你看到param.grad的形状和param一样这是正常的。4.2 反向传播误差是怎么一层层分下去的**反向传播Backpropagation**这个名字听起来很吓人本质就是链式法则的工程化实现。前向传播时每一层的计算都被记了下来反向传播时从损失出发一层一层往回算每一层参数对最终误差的责任。用生活类比一个项目组交付出错了先算出总损失然后一级一级往回追责看每个环节各占多少责任。链式法则就是追责的算法它保证了多层叠加时每一层都能拿到属于自己的那份责任。这里有两个高频术语要一起记梯度消失往回传的过程中梯度越乘越小浅层几乎收不到有效信号导致浅层学不动。梯度爆炸反过来梯度越乘越大参数一步跨太远直接飞出去。这两个词解释了为什么早期的深层网络训不动也解释了为什么后来出现了残差连接、批量归一化、ReLU 这一批技术。你把这些术语当成某个具体问题的诊断结论就不会记混了。4.3 epoch、batch、iteration三个最容易乱成一团的词这三个词必须一起记我把它们的关系整理成表术语含义关系batch size一次送进模型多少条样本你自己设定的超参数iteration处理完一个 batch、更新一次参数1 个 iteration 1 次参数更新epoch把整个训练集完整过一遍1 个 epoch 若干 iteration换算公式很简单每轮迭代次数 向上取整(训练集样本数 / batch size)举个例子训练集 60000 张图batch size 设成 64那么 60000 ÷ 64 937.5向上取整是 938 个 iteration 构成 1 个 epoch。如果训 20 个 epoch总的参数更新次数是 $938 \times 20 18{,}760$ 次。你在日志里看到的那个不断累加的数字就是 iteration。为什么 batch size 不能随便设因为它直接影响两件事一是显存占用batch 越大越吃显存二是梯度的稳定性batch 小的时候每次更新都基于少量样本梯度抖动大但泛化有时更好batch 大的时候梯度更准但容易陷进平坦的坏解。经验值是在显存允许范围内取 32 到 256 之间的 2 的幂然后配合学习率做调整——batch 翻倍时学习率往往也能适当放大一点。4.4 学习率与优化器决定每一步迈多大**学习率learning ratelr**就是下山时每步的步长。它是所有超参数里最需要认真调的一个。设得太大你会看到 loss 剧烈震荡甚至变成 NaN设得太小loss 掉得极慢你可能以为代码写错了。**优化器optimizer**是用梯度更新参数的具体算法。几代主要成员SGD最朴素的直接沿着梯度方向走。Momentum给梯度加惯性像下坡的滚球能冲过小的坑洼。Adam / AdamW为每个参数自适应调整步长是现在最常用的默认选择。RMSProp、Adagrad中间产物理解思想即可实际用得少一些。下面这段代码把前面所有术语都串了一遍值得你逐行读一次import torch import torch.nn as nn model nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10), ) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) for epoch in range(20): model.train() running_loss 0.0 for step, (x, y) in enumerate(train_loader): optimizer.zero_grad() # 清空上一轮的梯度这一步绝不能漏 logits model(x) # 前向传播 loss criterion(logits, y) loss.backward() # 反向传播算出每个参数的梯度 optimizer.step() # 更新参数 running_loss loss.item() print(fepoch {epoch}, avg loss {running_loss / len(train_loader):.4f})注意optimizer.zero_grad()这一行是新手最容易漏的。框架默认会把梯度累加而不是覆盖忘了清零梯度就会越积越大训练结果完全失真。5. 模型到底学得好不好过拟合、欠拟合与泛化误差5.1 训练误差和泛化误差不是一回事训练误差是模型在见过的数据上的错误率泛化误差是模型在没见过的新数据上的错误率。真正决定模型有没有用的是后者。而后者你永远只能估计不能精确测量因为你能拿到的数据总是有限的。这就是为什么必须有验证集和测试集。理论上泛化误差可以被各种界bound框住这类结果属于机器学习数学理论的范畴。作为工程从业者我的建议是知道有这回事、知道它告诉我们模型复杂度、样本量、泛化能力三者互相制约但不必一上手就去啃推导。先建立起训练分数高不代表模型好这个直觉比记住任何一个界的公式都重要。5.2 过拟合与欠拟合一张表分清症状训练误差验证误差典型原因处理方向欠拟合高高模型太简单、训得太少、学习率太小加层、加特征、训久一点过拟合低高模型太复杂、数据太少、训得太久正则化、加数据、早停、简化模型正常低低数据、模型、训练匹配保持继续观察判断的关键动作是把训练和验证两条曲线画在同一张图上。只看 loss 的绝对值没有意义看两条曲线的间距才有意义。间距越拉越大就是过拟合在发生。5.3 正则化给模型上枷锁的几件常用武器**正则化regularization**这个词的直译容易让人误解它不是什么高深技巧就是主动限制模型的能力逼它别把训练数据记得太死。常用手段L2 正则 / 权重衰减weight decay给大权重加惩罚让参数倾向于小值。PyTorch 的优化器里直接传weight_decay就行。Dropout训练时随机把一部分神经元输出置零逼网络不要过度依赖某几个神经元。注意预测时要关掉 Dropout代码里用model.eval()来切换。早停Early Stopping验证误差连续几轮不降就停下来是最省事也最有效的策略之一。数据增强Data Augmentation翻转、裁剪、加噪声相当于凭空造出更多样本对图像任务特别有效。批量归一化Batch Normalization稳定每层输入的分布既加速收敛也带一点正则效果。提示Dropout 和model.eval()的关系是经典的坑。训练时忘了model.train()、预测时忘了model.eval()都会导致结果异常但报错信息往往很隐晦。把这两个调用写成训练循环里的固定动作能省掉大量排查时间。5.4 数据怎么切别拿测试集调参标准的切法是训练集、验证集、测试集常见比例是 8:1:1 或者 7:1.5:1.5。验证集用来调超参数、选模型测试集只在最后用一次。如果你反复在测试集上看效果、根据它改结构那测试集实际上已经变成了验证集你报出来的数字就会偏乐观。这是学术和工程里都很常见的隐性错误值得写在笔记第一页。6. 卷积网络的术语从整张图全连到局部看6.1 为什么全连接层处理图像会崩一张 224×224 的彩色图有 $224 \times 224 \times 3 150{,}528$ 个数值。如果第一个全连接层只输出 1000 维参数量就是约 1.5 亿。这还没算后面的层。问题不只是参数多更在于全连接层把每个像素都当成独立特征丢掉了图像的空间结构——它不知道像素 (10,10) 和 (10,11) 是挨着的。卷积convolution解决了这两件事一是局部连接每个输出只看输入的一小块二是参数共享同一个卷积核在整张图上滑动复用。这两点让参数量骤降同时天然保留了空间信息。6.2 卷积核、步长、填充四个参数决定输出形状必须记牢的四个词卷积核kernel / filter那个在图上滑动的小窗口尺寸常见 3×3、5×5。步长stride窗口每次移动几格通常是 1 或 2。填充padding在图像边缘补一圈通常是 0防止输出越卷越小。通道数channels输入通道数要和上一层输出对齐输出通道数由你决定。输出尺寸的计算公式一定要会输出边长 floor((输入边长 - 卷积核边长 2 × 填充) / 步长) 1代入一个例子输入 224卷积核 3填充 1步长 1得到 $(224 - 3 2)/1 1 224$输出尺寸不变。这就是为什么3×3 卷积配填充 1几乎成了行业默认组合——它在不改变尺寸的前提下加深网络。参数量也可以算一个 3×3×3 卷积核输出 64 通道参数量是 $3 \times 3 \times 3 \times 64 64 1792$。对比前面全连接层的上亿参数差了五个数量级这就是卷积的威力所在。6.3 池化、感受野与下采样**池化pooling**是另一种缩小特征图的方式常见的是最大池化和平均池化。它没有可学参数作用是把特征图尺寸降下来、增加一点平移不变性。**感受野receptive field**指的是输出上的一个点对应到原始输入上能看到多大范围。随着层数加深感受野变大网络能从看到一条边逐步过渡到看到一只眼睛看到整张脸。这个术语特别有助于理解深层网络的工作方式浅层学局部纹理深层学语义部件。现代网络里池化有时会被步长更大的卷积取代因为后者也做下采样但保留可学习参数。这两种做法各有取舍看到别人用步长卷积替代池化不要觉得奇怪。6.4 从经典结构到注意力术语是怎么继续长的网络结构的演进其实就是术语的堆叠过程。LeNet 定了卷积池化全连接的基本范式AlexNet 把它做深并引入 ReLU 和 DropoutVGG 提出用小卷积核堆深度的思路ResNet 引入残差连接解决深层网络的退化问题再往后**注意力机制attention**从序列任务扩散到视觉任务出现了各种以注意力为核心的骨干网络包括局部窗口和跨窗口交互这类设计本质上都是在回答同一个问题如何让模型在有限算力下既看到局部细节也建立长距离依赖。看结构论文时我建议你这样读先看它要解决什么具体问题再看它加了什么新名词最后看这个新名词在计算上省了还是费了什么。术语只是外壳问题才是内核。7. 环境与工程侧术语能跑起来才算入门7.1 配置环境时反复出现的几个词绕不开的一组词驱动、计算平台运行时、加速库、包管理器。通俗讲驱动是硬件和系统之间的翻译官计算平台运行时是专为并行计算准备的一套接口加速库是针对神经网络算子做过优化的实现。这三者版本不匹配就会出现明明装了却用不上的情况。我的经验做法是先确认硬件可用再装框架。在终端里跑一行检查代码能打印出可用设备再往下走打印不出来就先解决环境不要在框架层面反复折腾。环境问题在环境层解决这是最省时间的思路。# 确认能否识别到可用的计算设备 python -c import torch; print(torch.cuda.is_available())提示用虚拟环境把每个项目的依赖隔离开是个几乎零成本但收益极高的习惯。同一台机器上不同项目要求不同版本框架是常态混装迟早出事。7.2 框架怎么选看你下一步要干什么PyTorch动态图、调试直观、生态活跃研究和教学场景里使用最广。入门我通常推荐它。TensorFlow / Keras部署链路成熟工业落地案例多。MATLAB 深度学习工具箱如果你本身在做信号、控制、传统工程仿真不想脱离原有环境用它可以少学一套工具链。工业视觉类工具像机器视觉软件里自带的深度学习模块优势在于标注、训练、部署集成在一个界面里适合产线上的检测任务学习曲线比纯代码平缓。国产框架适配特定硬件时值得关注接口设计大体相通。选择标准不是哪个最强而是你的数据在哪、你的部署目标在哪、你团队熟悉什么。为了追新而换框架是我见过性价比最低的操作之一。7.3 训练过程中的工程术语这些词在日志和文档里出现频率极高检查点checkpoint把某一时刻的模型参数存下来训练中断可以续上也可以用来回滚到效果最好的那一轮。混合精度用较低精度的浮点格式做部分计算省显存、提速代价是要注意数值稳定性。显存占用主因是模型参数、梯度、优化器状态、中间激活值四块。批大小增大时激活值线性增长这是最常见的炸显存原因。分布式训练多设备一起训。数据并行是把大批数据切开分给各设备模型并行是把模型本身切开后者实现复杂度高得多。梯度累积显存不够放大 batch就攒几次小 batch 的梯度再更新一次等效于大 batch。8. 几组高频混淆术语的对照与我的记忆方法8.1 五组最容易搞混的词概念 A概念 B区别一句话参数超参数参数是学出来的超参数是你定的训练误差泛化误差一个在见过的数据上一个在没见过的数据上epochiteration一个是走完全集一个是走完一个批池化卷积一个无可学参数做下采样一个有权重做特征提取前向传播反向传播一个算预测一个算梯度8.2 我自己的记忆办法给每个术语配一个动作抽象定义记不牢动作很好记。我在笔记里给常见术语都配了一个动词短句张量 → 装数据形状 → 对齐线性层 → 加权求和激活函数 → 加非线性损失函数 → 打分梯度 → 指方向反向传播 → 追责优化器 → 迈步子学习率 → 步子大小epoch → 走完全集一遍过拟合 → 背题背过头正则化 → 上枷锁卷积 → 拿小窗扫池化 → 缩图每次遇到不熟的术语先问自己它在数据流里干的是哪件事绝大多数时候答案会自己浮出来。这套办法陪我度过了最开始那段混乱期后面读论文、读源码时也一直在用。真正让你从能跑代码走到能改代码的从来不是记住了多少名词而是能不能在脑子里把这条数据流完整地跑一遍包括每一个形状、每一次参数更新和每一个判断模型好坏的指标。
返回列表