首页/新闻资讯/正文详情

Apex Amp 混合精度训练实战:从 opt_level 到统一 API 的完整指南

发布时间:2026/9/26 15:48:01 来源:云帆数科 栏目:资讯中心
Apex Amp 混合精度训练实战:从 opt_level 到统一 API 的完整指南
人工智能大模型音乐生成音频预训练【免费下载链接】jukeboxCode for the paper Jukebox: A Generative Model for Music项目地址https://gitcode.com/gh_mirrors/ju/jukebox点击查看免费下载本文以 NVIDIA Apex 仓库中 amp.rst 文档为主线系统讲解apex.ampAutomatic Mixed Precision这一自动混合精度工具如何在只改动 3 行代码的前提下启用 Tensor Core 加速训练如何理解 O0–O3 四个优化级别及其背后的六个核心属性以及新旧 API 的迁移要点。读完本文你将能够为自己的 PyTorch 训练脚本正确接入amp.initialize/amp.scale_loss并针对不同模型挑选最合适的精度策略。背景Amp 解决什么问题混合精度训练的核心矛盾在于FP16 能利用 NVIDIA Tensor Core 成倍加速 GEMM 与卷积等算子但 FP16 的动态范围约 5 个数量级远小于 FP32直接以 FP16 训练容易导致梯度下溢underflow、精度损失甚至发散。手动在脚本中穿插.half()转换不仅繁琐还极易出错。apex.amp的思路是让用户以默认的 FP32 方式构建模型与数据由 Amp 在后台自动完成何时转 FP16、何时保留 FP32、如何缩放损失的决策。正如仓库 README 所描述的apex.amp是通过只修改脚本中的 3 行代码即可启用混合精度训练的工具。在 amp.rst 中Amp 给出了一个完整的最小示例# 以默认 FP32 精度声明模型和优化器 model torch.nn.Linear(D_in, D_out).cuda() optimizer torch.optim.SGD(model.parameters(), lr1e-3) # 让 Amp 按 opt_level 的要求执行类型转换 model, optimizer amp.initialize(model, optimizer, opt_levelO1) ... # loss.backward() 改为 with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward()关键原则是无论选择哪个opt_level用户都不应手动对模型或数据调用.half()。Amp 希望用户从一段现有的 FP32 脚本出发加入上述三行代码即可开始混合精度训练当需要关闭 Amp 时enabledFalse原脚本的行为与未接入 Amp 时完全一致。因此遵循 Amp API 没有风险却可能带来可观的性能收益。由于无需手动转换模型与输入数据一段遵循新 API 的脚本可以仅通过更换opt_level在多种纯精度/混合精度模式间切换而不需要改动任何其他代码。opt_level 与属性理解 Amp 的配置模型Amp 允许用户方便地实验不同的纯精度与混合精度模式。常用默认模式通过选择优化级别opt_level来指定每个opt_level定义了一组控制 Amp 实现纯精度或混合精度训练的属性properties。你也可以向amp.initialize直接传入特定属性的值实现比默认配置更细粒度的控制——手动指定的值会覆盖opt_level建立的默认值。从源码看这六个属性在 frontend.py 的Properties类中统一管理并注册为一个options字典默认值分别为enabledFalse、opt_levelNone、cast_model_typeNone、patch_torch_functionsFalse、keep_batchnorm_fp32None、master_weightsNone、loss_scale1.0。属性及其语义如下属性作用cast_model_type将模型的参数与缓冲区buffers转换为目标类型。patch_torch_functions对所有 Torch 函数与 Tensor 方法进行打补丁patch将适合 Tensor Core 的算子如 GEMM、卷积以 FP16 执行将受益于 FP32 精度的算子以 FP32 执行。keep_batchnorm_fp32即便模型其余部分为 FP16也建议将 BatchNorm 权重保留在 FP32——这既提升精度又能启用 cudnn BatchNorm改善性能。master_weights为 FP16 模型权重维护一份 FP32 主权重master weights。优化器对 FP32 主权重执行 step以提升精度并捕获小梯度。loss_scale若为浮点数则作为静态固定损失缩放值若为字符串dynamic则由 Amp 自动随时间自适应调整缩放值。属性覆盖与一致性检查选中某个opt_level后你可以通过属性关键字参数做手动覆盖。但如果你试图覆盖一个对所选opt_level而言没有意义的属性Amp 会抛出带解释的错误。例如为opt_levelO1指定master_weightsTrue是不合理的O1是在 Torch 函数周围插入转换cast而不是转换模型权重数据、激活和权重在流经被 patch 的函数时被就地动态重新转换out-of-place recast on the fly。因此 O1 下模型权重本身可以也应该保持 FP32无需维护独立的 FP32 主权重。这一检查在源码中有明确体现。frontend.py 的Properties.__setattr__对每个属性做了守卫例如设置master_weights时若当前opt_level O1且值为非 None会调用warn_or_err报错设置cast_model_type时若为 O1 且值既非False也非torch.float32同样会警告。此外keep_batchnorm_fp32被限定为布尔值、字符串True/False或None否则触发断言。四个优化级别 O0–O3 详解Amp 识别的opt_level为O0、O1、O2、O3四种。其中O0与O3并非真正的混合精度但分别对建立精度基线与速度基线很有价值O1与O2是混合精度的两种不同实现建议两种都尝试看哪种对具体模型带来最好的加速与精度。注意O0/O1中的前缀是大写字母 Othe letter O不是数字 0。源码 frontend.py 的initialize中对非法的opt_level会抛出RuntimeError并明确提示这一点。在 frontend.py 中四个级别分别由O0、O1、O2、O3类实现它们的__call__方法直接对Properties对象写入默认值注册在opt_levels字典中供initialize调用。O0FP32 训练精度基线O0对你的模型而言通常是 no-op你的模型本来就是 FP32可用于建立精度基线。O0默认属性值cast_model_typetorch.float32patch_torch_functionsFalsekeep_batchnorm_fp32None不适用一切皆为 FP32master_weightsFalseloss_scale1.0O1混合精度常规使用推荐O1对所有 Torch 函数与 Tensor 方法打补丁按白名单-黑名单whitelist-blacklist模型转换其输入白名单算子如适合 Tensor Core 的 GEMM、卷积以 FP16 执行黑名单中受益于 FP32 精度的算子如 softmax以 FP32 执行。O1默认使用动态损失缩放除非被覆盖。O1默认属性值cast_model_typeNone不适用patch_torch_functionsTruekeep_batchnorm_fp32None不适用所有模型权重保持 FP32master_weightsNone不适用模型权重保持 FP32loss_scaledynamicO1 的白名单/黑名单机制源码级白名单与黑名单列表定义在 lists 目录 下的三个文件中torch_overrides.pyFP16_FUNCS包含conv1d/2d/3d、conv_transpose*、prelu以及 BLAS 类算子addmm、addmv、addr、matmul、mm、mvFP32_FUNCS包含exp、log、mean、sum、std、var、norm、pow等CASTS需要类型提升的多 Tensor 运算包含add、div、mul、比较类算子等SEQUENCE_CASTS包含cat、stack。值得注意的是bmm/addbmm/baddbmm会根据 CUDA 版本动态归类CUDA ≥ 9.1 才具备快速 FP16 batched matmul 内核因此版本足够新时加入 FP16 列表否则归入 FP32 列表。functional_overrides.pyFP16_FUNCS为torch.nn.functional下的conv1d/2d/3d、conv_transpose*、linearFP32_FUNCS包括softmax、log_softmax、softmin、softplus、layer_norm、group_norm、各类 losscross_entropy、mse_loss、nll_loss等以及interpolate。tensor_overrides.pyTensor 方法层面的对应列表。此外amp.py 中的init()会遍历这些列表对白名单函数用cached_cast包装maybe_halfFP16 转换带缓存、对黑名单函数用cached_cast包装maybe_float不缓存并处理CASTS/SEQUENCE_CASTS的类型提升以及对 RNN 及其 cell 的专门白名单处理。对于BANNED_FUNCS如F.binary_cross_entropyAmp 默认在遇到 FP16 输入时报错并给出替换建议改用BCEWithLogitsLoss或注册amp.register_float_function(torch, sigmoid)。O2几乎全 FP16 混合精度O2将模型权重转换为 FP16patch 模型的forward方法以将输入数据转换为 FP16将 BatchNorm 保留在 FP32维护 FP32 主权重并更新优化器的param_groups使optimizer.step()直接作用于 FP32 权重必要时随后执行 FP32 主权重→FP16 模型权重的拷贝同时实现动态损失缩放除非被覆盖。与O1不同O2不patch Torch 函数或 Tensor 方法。O2默认属性值cast_model_typetorch.float16patch_torch_functionsFalsekeep_batchnorm_fp32Truemaster_weightsTrueloss_scaledynamicO2 的 master weights 机制源码级在 _process_optimizer.py 的lazy_init_with_master_weights中每个torch.cuda.HalfTensor参数都会被替换为一份 FP32 主参数param.detach().clone().float()且requires_gradTrue并用load_state_dict(state_dict())技巧将原有的 per-param 状态张量重铸为 FP32。optimizer.step被包装为先执行原old_step()再调用_master_params_to_model_params()有 C 扩展时通过multi_tensor_applier调用amp_C.multi_tensor_scale一次完成全部参数拷贝最后清空主梯度。zero_grad也被重写为只清零模型梯度而置空主梯度。O3FP16 训练速度基线O3可能无法达到真正混合精度选项O1/O2的稳定性但它可用于建立模型的速度基线供O1/O2的性能对比参考。若模型使用 BatchNorm为建立光速speed of light基线可尝试在O3上额外覆盖keep_batchnorm_fp32True如前所述这会启用 cudnn BatchNorm。O3默认属性值cast_model_typetorch.float16patch_torch_functionsFalsekeep_batchnorm_fp32Falsemaster_weightsFalseloss_scale1.0从 frontend.py 中O1、O2、O3类的brief/more文档字符串还可以看到官方的推荐口径O1是首次尝试混合精度训练时最安全的方式O2的 FP32 主权重还可以改善收敛与稳定性O3用于建立性能上限。实战中通常的做法是先用O0确认精度基线用O3观察速度上限然后在O1/O2中选择精度与速度平衡最佳者。统一 APIinitialize、scale_loss 与 master_params新版 Amp 将所有功能收敛到apex.amp模块下核心入口有三个amp.initialize、amp.scale_loss、amp.master_params。它们由init.py 统一导出initialize来自 frontend.pyscale_loss/disable_casts来自 handle.pymaster_params来自 _amp_state.py。amp.initialize 的完整签名与参数amp.initialize(models, optimizersNone, enabledTrue, opt_levelO1, cast_model_typeNone, patch_torch_functionsNone, keep_batchnorm_fp32None, master_weightsNone, loss_scaleNone, cast_model_outputsNone, num_losses1, verbosity1, min_loss_scaleNone, max_loss_scale2.**24)各参数含义依据 frontend.py 的 docstring 与实现modelstorch.nn.Module或模块列表即待修改/转换的模型。optimizers可选优化器或优化器列表训练时必需推理时可选。enabled默认True若为False所有 Amp 调用变为 no-op脚本如同未接入 Amp 一样运行。opt_level默认O1接受O0/O1/O2/O3。cast_model_type/patch_torch_functions/keep_batchnorm_fp32/master_weights/loss_scale均为可选属性覆盖任何非None的关键字参数都会被解释为手动覆盖。cast_model_outputs默认None确保模型输出始终转换为指定类型无论opt_level。num_losses默认1预先告知 Amp 将使用多少个 loss/backward 次数配合amp.scale_loss的loss_id参数可为每个 loss/backward 使用不同的损失缩放值提升稳定性。verbosity默认1设为 0 可抑制 Amp 相关输出。min_loss_scale默认None动态损失缩放可选值的地板不用动态缩放时忽略。max_loss_scale默认2.**24动态损失缩放可选值的上限不用动态缩放时忽略。合法调用形式模型与优化器均可为单个对象或列表返回形式与传入形式保持一致model, optim amp.initialize(model, optim, ...) model, [optim1, optim2] amp.initialize(model, [optim1, optim2], ...) [model1, model2], optim amp.initialize([model1, model2], optim, ...) [model1, model2], [optim1, optim2] amp.initialize([model1, model2], [optim1, optim2], ...)一个很实用的特性是loss_scale与keep_batchnorm_fp32都接受数值或字符串如128.0、dynamic、True/False便于与 argparse 直接互操作——这一点在 Imagenet 示例 中体现得很清楚它通过--opt-level、--keep-batchnorm-fp32、--loss-scale三个命令行参数接收字符串然后原样传给amp.initialize。initialize 内部做了什么frontend.py 的initialize依次完成检查torch.backends.cudnn.enabled若被关闭则抛出RuntimeErrorAmp 依赖 cudnn校验opt_level合法性并从opt_levels字典取回对应默认属性应用用户手动覆盖调用 _initialize.py 的_initialize执行真正的初始化。_initialize内部的关键步骤源码证据前置检查check_models拒绝传入已被torch.nn.parallel.DistributedDataParallel、apex.parallel.DistributedDataParallel或torch.nn.parallel.DataParallel包装的模型因为并行包装必须在amp.initialize返回之后再加如示例代码注释所强调如果先 DDP(model) 再 amp.initialize可能破坏 DDP 的 allreduce hookscheck_params_fp32检查模型参数/缓冲区必须位于 CUDA 且为 FP32若为 Half 或非 CUDA 会warn_or_err提醒不需要手动 .half()check_optimizers拒绝传入已被FP16_Optimizer包装的优化器。模型转换当cast_model_type存在时若keep_batchnorm_fp32True则调用apex.fp16_utils.convert_network跳过 BatchNorm完成转换否则直接model.to(cast_model_type)随后用patch_forward包装每个模型的forward进入的输入经applier递归转换为目标类型applier支持 Tensor、字符串、ndarray、自定义 batch 类的.to()方法、Mapping 与 Iterable 容器输出的结果默认转回torch.float32——这正是用户永远不需要调用 .half()的实现基础。优化器处理FusedAdam走wrap_fused_adam目前限制为opt_levelO2、keep_batchnorm_fp32False、loss_scale为数值或dynamic其余优化器走 _process_optimizer.py 的_process_optimizer按master_weights分支安装 lazy-init /_prepare_amp_backward/_post_amp_backward/_master_params_to_model_params等钩子方法。损失缩放器为每个num_losses创建LossScaler见 scaler.py。O1 补丁当patch_torch_functionsTrue时调用amp_init完成 Torch 命名空间的 patch见下文并把optimizer.step包装为在disable_casts()上下文内执行——因为 step 只应作用于 FP32 主参数。amp.scale_loss统一的反向传播入口amp.scale_loss是一个上下文管理器context manager用法为with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward()其语义依据 handle.py 的实现与 docstring进入上下文时创建scaled_loss loss.float() * 当前损失缩放值yield 给用户调用backward()退出上下文时若delay_unscaleFalse梯度会被检查是否含 inf/NaN 并进行反缩放unscale随后即可调用optimizer.step()。完整签名amp.scale_loss(loss, optimizers, loss_id0, modelNone, delay_unscaleFalse, delay_overflow_checkFalse)。其中loss_id配合initialize的num_losses实现每个 loss 独立的缩放器delay_unscaleTrue是小众的忍者级性能优化通常强烈建议保持默认False因为它会引入多模型/优化器/loss 场景下的怪异陷阱。两个重要警告来自 docstring若 Amp 使用显式 FP32 主参数O2默认如此或手动master_weightsTrue任何 FP16 梯度都会先拷贝为 FP32 主梯度再反缩放optimizer.step()应用反缩放后的主梯度到主参数上。此时只有 FP32 主梯度会被反缩放FP16 模型参数的直接.grad属性在退出上下文后仍是缩放过的——这一细节影响梯度裁剪裁剪应基于主参数进行。梯度溢出时的处理源码级scaler.py 的LossScaler承担缩放/反缩放与溢出检测。当检测到溢出inf/NaN且为动态缩放时update_scale()将损失缩放减半受min_loss_scale地板约束并返回should_skipTrue此时 handle.py 的scale_loss会把optimizer.step临时替换为skip_step打印 Gradient overflow. Skipping step... 并跳过本次更新同时清空不会由zero_grad清零的主梯度。若连续scale_window默认 2000次迭代无溢出则损失缩放加倍上限max_loss_scale默认2**24。此外LossScaler在有 C 扩展--cuda_ext --cpp_ext安装时使用amp_C.multi_tensor_scale/multi_tensor_axpby融合内核批量完成 unscale 与梯度拷贝通过multi_tensor_applier否则回退到 Python 实现并打印警告。amp.master_params遍历主参数def master_params(optimizer): for group in optimizer.param_groups: for p in group[params]: yield p这是 _amp_state.py 中定义的一个生成器迭代amp.initialize返回的优化器所拥有的参数。在 O2master weights模式下它产出的是 FP32 主参数——当需要对这些参数做梯度裁剪时见下文高级用法应使用master_params(optimizer)而不是model.parameters()。高级用法新版统一 Amp API 支持跨迭代的梯度累积、单迭代多次反向传播、多模型/多优化器、自定义用户定义autograd 函数以及自定义数据 batch 类。梯度裁剪与 GAN 也需要特殊处理但这些处理方式不随opt_level改变。文档中对应的高级主题详见仓库内的 advanced.rst。梯度裁剪基于主参数由于 O2 模式下 FP16 模型参数的.grad仍是缩放过的正确做法是使用amp.master_params(optimizer)拿到 FP32 主参数再对主参数做裁剪# 推荐对主参数进行梯度裁剪 torch.nn.utils.clip_grad_norm_(amp.master_params(optimizer), max_norm)梯度累积与多次 backwardamp.scale_loss支持跨迭代梯度累积与单次迭代多次反向传播。若使用delay_unscaleTrue则本次 backward 退出后还不能调用optimizer.step()必须等后续某次 backward 以delay_unscaleFalse默认结束后再 step。多模型 / 多优化器 / 多 lossamp.initialize接受模型列表与优化器列表amp.scale_loss的optimizers参数可传单个优化器或优化器列表。当存在多个 loss 时通过num_lossesN与loss_idi组合Amp 为每个 loss 维护独立的损失缩放器model1, model2 ... optim1, optim2 ... [model1, model2], [optim1, optim2] amp.initialize( [model1, model2], [optim1, optim2], opt_levelO1, num_losses2) with amp.scale_loss(loss1, [optim1, optim2], loss_id0) as scaled_loss1: scaled_loss1.backward() with amp.scale_loss(loss2, [optim1, optim2], loss_id1) as scaled_loss2: scaled_loss2.backward() optimizer1.step(); optimizer2.step()即便num_losses保持默认 1Amp 也支持多个 loss/backward只是所有 loss 共享同一个全局损失缩放值。GAN 与自定义 batch 类GAN生成器与判别器可分别作为独立模型传入amp.initialize当需要生成器不更新只做判别之类的冻结场景时_initialize.py的to_type对requires_grad的输入 Tensor 有专门注释GANs require this说明其对输入数据的处理已考虑 GAN 场景。自定义数据 batch 类_initialize.py的applier函数会检查对象是否有.to()方法并调用它来转换 batch 内的浮点 Tensor——因此自定义 batch 类只需实现to(dtype)方法即可被patch_forward正确转换。从旧 API 迁移Transition GuideAmp 强烈建议迁移到新 API因为它更通用、更易用、面向未来原有的FP16_Optimizer类与旧版 Amp API 已被弃用deprecated随时可能被移除。旧 Amp API 用户在新 API 中opt_levelO1执行与旧 Amp 相同的 Torch 命名空间 patch。区别在于新 API 支持静态或动态损失缩放而旧 API 只支持动态损失缩放。迁移要点删除旧的amp_handle amp.init()调用及返回的amp_handle——amp.initialize()已承担并超越了amp.init()的职责原来通过amp_handle暴露的函数现在是amp模块下的自由函数反向传播上下文管理器需相应修改# 旧 API with amp_handle.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward() # - # 新 API with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward()值得注意的是旧 API 中为用户函数标注特定精度的调用如amp.half_function、amp.float_function、amp.promote_function以及注册形式的amp.register_half_function/register_float_function/register_promote_function实现见 amp.py在新 API 中仍然被支持。旧 FP16_Optimizer 用户opt_levelO2等价于FP16_Optimizer配合dynamic_loss_scaleTrue。反向传播同样需要改为统一版本optimizer.backward(loss) # - with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward()旧FP16_Optimizer的一个痛点在于用户必须手动将模型转为 half调用.half()或使用apex.fp16_utils的包装函数还必须手动对输入数据调用.half()。新 API 中这两者都不再需要——无论选择哪个--opt-level你都应该用默认 FP32 格式构建模型和传递输入数据Amp 会在model, optimizer amp.initialize(...)期间根据opt_level及覆盖标志自动完成正确的转换。浮点输入数据可以是 FP32 或 FP16但更简单的做法是直接保持 FP32——amp.initialize返回的模型其forward已被 patch会自动将输入数据转换到合适类型。一个完整的实践示例结合仓库中的 Imagenet 示例一个接入 Amp 的典型训练脚本骨架如下省略数据加载等细节import torch from apex import amp # 1. 以 FP32 构建模型与优化器 model model.cuda() optimizer torch.optim.SGD(model.parameters(), lrargs.lr, momentumargs.momentum, weight_decayargs.weight_decay) # 2. 初始化 Amp先于任何 DDP 包装 model, optimizer amp.initialize(model, optimizer, opt_levelargs.opt_level, # 如 O1 keep_batchnorm_fp32args.keep_batchnorm_fp32, loss_scaleargs.loss_scale) # 如 dynamic # 3. 分布式场景包装 DDP 必须在 amp.initialize 之后 if args.distributed: model DDP(model, delay_allreduceTrue) # 4. 训练循环 for epoch in range(args.start_epoch, args.epochs): for i, (input, target) in enumerate(train_loader): output model(input) loss criterion(output, target) with amp.scale_loss(loss, optimizer) as scaled_loss: scaled_loss.backward() optimizer.step() optimizer.zero_grad()命令行调用示例对应示例仓库中由 argparse 接收的参数python main_amp.py --arch resnet50 --opt-level O1 --loss-scale dynamic python main_amp.py --arch resnet50 --opt-level O2 --keep-batchnorm-fp32 TrueAmp 的安装方式参见仓库 README推荐带 CUDA/C 扩展安装pip install -v --no-cache-dir --global-option--cpp_ext --global-option--cuda_ext .这样可启用multi_tensor_apply融合内核unscale、梯度拷贝、master weight 拷贝均受益纯 Python 安装PyTorch 0.4 时代的需求下amp仍然可用但会退化为 Python 回退实现并可能更慢。小结apex.amp的设计哲学是让用户始终以 FP32 视角编写训练脚本把精度策略交给 opt_level。本文梳理了文档 amp.rst 的核心内容六个底层属性cast_model_type、patch_torch_functions、keep_batchnorm_fp32、master_weights、loss_scale构成了精度策略的原子单元O0–O3 四个级别提供了开箱即用的默认组合amp.initialize/amp.scale_loss/amp.master_params三个入口覆盖了训练的全部关键环节而旧 API 的迁移路径清晰且无损旧式函数标注仍然有效。结合 frontend.py、_initialize.py、_process_optimizer.py、scaler.py 与 handle.py 等源码可以看到每个 API 承诺背后都有完整的实现支撑。初次尝试时从O1最安全出发用O0/O3建立精度与速度基线再探索O2的 master weights 收益是一个稳妥的实践路径。赞分享人工智能大模型音乐生成音频预训练【免费下载链接】jukeboxCode for the paper Jukebox: A Generative Model for Music项目地址https://gitcode.com/gh_mirrors/ju/jukebox点击查看免费下载相关推荐Ignite混合精度训练实战AMP与Apex的完整对比Ignite混合精度训练实战AMP与Apex的完整对比 PyTorch Ignite是一个高级库用于帮助在PyTorch中灵活透明地训练和评估神经网络。在深深度学习模型训练使用 Apex AMP 混合精度训练 DCGANexamples/dcgan/main_amp.py 实战指南使用 Apex AMP 混合精度训练 DCGAN examples/dcgan/main_amp.py 实战指南 本指南围绕 Apex 仓库中 example人工智能深度学习分布式训练模型优化Colossal-AI 混合精度训练完全指南基于 Booster 的 AMP 配置与实战Torch/Apex/Naive AMPColossal AI 混合精度训练完全指南基于 Booster 的 AMP 配置与实战Torch/Apex/Naive AMP 导读 本文以 Colos人工智能大模型分布式训练深度学习模型优化高性能计算上一篇PageMenu菜单项居中布局打造优雅视觉平衡的终极指南下一篇嵌入式数据库sled高级应用合并操作符与触发器实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

使用 AWS SDK for Kotlin 操作 Amazon Data Firehose:创建、写入与删除 Delivery Stream 实战指南
使用 AWS SDK for Kotlin 操作 Amazon Data Firehose:创建、写入与删除 Delivery Stream 实战指南

示例工程教程后端 【免费下载链接】aws-doc-sdk-examples Welcome to the AWS Code Examples Repository. This repo contains code examples used in the AWS documentation, AWS SDK Developer Guides, and more. For more information, see the Readme.md file below. 项目地… · 2026/9/26 15:48:01

给爸妈配吸附性义齿,做子女的要先弄清哪几件事?/钟祥小灰兔科普
给爸妈配吸附性义齿,做子女的要先弄清哪几件事?/钟祥小灰兔科普

咱们钟祥人讲孝心,都是实打实的。上回在阳春大街碰见老同学,他说给老爷子买了副新假牙,结果老爷子吃饭还是嫌松,打喷嚏的时候赶紧用手捂着嘴,生怕假牙“跑”出来。这场景,好多街坊家里是不是都见过&#xf… · 2026/9/26 15:47:55

DeepSearcher 接入 Docling:本地文件加载与 Web 爬取一体化实战指南
DeepSearcher 接入 Docling:本地文件加载与 Web 爬取一体化实战指南

人工智能大模型RAGAI Agent深度研究知识库 【免费下载链接】deep-searcher Open Source Deep Research Alternative to Reason and Search on Private Data. Written in Python. 项目地址: https://gitcode.com/gh_mirrors/de/deep-searcher 点击查看 免费下载 本指… · 2026/9/26 15:47:55

14 天 Markdown 实战入门(VS Code 版)-- 第 13 章:导出与发布:HTML / PDF / Word、静态站点、GitHub 练习题
14 天 Markdown 实战入门(VS Code 版)-- 第 13 章:导出与发布:HTML / PDF / Word、静态站点、GitHub 练习题

如果你是通过搜索看到本篇文章,可以 点击这里 访问对应的正文文章。 1. 练习题 请先独立完成,再看参考答案。 题目 1:选择题 以下哪个工具可以导出 Markdown 为 PDF? A. VS Code 内置预览 B. Markdown Preview Enhanced C. Pandoc D. 以上都可以 题目 2:判断题 Pan… · 2026/9/26 16:27:06

libiec61850 1.5.1 新版本:TaoToken 统一 Key 接入与 settings.json 配置骨架
libiec61850 1.5.1 新版本:TaoToken 统一 Key 接入与 settings.json 配置骨架

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

STM32连接红外PM2.5传感器:GP2Y1010采样时序与浓度换算指南
STM32连接红外PM2.5传感器:GP2Y1010采样时序与浓度换算指南

STM32连红外PM2.5传感器,这个需求我在毕业设计和DIY环境监测项目里见得最多。红外式PM2.5传感器,典型代表是夏普GP2Y1010AU0F、GP2Y1014AU0F和韩系DSM501,核心原理是红外LED发射光束,照射到采样腔里的颗粒物后产生散射&#xff0c… · 2026/9/26 16:27:06

Unix与Linux核心差异解析:从内核原理到发行版选型实战
Unix与Linux核心差异解析:从内核原理到发行版选型实战

1. 从一通电话说起:为什么今天还要聊 Unix 和 Linux前几天一个做嵌入式的朋友打电话问我,说他接手了一个老项目,代码里到处是#ifdef __unix__和#ifdef __linux__的宏判断,他搞不清楚这两个到底是不是一回事,能不能直接… · 2026/9/26 16:27:06

SVPWM过调制详解:从母线电压利用率到FOC工程落地
SVPWM过调制详解:从母线电压利用率到FOC工程落地

做 FOC 电机控制做到中后期,几乎人人都会在母线电压利用率上碰一次壁。速度环的指令已经给满,电流环也在安全线上挣扎,可电机就是顶不上去;换个角度看,母线明明有 48V,折算到相电压却总是差一口气。我早期的… · 2026/9/26 16:27:06

5. 在 Mac 上使用 Python
5. 在 Mac 上使用 Python

Bob运行 macos系统的mac上的情况, 在原则方面与其他unix平台的情况是非常相似的。但是有一些额外特性是值得我们指出的。这些额外特性包括集成开发环境以及包管理器。5.首先要去完成获取以及安装这两个动作。macos 这个操作系统呀, 在从它的 10.8 版本一直到现在的某些特定版本… · 2026/9/26 16:27:00

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

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

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码