探究大模型微调 Lora 的不同形态(上篇): AdaLora、 AsLora、 PiSSA、 DoRA

peft代码解读:Prefix Tuning/LoRA/P-Tuning/Prompt Tuning peft 阅读详情

前言

最近本人一直在研究 SFT 的落地工作,其中 LoRA 是一个无法避开的微调技术。相比全参数微调,LoRA 提供了一种更为友好和安全的微调方案。

首先,LoRA 在资源使用上更为高效。它能显著节省显存,使得我们在有限的计算资源下也能训练 size 更大的模型。其次,LoRA 具有一定的正则化效果,类似于 dropout 或 mask。这种特性使得模型在学习下游任务时,能够减少遗忘现象, 关于这方面的详细分析,可以参考 LoRA Learns Less and Forgets Less。

https://arxiv.org/pdf/2405.09673

然而,在实际应用中,本人还存在很多疑问。例如,为什么 LoRA 的收敛速度相对全参数微调很慢?LoRA 能否达到与全参数微调相媲美的效果?如何选择 LoRA 的 rank 值?这些问题都值得深入探讨。

为了深入挖掘 LoRA 的潜能,解答这些问题并探索 LoRA 在大模型微调中的各种可能性。希望通过这篇文章,能为大家带来新的视角和思考。

前置的线代代数知识

在讨论各种方法之前,有必要先介绍下线性代数里面的两个知识点,因为跟后面提出的方法紧密相关。

  • 矩阵的秩:大家知道,矩阵在大多数情况下代表映射。而矩阵的秩几何意义就是映射后的目标空间是几维的,我们把它称为像空间(目标空间)的维度

    ,大家有没有发现,任何一个三维向量经过 映射后, 轴(也就是第三维度)的信息都丢失了,直接地说所有的三维向量经过 映射后都 “压缩” 到 轴张成的平面上。任何一个 维向量 ,经过上述的 映射后,得到的虽然表面上也是三维向量,但这些映射后的三维向量张成的空间是 维向量空间中的一个 维线性子空间,也就是映射后的目标空间是 维的,因此

    现在,问大家一个问题,矩阵

    的秩等于多少? 答案是这个矩阵的秩为 1

    • 举个例子 ,假设这里 是一个 的矩阵, 对应的映射几何意义是“ 所在的 维空间”到 “ 所在的 维空间” 的映射。假如矩阵
  • 知道矩阵秩的几何意义后, 就好办了,这里闲聊秩的两个性质

    右面是例子是秩为 2 的情况。

    对于 的极端清况, 可以写成列向量和行向量的乘积的形式,如下所示。

    在 的变换过程中, 分成以下两步进行

    若矩阵可以进行这样的瓶颈型分解,则 的秩显然不超过 , 只要一度被压缩到低的 维, 无论怎么扩张, 也不可能使维度增加了, 因为 一定不超过 维, 也可以说, 丢失的信息就找不回来了。

    • 第一步 对应“维向量 压缩”成“维向量 ”
    • 第二步 对应“维向量 扩张”成“维向量 ”
    • 性质 1: 对于一个 矩阵 , , .
      从直观上是显然的,前者成立的原因在于, 从根本上将, 目标空间是 维的, 而包含在其中的 无论如何也不可能让自己的维度超过 。对于后者, 因为原空间是 维的, 于是把空间全体通过 映射过去, 无论如何也不可能超过自己原来的维度 (毕竟, 我们做的只是线性映射)。
    • 性质 2: 假如矩阵 能分解成两个“瘦矩阵的乘积”,既宽仅为 的矩阵 和高仅为 的矩阵 的乘积, , 则 的秩一定不会超过 。
  • SVD分解(奇异值分解): 线性代数的世界里很多分解。但SVD的魅力最大。原因有两个:
    第一,SVD对矩阵的形状没有什么要求,任何矩阵都可以分解;第二,它能帮助我们提取出矩阵中最重要的部分,而恰恰我们很多场景都要分析矩阵重要性。

  • SVD定义

    进一步,矩阵 可以写成各个奇异值及其对应向量的求和形式:

    或者:

    这样的分解能分解出重要性。例如假设现在某个矩阵很好,维度为 ,本来要存 10000 * 10000 的参数。
    但经过分解后,发现第一个奇异值和第二个奇异值,已经占了总奇异值的 90%,那我们现在只要存 ,也就是 4 个奇异向量和两个奇异值,一共 4*10000+2 个参数,
    就能恢复这个矩阵的 90%!压缩比达到了 2500!这就是SVD分解的魅力。

    • 给定大小为 的实数矩阵 ,其中 可以等于或不等于 ,对 进行 SVD 后得到的输出为 。
      公式表示 SVD 会将大小为 的实数矩阵 分解为 的正交矩阵 , 的对角矩阵 以及 的正交矩阵 。
      由于正交矩阵的逆就是其转置矩阵,因此 和 ,其中两个单位矩阵的下标表示它们的大小分别为 和 。
      进一步地, 和 可以表示为:
  • SVD几何意义

    • 本质是变基(变到 表示的正交基,对应向量乘以 ),再拉伸压缩(对应向量乘以 ),再旋转(对应向量乘以 )。详细的可以看

https://www.bilibili.com/video/BV15k4y1p72z/?vd_source=1cb622387e8ff305c1e1079dfb1b59a4

聊聊 LoRA

LoRA: Low-Rank Adaptation of Large Language Models https://arxiv.org/abs/2106.09685

图片图片22年微软的工作

Lora 原理:

  • 通过在已经预训练的模型权重基础上添加两个低秩矩阵+的乘积来进行微调。具体来说,权重矩阵 维 被分解为一个高瘦矩阵 维 和一个矮胖矩阵 + 维 的乘积,从而达到降低秩的目的(回想一下秩的性质,因为能这样分解,因此最终 。

Lora论文中埋下的伏笔(都是后续工作的优化点):

  • 初始化方式:在原论文中,矩阵 使用随机高斯分布初始化,而矩阵 使用零初始化。这意味着训练一开始时, 。是否存在更好的初始化方法,可能可以加速训练或提高性能?当然是肯定有的。
  • 缩放因子 的作用: 按照 进行缩放,其中 是关于 中的一个常数,例如在 llama_factory 里,假如你不指定,那会把 设置为 的两倍。调节 大致等同于调节学习率。因此,论文将 设为他们尝试的第一个 ,并保持固定不变。这种缩放机制+的引入有助于在调整超参数时减少过多的重新调节需求。
  • 旁路矩阵的应用范围:在原论文中,LoRA 仅在注意力层引入了旁路矩阵,并且所有层的秩参数 都被统一设置为 、 或 8 ,没有根据不同层的特点进行区别设置。

Lora 碎碎念

碎碎念 1: Lora work的原因:

Lora 效果能好是建立在以下的假设上, 在预训练阶段, 模型需要处理多种复杂的任务和数据, 因此其权重矩阵通常是高秩的, 具有较大的表达能力, 以适应各种不同的任务。然而, 当模型被微调到某个具体的下游任务时, 发现其权重矩阵在这个特定任务上的表现实际上是低秩的。也就是说, 尽管模型在预训练阶段是高维的, 但在特定任务上, 只需要较少的自由度(低秩)就可以很好地完成任务。基于这一观察, LoRA提出在保持预训练模型的高秩结构不变的情况下, 通过添加一个低秩的调整矩阵来适应特定的下游任务。

碎碎念 2: 训练能省多少?
  • 已模型的其中一个矩阵为例: 例如 Qwen2-72B, transformers block 全连接模块的第一个全连接矩阵维度为 , 假如全参数微调, 单微调这个矩阵就涉及 个参数, 假如用 Lora 微调并取 rank=8呢? 涉及的参数量为 , 只有原来的 !
  • 已微调整个模型为例: 以微调整个模型为例, 假设使用的是 fp16 精度, 模型训练时显存的占用可以分为以下几部分

https://huggingface.co/docs/transformers/model_memory_anatomy#anatomy-of-models-memory

  • 模型权重: 2 字节 * 模型总参数量 +4 字节 * 训练参数量(因为在内存中需要保存一个用于前向传播的 fp16 版本模型,但训练的参数会保留一个 fp32 的副本)。
  • 梯度: 4 字节 * 训练参数量(梯度始终以 fp32 格式存储)。
  • 优化器: 8 字节 * 训练参数量(优化器维护 2 个状态)。
  • 中间状态: 由很多因素决定, 主要包括序列长度、隐藏层大小、批量大小等。
  • 临时缓存: 临时变量, 如为前向和后向传递中的中间量提供临时缓冲, 如某些函数占用的缓存,它们会在计算完成后被释放。

在不考虑中间状态和临时缓存的情况下, 单卡全参数微调 Qwen2-72B 需要的显存大小是 18*72=1296GB,显然这是不可能实现的。即使使用 8 卡并开启 DeepSpeed Zero3(将权重、梯度和优化器都切分到 8 卡上),每张卡的显存需求仍然为 162 GB ,也依然过高。 假设使用 LoRA 微调,假设 LoRA 训练参数量仅为原模型全参数量的 ,则单卡所需的显存可以按以下方式计算:显存需求 模型总参数量 训练参数量 。 假如 8 卡并开启 Zero3,是能把 72B 模型跑起来的。

碎碎念 3: 推理时能省多少?

在推理过程中,LoRA 的低秩调整矩阵可以直接与原始模型的权重合并,因此不会带来额外的推理延迟(No Additional Inference Latency)。这意味着,在推理阶段,计算效率与原始模型基本相同。

碎碎念 4: 不同的高效微调方法

目前,LoRA 已成为主流的高效微调方法之一。与其他方法相比:

  • Adapter:在层之间并行添加额外的结构,但这种额外结构无法与原始权重合并,导致推理时不仅需要更多内存,还会带来推理延迟。
  • Prefix tuning:通过占用输入 token 来实现微调,结构不够优雅,可能影响输入处理。
  • LoRA: 训练稳定,效果与其他方法相当,但推理时不产生额外延迟,且不增加显存需求,因而是首选。
碎碎念 5: 高效微调与全参数微调怎么选

谨慎使用全参数微调,除非同时满足以下两个条件

  • 你的训练集很大,(通常需要成千上万)
  • 你对训练数据集质量非常有信心,包括数据的一致性,多样性。

如果无法同时满足这两个条件,全参数微调的效果可能不如 LoRA。这是因为大模型的拟合能力过强,数据量不足或质量不高时,模型很容易过拟合,反而降低效果。在微调过程中,数据集的构建至关重要。对于如何构建合适的微调数据集,推荐阅读这篇文章:

How to fine-tune: Focus on effective datasets https://ai.meta.com/blog/how-to-fine-tune-llms-peft-dataset-curation/

聊聊Lora 训练时我观察到的现象

  • 现象 1:Lora 模型在训练过程中收敛速度较慢。例如,在实际业务场景中,通常需要训练到 10 个 epoch 左右,才在测试集上取得最高分,而全参数微调模型通常在 3-4 个 epoch 后就能达到最佳效果。此外,Lora 的拟合能力也较弱,模型对训练集的内容记忆不佳。
  • 现象 2:在处理某些复杂任务时,我尝试将 Lora 的 rank 参数增大至 64 或 128,结果模型性能反而下降,甚至出现训练崩溃、梯度变成 nan 的情况。这与直觉相悖,因为理论上对于复杂任务,rank 越大意味着可调整的参数越多,模型的效果理应更好。

ADALoRA: 建模成 SVD 分解的形态, 每次更新根据重要性得分来取哪些

图片

https://arxiv.org/pdf/2303.10512

  • 研究动机

原生 Lora 方法在每个 attention 层都引入了一个秩为 4 的矩阵,采用的是均分策略。然而,直觉告诉我们,模型中的某些矩阵比其他的更为重要,应该分配更多的参数来进行调整,而有些矩阵则相对不重要,不需要过多修改(回想 BERT 时代的经验,我们认为靠近输入层的权重主要学习语法和词法结构,微调时变化较小;而靠近输出层的权重主要涉及语义知识,需要根据下游任务进行较大的调整)。总而言之,Lora 这种"均分参数"的策略显然不是最优的。论文由此提出一个核心问题:

如何根据transformer不同层,不同模块的重要性自适应地分配参数预算,以提高微调的性能?

・方法论

  • 考虑到参数的重要性,自然会想到使用 SVD(奇异值分解)。SVD 的作用在于将矩阵分解为若干个不同重要性的分量之和。因此,一种直观的做法是:每次更新 A 和 B 时,先对所有的 和 B 矩阵进行 SVD 分解。如果某一层的矩阵的重要性较低,我们就只更新重要性高的部分, 不更新低重要性的部分。然而, 这种方法效率极低, 因为每个训练 step 都需要进行 SVD 分解。对于大型模型而言,频繁对高维矩阵应用 SVD 会非常耗时。为了解决这个问题,论文提出不精确地计算 SVD,而是通过参数化 来模拟 SVD 的效果。
  • 具体做法是,使用对角矩阵 来表示奇异值,正交矩阵 和 表示 的左右奇异向量。为了保证 和 的正交性,在训练过程中增加一个正则化惩罚项。这样,模型能够在训练过程中逐渐接近 SVD 分解的形式,避免了对 SVD 进行密集计算,同时提高了效率。

上面这一副图很有意思,这图想表达的是各层各矩阵模块分配到的参数budget情况。模型的中上层,尤其是、、、和 这些模块,分配到的预算明显较多。

  • 思考:合乎常理的感觉,训练时有些模块就是不用怎么动,有些模块就是要尽可能微调,让模型自己去判断。

rsLoRA: 调节 ,让输入和输出保持一个量级

图片

https://arxiv.org/pdf/2312.03732

  • 研究动机

    • Lora 的原论文有做一个实验,实验的结果是 rank=64 在训练中没有带来显著提升,所以论文得出了非常低的 rank(如 4、8、16)就“足够”的结论。然而,这显然是反直觉的。按理来说,任务与预训练之间的差异越大,所需的 rank 应该越高,因为这意味着可调节的参数也应该越多。然而,实际结果却显示,随着 rank 增加,模型效果反而变差(对应上述提到的现象二)。 例如,如下图所示,LoRA 部分中 rank=512 和 rank=2048 时的橙色和黄色曲线较高。按照常理,rank 增加,引入了更多可微调的参数,应该使训练过程钟 perplexity 下降得更快,但结果却与预期相反。图片进一步分析可以看出,作者试图引出的问题是:在使用 LoRA 适配器时,通过参数 (上文提到, 是关于 的一个常数)对矩阵乘积 进行缩放会影响模型的学习轨迹。具体而言,关键在于确保 的选择在某种程度上是 “合理的”,即矩阵 在整个学习过程中对于所有 rank 都保持稳定。同时, 不应该看得过于激进,以免导致学习不稳定或速度过慢。
  • 方法论

    • 进一步,论文提出,怎么选择 ?一个好的 应该是 rank–stabilized (秩稳定) 的。论文先定义什么叫做 rank–stabilized

以下两个条件成立时,适配器 是秩稳定的(rank–stabilized):

  • 条件一:如果输入到 adapter 的数据是独立同分布 (iid) 的,例如输入的第 m 阶矩是 ,那么适配器输出的第 m 阶矩也是 。
  • 条件二:如果损失函数对适配器输出的梯度是 ,那么损失函数对适配器输入的梯度在每个条件也是 。

Hold on,这里的 是什么意思啊?

意思是:在秩增加的情况下,某个量级(例如矩的大小或梯度的大小)保持恒定(或变化非常缓慢,不依赖于 )。这里的 “1” 表示与秩无关的常数。具体举个例子,输入到 adapter 的每个条件的第 m 阶矩为 ,这意味着无论 rank 是多少,输入数据的第 m 阶矩在每个条件中都是一个数级别的量。这表示输入的分布不会随着 增大而显著变化。 这个概念为了解决 adapter 在不同的秩设置下,都能保持稳定的学习和优化过程。

最终经过公式的推导(推导详见论文的 Appendix A),得到定理

考虑形式为 的 LoRA 适配器,其中 初始化时满足 , 的每个元素是独立同分布的,均值为 0,方差为 ,且不依赖于 。且仅当 时,所有适配器都是稳定的。特别地,上述结论在学习轨迹的任何点上都成立,且如果 ,那么对于足够大的 ,学习过程将是不稳定或坍塌的。

在这个定理中, 表示 需要按照 的比例缩放。这意味着当 rank 增大时, 应该被设定为 的形式(如 ,其中 是常数)。这种缩放确保了 LoRA 适配器在整个学习过程中保持稳定,防止在 很大时出现学习不稳定或梯度崩塌的情况。

  • 实验结果

如下图所示,在 rsLoRA 中,随着 rank 的增加,获得的 Perplexity 越低,这符合预期。

再看下面右图,在 LoRA 中,随着 rank 增加,gradient norm 变得非常低,出现了一定程度的梯度崩溃(gradient collapse,指梯度过小了,更新不到位)。相比之下,rsLoRA 在很大程度上抑制了这种现象,即使在 rank=2048 时,gradient norm 仍能保持相对较高。图片

  • 思考:这种训练的稳定性让我联想到 Layer Normalization 和 attention 机制中的归一化。稳定性对于模型训练至关重要,因为它能让模型在每一步都看到相对一致的分布输入。如果每一步的输入分布差异过大,模型容易出现学习不稳定甚至崩溃的现象。而保持输入的稳定性,能使模型更容易学习。

PiSSA: 用奇异值大的部分进行初始化

图片

  • 研究动机

原生 Lora 收敛得太慢了,Why? (对应上文的现象 1)

根据方程 Lora 的公式 , 和 的梯度分别

和

仔细看这个公式,在训练的初始阶段,梯度的大小主要由 和 的值决定。由于在 LoRA 中, 和 是通过高斯噪声和零初始化的,因此初始梯度可能非常小,进而使微调过程的收敛速度变慢。这种现象在实验中得到了验证,观察到 LoRA 在训练的初期往往在初始点附近徘徊,浪费大量时间。如下图所示,LoRA 在相同的训练步数下,前期收敛速度明显较慢,最终未能达到一个局部最优点。图片

  • 方法论

    • 论文提出的方式很简单,对原始矩阵进行 SVD 分解,拆开成两部分:主奇异值部分和残差奇异值部分,fine tuning 主奇异值部分,frozen 残差奇异值。具体分解如下,其中 由奇异值小的那部分组成, 和 ,也就是 ,要训练的那部分由奇异值大的部分组成

DORA: 把权重分解为幅值和方向两个模块进行独立微调

图片

https://arxiv.org/pdf/2402.09353

  • 研究动机

    • 为了弥补这一差距,论文首先引入了一种新颖的权重分解分析方法,以研究全参数微调和 Lora 微调之间的差异。基于这些发现,论文提出了一种名为 Weight-Decomposed Low-Rank Adaptation (DoRA) 的新方法。DoRA 通过将预训练权重分解为大小 (magnitude) 和方向 (direction) 两个组成部分分别进行微调,特别是利用 LoRA 有效地更新方向部分,以最小化可训练参数的数量。通过这种方法,DoRA 旨在提高 LoRA 的学习容量和训练稳定性。
  • 方法论

    • 通过将权重矩阵分解为幅度和方向两个独立的部分,揭示了 LoRA 与全参数微调 (FT) 在学习模式上的内在差异。具体的分析方法是:通过检查 LoRA 和 FT 相对于预训练权重在幅度和方向上的更新,深入揭示它们在学习行为上的根本区别。具体的,矩阵 的权重分解可表述为

其中, 是幅度向量, 是方向矩阵, 是矩阵按列的向量范数。这样的分解确保了 的每一列都是单位向量,并且相应的 中的标量定义了每个向量的幅度。基于这个分解,分析就可以做了。具体来说,对完全微调后的权重 以及合并 LoRA 后的权重 进行上述的分解,再与原来的权重 进行比较。例如, 和 之间的幅度和方向变化可以定义如下:

可以明显看出,LoRA 在所有中间步骤中表现出一致的正斜率趋势,表明方向和幅度的变化之间存在比例关系。相比之下,FT 显示出更为多样化的学习模式,具有相对负斜率。这种 FT 和 LoRA 之间的区别可能反映了它们各自的学习能力。尽管 LoRA 倾向于成比例地增加或减少幅度和方向更新,但它缺乏对更细微调整的能力。具体而言,LoRA 在执行伴随幅度显著变化的微小方向调整或相关反应下的表现不佳,而这种能力更是 FT 方法的特点。

论文怀疑,LoRA 的这种局限性可能源于同时对学习幅度和方向适应性的挑战,这对 LoRA 来说可能过于复杂,引出了论文的方法论。

  • 方法论

    • 根据作者对权重分解分析的见解,论文进一步引入了权重分解的低秩适应方法(DoRA)。DoRA 首先将预训练的权重分解为其幅度和方向分量,并对这两个分量进行微调。由于方向分量在参数数量上较大,论文进一步通过 LoRA 对其进行分解,以实现高效微调。
  • 作者的直觉有两个方面。

    • 首先,限制 LoRA 专注于方向调整,同时允许幅度分量可调,相较于 LoRA 在原方法中需要同时学习幅度和方向的调整,简化了任务。
    • 其次,通过权重分解使得方向更新的过程更加稳定。
  • 实验结果图片

上图© 中展示了在与 FT 和 LoRA 相同的设置下,合并后的 DoRA 权重与 之间的幅度和方向差异。从 DoRA 和 FT 的 回归线上,DoRA 和 FT 表现出相同的负斜率。论文推测,FT 倾向于负斜率是因为预训练权重已经具备了适合多种下游任务的大量知识。因此,在具备足够的学习能力时,仅通过更大幅度或方向的改变就足以进行下游适应。

  • 论文还计算了 FT、LoRA 和 DoRA 的 和 之间的相关性,发现 FT 和 DoRA 的相关性值分别为 和 ,均为负相关。而 LoRA 则显示为正相关,相关性值为 。Anyway,DoRA 展示了仅通过较小的幅度变化或相反的情况下进行显著方向调整的能力,同时其学习模式更接近 FT,表明其相较于 LoRA 具有更强的学习能力。图片
  • 思考: 感觉合乎常理, 找到一种更 match 问题特性的建模方式,这样模型不仅更容易学习,还能提高学习效果。

实验

实际业务数据通常与 benchmark 数据不同,往往是预训练模型未曾见过的,这有时会激活新的模式 (pattern)。在此次实验中,我们对比了 LoRA、rsLoRA 和 Dora 在三个具体下游业务任务中的表现。实验参数如下:

epoch=4, learning_rate = 4e-4, learn_scheduler=cosine, rank=8 或 32, base_model=Qwen2-72B, 训练结束后取 checkpoint 在测试集上进行评估

值得注意的是,rsLoRA 在 rank=32 的情况下训练失败,具体表现为 gradient norm 过大,train_loss 居高不下,因此没有将该结果列出。

首先,我们来看训练过程中的 gradient norm。可以明显看到 rsLoRA (rank=8) 的 gradient norm 始终较大,表明每个 step 的更新幅度较大,这与原论文中的结论一致。图片然后我们再对比下 train_loss、 eval_loss、下游任务测试集的表现图片

  • 观察 1: 对比实验 1 和实验 4,Lora(rank=8) 的效果比 Lora(rank=32) 差,这与现象二一致,证明单纯增大 rank 对 Lora 的性能提升作用有限,甚至反作用。
  • 观察 2: 对比实验 1 和实验 2,rsLoRA(rank=8) 的 train_loss 低于 Lora(rank=8),但 eval_loss 和任务指标差异不大。同时,rsLoRA(rank=32) 在训练中失效(训崩)。这可能表明,虽然 rsLoRA 能加速模型的学习,但由于没有优化好 Lora 的初始化,整体效果提升不显著。这也暗示了良好的初始化对 Lora 的重要性??(有没有高人指点)
  • 观察 3: Dora(rank=32) 的 train_loss 虽然不是最低的,但 eval_loss 是最低的,表现良好。无论 rank=8 还是 rank=32,Dora 的表现都优于其他方法。

最佳实践建议: 使用 Dora,rank不用调太大(例如 8 、 16 、 32都试试),学习率设置高一些,epoch 维持在 4-5 个左右,并使用 cosine learn scheduler。

总结

文章介绍了多个基于LoRA的改进方法,如AdaLoRA、rsLoRA、PiSSA、DoRA等,分别通过优化训练参数的分配、调节缩放因子、改进初始化等方式提高微调效果。实验结果表明,DoRA表现最佳,提供了模型训练中的最佳实践建议。

​

如何学习大模型

现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。

作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。

我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线

很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。

在这里插入图片描述

二、AI大模型视频教程

在这里插入图片描述

三、AI大模型各大学习书籍

在这里插入图片描述

四、AI大模型各大场景实战案例

在这里插入图片描述

五、结束语

学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。

再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。

因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。

LoRA,DoRA,RSLoRA,LoRA+ 是什么 LoRA:基础低秩分解,适合快速低成本微调DoRA:动态秩应对复杂任务,牺牲少量速度换精度RSLoRA:深度模型专属,通过递归缓解误差LoRA+:工程优化导向,结合场景定制(如NLP+CV混合任务)关键选择点:显存限制选LoRA,任务复杂选DoRA,超深模型选RSLoRA,追求极致性能选LoRA+。 阅读详情

相关推荐

nlp学习笔记——基于论文摘要的文本分类(topline)

本人自己根据手册上面的步骤跑了一遍,但是效果不理想只有0.7多,和其他群友交流得知,其他群友也有此现象,需调教prompt 参数才能获得更好的结果。大模型是基于深度学习的自然语言处理模型,有很多参数(是大模型的主要特点),可以理解并生成人类的自然语言。现在市面上有很多的大模型,譬如最火的chatgpt4OpenAI开发ChatGPT的三个主要步骤:大尺寸预训练+指令微调+RLHF。

weixin_71097539的博客 542

大模型高效微调综述下: DiffPruning、BitFit、LoRa、AdaLoRA、MAM Adapters、UniPELT

本文介绍了PEFT中Selective Methods的DiffPruning、 BitFit;重参数化方法中的LoRA和AdaLoRA;以及混合方法中的MAM Adapters和UniPELT。分类方法见PEFT综述论文《Scaling Down to Scale Up: A Guide to Parameter-Efficient Fine-Tuning》

qq_56591814的博客 5032

ZYNQ裸机LWIP双网口实现

文章目录前言一、LWIP双网口二、使用步骤1.部分说明2.部分代码总结 前言 一、LWIP双网口 由于项目需要多通讯口,查阅资料发现lwip支持多网口操作。于是,就开搞了。 二、使用步骤 1.部分说明 利用xemac_add函数在网卡链表中添加一个网卡,即使用netif块重复xemac_add操作。 先用轮询的方式实现双网口,后期修改为中断模式 2.部分代码 导入官方例程LWip echo server, 修改main函数,增加一个netif块 #if LWIP_IPV6==0 #if L..

weixin_35865280的博客 1万+

AdaLoRA论文概述

预训练语言模型(PLMs)在各种自然语言处理任务中表现出了优越的性能。为了适应下游任务,最常见的方法是对预训练模型的所有参数进行微调(全微调)。然而,预训练模型通常需要大量内存。例如,BERT模型包含多达3亿参数,T5模型包含多达110亿参数,GPT-3模型包含多达1750亿参数。在构建基于这些预训练模型的NLP系统时,通常需要同时处理多个任务。在有大量下游任务的情况下,全微调需要每个任务维护一个大型模型的单独副本,这导致了禁止性的内存消耗。

qq128252的博客 2132

用c++生成随机迷宫_自动生成随机迷宫(1)prim算法

“程序 = 数据 + 算法”,一款好的作品不单单是代码的堆砌,还有其灵魂的部分,那就是算法;算法是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。算法既然是解决某类型问题的规范,那么我们就学习它,并在我们的作品中使用它。从这一期开始做一个迷宫系列,包括用算法生成随机迷宫,用算法自动寻址走迷宫。 三大迷宫生成算法之一“随机Prim算法不是...

weixin_31787547的博客 2598

百度飞桨EasyDL图像分类:收费口车辆类型自动识别

车辆类型识别在高速收费路口的应用中,场景相对单一且拍摄角度一致,因此首次训练模型效果优异,准确率达到了95%,鉴于市面上的车辆类型不断更新迭代且部分车辆的主要特征也不断发生变化,因此可在模型投入应用后,持续收集模型实际运行过中产生的数据,定期添加数据进行模型迭代,保证模型效果。企业将AI模型应用到实际场景中以实现停车费用、过路费用的自动计算,在收费口中由相机对每个车辆进行拍摄,拍摄到的图片传输到预测设备上进行类型识别,基于识别出来的车辆类型自动进行费用计算。该功能可在发起训练时选选择对应的开关打开即可。..

PaddlePaddle 1670

PiSSA: Principal Singular Values and Singular Vectors Adaptation of Large Language Models

探究大模型微调 Lora 的不同形态: AdaLora、 AsLora、 PiSSA、 DoRA (qq.com)

tiger00O的博客 189

ctf——writeup

ctf线上赛——第三关地址:http://106.75.86.18:2222提示:ip spoofing——ip伪造首先访问目标地址,一个正常的网页,挺文艺的。上面有些提示。根据上面的提示,我们浏览网页。找了好久,终于找到你。这个跳转链接。(在这个页面下方)我们接着点击这个链接,就跳转到administrator.php这个页面了。看到这个登录页面,就要知道用户名,密码,可是没有怎么办。一开始也试

Child_by_dream的博客 1320

ADALoRA: 建模成 SVD 分解的形态,对每一层参数设置不同的秩

AdaLoRA: Adaptive Budget Allocation for Parameter-Efficient Fine-Tuning https://arxiv.org/pdf/2303.10512探究大模型微调 Lora 的不同形态: AdaLora、 AsLora、 PiSSA、 DoRA (qq.com)

tiger00O的博客 201

华为2022硬件逻辑笔试题

单选题(每题两分) 1.以下关于过程赋值的描述,不正确的是() A在非阻塞性过程赋值中,使用赋值符号“< =” B赋值操作符是“=”的过程赋值是阻塞性过程赋值 C在非阻塞性过程赋值中,对目标的赋值是非阻塞的(因为时延),但可预定在将来某个时间步发生(根据时延:如果是0时延,那么在当前时间步结束) D非阻塞性过程赋值在其后所有语句执行前执行 解:D 2.芯片的功耗和下列哪种不强相关_____________。 A资源利用率 B核电压 C组合逻辑级数 D翻转率 解:A 3.下列说

weixin_44072819的博客 1万+

RK3588 buildroot 双网口bonding调试

本文介绍了Rockchip Buildroot中配置网络冗余的方法,通过bonding技术将多个物理网口合并为逻辑接口。主要内容包括:1)7种常见bonding模式的特点及适用场景;2)RK3588的具体配置流程,包括内核配置(启用bonding驱动、修改dts)、添加ifenslave工具;3)系统编译和网络状态检查。

501846585_QQAndroid_java 459

【ICNP2020】AdapLoRa Resource Adaptation for Maximizing Network Lifetime in LoRa networks

在之前的论文中,我们分别关注了网络协议的设计以及路由决策问题。而网络部署和网络结构设计对我来说仅停留于概念。考虑这样的工程场景,我需要搭建一个带地面站的机群局域网,怎么搭建?是否有效?是否有水平?可靠性、稳定性如何?再或者,一个成熟的项目具备哪些评价指标,能否被同行认可?这篇文章解决了我的一些问题。

qq_44868018的博客 5599

20.Labview使用子vi进行VI间的数据传递详解(模块化)

子VI(模块化)题主在题注中也提到, 子VI的建立只是为了减少程序的负担,那么我们当使用子VI的时候首先就要明确什么是子VI?什么样的程序可以作为子VI?首先我们来看NI官方给的定义,具体说明如下图所示:题主已经将重点画出来了,在官方的解释中将子VI定义为了模块化,大家可以看到创建模块化子VI的特点在于减少代码重复工作优化了程序的框架结构。把一个代码打包,需要的时候就拎出来让他处理处理,不需要的时候就让它躺在我的列表里就行了也不占用运行空间。那么创建子VI需要需要注意以下几点:①任何一个程序。

关注我,更多技术问题欢迎私聊~ 3516

LoRA、QLoRA与AdaLoRA的低秩适配:如何让AI语言模型瘦身不减智?

LoRA(Low-Rank Adaptation)是一种通过低秩分解来高效地微调大型语言模型的方法。其基本思路是将模型权重分解为一个低秩矩阵的乘积,从而减少微调所需的参数数量和计算量。通过本文的详细介绍,我们深入探讨了三种用于训练低维模型的方法:LoRA、QLoRA和AdaLoRA。它们分别在减少模型复杂性、降低内存需求和提升适应能力等方面具有显著优势。以下是对这三种方法的总结:方法核心思想关键优势应用场景LoRA低秩分解权重矩阵减少参数数量大型语言模型的快速适应和微调QLoRA。

m0_59163425的博客 1876

DeepFashion 服装识别:基于 YOLOv10 的深度学习实现与 UI 界面开发

随着深度学习的发展,目标检测算法在服装识别任务中表现出色,特别是 YOLO(You Only Look Once)系列模型。服装识别(Fashion Recognition)是计算机视觉领域的重要任务,广泛应用于。已提供,可用于服装分类、款式识别、虚拟试衣等应用。训练服装识别模型,并开发。

YOLO 803

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

LoRA、AdaLoRA与QLoRA技术原理

通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练。

关于我的学习记录 1676

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

论文笔记9:AdaLoRA原理详解

当存在大量下游任务时,微调预训练模型中的所有参数这种方法会变得难以实现。因此诞生许多旨在以参数高效的方式学习预训练权重的增量更新。这些方法通常将增量更新的预算均匀分布在所有预训练权重矩阵上,而忽略了不同权重参数的不同重要性。因此,微调性能并非最优。为了弥补这一缺陷,提出了 AdaLoRA,根据权重矩阵的重要性得分自适应地分配参数预算。AdaLoRA 以奇异值分解的形式对增量更新进行参数化。

Tiziano68的博客 2453

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

大模型微调笔记:对比LoRA、QLoRA、AdaLoRA、LoRA+

大模型微调笔记:对比LoRA、QLoRA、AdaLoRA、LoRA+

一只努力学习的小碗~ 3835

LoRA及其变体概述:LoRA, DoRA, AdaLoRA, Delta-LoRA

LoRA及其相关方法的研究领域是一个非常活跃的研究领域,每天都有新的贡献。本文解释一些方法的核心思想。如果你对这些方法感兴趣请查看论文:作者:Dorian Drost。

deephub 1万+

大模型微调技术揭秘——LoRA

受此启发,微软提出了低秩适配(LoRA)方法,设计了特定结构,在涉及矩阵乘法的模块中引入两个低秩矩阵A和B,以模拟完全微调过程。这相当于只对语言模型中起关键作用的低秩本质维度进行更新。

Whitney66的博客 3942

大模型参数高效微调技术原理综述 之 LoRA、AdaLoRA、QLoRA

来自:吃果冻不吐果冻皮进NLP群—>加入NLP交流群随着,ChatGPT 迅速爆火,引发了大模型的时代变革。然而对于普通大众来说,进行大模型的预训练或者全量微调遥不可及。由此,催生了各种参数高效微调技术,让科研人员或者普通开发者有机会尝试微调大模型。因此,该技术值得我们进行深入分析其背后的机理,本系列大体分七篇文章进行讲解。大模型参数高效微调技术原理综述(一)-背景、参数高效微调简介大模型参...

zenRRan的博客 5151

大模型技术实践(四)|参数高效微调技术解析及AdaLoRA的应用

在流行的SuperGLUE基准测试中,Prompt Tuning的任务性能与传统的模型调优相当,且随着模型规模的增加,差距逐渐减小。2. 提出了Prompt Ensembling,即通过在同一任务上训练N个提示,也就是在同一个批次中,对同一个问题添加不同的Prompt,相当于为任务创建了N个独立的“模型”,同时仍然共享核心语言建模参数。P-Tuning与全参数效果相当,且在一些任务上优于全参数微调,可以显著提高GPT模型在自然语言理解方面的性能,并且BERT风格的模型也可以获得较小的增益。

specssss的博客 1117

什么是 AdaLoRA(Adaptive Low-Rank Adaptation,自适应低秩适配)

AdaLoRA(Adaptive Low-Rank Adaptation,自适应低秩适配)是 LoRA(Low-Rank Adaptation)的一种改进版本,旨在通过自适应地调整低秩更新矩阵的秩(rank)来增强参数高效微调(Parameter-Efficient Fine-Tuning, PEFT)的性能和效率。AdaLoRA 在训练过程中根据参数的重要性动态分配秩值,使得模型能够为不同权重矩阵分配合适的容量,从而在保持低参数量的同时提升任务适配能力和泛化性能。AdaLoRA 由 Zhang 等人在 2

彬彬侠的博客 1578

大模型微调技术 --> LoRA 系列之 AdaLoRA

之前的微调方法(如低秩更新)通常将增量更新的预算均匀地分布在所有预训练的权重矩阵上,并且忽略了不同权重参数的不同重要性。结果,微调结果不是最优的。为了弥补这一差距,我们提出了AdaLoRA,它根据权重矩阵的重要性得分自适应地在权重矩阵之间分配参数预算。特别地,AdaLoRA以奇异值分解的形式将增量更新参数化。这种新的方法允许我们有效地修剪不重要更新的奇异值,这实质上是减少它们的参数预算,但避免了密集的精确 SVD 计算。

weixin_46034279的博客 1868
上一篇: 给大模型装上手,通过 Function Calling 查询数据库:实现高效数据处理的新方式
下一篇: 如何使用大型语言模型(LLMs)自动检测BOLA漏洞
AI_小站
博客等级 码龄3年 6255粉丝 1152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值