万字长文,YOLO 模型训练提升指南:快速掌握核心技巧 | 基础篇

YOLOv8/v10/11 模型训练技巧 | 基础篇

作者:CSDN博主 迪菲赫尔曼

引言

大家好,这里为大家准备了一些简单实用的训练小技巧。这些技巧不仅能帮助大家快速上手训练,还能节省不少时间,提高训练效率。无论是刚开始学习计算机视觉还是已经有一些经验,希望这篇指南能帮助你少走弯路,更轻松地训练出高质量的模型。当然这些技巧不仅仅适用于 YOLO 模型 ,这些适用于所有的深度学习模型。



什么是机器学习模型的训练?

在这里插入图片描述
模型训练的过程可以想象成教孩子认识各种物体的过程。比如,一开始会给孩子看很多图片,这些图片已经标记好了是什么内容,比如苹果、橘子、汽车等等。孩子看到图片后会尝试说出它是什么,我们会告诉他对错,从而让他学会正确识别。类似地,模型一开始接收大量已经标记好的图像,尝试对这些图像进行预测,然后将结果与真实标签进行比较,看看哪里错了。

每次出错时,模型会根据这些错误信息调整内部的参数(比如权重和偏差),这个过程叫做“反向传播”。就像孩子在不断练习中逐渐记住了物体的样子,模型通过多次调整,慢慢学会了识别图像中的特征,比如形状、颜色、纹理等。

如果一开始给模型看很多苹果和橘子的图片,它可能一开始无法区分,但经过不断的训练和调整后,它能学会通过形状和颜色来区分苹果和橘子。最终,模型不仅能够识别这些简单的物体,还能更复杂地理解图像中的内容,例如在图像中找到特定的对象(对象检测)、对图像进行详细的划分(实例分割),或者对整个图像进行分类(图像分类)。

当你已经了解了模型训练的基本流程后,接下来就可以深入的学习如何优化 YOLO 模型的训练,以及在整个训练过程中需要重点关注的关键因素。


大数据集训练的挑战与对策

在训练模型的过程中,我们经常会遇到训练速度缓慢、精度不足、模型不收敛等问题,还容易陷入一些常见的误区。接下来,让我们逐一解释这些问题的原因及解决方法。


批量大小:加速模型训练最有效方法

在这里插入图片描述
批量大小(batch size)指的是每次训练迭代中,模型处理的图像数量。选择合适的批量大小可以有效利用 GPU 资源,减少训练时间。批量大小的选择直接影响显存(GPU 的内存)占用以及 GPU 的利用率。

显存是 GPU 上专门用于存储模型参数和训练数据的内存。如果批量大小过大,显存可能会被耗尽,从而导致内存不足的错误(也就是我们经常看到的 OOM,CUDA Out Of Memory,也叫显存爆炸)。而 GPU 利用率则表示 GPU 的计算资源的使用程度。理想情况下,我们希望 GPU 利用率越高越好,因为这表示 GPU 在全速工作,训练速度会更快。
在这里插入图片描述

批量大小和训练速度之间的关系是,如果批量大小过小,GPU 可能无法被充分利用,导致训练速度变慢。而批量大小越大,虽然可以提高 GPU 利用率,但也会增加显存的使用量,甚至可能导致内存不足。在大数据集训练时,建议使用 GPU 能够支持的最大批量大小,以确保高效利用其性能。

如果在训练过程中遇到内存不足的问题,可以逐步减小批量大小,直到模型能顺利进行训练。对于 YOLOv8/v10/v11,可以在训练配置中将 batch_size 设置为合适的值,或者将 batch=-1 以自动确定最佳批量大小。这种自动设置的方式可以帮助根据硬件条件找到一个平衡点,既充分利用 GPU 资源,又避免显存耗尽。

此外,在写论文时,可能会考虑是否保持 batch_size 一致,这一点确实会对模型的精度产生影响,因为不同的 batch_size 会影响模型在每个迭代中看到的数据量,这将间接影响模型的梯度更新方式和参数优化过程。较大的 batch_size 通常会导致模型在每次更新中更稳定,但可能对局部特征不敏感;而较小的 batch_size 则更容易捕捉到数据中的细节和随机性,但可能导致训练过程中的波动更大。因此,batch_size 不一致会影响模型最终的收敛效果和精度表现。


子集训练:节省时间的好方法

在这里插入图片描述
在初期测试阶段,如果完整训练模型过于耗时,可以选择子集训练。所谓子集训练,就是只用数据集中的一部分数据来训练模型。这样可以快速迭代,找到最佳的模型结构。然而需要注意的是,子集训练虽然能够在初期快速验证模型的效果,但并不代表这个模型在整个数据集上也能表现良好,因为子集可能无法涵盖所有数据的多样性,导致模型在完整数据集上泛化性能不足。

在 YOLO可以通过 fraction 参数来实现子集训练,例如设置 fraction=0.1 表示只用 10% 的数据集进行训练。这样可以在短时间内进行多次试验,以便在最终完整训练前找到最佳参数配置。


数据增强:提升模型样本量

在这里插入图片描述

YOLOv8 提供了很多数据增强的参数,这些参数可以帮助增加样本的多样性,从而提升模型的泛化能力,但在使用时要妥善选择,结合业务场景进行适当的调整,避免由于不当的增强方式导致模型性能的下降。例如,在某些场景下,过度的形变可能会使图像失真,影响模型的学习效果。举个更具体的例子,如果您在检测交通标志时使用了旋转或剪切增强,可能会导致某些标志的形状和方向发生变化,从而使模型误判。比如‘限速标志’如果经过了较大的旋转角度,模型可能会因为图像失真而无法正确识别其具体的限制速度。此外,在人脸识别任务中,过度的色调或饱和度增强也可能使得人脸特征变得不明显,导致模型精度下降。因此,在使用这些数据增强方法时,建议根据业务场景进行选择,例如在对方向敏感的对象检测中避免使用翻转或大幅旋转增强。

  • hsv_h: 0.015 - 图像 HSV 色调增强(百分比),可以稍微改变图像的色调来模拟不同光照条件。
  • hsv_s: 0.7 - 图像 HSV 饱和度增强(百分比),增加或减少颜色的饱和度来增强模型在不同色彩条件下的表现。
  • hsv_v: 0.4 - 图像 HSV 亮度增强(百分比),调整图像的亮度,模拟不同光照强度。
  • degrees: 0.0 - 图像旋转(正负度数),轻微旋转图像可以增强模型的方向不变性。
  • translate: 0.1 - 图像平移(正负百分比),模拟对象在图像中的不同位置。
  • scale: 0.5 - 图像缩放(正负增益),通过调整对象的大小来提升模型对不同尺度下对象的适应能力。
  • shear: 0.0 - 图像剪切(正负度数),用于模拟对象的倾斜变化。
  • perspective: 0.0 - 图像透视(正负百分比),用于模拟透视失真的变化。
  • flipud: 0.0 - 上下翻转图像(概率),适用于某些对方向不敏感的对象检测。
  • fliplr: 0.5 - 左右翻转图像(概率),通常用于对象检测来增强模型对对称物体的检测能力。
  • bgr: 0.0 - 图像通道 BGR(概率),用于在 BGR 和 RGB 颜色空间之间切换。
  • mosaic: 1.0 - 图像 Mosaic(概率),将多张图像拼接在一起,增加复杂度。
  • mixup: 0.0 - 图像 Mixup(概率),将多张图像融合,增强数据的多样性。
  • copy_paste: 0.0 - 复制粘贴增强(概率),将图像中的某些部分复制并粘贴到其他图像中,增强样本的多样性。
  • copy_paste_mode: "flip" - 复制粘贴的方式,可以选择翻转、Mixup 等增强方法。
  • auto_augment: randaugment - 自动增强策略,用于分类任务,可选择 randaugmentautoaugmentaugmix
  • erasing: 0.4 - 在分类训练中随机擦除的概率,范围为 0-0.9,用于模拟部分遮挡。
  • crop_fraction: 1.0 - 图像裁剪比例,用于分类,范围为 0.1-1,1.0 表示不裁剪。

在检测交通标志的场景中,fliplr 左右翻转增强可能是不合适的,因为某些标志翻转后意义就完全变了,这时就需要结合实际业务场景,避免因不合适的数据增强而引起模型性能的下降。


缓存:提升数据处理速度

在这里插入图片描述

缓存是一种减少数据加载时间、提升训练效率的重要手段。通过将预处理后的图像保存在内存(RAM)或磁盘中,可以减少每次迭代时的数据读取时间。

RAM 和磁盘的区别

  • RAM(随机存取存储器)是计算机中的高速内存,用于临时存储当前正在处理的数据。它的读写速度非常快,所以在训练过程中将数据缓存到 RAM 中可以大大提高读取速度。但 RAM 的容量通常较小,而且较昂贵,因此在处理非常大的数据集时可能会遇到内存不足的问题。
  • 磁盘(例如硬盘或固态硬盘)是用于长期存储数据的设备,容量大且价格相对低廉,但读写速度较 RAM 慢。将数据缓存到磁盘上可以节省 RAM 空间,但读取速度相比于 RAM 要慢一些。

在 YOLOv8/v10/v11 中,可以通过 cache 参数来设置缓存策略:

  • 设置 cache=True 会将图像缓存到 RAM 中,读取速度最快,但需要较大内存。
  • 设置 cache='disk' 则将图像缓存到磁盘上,比从头读取快,但比使用 RAM 慢
  • 如果禁用缓存(cache=False),每次都需要从硬盘读取数据,速度最慢

线程:提升数据加载速度

在这里插入图片描述

workers 指在数据加载过程中所使用的工作线程或进程数目。在深度学习中,特别是在处理大规模数据集时,数据加载效率对训练速度有很大的影响。workers 参数的设置能够显著影响模型训练的整体效率。

在 YOLOv8/v10/v11 的数据加载器中,可以通过 workers 参数来指定数据加载过程中使用的并行加载线程或进程的数量:

  • workers=0:表示使用主线程来加载数据,不使用并行处理。这通常会导致数据加载成为训练的瓶颈,因为主线程在加载数据时无法同时进行训练。因此,这种设置只适合小数据集或实验环境。
  • workers>0:表示使用多个工作线程或进程来并行加载数据,可以有效地减少数据加载的等待时间,尤其是在磁盘读取或预处理较耗时的场景下。通常来说,更多的 workers 可以更好地利用多核 CPU 的能力,从而加速数据的读取和预处理过程。

在设置 workers 的值时,需要考虑几个关键因素:

  1. CPU 核心数:如果设置的 worker 数量超过了可用的 CPU 核心数,可能会导致 CPU 负载过高,从而影响整体性能。一般建议将 worker 数量设置为 CPU 核心数的 1/2 到 2 倍,以便合理利用 CPU 资源。

  2. 数据集的大小和预处理复杂度:如果数据集非常大,或者需要复杂的图像预处理步骤,可以增加 worker 数量来加速数据加载。反之,如果数据集较小或预处理较简单,过多的 workers 反而可能造成不必要的资源浪费。

  3. 内存(RAM)限制:增加 workers 数量也会增加内存消耗,因为每个 worker 可能会缓存一定数量的预处理数据。因此,在设置较多 workers 时,需要确保内存充足,避免因内存不足导致系统性能下降。

workers 参数和 cache 参数可以结合起来使用,以最大化数据加载的效率。例如可以使用 cache=True 将图像缓存到 RAM 中,达到最快读取速度,同时使用多个 workers 来加速数据的加载和预处理。这种组合可以显著减少数据加载成为训练瓶颈的可能性,从而提升整体训练效率。一般机器下 workers 最少可以设置成 8


混合精度训练:提升效率的利器

在这里插入图片描述

混合精度训练结合使用 16 位(FP16)和 32 位(FP32)浮点数来进行计算,既提高了计算速度,又节省了内存。现在大多 GPU 都支持这种技术,它能让模型在保持精度的同时更快地进行训练。

混合精度训练简单来说就是利用 FP16 来进行大部分计算操作,而在需要高精度时则使用 FP32。FP16 浮点数占用的内存更小,因此可以在一次计算中处理更多的数据,使得 GPU 的计算单元利用率提高。由于 FP16 的计算量更低,所以训练速度更快。与 FP32 相比,FP16 数值需要的内存大小减少了一半,因此可以显著降低显存占用量。这意味着我们可以在同样的显存下使用更大的批量大小(batch size),从而提高模型的训练效果和收敛速度。

在 YOLOv8/v10/v11 中,只需在训练配置中设置 amp=True 即可启用混合精度训练,这是一种简单且有效的方法来加速模型训练并减少内存占用。

然而需要注意的是,在一些较低版本的 PyTorch 中,开启混合精度训练可能会遇到兼容性问题,导致训练报错。因此,在使用混合精度训练之前,个人建议 PyTorch 版本在 2.0 以上。


使用预训练权重:快速接近模型上限

在这里插入图片描述

使用预训练权重可以显著缩短模型的训练时间。所谓预训练权重,就是在大数据集上预先训练好的一组模型参数,你可以直接用它们来微调适应你的数据集。这些权重并不是随便用的,必须确保它们与你的模型结构相匹配,因为预训练权重是根据特定的模型架构训练出来的,权重和模型结构需要对应,否则可能导致模型无法正确加载或训练。

使用预训练权重的好处包括:大大减少了训练时间、提高了模型的初始性能以及减少了对大规模计算资源的依赖。因为预训练权重已经学习到了基础的特征(例如边缘、颜色、形状等),所以它们可以为你的任务提供一个良好的起点,尤其是在数据有限的情况下,迁移学习能够帮助模型更快地收敛。

不过在使用预训练权重进行研究和写论文时,需要注意控制变量法,确保其他实验条件一致,避免由于预训练权重的不同而导致结果不具有可比性。

在 YOLOv8/v10/v11 中,通过设置 pretrained=True 或指定预训练模型的路径,可以快速加载这些权重,以提升训练效果。这种方法被称为迁移学习,它可以让模型从一开始就具备对基础特征的理解,从而更好地适应新的任务。


冻结:迁移学习的核心概念

权重这部分除了 pretrained 参数外还有一个 freeze 参数,

freeze 参数用于在模型的迁移学习过程中控制哪些层的权重保持不变,也就是冻结某些层。这在迁移学习中非常重要,因为它可以帮助我们在微调过程中锁定某些基础特征层,专注于调整模型的高层特征,从而更快地适应新任务,尤其是当数据量有限时,这种方法可以有效减少过拟合风险。

当模型使用预训练权重时,前几层通常会学习一些通用的特征(例如,卷积神经网络中的边缘检测、纹理、颜色等)。这些基础特征对大多数图像任务都是有用的。因此,我们可以选择冻结这些层,不再更新它们的权重,从而更快地微调模型。

通过冻结较早的层,仅更新与特定任务相关的高层特征,可以减少训练时间,提高效率。这在处理较小的数据集时特别有帮助,因为冻结这些层可以减少过拟合的风险。

freeze 参数的常见设置方法

冻结底层层数:通常你可以设置 freeze=0freeze=1freeze=2 等,这样可以冻结模型的前几层;或者可以写成列表 freeze=[0, 3, 6] 冻结你指定的层。这些层通常提取较低级别的特征(例如,边缘和简单的形状),因此它们对各种任务都是通用的,不需要再重新学习。

如果设置 freeze=None,那么不冻结任何层,所有的层都会更新,这意味着模型会从预训练权重的基础上,继续对所有层进行训练,这种方式对于大规模的数据集比较合适,特别是新任务的特征和预训练任务有很大不同的时候。

冻结大部分层,只更新后面层:通常可以只微调最后几层,这样做的好处是利用预训练网络前面学到的通用特征,只对更高级的、与特定任务相关的特征进行调整。


提高训练效率的其他技巧

分布式训练

在这里插入图片描述

如果你的数据量非常庞大,可以考虑使用分布式训练,即将训练任务分配到多个 GPU 或机器上,以减少训练时间。通过设置 device 参数,可以轻松实现多卡训练。例如,可以将 device="cuda:0"device="0,1,2,3" 这样指定 GPU 设备,以利用多个显卡进行训练,这样可以有效加快模型的训练速度。许多深度学习框架,如 PyTorch,都提供分布式训练的支持。

此外,可以使用 nvidia-smi 命令查看显卡的使用情况,包括显存占用、显卡编号等信息,这对于分布式训练中合理分配显卡资源非常有帮助。

在这里插入图片描述


选择训练周期数

在这里插入图片描述

训练周期数(epochs)是指模型遍历整个数据集的次数,有时候也叫“轮数”。一般情况下,300 轮就足够了。少数的数据集可能需要更多的训练轮数,但如果需要的收敛轮数大于 300,建议考虑稍微调大学习率,这样可以帮助模型更快地收敛。

在 YOLOv8/v10/v11 中,可以通过训练脚本中的 epochs 参数来设置训练周期数,以适应不同的项目需求。


提前停止:防止过拟合

在这里插入图片描述

提前停止(早停)是一种通过监控模型在验证集上的表现来确定何时结束训练的方法。

早停具体关注的指标的变化是加权计算出来的,即 0.1 * mAP0.5 + 0.9 * mAP0.5-0.95

详细的代码位置在 ultralytics/ultralytics/utils/metrics.py

在这里插入图片描述

早停可以避免模型过拟合,从而节省计算资源和训练时间。提前停止的核心思想是在验证集的表现不再提升时及时终止训练,这样可以防止模型在训练集上过度拟合,导致在新数据上的性能下降。

在 YOLOv8/v10/v11 中,可以通过设置 patience 参数来启用提前停止。例如,设置 patience=5 表示如果验证集指标在连续 5 个周期内没有改善,训练将会停止。此外,还可以结合其他超参数,如学习率调度器,来进一步提升模型的泛化能力,使得模型能够在适当的时间点结束训练并达到最佳性能。

早停函数的代码在 ultralytics/ultralytics/utils/torch_utils.py

在这里插入图片描述


云训练与本地训练

在训练模型时,可以选择在云端进行训练,或者在本地机器上进行训练。

云训练具有强大的可扩展性,适合处理大数据集和复杂模型。云平台如 AWS、Google Cloud、Azure 等提供按需的高性能 GPU,可以加快训练速度。然而,云训练成本较高,特别是长时间使用时,数据传输的成本和延迟也需要考虑。

国内也有很多优秀的云计算厂商,例如我们常见的 autodl,以及蓝耘等。点击这个链接,可以领取 50 元的算力券,除了官方注册的20外,还有点击链接领取的我的粉丝专属20元,足够使用 24 小时的 4090 显卡,非常适合想要尝试云训练的用户。

在这里插入图片描述

本地训练则能提供更高的控制性和安全性,适合长期项目,因为数据留在本地,不需要担心隐私问题。但本地机器的资源可能有限,训练大型模型时可能会耗费更多时间。此外,不推荐使用个人笔记本电脑进行训练,即便是配有 4090 显卡的笔记本,其显存和整体性能依然不如台式机,特别是在处理大型数据集时,可能会遇到显存不足的问题。


如何选择优化器

在这里插入图片描述

优化器负责调整模型的参数,以最小化损失函数,进而提高模型的性能。选择合适的优化器能显著影响模型的收敛速度和准确性,目前 YOLO 项目内置多种的优化器供大家选择:

  • SGD(随机梯度下降):SGD 是一种简单且经典的优化器,适合大多数场景,但在复杂模型上收敛较慢,且可能陷入局部最小值。它每次只使用一个小批量的数据来更新模型参数,因此在优化过程中容易受到噪声影响,但是这也是最常用的优化器,一般用这个都要跑300轮。

  • Adam(自适应矩估计):Adam 结合了动量和学习率自适应调整的优点,适用于有噪声的数据和稀疏梯度的场景。它通过结合过去梯度的指数移动平均来调整每个参数的学习率,使得收敛速度快,且通常效果较好,因此也是 YOLOv8/v10/v11 推荐的优化器,用这个优化器训练的话,轮数要比 SGD 少一些,但是整体时间对比下来差别不是太大。

  • Adamax:Adamax 是 Adam 的变体,适用于处理不稳定或异常值的数据。它使用无穷范数来控制学习率的更新,能够有效处理梯度幅度变化较大的情况,适合一些需要稳定性的训练场景。

  • AdamW:AdamW 是对 Adam 的改进版本,它在原始 Adam 的基础上加入了权重衰减(weight decay)项,用于防止过拟合。相比于 Adam,AdamW 更适合大型神经网络,特别是在训练深层模型时表现更加优异。

  • NAdam(带有动量的 Adam):NAdam 是 Adam 的改进版,结合了 Nesterov 动量加速的方法,使得每次更新参数时可以预估未来的梯度方向,从而加速收敛。它在某些场景下比 Adam 表现更好,特别是在模型难以快速收敛的情况下。

  • RAdam(Rectified Adam):RAdam 是针对 Adam 收敛过程不稳定的问题提出的一种改进优化器。它通过自适应地调整学习率来解决早期阶段收敛不稳定的问题,从而提高训练过程的稳定性,尤其是在初期训练阶段,能够让模型更稳健地学习。

  • RMSProp(均方根传播):RMSProp 是一种通过调整每个参数的学习率来解决梯度消失问题的优化器,通常适用于循环神经网络(RNN)和 LSTM 等模型。它会根据过去的梯度平方的均值来动态调整学习率,使得训练在复杂损失函数的表面能更平滑地进行。

在 YOLOv8/v10/v11 中,可以通过 optimizer 参数选择优化器,包括 SGD、Adam、AdamW、NAdam、RAdam 和 RMSProp 等,或者直接设置为 auto,让系统自动选择最适合的优化器。

优化器的相关代码在 ultralytics/ultralytics/engine/trainer.py

在这里插入图片描述


那么基础篇就先给大家分享这么多内容,这些都属于正常的优化范畴,下期给大家介绍一些炼丹老司机使用的实用作弊技巧~


有问题欢迎大家指正,如果感觉有帮助的话请点赞支持下👍📖🌟

请添加图片描述

YOLO (You Only Look Once) 模型训练过程中如果遇到精度长时间不变的情况,通常意味着遇到了“平台期”现象或是其他潜在的问题。下面是一些可能导致 YOLO 训练精度停滞的原因及解决方案: ### 可能原因分析 1. **学习率过高或过低**:不合适的学习率可能会导致模型无法有效更新权重,进而影响精度提升。 - *解决办法*:尝试调整初始学习率,并采用动态学习率策略如余弦退火、阶梯式衰减等。 2. **数据集质量不佳**:标注错误、样本不平衡等问题都会阻碍模型收敛至更高的准确度水平。 - *解决办法*:检查并清理异常标签;对于类别分布极不均衡的数据集可以考虑引入加权损失函数或其他正则化手段。 3. **缺乏足够的多样性**:当输入图像过于单一时,网络难以学到泛化的特征表示。 - *解决办法*:通过增强技术增加数据量及其变化程度,例如随机裁剪、旋转翻转、颜色抖动等方式生成更多样化的训练素材。 4. **超参数配置不当**:某些关键性的设置比如批量大小(batch size),锚点框(anchor boxes), 网络结构选择等因素也会影响最终效果。 - *解决办法*:基于现有研究结果合理设定这些值,必要时可通过网格搜索法找到最优组合。 5. **硬件资源限制**:GPU显存不足使得只能使用较小批次进行迭代优化,不利于充分挖掘深层神经元之间的关联特性。 - *解决办法*:尽可能分配更多的计算资源给任务执行过程,若条件不允许,则需折衷寻找适合当前环境的最佳实践方案。 6. **早停机制误触发**:部分框架默认设置了早期停止(Early Stopping),这会在一定轮次内验证性能未见改善即终止程序运行。 - *解决办法*:确认是否开启了此类功能以及其阈值范围,适当放宽标准继续观察后续进展。 7. **梯度过大/消失问题**:特别是在较深层次级联下容易发生这种情况,造成反向传播效率低下甚至失效。 - *解决办法*:应用Batch Normalization层稳定激活状态转移路径;利用ResNet残差连接缓解长距离依赖带来的挑战。 8. **预处理步骤缺失或失误**:诸如归一化(normalization)、尺寸标准化(resizing)等方面若有疏忽同样会干扰到实际识别能力的发展轨迹。 - *解决办法*:严格按照官方文档推荐流程完成前置准备工作,保证所有元素都处于预期范围内工作。 9. **版本兼容性和库冲突**:不同版本间API差异或者第三方依赖项存在竞争状况均有可能引发意外后果。 - *解决办法*:确保所使用的工具链保持最新且一致的状态,避免不必要的插件加载负担系统性能开销。 --- 综上所述,针对YOLO模型训练过程中出现的精度增长瓶颈现象,可以从以上几个方面逐一排查和改进,结合具体的业务场景做出针对性调整,从而突破现有的精确度极限。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迪菲赫尔曼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值