如何拯救LoRA初始化?LoRA-GA:性能显著提升+收敛速度更快!

news2024/11/25 7:13:16

文章链接:https://arxiv.org/pdf/2407.05000

亮点直击

  • 提出了 LoRA-GA,一种新颖的 LoRA 初始化方法,通过近似低秩矩阵的梯度与全权重矩阵的梯度来加速收敛
  • 确定了在非零初始化下的缩放因子,该因子确保适配器输出的方差不受适配器的秩和输入维度的影响。
  • 通过广泛的实验验证了 LoRA-GA,证明了与原版 LoRA 相比,其性能显著提升且收敛速度更快。具体而言,LoRA-GA 在 T5-Base 的 GLUE 子集上比 LoRA 提高了 5.69%,在 Llama 2-7B 上在 MT-bench、GSM8K 和 HumanEval 上分别提高了 0.34%、11.52% 和 5.05%,同时实现了高达 2-4 倍的收敛速度提升。

微调大规模预训练模型在计算和内存成本方面是非常昂贵的。LoRA 作为最流行的参数高效微调 (PEFT) 方法之一,通过微调一个参数显著更少的辅助低秩模型,提供了一种成本有效的替代方案。尽管 LoRA 显著减少了每次迭代的计算和内存需求,但大量实证证据表明,与完全微调相比,它的收敛速度明显较慢,最终导致总体计算增加且测试性能往往较差。本文对 LoRA 的初始化方法进行了深入研究,并表明细致的初始化(不改变架构和训练算法)可以显著提高效率和性能。本文引入了一种新颖的初始化方法,LoRA-GA(带梯度近似的低秩适应),该方法在第一步将低秩矩阵乘积的梯度与完全微调的梯度对齐。广泛实验表明,LoRA-GA 达到了与完全微调相当的收敛速度(因此显著快于原版 LoRA 及其他各种最新改进方法),同时达到相当或更好的性能。例如,在 T5-Base 的 GLUE 数据集子集上,LoRA-GA 平均比 LoRA 提高了 5.69%。在更大的模型如 Llama 2-7B 上,LoRA-GA 在 MT-bench、GSM8K 和 Human-eval 上分别表现出 0.34%、11.52% 和 5.05% 的性能提升。此外,与原版 LoRA 相比,收敛速度提高了 2-4 倍,验证了其在加速收敛和提升模型性能方面的有效性。

方法

本节分析了 LoRA 的初始化并介绍了LoRA-GA。它包括两个关键组件,分别检查每个组件,并介绍它们在 LoRA-GA 中的整合。

  1. 近似全微调的梯度方向
  2. 确保初始化过程中的秩和Scale稳定性。

原版 LoRA 回顾

LoRA 的结构 基于微调更新是低秩的假设,LoRA提出了使用两个低秩矩阵的乘积来表示原始矩阵 W 的增量部分。在这里,W 是模型中线性层的权重矩阵。例如,在transformers中,它可以是自注意力层的 Q、K、V 或 O 矩阵,或者是 MLP 层的权重矩阵。具体来说,LoRA 具有以下数学形式。

其中 W ′ , W 0 ∈ R m × n W', W_0 \in \mathbb{R}^{m \times n} W,W0Rm×n B ∈ R m × r B \in \mathbb{R}^{m \times r} BRm×r,以及 A ∈ R r × n A \in \mathbb{R}^{r \times n} ARr×n,且 r ≪ min ⁡ ( m , n ) r \ll \min(m, n) rmin(m,n) W 0 W_0 W0 是预训练的权重矩阵,在微调过程中保持冻结,而 A A A B B B 是可训练的。

LoRA 的初始化

在 LoRA 的默认初始化方案中,矩阵 A A A 使用 Kaiming 均匀分布初始化,而矩阵 B B B初始化为全零。因此, B A = 0 BA = 0 BA=0 并且 W 0 ′ = W 0 W'_0 = W_0 W0=W0,确保初始参数不变。

如果附加项 Δ W = η B A \Delta W = \eta BA ΔW=ηBA 最初是非零的(例如 [37]),则可以调整冻结参数以确保初始参数不变。这可以表示为:

其中 W frozen = W 0 − η B init A init W_{\text{frozen}} = W_0 - \eta B_{\text{init}} A_{\text{init}} Wfrozen=W0ηBinitAinit 是冻结的,在这种情况下, B B B A A A 是可训练的。

梯度近似

目标是确保第一步更新 Δ ( η B A ) \Delta(\eta BA) Δ(ηBA) 近似于权重更新的方向 Δ W \Delta W ΔW,即 Δ ( η B A ) ≈ ζ Δ W \Delta(\eta BA) \approx \zeta \Delta W Δ(ηBA)ζΔW,其中 ζ \zeta ζ 是某个非零正常数。后面将讨论如何选择 ζ \zeta ζ,现在可以将 ζ \zeta ζ 视为一个固定常数。

考虑学习率为 λ \lambda λ 的梯度下降步骤, A A A B B B 的更新分别为 Δ A = λ ∇ A L ( A init ) \Delta A = \lambda \nabla_A L(A_{\text{init}}) ΔA=λAL(Ainit) Δ B = λ ∇ B L ( B init ) \Delta B = \lambda \nabla_B L(B_{\text{init}}) ΔB=λBL(Binit)。假设学习率 λ \lambda λ 很小,第一步更新的 η B A \eta BA ηBA可以表示为:

为了衡量其对全微调中权重更新的缩放近似质量 ζ Δ W = ζ λ ∇ W L ( W 0 ) \zeta \Delta W = \zeta \lambda \nabla_W L(W_0) ζΔW=ζλWL(W0),使用这两个更新之间差异的 Frobenius 范数作为标准:

Lemma 3.1. 假设损失函数是 L L L 并且 y = W ′ x = ( W 0 + η B A ) x y = W'x = (W_0 + \eta BA)x y=Wx=(W0+ηBA)x,其中 y y y 是一层的输出, x x x 是输入,那么 A A A B B B 的梯度是 W ′ W' W 梯度的线性映射:

值得注意的是,在训练开始时,LoRA 中的 ∇ W ′ L \nabla_{W'}L WL 和全微调中的 $\nabla_W L $ 是相等的。通过将Lemma 3.1 中的梯度代入公式 1,可以将标准重写如下:

这一标准评估了适配器的梯度在多大程度上近似于全微调的梯度方向,最小化它可以使 LoRA 的梯度更接近全微调的梯度,并带有缩放因子 ζ \zeta ζ

定理 3.1. 对于给定 ζ \zeta ζ 的方程 3 中的优化问题,如果 ∇ W L \nabla_W L WL 的奇异值分解 (SVD) 是 ∇ W L = U S V T \nabla_W L = USV^T WL=USVT,其解为:

其中 I A I_A IA I B I_B IB 是索引集。

定理 3.1 为给定特定 ζ \zeta ζ A init A_{\text{init}} Ainit B init B_{\text{init}} Binit提供了适当的初始化方案。 ζ \zeta ζ 的选择会影响更新 η B A \eta BA ηBA 的缩放,具体的 $\zeta $ 选择下节讨论。

Scale稳定性

受到 rsLoRA和 Kaiming 初始化的启发,定义了稳定性:

定义 3.1. d out , d in , r → ∞ d_{\text{out}}, d_{\text{in}}, r \to \infty dout,din,r 时,适配器 η B A \eta BA ηBA 显示出两种不同类型的Scale稳定性:

  1. 前向稳定性:如果适配器的输入是独立同分布 (i.i.d.),且其 2 阶矩为 Θ r , d out , d in ( 1 ) \Theta_{r,d_{\text{out}},d_{\text{in}}}(1) Θr,dout,din(1),则输出的 2 阶矩保持为 Θ r , d out , d in ( 1 ) \Theta_{r,d_{\text{out}},d_{\text{in}}}(1) Θr,dout,din(1)
  2. 后向稳定性:如果相对于适配器输出的损失梯度为 Θ r , d out , d in ( 1 ) \Theta_{r,d_{\text{out}},d_{\text{in}}}(1) Θr,dout,din(1),则相对于输入的梯度保持为 Θ r , d out , d in ( 1 ) \Theta_{r,d_{\text{out}},d_{\text{in}}}(1) Θr,dout,din(1)

定理 3.2. 给定定理 3.1 中提出的初始化方案,假设 A init A_{\text{init}} Ainit 和 $ B_{\text{init}}$ 中的正交向量是从 R d in \mathbb{R}^{d_{\text{in}}} Rdin R d out \mathbb{R}^{d_{\text{out}}} Rdout 中的单位球面上随机选择的,并且这些向量彼此正交,且 $ \eta = \Theta_{r,d_{\text{out}},d_{\text{in}}}(1/\sqrt{r}) ,如 r s L o R A 所建议的。在这些条件下,适配器在 ,如 rsLoRA 所建议的。在这些条件下,适配器在 ,如rsLoRA所建议的。在这些条件下,适配器在\zeta = \Theta_{r,d_{\text{out}},d_{\text{in}}} \left( \sqrt{d_{\text{out}}/r^2} \right)$ 时是前向Scale稳定的,且在 ζ = Θ r , d out , d in ( d in / r 2 ) \zeta = \Theta_{r,d_{\text{out}},d_{\text{in}}} \left( \sqrt{d_{\text{in}}/r^2} \right) ζ=Θr,dout,din(din/r2 ) 时是后向Scale稳定的。

类似于 Kaiming 初始化的结果,观察到 ζ = Θ r , d out , d in ( d out / r 2 ) \zeta = \Theta_{r,d_{\text{out}},d_{\text{in}}} \left( \sqrt{d_{\text{out}}/r^2} \right) ζ=Θr,dout,din(dout/r2 ) ζ = Θ r , d out , d in ( d in / r 2 ) \zeta = \Theta_{r,d_{\text{out}},d_{\text{in}}} \left( \sqrt{d_{\text{in}}/r^2} \right) ζ=Θr,dout,din(din/r2 ) 各自都有效。对于本文中介绍的所有模型,任一形式均确保收敛。因此,在所有后续实验中,采用 ζ = Θ r , d out , d in ( d out / r 2 ) \zeta = \Theta_{r,d_{\text{out}},d_{\text{in}}} \left( \sqrt{d_{\text{out}}/r^2} \right) ζ=Θr,dout,din(dout/r2 )

LoRA-GA 初始化

结合梯度近似和稳定Scale组件,提出了 LoRA-GA 初始化方法。首先,使用定理 3.1 的解来初始化 A init A_{\text{init}} Ainit B init B_{\text{init}} Binit。然后,根据定理 3.2 确定缩放因子 ζ \zeta ζ 以确保秩和Scale的稳定性。因此,基于定理 3.1 和 3.2,本文提出了一种新颖的初始化方法,即 LoRA-GA。

LoRA-GA:采用 η = α r \eta = \frac{\alpha} {\sqrt r} η=r α ζ = α 2 γ 2 d out / r 2 \zeta = \frac{\alpha^2} {\gamma^2} {\sqrt{d_{\text{out}}/r^2}} ζ=γ2α2dout/r2 ,其中 γ \gamma γ 是一个超参数。定义索引集 I A = { i ∣ 1 ≤ i ≤ r , i ∈ N } I_A = \{i \mid 1 \leq i \leq r, i \in \mathbb{N} \} IA={i1ir,iN} I B = { i ∣ r + 1 ≤ i ≤ 2 r , i ∈ N } I_B = \{i \mid r + 1 \leq i \leq 2r, i \in \mathbb{N} \} IB={ir+1i2r,iN}。将 ∇ W L \nabla_W L WL 的奇异值分解 (SVD) 表示为 ∇ W L = U S V T \nabla_W L = USV^T WL=USVT。初始化如下:

为了在 LoRA-GA 初始化期间节省 GPU 内存,采用了类似于 [39] 的技术。通过挂钩到 PyTorch 的反向传播过程,逐层计算梯度,并立即丢弃计算出的梯度。这确保了内存使用保持在 O ( 1 ) O(1) O(1) 级别,而不是 O ( L ) O(L) O(L),其中 L L L 是层的数量。这种方法使得初始化阶段的内存消耗低于后续 LoRA 微调阶段。算法见下算法 1。如果采样的批量大小很大,还可以使用梯度累积进一步节省内存,如下算法 2 所示。

实验

本节中,评估了 LoRA-GA 在各种基准数据集上的性能。首先,使用 T5-Base 模型 在 GLUE 数据集 的一个子集上评估自然语言理解 (NLU) 能力。随后,使用 Llama 2-7B 模型评估对话、数学推理和编码能力。最后,进行消融研究以证明本文方法的有效性。

Baselines 将 LoRA-GA 与几个基线进行比较,以展示其有效性:

  1. 完全微调 (Full-Finetune) :对模型进行全参数微调,这需要最多的资源。
  2. 原版 LoRA (Vanilla LoRA) :通过在线性层中插入低秩矩阵乘积 B A BA BA对模型进行微调。 A A A 使用 Kaiming 初始化,而 B B B 初始化为零。
  3. 具有原始结构的 LoRA 变体:包括几个保留原始 LoRA 结构的方法:
    • rsLoRA :引入新的缩放因子以稳定 LoRA 的Scale。
    • LoRA+ :使用不同的学习率更新 LoRA 中的两个矩阵。
    • PiSSA :建议在训练开始时对权重矩阵 W W W 进行 SVD,并基于具有较大奇异值的组件初始化 A A A B B B
  4. 具有修改结构的 LoRA 变体:包括修改原始 LoRA 结构的方法:
    • DoRA :通过添加可学习的幅度来增强模型的表达能力。
    • AdaLoRA :在微调过程中使用 SVD 动态剪枝不重要的权重,在固定参数预算内允许更多的秩分配给重要区域。

自然语言理解实验

模型与数据集 在 GLUE 基准的多个数据集上微调 T5-Base 模型,包括 MNLI、SST-2、CoLA、QNLI 和 MRPC。使用准确率作为主要指标,在开发集上评估性能。

实现细节 使用提示微调 (prompt tuning) 方法对 T5-Base 模型进行 GLUE 基准的微调。这涉及将标签转换为令牌(例如,“positive” 或 “negative”),并使用这些令牌的归一化概率作为分类的预测标签概率。每个实验使用 3 个不同的随机种子进行,并报告平均性能。

结果
如下表 1 所示,LoRA-GA 一直优于原版 LoRA 和其他基线方法,取得了与完全微调相当的性能。特别是,LoRA-GA 在较小的数据集如 CoLA 和 MRPC 上表现突出,展示了其在有限训练数据下更快收敛和有效利用的能力。

大语言模型实验

模型与数据集 为了评估 LoRA-GA 的可扩展性,在三个任务上训练了 Llama 2-7B 模型:对话、数学和代码。

  1. 对话 (Chat) :在 WizardLM的 52k 子集上训练模型,过滤掉以“作为 AI”或“对不起”开头的回应。在 MT-Bench 数据集上测试模型,该数据集由 80 个多轮问题组成,旨在评估大语言模型的多个方面。回答的质量由 GPT-4 进行评判,报告第一次回答的得分。
  2. 数学 (Math) :在 MetaMathQA 的 100k 子集上训练模型,这个数据集从其他数学指令调整数据集(如 GSM8K和 MATH)中引导而来,具有更高的复杂性和多样性。选择从 GSM8K 训练集中引导的数据并应用过滤。准确率在 GSM8K 评估集上报告。
  3. 代码 (Code) :在 Code-Feedback的 100k 子集上训练模型,这是一个高质量的代码指令数据集,去除代码块后的解释。模型在 HumanEval上进行测试,该数据集包含 180 个 Python 任务,报告 PASS@1 指标。

实现细节 本文的模型使用标准的监督学习进行语言建模训练。输入提示的损失设置为零。每个实验使用 3 个不同的随机种子进行,并报告这些运行的平均性能。

结果 结果如下表 2 所示,表明 LoRA-GA 优于或与其他方法相当,包括完全微调。具体而言,LoRA-GA 在 GSM8K 和 Human-eval 数据集上表现出色,突显了其在处理具有更高复杂性和多样性的任务方面的有效性。在 MT-Bench 上,LoRA-GA 也展现了竞争力的性能,尽管略微落后于 DoRA。然而,LoRA-GA 在参数较少且大约仅需 DoRA 70% 的训练时间的情况下实现了这些性能。此外,如下图 2(左)所示,本文的方法在收敛速率上显著快于原版 LoRA,其收敛速率与完全微调相当。

影响秩

将 GSM8K 和 Human-eval 数据集上的性能差异(与完全微调相比)主要归因于低秩近似所带来的表示限制。为了解决这个问题,尝试了更高的秩设置,具体为秩=32 和秩=128。发现表明,LoRA-GA 在不同秩设置下保持稳定,并且在某些情况下,甚至超越了完全微调的性能。如图 2(左)所示,初始化方法下更高的秩也导致了与完全微调相似的损失曲线。

消融研究

研究者们进行了消融研究,以评估 LoRA-GA 中非零初始化、稳定输出和梯度近似的贡献,使用了五种不同的实验设置。每个设置的详细信息见下表 3。

消融结果

结果如下表 4 和表 6 所示。对于小型和大型模型,观察到,仅将 LoRA 的初始化更改为高斯初始化并未带来性能提升,甚至可能导致轻微的性能下降。然而,当与“+SO”(稳定输出)或“+GA”(梯度近似)结合使用时,性能优于 LoRA。LoRA-GA,结合了这两种技术,表现优于其他方法。如上图 2(左)和下图 4 所示,+SO 和 +GA 也提高了收敛速度,并且当两者结合时,训练损失曲线甚至更接近完全微调的曲线。这表明,输出稳定性和梯度近似都对 LoRA 的改进有所贡献,各自解决了模型性能的不同方面。

内存成本和运行时间

研究者们在单个 RTX 3090 24GB GPU、128 核 CPU 和 256GB RAM 上对 LoRA-GA 进行了基准测试。如下表 5 所示,本文的新方法的内存消耗不超过 LoRA 训练时的内存消耗,表明没有额外的内存需求。此外,与后续的微调过程相比,这项操作的时间成本相对微不足道。例如,在 Code-Feedback 任务中,训练过程大约花费了 10 小时,而初始化仅需约 1 分钟,这一时间差异可以忽略不计。

结论

本文提出了一种用于低秩适应(LoRA)的新初始化方案,旨在加速其收敛。通过研究 LoRA 的初始化方法和更新过程,开发了一种新初始化方法——LoRA-GA,该方法从第一步起就将低秩矩阵乘积的梯度近似为完全微调的梯度。

通过大量实验,展示了 LoRA-GA 能够实现与完全微调相当的收敛速度,同时提供类似或更优的性能。由于 LoRA-GA 仅修改了 LoRA 的初始化,而未改变架构或训练算法,它提供了一种高效且易于实施的方法。此外,它还可以与其他 LoRA 变体结合使用。例如,ReLoRA 定期将适配器合并到冻结权重 W 中,这可能使 LoRA-GA 在更多步骤中展现其优势。将此作为一个有趣的未来研究方向。

参考文献

[1] LoRA-GA: Low-Rank Adaptation with Gradient Approximation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1986191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PCIe学习笔记(17)

延迟容忍报告(LTR)消息 LTR消息可选地用于报告有关其读/写服务延迟容忍度的设备行为。 (通过 LTR,PCIe 设备可以告知系统它们能容忍的最大响应延迟是多少,只要系统在这个时间之内对 PCIe 设备提出的请求做出响应即可。) LTR消息…

统信UOS微信常见问题

统信UOS微信常见问题 1. 家庭版如何激活? ①注册Union ID账号 ②绑定微信 ③登录Union ID激活系统 2. 应用商店微信qq下载失败,进行系统更新,提示依赖错误,检查更新失败怎么解决? 问题描述 安装应用商店内的应用无法…

【Linux操作系统】关于深度睡眠与浅度睡眠进程的理解

目录 一、可中断的睡眠状态(S浅度睡眠状态)二、不可中断的睡眠状态(D深度睡眠状态)三、关于S浅度睡眠状态与D深度睡眠状态的理解 一、可中断的睡眠状态(S浅度睡眠状态) S(sleeping)…

利用阿里云镜像仓库创建属于自己的私有镜像仓库

阿里云官网:https://www.aliyun.com/ 在阿里云官网注册账号,然后进入控制台 选择容器镜像服务 创建命名空间 创建镜像仓库 选择命名空间,仓库类型(设为私有的话,镜像不能随意拉取) 点管理 按以下步骤将…

炒黑豆:营养与美味的黑色宝藏

在众多的美食中,炒黑豆宛如一颗低调的黑色珍珠,散发着独特的魅力。食家巷炒黑豆,看似简单,却蕴含着不简单的营养价值。黑豆本身富含蛋白质、维生素、矿物质等多种营养成分,经过炒制之后,其香味被进一步激发…

(javaweb)Http协议

目录 一.http概述 二.Http-请求协议 三.Http响应协议 四.Http协议解析 一.http概述 ---复制这个位置会自动前面有http 浏览器给服务器发送请求携带请求数据,服务器解析数据,服务器前提需要知道具体格式等等--约定 服务器处理完请求,需要给…

jpg图片怎么批量转为png?jpg图片批量转为png的几种方法

jpg图片怎么批量转为png?在处理大批量的jpg图片,并将它们转换为png格式时,选择合适的工具和策略至关重要。这种转换通常涉及到不同的应用场景和需求,例如在网页设计中需要透明背景,或者在打印工作中需要更高的图像质量…

AOP学习

AOP概述 AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理。 什么是SpringAOP? ⽽ AOP 是⼀种思想,⽽ Spring AOP 是⼀个框架,提供了⼀种对 AOP 思…

速看!2025第三届深圳国际生态环境监测产业博览会

2025第三届深圳国际生态环境监测产业博览会 展会主题:生态环境智慧监测创新,助力生态环境高水平保护 展会时间:2025年7月24-26日 展会地点:深圳会展中心(福田) 主办单位: 广东省环境监测协…

Flutter 学习之旅

本文只针对个人学习所遇问题,以及解决方案进行记录,不深刨原理。 不深刨原理是因为我也才开始学习,讲不明白,有可能还误导大家 ,希望多多包涵。 问题一: 如何通过appBar去设置状态栏字体颜色以及状态栏透…

关于消息队列,共享内存相关作业

1>使用消息队列完成两个进程之间相互通信 1.c #include<myhead.h> //消息类型 struct msgbuf {long mtype; //消息类型char mtext[1024];//消息数据 }; #define SIZE sizeof(struct msgbuf)-sizeof(long) int main(int argc, const char *argv[]) {//创建一个进程pid…

虚拟机能访问网页但ping不通百度

最近遇到了奇怪的问题&#xff0c;虚拟机能访问网页&#xff0c;但ping不通百度&#xff0c;记录一下问题的排查过程。 能访问网页&#xff0c;说明DNS、TCP和HTTP没有问题&#xff0c;ping不通&#xff0c;说明ICMP应该出了问题。 首先通过traceroute追踪报文的转发过程&…

算法板子:最短路问题——包含朴素Dijkstra算法、堆优化版的Dijkstra算法、SPFA算法、Floyd算法

目录 1. 几种算法的用途2. Dijkstra算法——求源点到其他所有点的最短路径(不能处理负边权)&#xff08;1&#xff09;朴素Dijkstra算法——适用于稠密图&#xff08;2&#xff09;堆优化版的Dijkstra算法——适用于稀疏图 4. SPFA算法——求源点到其他所有点的最短路径、判断是…

孤独行走的视频素材去哪里找?孤独的人的视频素材哪里找啊

在视频创作和情感表达的项目中&#xff0c;选择合适的孤独行走视频素材尤为重要&#xff0c;因为这种素材能够有效传递人物的情绪状态&#xff0c;增强作品的情感层次。下面将介绍几个优质的视频素材平台&#xff0c;以帮助您更方便地寻找到适合表达孤独与深思的高质量视频素材…

C#基础——泛型

泛型 C# 中的泛型是一种强大的编程特性&#xff0c;它允许你编写类型安全且灵活的代码。泛型允许你定义类、结构体、接口、方法和委托&#xff0c;而不必在编译时指定具体的数据类型。相反&#xff0c;你可以使用类型参数来定义泛型类型或方法&#xff0c;然后在使用时指定具体…

免费的SD-WAN服务

SD-WAN&#xff0c;SASE&#xff0c;零信任是近年来比较火的概念&#xff0c;SD-WAN发展已经很久了&#xff0c;但是真正能够自主研发做SD-WAN的企业其实并不算太多。 比扬云的SD-WAN产品是自主研发的&#xff0c;可控性强&#xff0c;最重要的是具有免费版本&#xff0c;可以免…

C#使用NPOI进行Excel和Word文件处理(二)

文章目录 使用NPOI在C#中处理Excel和Word文件1. 什么是NPOI&#xff1f;2. 安装NPOI2.1 VisualStudio2019引入NPOI 3. 处理Excel文件读取Excel文件写入Excel文件 4. 处理Word文件读取Word文件写入Word文件 总结Github 地址链接参考例子 使用NPOI在C#中处理Excel和Word文件 在C…

软件生命周期(二)

1. 软件生命周期定义 软件生命周期&#xff08;SDLC&#xff09;是软件开始研制到最终废弃不用所经历的各个阶段 – 软件开发模型 2. 瀑布型生命周期模型 瀑布模型规定自上而下&#xff0c;相互衔接的固定次序&#xff0c;如同瀑布流水&#xff0c;逐级下落&#xff0c;具有…

探索全光网技术 | 全光网络技术方案选型建议三(医院场景)

目录 一、场景设计需求二、医院场景拓扑三、部署方式四、产品相关规格说明五、方案优势与特点 注&#xff1a;本文章参考资料为&#xff1a;华三官方资料 - “新华三全光网络3.0解决方案&#xff08;教育&#xff09;”与 锐捷官方资料 - “【锐捷】高校极简以太全光3.X方案设计…

c++ + linux+cmake编译动态库+so调用

步骤如下&#xff1a; 1. 创建动态库2. 编译动态库3. 使用动态库4. 编译程序并链接动态库5. 运行程序 1. 创建动态库 // hello.cpp #include <iostream> #include <string> using namespace std; int hello(string a) {cout << "hello "<< …