深度学习这几年热度一直居高不下,身边越来越多朋友问我:到底该怎么入门?学哪些东西才算学会?网上教程铺天盖地,但大多是碎片化的知识点,今天讲个概念、明天跑个模型,很难形成体系。我把这段时间系统学习和反复实践的内容整理成笔记,围绕模型原理、学习路径、框架选型和训练实操展开,希望能给想入门深度学习或者学了一段时间还觉得混乱的朋友一个清晰的地图。
这份笔记不是什么高深的理论推导合集,也不是简单的API调用手册,而是站在“真正要把模型训练出来、把问题解决掉”的角度,把自己踩过的坑、试过的方案、想明白的原理都写下来。适合正在学习机器学习基础、准备切入深度学习的学生,也适合已经在工作中用过一些现成模型、想补全底层认知的开发者。
1. 深度学习到底在学什么
1.1 从感知机到神经网络:模型在做什么
很多人第一次接触深度学习,会被“神经网络”“多层感知机”“反向传播”这些词唬住。换个角度理解:深度学习本质上是一个“自动找公式”的过程,只不过这个公式不是人写出来的,是机器从数据里拟合出来的。传统编程是人定规则,输入数据、经过一套if-else或者数学表达式,得到输出;深度学习反过来,把输入和输出喂给模型,让模型自己调整内部的一大堆参数,直到它能准确描述输入和输出之间的关系。
神经网络的结构最初确实受到生物神经元启发,但实际操作时可以把它理解成一个“带参数的多层复合函数”。比如一个简单的网络,输入层接收特征,经过一层又一层的加权求和和非线性变换,最后在输出层给出预测结果。每一层的参数就是那些权重和偏置,训练的过程就是不断修正这些参数,让预测结果逼近真实答案。
从一开始的单层感知机,到多层网络,再到现在的各种复杂结构,进步的核心只有一个:表达能力的提升。单层感知机只能解决线性可分的问题,多层网络能拟合任意复杂的函数。为什么深度学习效果好?因为它用“层”这种结构把数学模型变成了可以堆叠、可以组合的积木,再配合大规模数据和计算资源在不同任务上训练出有效的映射关系。
1.2 学习等于优化:损失函数与梯度下降
深度学习中“学习”这个词看起来很玄,落到数学上其实就是“最小化损失函数”。损失函数衡量的是模型预测值与真实值之间的差距,训练的目标就是让这个差距越来越小。任务不同,损失函数也不同:回归任务常用均方误差,分类任务常用交叉熵。选择损失函数不只是看名字,要在意它的梯度特性。比如均方误差配sigmoid激活函数时,输出层梯度会遇到饱和区,收敛很慢;换成交叉熵后梯度被简化,训练顺畅得多。
找到最小值的工具是梯度下降。梯度的方向指向函数值增长最快的方向,所以沿梯度的反方向更新参数就能让损失下降。学习率决定每一步走多远,太大容易震荡甚至发散,太小则龟速收敛。实际操作中很少用纯梯度下降,常用的有带动量的SGD、自适应学习率的Adam等,它们解决的核心问题都是“如何在崎岖的损失表面上更稳定地下降”。
让我用生活类比解释这一点:损失函数就像一片高低起伏的山地,模型参数是你的位置,目标是最低点。梯度下降是蒙眼下山,靠脚下的坡度判断方向;学习率就是步长,步子太大可能跨过山谷跑到另一座山上,步子太小半天走不到山脚。动量是记住之前的方向,在狭窄沟壑里不会来回震荡;Adam这样的自适应方法则根据不同方向的历史梯度自动调整步幅,在复杂地形里更省心。
深度学习的学习过程,本质上就是设计一个足够大的参数化函数族,再设计一个能衡量好坏的目标函数,最后用一个不断迭代的优化算法去搜索最优参数。理解这一条主线,后面学任何模型、任何框架都会觉得顺理成章。
2. 系统学习路径与知识体系搭建
2.1 数学基础:到底需要学到什么程度
很多人在入门深度学习之前,先被数学吓退。线性代数、概率论、微积分、凸优化,感觉要全学一遍才能动手。真实情况是:深度学习的核心数学要求没有想象中高,但基础概念躲不开。微积分里最重要的是链式法则,因为反向传播就是链式法则的反复应用,这个概念理解不了,梯度是怎么从输出层传回输入层的就永远是黑盒。线性代数里最重要的是矩阵乘法,输入数据是一批样本组成矩阵,权重也是矩阵,前向传播就是一连串矩阵乘法加激活函数;理解矩阵的维度和形状,模型代码的debug能力会上一个台阶。概率论里最重要的是条件概率和极大似然估计,交叉熵损失的本质就是从极大似然推导出来的。
我的建议是不要等数学学好了再开始。先把线性代数的矩阵乘法、微积分的偏导和链式法则、概率论的基础概念用一个月快速过一遍,然后立刻上手写代码。在写代码或者训练过程中遇到不懂的数学概念再回头翻书,带着问题学效率高得多。数学是工具不是门槛,是边用边补的,不是等全学会才动手的。
2.2 经典网络架构一览:CNN、RNN与Transformer
深度学习经过这些年发展,沉淀下来几个必须掌握的架构。第一个是卷积神经网络(CNN),专为处理网格结构数据设计,最典型的就是图像。它的核心是卷积核在输入上滑动做加权求和,用很小的参数共享方式提取局部特征。底层卷积识别边缘、纹理,高层卷积组合出物体部件甚至完整物体。CNN带来的启发是:不是所有层都必须全连接,利用数据的局部相关性和平移不变性,可以极大减少参数,也让模型更适合图像。实际搭建时还要理解池化(下采样)的作用,通道数的变化,以及感受野的概念。
第二个是循环神经网络(RNN)家族,包括LSTM和GRU,针对序列数据设计,比如文本、时间序列、语音。RNN的核心思想是在时间步上共享同一套参数,把上一时刻的隐藏状态传到下一时刻,实现对序列的建模。但它的问题是长距离依赖很难学,梯度在时间步之间反复相乘会指数级消失或爆炸。LSTM通过门控机制控制信息的写入、遗忘和输出,缓解了这个问题。
第三个必须重点说的是Transformer,它现在几乎是深度学习在自然语言处理和其他很多领域的默认选择。核心是自注意力机制,可以同时建模序列中任意两个位置之间的关系,不再受距离限制。自注意力算出一个注意力权重矩阵,表示每个词对其他词的关注程度,再用这个加权和更新每个位置的表示。Transformer还引入了位置编码,因为结构本身不分先后顺序,必须把位置信息显式加进去。理解Transformer不是终点,现在很多新模型依然在变着花样改注意力机制、改位置编码、改层级结构。
2.3 框架与工具:该选哪个、学到什么程度
工具选择是新手纠结很久的问题。目前最主流的是两个框架:一个是动态计算图路线,特点是写起来像普通Python代码,边定义边执行,调试方便,研究社区和论文复现大多用这个;另一个是静态图路线,先定义完整计算图再执行,部署性能优化空间大,早期工业界使用广泛,但现在前者也提供了强大的部署方案。
我的建议很直接:初学者优先选择主流的动态图框架。理由只有一个:学习成本低,调试直观,遇到问题社区里能搜到大量解决方案。另一个框架值得了解,但不必优先投入时间。很多教程会让人纠结框架选型,其实框架只是工具,深度学习核心能力在于问题建模、数据理解和模型设计,不在API熟练度。换框架之后只要概念扎实,适应期很短。
除了框架本身,还要学会几个配套工具:数据处理的库、实验管理的平台、可视化工具。处理图像分类任务至少要学会数据增强、归一化、数据集划分;训练时用可视化工具绘制损失曲线和准确率曲线,能直观看到模型收敛情况。环境配置方面,建议直接用包管理工具创建独立的虚拟环境,避免依赖冲突。
3. 核心概念与训练流程实操解析
3.1 从零搭建一个分类模型:数据准备
纸上得来终觉浅,直接上手搭一个图像分类模型是最好的学习方式。用经典的手写数字数据集举例,它包含0到9共10个类别的灰度图像,规模和难度都很适合入门。这个任务可以从零跑通整个流程,又不至于因为训练时间太长而磨掉耐心。
第一步是数据处理。原始数据下载下来之后通常是图像数组和对应标签,需要做几个标准操作:转换成浮点数并缩放到[0,1]区间,让数值范围一致,否则梯度更新不稳。然后划分训练集和验证集,训练集用来更新参数,验证集用来评估模型泛化能力。这里要强调一个原则:验证集绝不能用来做训练,否则模型就“记住”了验证集,评估结果就虚高了。日常实践中,我们还会把测试集完全隔离,只在最终评估时使用一次。
如果希望模型更鲁棒,可以在训练时做数据增强。图像方向的数据增强包括随机旋转、随机平移、随机翻转、添加噪声等。数据增强的本质是引入先验知识——手写数字的方向、位置略有变化仍然代表同一个数字,这相当于免费扩大了训练集,让模型见过更多样的数据形态,减少过拟合。但要注意增强幅度不宜过大,比如旋转超过某个角度后数字的语义可能被破坏。
3.2 定义模型结构与损失函数
数据准备好之后,定义模型结构。考虑用三层卷积加池化、最后两层全连接的结构。输入是单通道灰度图,第一个卷积层从1通道变成16通道,第二层变成32通道,第三层变成64通道。每次卷积之后接ReLU激活函数和最大池化,缩小特征图尺寸。最后把特征图展平,接两个全连接层,输出10个类别的logits。
为什么选择卷积而不是直接全连接?因为图像像素数量大,直接用全连接层参数会爆掉,而且忽略空间结构。比如一张28x28的图,展平是784维,全连接层即使只接到128个隐单元也有十万级别的参数,非常容易过拟合。卷积核只关心局部区域,多个卷积核堆叠又能覆盖全局,参数少、效果好。
损失函数用交叉熵。PyTorch这类框架里,直接计算交叉熵时要在输出层保持logits,不要提前做softmax。原因要说明:框架的交叉熵函数内部用logsumexp实现,在数值上比“先算softmax再算log”更稳定,避免了中间结果过大或过小带来的浮点精度问题,这属于实际写代码才能体会的细节。如果自己手动做softmax再接负对数似然,精度和梯度稳定性都更容易出问题。
3.3 训练循环:梯度清零、前向传播、反向传播、参数更新
训练循环是每个深度学习工程师都要手写到烂熟的部分。一个完整的训练循环四步:梯度清零、前向传播计算损失、反向传播计算梯度、优化器更新参数。每一步背后都有设计意图,不只是走程序。
梯度清零放在最前面是因为默认梯度会累积。PyTorch的设计里,调用反向传播时梯度会加到之前的梯度上而不是替换掉,这是为了支持某些需要对梯度累加的场景。如果不每轮清零,梯度就会持续累积到上一轮甚至上上轮的值,参数更新方向完全错乱。这一点是很多第一次手写训练循环的人遇到的第一个问题。
反向传播调用只计算梯度,不直接更新参数。梯度在计算图里从输出端往输入端传播,每层的参数都得到一个梯度值。优化器拿到这些梯度后,按照自己定义的更新规则(标准SGD、带动量、Adam等)更新参数。学习率的设置在实操中影响很大,常见做法是先用一个中等值,比如千分之一量级,观察损失曲线的变化趋势,再逐步调大或调小。也可以配合学习率调度器,训练过程中按预设策略改变学习率,比如余弦退火、逐步衰减。
验证时把模型切到评估模式,关闭梯度计算。验证集上的准确率才是模型真实能力的参考。要小心的是:每次验证都做完整推理,数据集大时比较耗时,所以一般每隔一个或几个epoch做一次,而不是每个batch做一次。评估模式下像Dropout、BatchNorm这些层的行为会变化,忘了切换模式会得到不稳定的验证结果,这也是常见问题。
3.4 训练策略与超参数调优
训练过程中会遇到一个现象:训练集损失一直下降,验证集损失却不再下降甚至上升,这就是过拟合信号。应对策略有几个,按推荐顺序排列:增加数据增强、增加Dropout、降低模型容量、早停。实际处理时不要一上来就用大杀器——比如换更大的模型就是反方向操作,也不要第一件事就调网络结构。先把数据增强和正则化加满,观察效果,再用早停兜底。
另一个影响训练效果的关键超参数是batch size。batch size决定每个批次样本数,它同时影响梯度估计的准确度和训练的稳定性。batch size过小,梯度噪声大,训练不稳定;过大,虽然梯度估计更准确,但容易收敛到尖锐极小值,泛化能力可能变差。还要考虑物理限制,batch size设太大,显存不够直接报错。实操中常用经验值是32、64、128这几个档位,从数据规模选择合适的值。
让我再强调学习率的重要性。学习率几乎是最重要的超参数,它的调试优先级高于网络宽度、深度、优化器选择。我在实践中常见的做法是先用稍小的batch size、固定的学习率快速跑几百步,看损失曲线的形态:如果损失震荡剧烈甚至发散,降低学习率;如果损失下降得非常平缓,提高学习率。确定了大致量级之后,再细化调整,并逐步引入调度策略。训练初期用大一点的学习率快速穿过平坦区域,后期用小学习率在局部极小值附近细致搜索,这是调度策略的基本逻辑。
4. 常见问题与排查技巧实录
4.1 损失不下降:定位问题从简单处开始
我在学深度学习时,遇到最多的情况就是模型训练半天,损失曲线像一条水平线,既不下降也不上升。遇到这类问题,经验丰富的工程师往往是先排除最简单的原因。
第一,检查数据预处理是否正确。最常见的是归一化没做好,图像像素值直接灌进模型,或者标签错位。第二,检查损失函数和模型输出是否匹配。多分类任务模型最后一层输出单元数必须等于类别数,损失函数选的类别要对应。第三,检查网络是否够简单。如果自定义网络结构太深或者初始化不当,梯度在层层传递中可能消失,参数几乎不更新。判断方法很简单:打印每一层参数的梯度范数,看看是否极小甚至为零。第四,检查学习率设置。
如果损失一直不降,还有一个调试技巧:先在一个batch上做拟合。取一个小批量数据,反复训练几十个步,如果模型连这一批数据都拟合不了,说明代码逻辑或者模型结构有问题;如果这一小批能拟合到很低的损失,说明问题出在数据、学习率或者优化策略上。这个小技巧能快速把问题空间一分为二。如果连几十个样本都学不会,就不要盲目调超参数了。
4.2 梯度爆炸与梯度消失:症状、原因与处理
训练过程中如果发现损失突然变成NaN,大概率是梯度爆炸。所谓梯度爆炸,就是反向传播过程中梯度值不断增大,导致参数更新幅度巨大,数值溢出变成非数值。常见的触发场景包括:网络层数很深、没有合适的归一化层、学习率过大、激活函数选择不当。
处理梯度爆炸的常用手段是梯度裁剪,直接把梯度模长限制在一个阈值内。这操作简单有效,训练循环里加上几行代码就能实现。但依赖裁剪只是事后补救,根本上要检查网络结构是否合理,是否每层都做了适当的归一化。
梯度消失相反,梯度在反向传播过程中逐渐衰减到接近于零,导致浅层参数几乎不更新。经典的sigmoid激活函数没有经过缩放但取值范围在0到1之间,导数最大值本身就不大,多层乘下来必然衰减到零。解决办法是换用ReLU这类导数恒定为1的激活函数(正半轴),配合良好的初始化方式和归一化层。这些手段组合使用,梯度消失问题基本可控。
我的排查顺序通常是:先看损失曲线形状,如果从第一个epoch开始就是NaN,大概率是学习率太大或者数据异常;如果训练到中途变成NaN,大概率是累积误差或网络结构问题;如果是验证一个batch能拟合、全量训练不收敛,则优先关注数据划分和batch size。
4.3 显存溢出与训练效率问题
深度学习中显存溢出几乎是每个实操者都经历过的。模型越大、batch size越大,GPU显存占用越高。出现out of memory错误时,第一反应往往是调小batch size,实际操作中它确实是最直接有效的方法。但有些技巧可以更充分地利用显存:
混合精度训练值得了解,它把部分计算用半精度执行,显存占用几乎减半,同时利用支持加速的硬件特性让训练更快。代价是实现和调试复杂度增加了,某些操作在降低精度下数值稳定性会变差,需要配合动态损失缩放技术。自动微分框架里通常有现成接口,开启混合精度只需少量代码改动,大部分场景收益都非常明显。
梯度累积是另一个技巧。当batch size受显存限制无法增大时,可以累积多个小batch的梯度后统一更新参数,达到与大batch size近似的效果。这个操作在实现上的注意点是,梯度清零的时机需要调整,不能每个batch都清,而要每累积到预设步数再清一次。
分布式训练是效率和显存的终极方案,数据并行把一批数据切分到多张卡上,每张卡各持一份模型副本,训练时同步梯度更新参数。多卡训练需要额外关注通信开销与batch size的全局语义(实际batch size是单卡batch size乘以卡数),但已经不是入门阶段必须掌握的内容了。入门阶段我更推荐抓住单卡训练的基本功,先把训练效率和显存使用理解透彻,分布式是后续水到渠成的事情。
4.4 训练结果与验证结果不一致:不涨点排查
好不容易把训练跑通,发现验证集准确率不太对劲,或者本地验证表现良好,一到新数据就崩。这类问题通常有明确的根源,按优先级逐一排查。
如果验证集和训练集的损失曲线一开始就背离,检查是否存在数据泄漏。数据预处理如果在划分训练验证集之前就做了全局归一化,验证集的均值方差信息就泄露进了模型,评估结果会虚高。正确做法是只在训练集上拟合归一化参数,再应用到验证集。
如果训练集表现好、验证集表现差,典型的过拟合信号。从数据增强、正则化、早停三个方向处理。很多人在这一步卡住是因为急着换模型——换模型是最后的选项,不是首选。
如果验证时忘记切换评估模式,BatchNorm会使用batch内的统计量而不是全局统计量,验证结果也会不稳定。尤其在小batch size下,不同batch的均值方差差异很大,验证集准确率时高时低就有可能是这个原因。
这几类问题是训练流程中的高频故障,排查思路基本围绕“数据怎么准备、模型怎么配置、代码模式是否正确”展开。把这些常见故障的处理方法练熟了,深度学习的基础实操能力就算扎实了。
5. 项目练习与进阶建议
5.1 从分类任务到真实项目:如何设计自己的练手项目
完成一个手写数字分类模型只是第一步。要真正掌握深度学习的工作流,必须走向更复杂的项目。这里推荐一个进阶路线:先做一个图像分类任务,比如街景门牌号分类;再做一个目标检测任务,比如检测图片中的车辆;接着做一个文本分类任务,比如情感分析;最后尝试把几个任务组合在一起,比如做一个多模态的分类系统。
每个新任务都会逼着你学习新技能。目标检测需要理解锚框、边界框回归、IoU(交并比)这些概念,这些是从分类到检测的天花板式跨越;文本分类需要处理词嵌入、序列长度、注意力掩码;组合多模态任务则需要设计数据管道和模型融合策略。每跨一步,你对深度学习的理解都会加深一层。
练手项目不需要追求原创模型结构。理想的状态是用现成的经典模型做底座,替换成自己的数据,跑通流程后尝试调整结构和超参数,观察哪些改动有效、哪些无效。这种实验经验多了,才能真正建立“模型效果如何受数据、结构、超参数影响”的直觉。
5.2 跟着论文复现的节奏走
阅读深度学习论文对入门者是很好的进阶方式,但直接读最新论文很容易劝退。我的建议是从经典论文开始,比如图像分类领域的里程碑模型、目标检测领域的经典两阶段模型,以及transformer架构的原始论文。读论文不需要从头到尾每个公式都推导,重点是理解三个问题:问题是什么、方案是什么、实验怎么证明方案有效。
边读边复现是学习效率最高的方式。复现时不用追求和原版一模一样,用相同的模型结构、相同的数据集,看能否达到论文中报告的水平就可以。如果达不到,排查是数据增强问题、学习率问题还是训练时长问题,这个过程学到的经验比纸面阅读多得多。
论文复现会遇到一个常见心态问题:觉得自己复现的结果不如论文里报的数字,就怀疑自己能力不足。实际原因是论文报告的数字往往经过多轮超参数调优,有时甚至会选择最有利的结果呈现。复现的目标不是追逐绝对数字,而是体会模型设计和训练策略之间的相互作用。
5.3 基础概念吃透再追新模型
深度学习领域新模型层出不穷,每隔一段时间就有新结构出现。初学者很容易被新名词吸引,今天看到一个模型很火就去学,明天看到一个新技术又去追,最后学了一堆名词,基础概念却一知半解。
我的建议是:经典概念吃透之前不要急着追新。数据加载与增强、模型搭建、训练循环、过拟合处理、超参数调优这五件事是基本功。只要这五件事融会贯通,面对再新的模型也只是套用同样流程去理解和实践。反过来,基础不牢就去读最新论文,看到全新的模块设计会完全丧失分析能力,只会照着别人的代码跑通,换个场景就束手无策。
现在主流的预训练模型大多可以在本地或者云端直接用,入门后期可以尝试“用预训练模型做迁移学习”这条路。加载一个在大规模数据上预训练好的模型,冻结部分层,在目标任务上微调,用很小的成本和数据量就能获得不错的性能。迁移学习是工程落地的重要技能,也是理解“预训练+微调”这个范式的最佳入口。
6. 学习资源与长期成长路径
6.1 课程、书籍与社区渠道的搭配
深度学习的学习资源非常多,按自己的水平搭配一套组合是事半功倍的关键。系统和经典的课程仍然值得看,深度学习的基础知识这么多年没有本质变化,经典课程讲得非常透彻。建议不要只是听,每看一小节就动手写代码验证,这样效率远高于关掉课程之后再来补。
书籍方面,挑选几本经典教材精读,把其中的推导过程自己推一遍。不必追求看完所有章节,把核心的优化理论、卷积网络、循环网络这几章吃透就够了。书的好处是体系完整、推导严谨,适合用来建立“为什么这样设计”的深层理解;课程和博客的好处是直观、新颖、贴合最新趋势。
社区渠道的价值在于解决具体问题。写代码卡住、训练不收敛、数据预处理有疑问,优先搜索和查阅技术问答社区。提问本身也有技巧:把数据形状、模型结构、损失曲线、错误信息贴全,往往自己写清楚问题描述的过程中就找到了答案。但我一直主张:能自己查资料解决的问题,不要轻易问人,这个过程中锻炼出来的排查能力比答案本身更有价值。
6.2 如何设定阶段性的学习目标
学习深度学习最大的挑战不是难度,而是覆盖面太宽,容易陷入“什么都想学”的焦虑。我的解决方案是设定阶段性目标,每个阶段只聚焦一件事,完成之后再进入下一阶段。
第一阶段目标:能够从零训练一个分类模型。理解数据、模型、训练循环、评估这四个核心环节。第二阶段目标:能够复现一篇经典论文的核心结果。第三阶段目标:能够在真实数据集上完成一个完整的项目,包括数据处理、模型设计、训练调优、结果分析。每个阶段之间保持一定的挑战性,但不要跨越太多。
在这些阶段中,要学会写实验记录。每个实验的数据集、模型结构、超参数、损失曲线、最终结果都记录下来。深度学习训练是一个高度依赖经验和对比的过程,没有实验记录,你会反复踩同一个坑却不自知。哪怕只是一个简单的表格,记录实验编号、改动内容、关键结果,三个月后回看时会发现它们是你最宝贵的经验资产。
6.3 独立解决一个完整问题的能力
有人说深度学习的门槛在“能跑通别人的代码”和“能独立解决一个问题”之间。跑通别人的代码只需要环境配置和耐心,独立解决一个问题则需要把问题拆解、数据构建、模型选择、训练调优、结果评估整个流程串起来,每一步都要自己拿主意。
独立解决问题的第一步是明确评估指标。分类任务看准确率还是F1值?检测任务看mAP?回归任务看MAE还是RMSE?指标不同,模型设计和优化方向都不同。第二步是搭建一个最简单的基准线,越简单越好,快速跑通全流程。有了基准线之后,再逐步优化,每一次只改变一个变量,观察效果。第三步是完整体验“从坏结果到好结果”的过程,不要期待第一次训练就能得到漂亮的指标。
这个过程练的不是某一个具体知识点,而是全局视角和决策能力。数学模型会有各种意想不到的问题,数据里会有各种噪声和缺失,实验环境会出各种幺蛾子——真正会做深度学习的人,是能在这些混乱中理出一条路,把一个看起来复杂的问题简化到可以解决的程度。这种能力没有捷径,只能通过不断解决一个又一个具体问题来打磨。
我在带过几个入门的朋友之后发现一个共同规律:进步最快的不是基础最好的,而是遇到问题不逃避、愿意一层层往下排查的那些人。深度学习这个领域有一点特别公平:只要你愿意动手,环境就会给你反馈,而每一次报错、每一个不收敛的实验,都在教你一些新课。把笔记做成这样一份实操手册,也是因为我相信,真正能让你掌握深度学习的,不是看多少教程,而是你亲手把一个个报错和经验刻进脑子里的过程。希望你也能在训练出第一个漂亮模型的那一刻,体会到这种乐趣。