记录向:基于 Qwen-VL 的多模态助手 LoRA 微调实践
前言
之前的学习大多是对焦于网络方面,最近准备系统学习 LLM 方面的内容,我平时的学习习惯是任务驱动型,因为我觉得一上来就直接啃概念有点枯燥,那么我们接下来针对一个任务开展我们后续的学习
任务要求
根据任务需求,我们要基于 qwen3.5-9b 大模型,自己设计数据并训练一个具备“看图 + OCR + 剧情推演”能力的个人多模态助手;这个文档主要记录我从“看到任务——思考过程——训练方式”的全过程,下面是具体的流程:
模型介绍
我们采用的大模型是 qwen3.5-9b ,是阿里 Qwen 团队开源的一款约 9B 参数的原生多模态大模型,既能处理文字,也能理解图片
qwen3.5-9b,是一个具有 9b 参数的大模型,也就是 90 亿参数的大模型;这个大模型是已经具备有图片理解,文字识别的能力,因此我们是在一个具备学习能力的模型的基础上把它培养成一个更擅长某种任务的模型;
模型的下载可以通过魔塔社区直接下载,权重所占内存大约是 19gb,建议设备内存大小不要低于150gb;本次实验我采用的显卡是 5090,硬盘的大小是 150gb;
数据集构建
前面所述,我们的模型是具有一定的能力的,我们想做的事情是让模型理解并做出简单的推理,判断有哪些合理的后续,按照我们规定的形式组织出来;本质上是在建立一个输入-输出的映射,因此我们现在的核心并不是去找图片,而是先规定一个合格的数据是什么样的?
数据集规则
一条合格的 SFT 数据,应该是包括”用户的输入,助手的思考“,其中助手的思考是值得研究的,原因是这就是我们想灌输给模型的行为;在我们这边的例子可以是:
根据图像的 xx 内容,当前在干嘛。
接下来有可能发生:
1. 平淡版:
无事发生……
2. 正常版:
可能……
3. 大脑洞版:
……
我们规定了模型输出的三个特点,分别是:理解、合理预测、创造性推演;其中要注意脑洞不可以太过离谱,否则模型会不按照有逻辑的情况下胡乱编造,也就是脑洞推翻推理;
数据集搭建
我们可以先用xlsx文档记录一下数据集,比如下图所示:

接着我们可以让 cluade code 帮我们转化成为 JSON 的形式,Qwen 官方推荐的 ms-swift 训练格式,ms-swift 官方文档明确支持这种 messages + images 的多模态数据组织方式
至于需要多少条数据,我们可以先简单的采用几十张试试,如果后面发现效果不是很好的话,再继续添加数据;我这边是简单的先试了64张左右的数据;
SFT
全称是:Supervised Fine-Tuning,监督微调
可以理解为是:拿一个已经训练好的大模型,给它大量“问题 → 标准答案”的示范,让它学会按照你希望的方式回答;其中这样大量的示例,我们称为 SFT 数据集
因此 SFT 并不是重新训练大模型,而是在原有的大模型的基础上,通过模型生成的答案与标准答案之间进行对比,反向传播更新模型的参数,上述就是 SFT 训练的流程;
微调过程
上述是微调前的数据准备阶段,下面我们来看看模型是怎么微调的:
微调概念
首先我们介绍一下什么是微调,微调也被称为是 LoRA,指的是把原来模型的所有参数冻结,另外增加一小组参数进行训练;这里要和冻结部分参数,接着修改未冻结参数做区分;有需要看原论文的,论文地址我放到下面:
论文的地址:2106.09685
LoRA 全称是 Low-Rank Adaptation,低秩适配,自然就会有一个问题什么是秩,什么又是低秩?
秩
秩是线性代数里描述一个矩阵有多少个独立信息方向的量,我们用一个矩阵作为例子:
A
=
(
1
2
2
4
)
A = \begin{pmatrix} 1 & 2 \\ 2 & 4 \end{pmatrix}
A=(1224)
我们可以发现第二行实际上就是第一行乘 2 得到的结果,说明它本没有提供信息的信息,因此这个矩阵虽然有两行两列,但真正独立的信息只有 1 个方向;
这就好假设有 100 个特征,但其中 90 个其实都可以由另外 10 个特征组合出来,那么虽然表面上有 100 维,真正独立的信息可能只有 10 维
低秩
Transformer 里面有大量线性层,假设其中一个权重矩阵是:
W
0
∈
R
d
×
k
W_0 \in \mathbb{R}^{d \times k}
W0∈Rd×k
如果进行普通全参数微调,我们其实想学的是:
W
=
W
0
+
Δ
W
W = W_0 + \Delta W
W=W0+ΔW
但是假设 W0 的大小是 4096,那么所学习到的 ΔW 的大小也是 4096,这样的话差不多 1678 万个参数,那么只是一层的参数就已经很多
于是 LoRA 提出了关键假设:
我们真正需要是 ΔW,没有必要拥有那么高的自由度,它可以用一个低秩结构表示
于是就把 ΔW 改为:
Δ
W
=
B
A
\Delta W = BA
ΔW=BA
原来的 Linear:
h
=
W
0
x
h = W_0 x
h=W0x
加上 LoRA 后:
h
=
W
0
x
+
B
A
x
h = W_0 x + BA x
h=W0x+BAx
实际 LoRA 还会乘一个缩放因子,通常写成:
h
=
W
0
x
+
α
r
B
A
x
h = W_0 x + \frac{\alpha}{r} BA x
h=W0x+rαBAx
论文中说明,训练开始时 A 使用随机高斯初始化、B 初始化为 0,因此最开始 BA=0,也就是说刚把 LoRA 插进去时,模型行为仍然等于原始模型;之后训练才慢慢让这条 LoRA 分支产生影响
微调阶段
首先看一下本次微调使用的训练启动脚本 train_lora.sh,这个脚本主要记录模型路径、数据集路径、LoRA 参数和训练参数,接下来 ms-swift 会读取这些配置,框架根据配置加载模型和 LoRA;

ms-swift 是一个专门帮你训练、微调、推理、部署大模型的“训练框架”,比如说代码中的:
swift sft \
--model /root/autodl-tmp/models/Qwen3.5-9B \
--dataset train.jsonl \
--tuner_type lora \
--lora_rank 8
含义是告诉 ms-swift,我要做一次 SFT 模型用 Qwen3.5-9B、数据在train.jsonl、微调方法用 LoRA、LoRA 的 rank 设为 8;
注意这里的 --tuner_type lora 并不是 LoRA 算法本身,只是告诉 ms-swift:本次微调采用 LoRA,ms-swift 随后会根据这些配置加载 Qwen3.5-9B,找到需要进行适配的 Linear 层,并为这些层加入 LoRA 参数
接着我们可以看一下 LoRA 的代码,因为源码太多了,我们用一个最小单元的来表示:
class LoRALinear(nn.Module):
def __init__(self, linear, r=8, alpha=32):
super().__init__()
self.base_layer = linear
# 原始 Linear 不训练
for p in self.base_layer.parameters():
p.requires_grad = False
# LoRA A
self.lora_A = nn.Linear(
linear.in_features,
r,
bias=False
)
# LoRA B
self.lora_B = nn.Linear(
r,
linear.out_features,
bias=False
)
self.scaling = alpha / r
def forward(self, x):
original = self.base_layer(x)
lora = self.lora_B(
self.lora_A(x)
)
return original + self.scaling * lora
我们可以看到,原始模型的参数是完全冻结的,因此在反向传播的过程中,是不会修改任何参数的,只会修改 A 和 B 的参数;
接下来就是训练的阶段,过程与常规的深度学习是类似的,不再过多赘述
效果展示
我们看看微调后模型的输出:

同时看看网页版千问的回答:

比对两者的回答,我们微调后的优势主要体现在“脑洞约束方面”和“结构化输出”,我们微调后的脑洞并不会过分地无逻辑输出,而是遵循图片信息为前提,开发脑洞;
网页版本在发散程度上更加优秀,可以同时触发多个发展线,并且会把物品赋予其他含义,对图片做分析的时候,会考虑情感上的变化,这个是我们目前微调后缺乏的能力;
想到这里,我有一个问题:随着大模型的进化,目前 skill 可能在垂直领域上依旧有用,但是可能在未来两年,随着大模型能力的提升,这些东西可能就会失去作用;那微调也会出现这样的局面吗?随着模型能力的提升,微调会不会替代呢?
总结
这边文章主要是简单记录了一次实践的过程,以成功推理和概念理解为主要目的,后续我们会在这个任务的基础上做进一步的学习;希望大家可以持续关注我的频道,蟹蟹各位家人!
更多推荐


所有评论(0)