PyTorch Autograd与计算图深度解析:从反向传播原理到梯度调试实战
1. 为什么每个PyTorch使用者都该吃透Autograd刚接触PyTorch那会儿我以为loss.backward()就是一句魔法咒语——写上去梯度就出来了参数就能更新了。直到有一次自定义了一个带条件分支的损失函数训练死活不收敛梯度全是None排查了整整一个下午才发现问题出在计算图的构建逻辑上。从那次之后我才真正意识到Autograd和计算图不是了解一下就行的背景知识而是决定你能不能写出正确训练代码的硬门槛。这篇文章面向的是已经能用PyTorch搭简单网络、跑通训练流程但对反向传播底层机制还停留在调包层面的开发者。我会从计算图的构建逻辑讲起拆解Autograd的核心工作原理然后落到实操——怎么用torch.autograd做梯度调试、怎么处理梯度消失和爆炸、怎么在自定义Function时避免踩坑。每个环节我都会给出可直接运行的代码和参数说明尽量做到你看完就能上手验证。核心关键词就三个计算图Computational Graph、自动微分Automatic Differentiation、梯度传播Gradient Propagation。这三个概念串起来就是Autograd的全部骨架。搞懂它们你排查梯度问题的效率至少翻倍。2. 计算图到底长什么样从一张流水线图说起2.1 计算图的本质节点、边与数据流很多人第一次听到计算图会觉得抽象其实你可以把它想象成一条工厂流水线。原材料输入数据从一端进去经过一道道工序运算操作最终产出成品输出结果。每一道工序就是一个节点Node工序之间的传送带就是边Edge传送带上流动的半成品就是张量Tensor。PyTorch的计算图是动态图Dynamic Graph也叫定义即运行Define-by-Run。什么意思就是你每执行一行涉及张量运算的代码图就实时地长出来一部分。这跟早期TensorFlow 1.x的静态图完全不同——静态图是先定义好整个计算流程再喂数据动态图是边算边建。这个区别为什么重要因为动态图意味着你可以用Python原生的if、for、while来控制计算流程每次前向传播的计算图结构可以不一样。这对处理变长序列、递归网络、条件分支等场景极其友好。代价是图的构建和销毁有额外开销但对于绝大多数研究和工程场景这个开销完全可以接受。2.2 叶子节点、非叶子节点与grad_fn在PyTorch里每个张量都有一个.grad_fn属性它指向创建这个张量的运算节点。如果你手动创建的张量比如torch.randn(3, 4)它的grad_fn是None这种张量叫叶子节点Leaf Tensor。经过运算产生的张量grad_fn会指向对应的反向传播函数。import torch x torch.randn(3, 4, requires_gradTrue) # 叶子节点grad_fnNone y x * 2 # 非叶子节点grad_fnMulBackward z y.sum() # 非叶子节点grad_fnSumBackward print(x.is_leaf) # True print(y.is_leaf) # False print(z.grad_fn) # SumBackward0 object at ...这里有个关键点只有叶子节点的.grad会被默认保留。非叶子节点的梯度在反向传播过程中计算完就释放了除非你调用.retain_grad()。这个设计是为了省显存——中间结果的梯度通常不需要长期保存。注意requires_gradTrue的张量才能参与梯度计算。如果你发现某个张量的.grad是None第一件事就是检查它的requires_grad属性和它是否在计算图中。2.3 前向传播与反向传播的图结构差异前向传播时PyTorch记录的是每个运算的前向函数和反向函数。反向传播时从损失函数出发沿着计算图反向遍历用链式法则逐层计算梯度。这里有个容易忽略的细节反向传播的计算图并不是前向图的简单反转。有些运算在前向时是多对一比如sum反向时就是一对多把梯度广播回去有些运算在前向时是一对多比如split反向时就是多对一把梯度加起来。理解这一点对排查梯度形状不匹配的问题非常关键。3. Autograd的引擎室反向传播是怎么跑起来的3.1 链式法则在计算图上的落地自动微分的数学基础就是链式法则。假设有一个复合函数 $y f(g(h(x)))$那么 $\frac{dy}{dx} \frac{dy}{df} \cdot \frac{df}{dg} \cdot \frac{dg}{dh} \cdot \frac{dh}{dx}$。Autograd做的就是把这个过程自动化从输出节点开始逐步向前计算每个节点的局部梯度然后乘起来。PyTorch采用的是反向模式自动微分Reverse-Mode AD。为什么选反向模式因为神经网络的典型场景是多输入、单输出大量参数一个标量损失反向模式的计算复杂度只跟输出维度相关效率远高于前向模式。具体来说当你调用loss.backward()时PyTorch会从loss节点开始初始化梯度为1因为$\frac{\partial loss}{\partial loss} 1$沿着grad_fn链反向遍历每个节点接收来自下游的梯度乘以自己的局部梯度传给上游遇到叶子节点时把累积的梯度存入.grad3.2 梯度累积机制与zero_grad的必要性PyTorch的梯度是累积的不是覆盖的。这意味着如果你不手动清零每次backward()的梯度会加到上一次的梯度上。x torch.tensor([1.0, 2.0], requires_gradTrue) y (x ** 2).sum() y.backward() print(x.grad) # tensor([2., 4.]) y.backward() # 再次反向梯度累积 print(x.grad) # tensor([4., 8.]) - 变成了两倍这就是为什么训练循环里必须写optimizer.zero_grad()。但梯度累积也不全是坏事——如果你想用更大的等效batch size但显存不够可以故意累积几次梯度再更新参数这是常见的显存优化技巧。实操心得optimizer.zero_grad()和model.zero_grad()在大多数情况下等价但如果你有多个优化器管理不同部分的参数建议用optimizer.zero_grad()它只清零该优化器负责的参数梯度。3.3 动态图的重建与释放时机每次前向传播都会构建一张新的计算图。前向结束后图存在于内存中等待反向传播。反向传播完成后图会被释放除非你设置了retain_graphTrue。这里有个常见的坑如果你需要对同一个loss做多次backward()比如GAN训练中先更新判别器再更新生成器必须设置retain_graphTrue否则第二次反向时会报Trying to backward through the graph a second time的错误。# GAN训练中的典型场景 fake_loss.backward(retain_graphTrue) # 第一次反向保留图 real_loss.backward() # 第二次反向释放图但要注意retain_graphTrue会阻止图的释放增加显存占用。如果不是必须尽量不要用。4. 自定义Function什么时候需要怎么写才不出错4.1 自定义Function的适用场景大多数时候你用PyTorch内置的运算就够了Autograd会自动处理梯度。但以下场景你可能需要自定义torch.autograd.Function你需要实现一个PyTorch没有的运算且希望它支持自动微分你需要对梯度计算做数值稳定处理比如在softmax中减去最大值你需要实现一个前向和反向不对称的运算比如Straight-Through Estimator你想用自定义的CUDA核函数加速某个运算4.2 自定义Function的标准写法自定义Function需要继承torch.autograd.Function实现两个静态方法forward和backward。class MyReLU(torch.autograd.Function): staticmethod def forward(ctx, input): # ctx用于保存反向传播需要的信息 ctx.save_for_backward(input) return input.clamp(min0) staticmethod def backward(ctx, grad_output): input, ctx.saved_tensors grad_input grad_output.clone() grad_input[input 0] 0 return grad_input # 使用方式 my_relu MyReLU.apply x torch.randn(5, requires_gradTrue) y my_relu(x) y.sum().backward() print(x.grad)几个关键点forward的第一个参数必须是ctx用于在前后向之间传递信息backward的返回值个数必须和forward的输入个数一致不包括ctx如果某个输入不需要梯度对应的返回值可以是Nonectx.save_for_backward()只能保存张量保存其他类型用ctx.other_info value4.3 梯度检查用gradcheck验证自定义Function写完自定义Function后强烈建议用torch.autograd.gradcheck做数值验证。它通过有限差分法计算数值梯度跟你实现的反向传播做对比。from torch.autograd import gradcheck func MyReLU.apply x torch.randn(20, dtypetorch.double, requires_gradTrue) test gradcheck(func, (x,), eps1e-6, atol1e-4) print(test) # True表示梯度计算正确注意gradcheck要求输入是double类型因为float精度不够数值梯度会有较大误差。另外如果你的Function中有不可导的点比如ReLU在0处gradcheck可能会失败这是正常的。5. 梯度问题的排查与调优实战5.1 梯度消失与爆炸的识别与应对梯度消失和爆炸是训练深层网络时最常见的问题。识别方法很简单在训练循环中打印梯度范数。total_norm 0 for p in model.parameters(): if p.grad is not None: param_norm p.grad.data.norm(2) total_norm param_norm.item() ** 2 total_norm total_norm ** 0.5 print(fGradient norm: {total_norm})如果梯度范数随着层数增加指数级衰减就是梯度消失如果指数级增长就是梯度爆炸。应对策略问题解决方案原理梯度消失使用ReLU等非饱和激活函数饱和激活函数如Sigmoid的导数在两端趋近于0梯度消失引入残差连接残差连接提供梯度高速公路梯度可以直接回传梯度消失使用BatchNorm归一化层使激活值分布稳定梯度更健康梯度爆炸梯度裁剪限制梯度范数的最大值梯度爆炸降低学习率减小参数更新步长梯度爆炸权重初始化调优如Xavier、Kaiming初始化梯度裁剪的代码实现torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)max_norm的选择需要根据具体任务调一般从1.0开始试。如果梯度范数经常超过这个值说明模型可能不稳定如果从来没超过说明裁剪没起作用。5.2 梯度为None的常见原因排查梯度为None是最让人头疼的问题之一。以下是一个排查清单检查requires_grad确保参与计算的输入张量设置了requires_gradTrue检查计算图是否断裂如果中间某步用了.detach()或.numpy()图就断了检查是否在torch.no_grad()上下文中推理模式下不会构建计算图检查自定义Function的backward返回值如果返回了None梯度就传不回去检查是否用了原地操作某些原地操作会破坏计算图# 典型错误detach()导致图断裂 x torch.randn(3, requires_gradTrue) y x * 2 z y.detach() * 3 # z和x之间的图断了 z.sum().backward() # 报错或x.grad为None5.3 用hook机制监控中间层梯度PyTorch提供了register_hook可以在反向传播过程中监控任意张量的梯度。def print_grad(grad): print(fGradient: {grad.norm()}) x torch.randn(3, requires_gradTrue) y x * 2 y.register_hook(print_grad) # 反向传播时打印y的梯度 z y.sum() z.backward()这个技巧在调试复杂模型时非常有用可以精确定位到哪一层的梯度出了问题。6. 高阶技巧让Autograd为你所用6.1 高阶导数与create_graphPyTorch支持高阶导数只需要在backward()时设置create_graphTrue。x torch.tensor([2.0], requires_gradTrue) y x ** 3 dy_dx torch.autograd.grad(y, x, create_graphTrue)[0] print(dy_dx) # tensor([12.], grad_fnMulBackward) d2y_dx2 torch.autograd.grad(dy_dx, x)[0] print(d2y_dx2) # tensor([12.])高阶导数在实现WGAN-GP梯度惩罚等算法时是必需的。6.2 用torch.autograd.grad替代backwardtorch.autograd.grad比backward更灵活它直接返回梯度值而不修改.grad属性适合需要精细控制梯度流的场景。x torch.randn(3, requires_gradTrue) y x ** 2 grad torch.autograd.grad(y.sum(), x, retain_graphTrue) print(grad[0]) # 直接拿到梯度x.grad仍然是None6.3 性能优化no_grad与inference_mode推理阶段不需要构建计算图用torch.no_grad()可以显著减少显存占用和加速计算。with torch.no_grad(): output model(input)PyTorch 1.9之后引入了torch.inference_mode()比no_grad更激进进一步减少开销但限制也更多不能用于需要梯度的场景。实操心得在验证循环中model.eval()和torch.no_grad()要配合使用。model.eval()只改变Dropout和BatchNorm的行为不会关闭计算图构建torch.no_grad()才是真正省显存的关键。7. 我踩过的那些坑与对应解法7.1 原地操作导致的诡异报错PyTorch对原地操作in-place operation非常敏感因为原地操作会修改张量的值而反向传播需要用到前向时的值。x torch.randn(3, requires_gradTrue) y x * 2 y 1 # 原地操作可能报错 z y.sum() z.backward() # RuntimeError: a leaf Variable that requires grad is being used in an in-place operation解法很简单用非原地版本y y 1。7.2 多卡训练中的梯度同步问题用DataParallel或DistributedDataParallel时梯度会在多卡之间同步。如果你发现单卡正常但多卡不收敛检查一下是否所有参数都参与了前向传播。如果有参数在某些卡上没有用到梯度同步会出问题。7.3 混合精度训练中的梯度缩放用torch.cuda.amp做混合精度训练时梯度会先被缩放再反向传播防止下溢。如果你手动操作梯度记得先scaler.unscale_(optimizer)再裁剪。scaler torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): output model(input) loss criterion(output, target) scaler.scale(loss).backward() scaler.unscale_(optimizer) # 先还原梯度 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) scaler.step(optimizer) scaler.update()7.4 常见问题速查表问题现象可能原因排查方法解决方案梯度为None计算图断裂检查.grad_fn链移除.detach()或.numpy()梯度为None输入未设requires_grad打印tensor.requires_grad设置requires_gradTrue梯度爆炸学习率过大打印梯度范数降低学习率或梯度裁剪梯度消失激活函数饱和检查激活值分布换ReLU或加残差连接第二次backward报错图已释放检查是否用了retain_graph设置retain_graphTrue显存溢出计算图太大用torch.cuda.memory_summary()减小batch size或用no_grad自定义Function梯度不对backward实现错误用gradcheck验证修正backward逻辑8. 从Autograd到实际训练的完整链路8.1 一个完整的训练循环拆解把前面讲的所有知识点串起来一个标准的训练循环是这样的model MyModel() optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion torch.nn.CrossEntropyLoss() for epoch in range(num_epochs): model.train() for batch_x, batch_y in train_loader: optimizer.zero_grad() # 1. 清零梯度 output model(batch_x) # 2. 前向传播构建计算图 loss criterion(output, batch_y) # 3. 计算损失 loss.backward() # 4. 反向传播计算梯度 torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) # 5. 梯度裁剪 optimizer.step() # 6. 更新参数 model.eval() with torch.no_grad(): # 7. 验证阶段关闭计算图 for batch_x, batch_y in val_loader: output model(batch_x) val_loss criterion(output, batch_y)每一步都对应着Autograd的某个机制zero_grad对应梯度累积的清零forward对应计算图构建backward对应反向模式自动微分step对应参数更新。8.2 梯度累积模拟大batch训练显存不够但想要大batch的效果梯度累积可以帮你accumulation_steps 4 optimizer.zero_grad() for i, (batch_x, batch_y) in enumerate(train_loader): output model(batch_x) loss criterion(output, batch_y) / accumulation_steps # 损失缩放 loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()注意损失要除以累积步数否则梯度会放大对应倍数。8.3 学习率与梯度的联动调优学习率和梯度是联动的。梯度大时学习率要小梯度小时学习率可以适当大。一些自适应优化器如Adam内部已经做了这种调整但手动调参时仍然需要注意如果梯度范数持续很大10先检查数据归一化和权重初始化如果梯度范数持续很小1e-6检查是否有梯度消失学习率warmup可以帮助模型在训练初期稳定下来我个人在实际操作中的体会是与其花大量时间调学习率不如先把梯度监控做好。梯度健康了学习率的调整空间会大很多。很多时候训练不收敛根源不在学习率而在梯度本身出了问题。最后分享一个小技巧如果你怀疑某个层的梯度有问题可以用register_hook单独监控那一层打印梯度的均值和方差。正常情况下梯度的均值应该在0附近方差不应该跨层变化太大。如果某一层的梯度方差突然变得极小或极大问题大概率就出在那里。

相关新闻

InternVL3.5-2B-Pretrained生产级部署实战:LMDeploy与vLLM多模态推理加速全解析

InternVL3.5-2B-Pretrained生产级部署实战:LMDeploy与vLLM多模态推理加速全解析

【免费下载链接】InternVL3_5-2B-Pretrained 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL3_5-2B-Pretrained 点击查看 免费下载 InternVL3.5-2B-Pretrained 是 InternVL3.5 开源多模态大模型系列的预训练基座模型,总参数约 2.3B(0…

2026/10/18 3:30:17 阅读更多 →
高德地图JS API设备图标设置:自定义、性能优化与交互实战

高德地图JS API设备图标设置:自定义、性能优化与交互实战

1. 项目背景与核心需求拆解设备图标在地图上的展示,看起来只是“换个图”这么简单,但真正落到项目里,涉及的问题远比想象中多。我做过好几个物联网监控和车辆调度类的项目,每次到地图展示这一环,团队里总会有人问&…

2026/10/18 3:30:17 阅读更多 →
Unity MatchTarget与IK动画融合实战:角色精确交互与导播视角适配

Unity MatchTarget与IK动画融合实战:角色精确交互与导播视角适配

1. 从一次角色脚底打滑说起:为什么需要MatchTarget做3D动作类项目的人多半遇到过这种场景:角色从待机切到翻越动作,动画本身很漂亮,但脚踩在箱子边缘时要么悬空、要么陷进去,手抓栏杆的位置也总是差那么几厘米。美术在…

2026/10/18 3:30:17 阅读更多 →

最新新闻

Tomcat 7.0.108 部署实战:JDK 版本选择、环境变量配置与常见启动问题排查

Tomcat 7.0.108 部署实战:JDK 版本选择、环境变量配置与常见启动问题排查

简介:Apache Tomcat 7.0.108 是一款开源的 Java Servlet 容器,主要用于运行 JSP 与 Servlet 等 Java Web 应用。这份 zip 压缩包面向 Java Web 开发者、运维人员及计算机专业学生,帮助其快速搭建本地服务器环境,完成 Web 项目的部…

2026/10/18 4:20:29 阅读更多 →
机器人十年演进:从固定程序到环境自适应智能体

机器人十年演进:从固定程序到环境自适应智能体

聊到机器人技术十年演进,我最直观的感受是:十年前的机器人更像一台会点数的机床,现在的机器人开始像一个有感官、会判断的操作员。十年前我在现场调试机械臂,拿着示教器一格一格地按点位,工件稍微偏一点就抓空&#xf…

2026/10/18 4:20:29 阅读更多 →
MATLAB手写K-means聚类算法及评价指标全解析

MATLAB手写K-means聚类算法及评价指标全解析

1. 项目概述与选题背景1.1 这个项目解决什么问题聚类分析在数据挖掘任务里属于频次特别高的前置步骤,而K-means又是聚类算法当中最基础、最常用的一把尺子。很多人学机器学习第一周就会接触K-means,原理看着就五行代码的事儿——选中心、算距离、更新中心…

2026/10/18 4:20:29 阅读更多 →
用HTML动画演示二极管工作原理:PN结与伏安特性可视化

用HTML动画演示二极管工作原理:PN结与伏安特性可视化

前阵子有朋友跟我抱怨,说“二极管工作原理”这一章在课本上翻来覆去就那几页,老师PPT里也画了PN结、画了耗尽层,上课听的时候觉得懂了,一做题就懵,尤其是“为什么正向导通、反向截止”这件事,脑子里始终没有…

2026/10/18 4:20:29 阅读更多 →
Vue3 + Webpack5 + Element Plus 环境搭建避坑指南

Vue3 + Webpack5 + Element Plus 环境搭建避坑指南

简介:这是一份基于 webpack5 搭建 Vue3 开发与生产环境的实战示例工程,面向希望熟悉 webpack5 新特性、掌握前端构建优化的初中级开发者。资源以 JavaScript 为主,配合 Vue 单文件组件与样式文件,完整演示了从开发调试到生产打包的…

2026/10/18 4:20:29 阅读更多 →
OpenClaw语音控制实战:多用户语音识别与声纹认证的权限隔离方案

OpenClaw语音控制实战:多用户语音识别与声纹认证的权限隔离方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 4:19:29 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →