Multi-LoRA技术全解析:大模型部署的省钱秘籍,参数高效微调必看指南
本文介绍了Multi-LoRA技术在大模型部署中的应用,通过低秩分解降低微调参数和计算成本。文章对比了多种参数高效迁移学习方法,重点阐述了LoRA原理:冻结原模型参数,仅微调低秩扩展矩阵。通过MNIST手写数字识别案例,展示了LoRA的训练和推理过程,证明其能有效提升特定任务识别能力,同时显著减少训练资源消耗。Multi-LoRA技术为大模型多任务部署提供了高效解决方案。
本文详细介绍了Multi-LoRA(低秩适配)技术,通过低秩分解原理降低大模型微调的参数量和计算成本。文章对比了多种参数高效迁移学习方法,并通过MNIST手写数字识别案例展示了LoRA的训练和推理过程。LoRA通过冻结原模型参数,仅微调低秩扩展参数,实现了多场景高效部署,显著降低了训练成本,是解决大模型多任务部署的有效方案。
点击蓝字 关注我们
Multi-LoRA(Low-Rank Adaptation)作为大模型部署的常见方案,解决了多场景的训/推成本高的问题。本文围绕Multi-LoRA,结合原理和案例展开叙述。读者参考本文提供的notebook用例[1](有条件可尝试在线运行[2]),按照步骤实践后,基本能掌握相关概念与Multi-LoRA的构建过程。
关键问题:
- 什么是低秩分解,对计算有什么影响?
- LoRA特点是什么,一般应用在哪些层上?
- Multi-LoRA原理是什么,如何提升其性能?
1 基本原理
1.1 问题与方案
为了让大模型在细分领域要取得更好的效果,会用领域数据进行微调训练,且微调模型时期望用少量的训练步骤完成对权重的更新。微调一个大模型要有匹配的硬件资源和足够的训练时间。对于动辄百亿参数的大模型而言,可能出现如下问题:
- 硬件资源无法支持起基础模型的训练。如显存不足、算力太低(训练时间过长);
- 训练不收敛或者效果不佳;
- 大模型的通用能力可能下降。
既然大模型的全量调参成本高,是否能仅微调部分达到全量微调的效果?这个问题已有不少的研究,如:
-
适配器(Adapter[3]):一种在模型中插入新层的方式,仅训练插入的适配器;
-
前缀调优(Prefix Tuning[4]):给Attention KV层中添加一个前缀,并只训练这个附加的前缀参数;类似的还有提示词调优(Prompt Tuning[5]);

-
局部训练:仅训练Transformer 的 LayerNorm参数[6],或者仅训练 Bias ( BitFit[7]);
-
低秩适配(LoRA):给模型增加降秩权重,且仅训练该新增的权重;
当然这些方法也可以混合使用[8]。

参数高效迁移学习 (PETL,parameter-efficient transfer learning)
上述迁移学习的方式各有特点,此处不展开讨论,主要聚焦LoRA方法的相关内容。
1.2 低秩分解的原理
LoRA原理涉及的关键知识:任意矩阵都能进行奇异值分解(Singular Value Decomposition,SVD);当矩阵是不满秩矩阵(Rank-deficient Matrix)时,可以用低秩的分解矩阵来代替原矩阵。
具体展开说明。对于一般矩阵
通过SVD计算能够得到三个子矩阵,公式如下:
其中
,矩阵的秩数满足:
是一个对角矩阵,对角上非零元素个数等于秩数,当W为不满秩矩阵时,
,,
这里举个3x2矩阵分解的例子,如下图所示,将矩阵W进行SVD处理,得到分解矩阵。其中3x2的对角矩阵最后一行必为0,所以可以简化表达;进一步若还存在‘0’的对角元素,可以进一步简化。简化后的分解矩阵乘积依然等于原矩阵。

优势:当矩阵的尺寸(m,n)较大时,分解矩阵的特点是元素个数相比原矩阵的更少,r越小元素越少。比如当r=1,m=n=1000时,原矩阵元素个数为1000,000,分解矩阵元素总数为2001,比值小于0.5%。参数量少带来好处是:计算量少、存储量少。
1.3 低秩分解的代码实践
这里我们通过一个简单的乘法示例来验证分解矩阵的特点。先创建一个非满秩的矩阵W并进行SVD计算,接着建立B、A矩阵,最后定义一个乘加运算,对比原矩阵与分解矩阵的计算差异。
- step1:创建一个非满秩矩阵
import torch
import numpy as np
d, k = 10, 10
# 创建一个非满秩矩阵(a rank-deficient matrix)
W_rank = 2
W = torch.randn(d,W_rank) @ torch.randn(W_rank,k)
W_rank = np.linalg.matrix_rank(W)
打印相关结果:

- step2:进行SVD分解,构建B、A矩阵。
# 对W进行SVD处理:(W = UxSxV^T)
U, S, V = torch.svd(W)
# 对于对角矩阵S保留前rank个数据即可,相应的U和V也只要保存前rank行的数据。
U_r = U[:, :W_rank]
S_r = torch.diag(S[:W_rank])
V_r = V[:, :W_rank].t()
# 定义: B = U_r * S_r;A = V_r
B = U_r @ S_r
A = V_r
打印相关参数:

- step3:构建一个线性层,对比计算差异:
# 创建一个线性运算的输入, y = Wx + b
bias = torch.randn(d)
x = torch.randn(d)
# 原始计算 y = Wx + bias
y = W @ x + bias
# 分解矩阵计算 y' = (B*A)x + bias
y_prime = (B @ A) @ x + bias
print(f"The result is allclose: {torch.allclose(y, y_prime)}")
可以看到打印输出为True,该例中W的元素个数为100,B和A的元素总数为40。
低秩分解降低了元素总数,且不改变计算结果;如果分解运算为一次运算,则在算量上面也更少。
2 LoRA
2.1 计算公式
LoRA正是用低秩分解矩阵的特点来降低微调矩阵的元素个数,原矩阵为
[9]:
其中
微调时
2.2 LoRA训练/推理实践
一个数字0~9手写体识别训练场景,数据采用MNIST。训练一个3层的MLP,让其具备数字手写体识别的能力。为了体现LoRA的作用,需要对数据集进行处理,先全量训练,再增加LoRA微调。大致步骤如下:
-
step1:构建主模型并训练,训练数据集去掉数字‘1’;
-
step2:测试主模型的识别能力;
-
step3:创建LoRA层;
-
step4:主模型的参数冻结,用数字‘1’的数据进行微调;
-
step5:测试LoRA模型,观测数据‘1’识别度差异。

构建一个简单的模型:
# 创建一个全连接的网络用于手写体识别:
class MLP(nn.Module):
def __init__(self, hidden_size_1=1000, hidden_size_2=2000):
super(MLP,self).__init__()
self.linear1 = nn.Linear(28*28, hidden_size_1)
self.linear2 = nn.Linear(hidden_size_1, hidden_size_2)
self.linear3 = nn.Linear(hidden_size_2, 10)
self.relu = nn.ReLU()
def forward(self, img):
x = img.view(-1, 28*28)
x = self.relu(self.linear1(x))
x = self.relu(self.linear2(x))
x = self.linear3(x)
return x
net = MLP().to(device)

模型结构
接着定义模型的训练函数、测试函数:
# 训练函数定义:
def train(train_loader, net, epochs=5, total_iterations_limit=None):
cross_el = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)
total_iterations = 0
for epoch in range(epochs):
net.train()
loss_sum = 0
num_iterations = 0
data_iterator = tqdm(train_loader, desc=f'Epoch {epoch+1}')
if total_iterations_limit is not None:
data_iterator.total = total_iterations_limit
for data in data_iterator:
num_iterations += 1
total_iterations += 1
x, y = data
x = x.to(device)
y = y.to(device)
optimizer.zero_grad()
output = net(x.view(-1, 28*28))
loss = cross_el(output, y)
loss_sum += loss.item()
avg_loss = loss_sum / num_iterations
data_iterator.set_postfix(loss=avg_loss)
loss.backward()
optimizer.step()
if total_iterations_limit is not None and total_iterations >= total_iterations_limit:
return
# 测试函数定义:
def test(model=net):
correct = 0
total = 0
wrong_counts = [0 for i in range(10)]
with torch.no_grad():
for data in tqdm(test_loader, desc='Testing'):
x, y = data
x = x.to(device)
y = y.to(device)
output = model(x.view(-1, 784))
for idx, i in enumerate(output):
if torch.argmax(i) == y[idx]:
correct +=1
else:
wrong_counts[y[idx]] +=1
total +=1
result_str = ""
for i in range(len(wrong_counts)):
result_str += f'The wrong counts of digit {i}: {wrong_counts[i]}\n'
print(f'\nAccuracy: {round(correct/total, 3)}\n{result_str}')
- step1:构建主模型并训练,训练数据集去掉数字‘1’。
# 下载MNIST手写体数字识别的数据
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
# 加载手写体数据:
mnist_trainset = datasets.MNIST(root='./data', train=True, download=True, transform=transform) # 训练集
train_loader = torch.utils.data.DataLoader(mnist_trainset, batch_size=10, shuffle=True)
mnist_testset = datasets.MNIST(root='./data', train=False, download=True, transform=transform) # 测试集
# 去掉数字‘1'的数据,模型对‘1'的识别率存在问题
exclude_indices = torch.tensor([False if x == 1 else True for x in mnist_trainset.targets])
mnist_trainset.data = mnist_trainset.data[exclude_indices]
mnist_trainset.targets = mnist_trainset.targets[exclude_indices]
# 训练模型:
- step2:测试主模型的识别能力。
test_loader = torch.utils.data.DataLoader(mnist_testset, batch_size=10, shuffle=True)
test()

可以看到,数字‘1’在测试集上表现不佳。
- step3:创建LoRA层
# 定义LoRA对权重修改:
class LoRAParametrization(nn.Module):
def __init__(self, features_in, features_out, rank=1, alpha=1, device='cpu'):
super().__init__()
# 低秩矩阵的定义:
self.lora_A = nn.Parameter(torch.zeros((rank,features_out)).to(device))
self.lora_B = nn.Parameter(torch.zeros((features_in, rank)).to(device))
nn.init.normal_(self.lora_A, mean=0, std=1)
# 参考论文:https://arxiv.org/pdf/2106.09685 4.1节 设置一个比例系数:
self.scale = alpha / rank
# LoRA开关:
self.enabled = True
def forward(self, original_weights):
if self.enabled:
# Return W + (B*A)*scale
return original_weights + torch.matmul(self.lora_B, self.lora_A).view(original_weights.shape) * self.scale
else:
return original_weights

将LoRA层注册到模型中:
def linear_layer_parameterization(layer, device, rank=1, lora_alpha=1):
# LoRA仅修改W,忽略bias修改。
features_in, features_out = layer.weight.shape
return LoRAParametrization(
features_in, features_out, rank=rank, alpha=lora_alpha, device=device
)
# 保存一份原始权重数据,用于后续校验
original_weights = {}
for name, param in net.named_parameters():
original_weights[name] = param.clone().detach()
# 注册LoRA权重到原始层中:
parametrize.register_parametrization(
net.linear1, "weight", linear_layer_parameterization(net.linear1, device)
)
parametrize.register_parametrization(
net.linear2, "weight", linear_layer_parameterization(net.linear2, device)
)
parametrize.register_parametrization(
net.linear3, "weight", linear_layer_parameterization(net.linear3, device)
)
# 定义LoRA开关函数:
def enable_disable_lora(enabled=True):
for layer in [net.linear1, net.linear2, net.linear3]:
layer.parametrizations["weight"][0].enabled = enabled
打印原始参数和添加LoRA参数的对比,LoRA占比仅0.242%。

- step4:用数字‘1’数据微调;
# 将原始权重冻结:
for name, param in net.named_parameters():
if 'lora' not in name:
print(f'Freezing non-LoRA parameter {name}')
param.requires_grad = False
# 过滤数据,仅保留‘1'的数据:
mnist_trainset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
exclude_indices = torch.tensor([True if x == 1 else False for x in mnist_trainset.targets])
mnist_trainset.data = mnist_trainset.data[exclude_indices]
mnist_trainset.targets = mnist_trainset.targets[exclude_indices]
train_loader = torch.utils.data.DataLoader(mnist_trainset, batch_size=10, shuffle=True)
# 用数据‘1'训练带有LoRA的模型:
train(train_loader, net, epochs=1, total_iterations_limit=100)
- step5:测试LoRA模型,观测数据‘1’识别度差异。
# 测试有LoRA的情况:
enable_disable_lora(enabled=True)
test()
打印正确率,找到数字‘1’的错误个数,相比原模型明显降低了。

与原始模型的测试输出的正确率进行一个对比,除了数字‘1’以外其它数字的识别精度均下降。

LoRA特点小结
优点:
-
LoRA采用了横向扩展参数的方式,训练时原模型参数冻结、仅微调扩展参数,扩展参数采用低秩矩阵,保证了较低的参数量。
-
实践证明了LoRA的有效性,甚至能让小模型微调能达到大模型的水平[10]。
-
LoRA的适配方式能够保证各个垂直领域解耦训练,互不干扰。
不足:
- 分解矩阵B、A的秩小于原矩阵W,表达能力弱,导致LoRA的效果可能弱于全量微调;
- 当主模型参数量比较大且r取值不能太小时,LoRA训练成本依然很高。
AI时代,未来的就业机会在哪里?
答案就藏在大模型的浪潮里。从ChatGPT、DeepSeek等日常工具,到自然语言处理、计算机视觉、多模态等核心领域,技术普惠化、应用垂直化与生态开源化正催生Prompt工程师、自然语言处理、计算机视觉工程师、大模型算法工程师、AI应用产品经理等AI岗位。

掌握大模型技能,就是把握高薪未来。
那么,普通人如何抓住大模型风口?
AI技术的普及对个人能力提出了新的要求,在AI时代,持续学习和适应新技术变得尤为重要。无论是企业还是个人,都需要不断更新知识体系,提升与AI协作的能力,以适应不断变化的工作环境。
因此,这里给大家整理了一份《2025最新大模型全套学习资源》,包括2025最新大模型学习路线、大模型书籍、视频教程、项目实战、最新行业报告、面试题等,带你从零基础入门到精通,快速掌握大模型技术!
由于篇幅有限,有需要的小伙伴可以扫码获取!

1. 成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。
2. 大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)

3. 大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。

4. 大模型项目实战
学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

5. 大模型行业报告
行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

6. 大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

为什么大家都在学AI大模型?
随着AI技术的发展,企业对人才的需求从“单一技术”转向 “AI+行业”双背景。企业对人才的需求从“单一技术”转向 “AI+行业”双背景。金融+AI、制造+AI、医疗+AI等跨界岗位薪资涨幅达30%-50%。
同时很多人面临优化裁员,近期科技巨头英特尔裁员2万人,传统岗位不断缩减,因此转行AI势在必行!

这些资料有用吗?
这份资料由我们和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。


大模型全套学习资料已整理打包,有需要的小伙伴可以
微信扫描下方CSDN官方认证二维码,免费领取【保证100%免费】

更多推荐



所有评论(0)