self-llm 实战:Qwen3-VL 视觉语言模型 LoRA 微调 LaTeX OCR 公式识别——transformers + PEFT + SwanLab 全流程指南
2026/9/12 14:30:01 网站建设 项目流程

self-llm 实战:Qwen3-VL 视觉语言模型 LoRA 微调 LaTeX OCR 公式识别——transformers + PEFT + SwanLab 全流程指南

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

本文基于开源项目《开源大模型食用指南》(self-llm)中 Qwen3-VL-4B-Instruct LoRA 可视化微调案例 文档,系统讲解如何利用 transformers、peft 与 SwanLab,对 Qwen3-VL-30B-A3B-Instruct 和 Qwen3-VL-4B-Instruct 两个视觉语言模型进行 LoRA 微调,使其学会将数学公式图片转录为 LaTeX 代码。读者将掌握多模态数据集的加载与整理、Qwen3-VL 微调数据的对话模板构造、自定义 DataCollator 的编写、LoRA 参数配置、SwanLab 训练监控集成,以及微调前后模型的推理对比评估方法。

任务背景:为什么选择 Qwen3-VL 做 LaTeX OCR

Qwen3-VL 是 Qwen 系列中能力较强的视觉语言模型之一,在文本理解与生成、视觉感知与推理、扩展上下文长度、空间与视频动态理解等方面都有显著改进。该系列同时提供 Dense 与 MoE(Mixture of Experts)两种架构,以及 Instruct 与推理增强型 Thinking 版本,可以按需选择部署。其中值得关注的增强能力之一是 OCR:模型卡片介绍其可支持 32 种语言(相比前代 19 种有提升),在弱光、模糊和倾斜条件下表现稳健,更适合处理稀有/古代字符与行话,并且改进了长文档结构解析。

本项目要解决的实战问题,正是发挥 Qwen3-VL 强 OCR 能力的典型场景:给定一张数学公式渲染图,模型需要输出对应的 LaTeX 源码。我们使用 linxy/LaTeX_OCR 开源数据集,用 LoRA 低成本微调的方式,让通用视觉语言模型变成专业的"公式转 LaTeX"识别器。

备注:本教程使用的代码同时兼容 Qwen2.5 系列视觉模型,例如Qwen/Qwen2.5-VL-3B-Instruct也可在该脚本上直接运行(参见 train_qwen3_vl.py 中被注释的模型路径)。

环境配置:显卡、CUDA 与依赖库安装

硬件需求评估

本次实验涉及两个规模的模型,显存需求差异较大:

模型显存需求推荐硬件训练耗时参考
Qwen/Qwen3-VL-30B-A3B-Instruct124+ GB两张 NVIDIA H20batch size 为 8 时约 15 分钟
Qwen/Qwen3-VL-4B-Instruct20+ GB单张 24 GB 显卡(如 3090、4090)batch size 为 1 时约 5 分钟

如果计算资源有限,强烈建议使用 Qwen3-VL-4B-Instruct 完成实验,仅需一张 24 GB 显存的显卡即可覆盖训练与推理全流程;选择 30B-A3B 模型则需要两张 H20 级别的高显存 GPU。训练耗时与per_device_train_batch_size直接相关,批次越大耗时越长,可据此在显存允许范围内权衡。

基础运行环境

确保电脑上至少有一张 NVIDIA 显卡并已安装好 CUDA 环境,Python 版本 >= 3.12,并安装能够调用 CUDA 加速的 PyTorch。本教程对应的镜像配置为 PyTorch 2.8.0、Python 3.12、CUDA 12.8。

依赖库清单

先升级 pip 并更换国内 PyPI 源以加速安装:

python -m pip install --upgrade pip pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

本次微调主要依赖的第三方库及其版本如下(与仓库中 requirements.txt 一致):

notebook==7.4.7 numpy<2.0 datasets==4.2.0 peft==0.17.1 accelerate==1.10.1 mpmath==1.3.0 networkx==3.4.2 regex==2025.9.18 sympy==1.14.0 tokenizers==0.22.1 torch==2.8.0 torchvision>=0.23.0 transformers>=4.41.2 triton==3.4.0 qwen-vl-utils==0.0.14 matplotlib>=3.10.7 modelscope==1.30.0 python-dotenv>=1.1.1 swanlab

将上述内容写入requirements.txt后执行:

pip install -r requirements.txt

其中几个关键依赖的分工值得说明:

  • transformers>=4.41.2:提供TrainerAutoProcessorAutoTokenizerAutoConfig等核心训练与加载组件;
  • peft==0.17.1:提供LoraConfigTaskTypeget_peft_model等 LoRA 适配器实现;
  • qwen-vl-utils==0.0.14:提供process_vision_info,负责从对话消息中抽取图像/视频输入,是多模态数据处理的关键工具;
  • swanlab:实验跟踪平台 SDK,与 transformers 的Trainer集成后自动记录训练指标;
  • modelscope==1.30.0:用于在国内网络环境下稳定下载模型权重;
  • python-dotenv:用于从.env文件加载 SwanLab API Key 等环境变量。

准备数据集:linxy/LaTeX_OCR 五个子集详解

本次使用开源数据集linxy/LaTeX_OCR。它是一个专门用于"公式图片 → LaTeX 源码"任务的数据集,内部包含五个子集(通过name参数选择),每个子集基本只有两个字段:image(公式渲染图片)与text(对应的 LaTeX 标注):

子集名称样本量说明
small110 条小数据集,用于测试
full约 100k(略小于)印刷体完整数据集;作者用 LaTeX 抽象语法树剔除了许多无法渲染的 LaTeX,因此实际数量略小于 100k
synthetic_handwrite约 100k(略小于)手写体完整数据集,基于full的公式用手写字体合成,可视为人在纸上的手写体
human_handwrite较小数据集更符合人类在电子屏上书写的笔迹,主要来源于 CROHME,同样经过 LaTeX 抽象语法树校验
human_handwrite_print较小数据集来自human_handwrite的印刷体版本,公式部分与human_handwrite相同,图片部分由公式用 LaTeX 渲染而来

加载训练划分并检查样本

通过split参数可以指定trainvalidationtest等划分。以下示例展示加载small子集的训练划分并快速检查样本内容:

from datasets import load_dataset train_dataset = load_dataset("linxy/LaTeX_OCR", name="small", split="train") print(train_dataset[2]["text"]) print(train_dataset[2]) print(len(train_dataset))

输出示例:

\rho _ { L } ( q ) = \sum _ { m = 1 } ^ { L } \ P _ { L } ( m ) \ { \frac { 1 } { q ^ { m - 1 } } } . { 'image': <PIL.PngImagePlugin.PngImageFile image mode=RGB size=200x50 at 0x15A5D6CE210>, 'text': '\\rho _ { L } ( q ) = \\sum _ { m = 1 } ^ { L } \\ P _ { L } ( m ) \\ { \\frac { 1 } { q ^ { m - 1 } } } .' } 50

可以看到,image字段是 PIL 图像对象,text字段是空格分隔 token 的 LaTeX 源码,这正是我们微调时需要的"图片 → 文本"监督信号。

一次性加载全部划分

若需同时获得训练、验证、测试三个划分,可直接加载整个DatasetDict

from datasets import load_dataset dataset = load_dataset("linxy/LaTeX_OCR", name="small") print(dataset)

输出:

DatasetDict({ train: Dataset({ features: ['image', 'text'], num_rows: 50 }) validation: Dataset({ features: ['image', 'text'], num_rows: 30 }) test: Dataset({ features: ['image', 'text'], num_rows: 30 }) })

模型下载与磁盘空间规划

为避免网络问题导致模型下载失败,教程使用 ModelScope 下载模型。两个模型的地址对应:

  • Qwen/Qwen3-VL-30B-A3B-Instruct
  • Qwen/Qwen3-VL-4B-Instruct

将模型下载到指定目录,例如下载 30B-A3B 模型:

modelscope download --model Qwen/Qwen3-VL-30B-A3B-Instruct --local_dir ./Qwen3-VL-30B-A3B-Instruct

下载 4B 模型:

modelscope download --model Qwen/Qwen3-VL-4B-Instruct --local_dir ./Qwen3-VL-4B-Instruct

磁盘空间方面需要特别注意:Qwen3-VL-30B-A3B-Instruct权重约 60 GB,下载前需保证磁盘空闲空间在 65 GB 以上;Qwen3-VL-4B-Instruct约 8 GB,存储空间需在 10 GB 以上。

若在 AutoDL 云平台上直接运行仓库代码,需要把模型放到代码指定的路径(训练脚本中的默认路径为/root/autodl-tmp/Qwen3-VL-4B-Instruct)。文档作者特别提醒:AutoDL 的autodl-fs目录会长期占用用户空间,未及时清理会持续计费,建议改用auto-tmp目录存放模型,但切换后需同步修改代码中加载模型的路径(即model_idoutput_dir)。

集成 SwanLab:一行回调实现训练可视化监控

SwanLab 与 transformers 已完成官方集成:只需在Trainercallbacks参数中添加SwanLabCallback实例,即可自动记录超参数和训练指标。最小集成代码如下:

from swanlab.integration.transformers import SwanLabCallback from transformers import Trainer swanlab_callback = SwanLabCallback() trainer = Trainer( ... callbacks=[swanlab_callback], )

使用前需要在 SwanLab 官网注册账号并获取 API Key。首次训练启动时按提示粘贴即可,之后无需重复登录(SwanLab 对个人使用免费)。

在本项目的 train_qwen3_vl.py 中,回调被进一步定制化:通过project指定项目名、experiment_name指定实验名,并把模型、数据集、提示词、训练样本数与 LoRA 关键超参数通过config一起记录,方便后续复现与对比:

swanlab_callback = SwanLabCallback( project="Qwen3-VL-finetune", experiment_name="qwen3-vl-latex-ocr", config={ "model": model_id, "dataset": "linxy/LaTeX_OCR", "prompt": PROMPT_TEXT, "train_data_number": len(train_data), "lora_rank": lora_config_dict["lora_rank"], "lora_alpha": lora_config_dict["lora_alpha"], "lora_dropout": lora_config_dict["lora_dropout"], }, )

代码中 API Key 设置为从环境变量加载:需要创建一个.env文件,内容为:

SWAN_LAB=你的API Key

训练脚本通过load_dotenv()读取该文件,并将其注入SWANLAB_API_KEY环境变量(见 train_qwen3_vl.py)。训练开始后,SwanLab 页面会展示实验列表,点击任一实验即可查看 loss、grad_norm、learning_rate、epoch 等指标随 step 的变化曲线,训练全程一目了然。

LoRA 原理与配置详解

LoRA 低秩适配原理

LoRA 的全称是 Low-Rank Adaptation(低秩适配)。传统的全参数微调需要更新模型中所有的参数,成本高昂;而 LoRA 的核心思想是:权重变化矩阵 ΔW 可以被近似地分解为两个更小的矩阵的乘积,仅更新这两个小矩阵即可

论文中完整的前向传播公式为:

$$h=W_{0}x+\Delta Wx=W_{0}x+BAx$$

其中 $W_0$ 是冻结的原始权重,$B \in \mathbb{R}^{d \times r}$ 与 $A \in \mathbb{R}^{r \times k}$ 是低秩分解得到的可训练矩阵。LoRA 在推理时不会增加额外的计算延迟,因为旁路结构可以在推理前合并回原始权重矩阵——通过简单的矩阵加法 $W' = W_0 + BA$ 即可将适配器权重融合进主干网络。

引入缩放因子 α 后,前向传播公式变为:

$$h = W_{0}x + \frac{α}{r}BAx$$

α 是一个常量,这样做的优势是:当改变秩 r 的大小时,可以减少重新调整超参数的需要(即 rank-stabilized 缩放)。

LoRA 配置参数解析

本项目创建的 LoRA 配置代码如下(见 train_qwen3_vl.py):

lora_config_dict = { "lora_rank": 128, "lora_alpha": 16, "lora_dropout": 0, } target_modules = ["q_proj", "k_proj", "v_proj", "o_proj"] config = LoraConfig( task_type=TaskType.CAUSAL_LM, target_modules=target_modules, inference_mode=False, r=lora_config_dict["lora_rank"], lora_alpha=lora_config_dict["lora_alpha"], lora_dropout=lora_config_dict["lora_dropout"], bias="none", )

各参数的核心含义与调整建议:

参数取值含义与说明
task_typeTaskType.CAUSAL_LM任务类型,指定为因果语言建模(对应 decoder-only 结构的 Qwen3-VL)
target_modules["q_proj", "k_proj", "v_proj", "o_proj"]LoRA 适配器作用的模块,即 Transformer 自注意力机制中的四个核心线性投影层,负责生成查询(Q)、键(K)、值(V)与注意力输出(O)
r(rank)128LoRA 的秩,即低秩分解矩阵的维度。秩越大可学习的表达能力越强,但参数量与显存开销也随之增大
lora_alpha16LoRA 的缩放因子 α,即公式中的 α,用于缩放低秩分支的贡献
lora_dropout0LoRA 层的丢弃率,设置为 0 表示不施加 dropout
bias"none"是否训练偏置项,none表示所有 bias 保持冻结
inference_modeFalse当前处于训练模式(非推理模式)

微调完整代码实现与解析

完整的训练脚本位于 train_qwen3_vl.py,也是原文档 微调案例 中的完整代码。下面按模块拆解其实现要点。

数据整理:构造多模态对话样本(process_func)

process_func负责把数据集中的imagetext字段转换为模型的训练输入。核心逻辑:

PROMPT_TEXT = "Transcribe the LaTeX of this image." def process_func(example, tokenizer, processor): MAX_LENGTH = 8192 image = example["image"] output_content = example["text"] messages = [ { "role": "user", "content": [ {"type": "image", "image": image}, {"type": "text", "text": PROMPT_TEXT}, ], } ] text = processor.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) image_inputs, video_inputs = process_vision_info(messages) inputs = processor( text=[text], images=image_inputs, videos=video_inputs, do_resize=True, ) ...

几个关键设计点:

  1. 多模态对话模板:把图片与提示词组成user角色的多模态消息,content列表里同时包含{"type": "image", "image": image}{"type": "text", "text": PROMPT_TEXT},提示词为"Transcribe the LaTeX of this image."(转写这张图片中的 LaTeX);
  2. apply_chat_template:由 processor 根据模型自带的 chat template 把消息渲染成文本序列,add_generation_prompt=True追加生成提示符;
  3. process_vision_info:从消息中抽取真实的图像/视频输入;
  4. 标签遮蔽(masking):响应部分的 token 追加eos_token_id作为结束符(若 eos 为 None 则回退到pad_token_id),并把指令部分的标签全部置为-100[-100] * len(instruction_input_ids) + response_input_ids),这样训练时 loss 只计算模型对 LaTeX 答案的预测,不计算对提问与图片标记的预测;
  5. 长度截断MAX_LENGTH = 8192,超出部分直接截断,保证 batch 内样本可控。

process_func通过dataset.map(..., fn_kwargs={"tokenizer": tokenizer, "processor": processor})应用到训练与评估数据集,remove_columns=train_data.column_names移除原始列,只保留input_idsattention_masklabelspixel_valuesimage_grid_thw五类模型输入。

自定义 DataCollator:Qwen3VLDataCollator

由于多模态样本长度不一,需要自定义 collator 完成动态 padding。Qwen3VLDataCollator的核心逻辑(见 train_qwen3_vl.py):

  • 以 batch 内最长序列为max_length,将input_ids填充为pad_token_id(若 tokenizer 无pad_token_id则回退到eos_token_id,两者均为 None 时抛出ValueError);
  • attention_mask用 0 填充,labels-100填充(-100 是 PyTorch 交叉熵损失约定忽略的位置,与训练数据中的指令掩码语义一致);
  • 视觉侧将各样本的pixel_values沿 batch 维torch.catimage_grid_thw(Qwen3-VL 图像分块网格的 T/H/W 信息)则torch.stack成 batch。

最终返回input_idsattention_masklabelspixel_valuesimage_grid_thw五元组,与process_func的输出字段一一对应。

动态加载 Qwen3-VL 模型架构

训练脚本没有硬编码模型类,而是通过AutoConfig读取模型的model_typearchitectures字段,再用importlib动态导入对应的 modeling 模块,从而对 Qwen3-VL 系列甚至 Qwen2.5-VL 保持通用性(见 train_qwen3_vl.py):

tokenizer = AutoTokenizer.from_pretrained(model_id, cache_dir=os.environ.get("HF_HOME", "./"), use_fast=False, trust_remote_code=True) processor = AutoProcessor.from_pretrained(model_id, cache_dir=os.environ.get("HF_HOME", "./"), use_fast=False) config = AutoConfig.from_pretrained(model_id, cache_dir=os.environ.get("HF_HOME", "./"), trust_remote_code=True) arch = (config.architectures or [None])[0] module_name = f"transformers.models.{config.model_type}.modeling_{config.model_type}" module = importlib.import_module(module_name) model_cls = getattr(module, arch) model = model_cls.from_pretrained( model_id, cache_dir=os.environ.get("HF_HOME", "./"), device_map="auto", trust_remote_code=True, ) model.to(dtype=torch.bfloat16) model.config.use_cache = False

训练阶段的关键设置:

  • device_map="auto"让 accelerate 自动把模型各层分配到可用 GPU 上,是 30B-A3B 双卡训练的基础;
  • model.to(dtype=torch.bfloat16)使用 BF16 混合精度,降低显存占用;
  • model.config.use_cache = False:训练阶段关闭 KV cache(节省显存),推理阶段再开启。

LoRA 注入与梯度设置

peft_model = get_peft_model(model, config) peft_model.enable_input_require_grads()

get_peft_model根据前面定义的LoraConfigq_projk_projv_projo_proj四个投影层注入低秩旁路;enable_input_require_grads()确保即使使用梯度检查点(gradient checkpointing),输入层的梯度也能正常回传。

训练超参数详解

TrainingArguments配置如下(见 train_qwen3_vl.py):

args = TrainingArguments( output_dir=output_dir, per_device_train_batch_size=8, # 每个GPU的batch size gradient_accumulation_steps=1, # 梯度累积步数 logging_steps=10, logging_first_step=5, num_train_epochs=8, # 训练轮数 save_steps=50, # 每多少步保存一次模型 save_total_limit=3, # 最多保存模型数量 learning_rate=1e-4, # 学习率 gradient_checkpointing=True, # 梯度检查点 gradient_checkpointing_kwargs={"use_reentrant": False}, report_to="none", )

各参数的作用与调参建议:

参数取值说明
output_diroutput_dir模型与日志输出目录
per_device_train_batch_size8每个 GPU 上的 batch size。30B-A3B 显存紧张时可调小(如 1),文档实验表明它对最终效果有显著影响(详见后文效果分析)
gradient_accumulation_steps1梯度累积步数,等效扩大 batch size 的另一种手段
logging_steps/logging_first_step10 / 5日志记录间隔,首次记录在第 5 步
num_train_epochs8训练轮数。作者从 1 轮逐步试到 9 轮,发现 9 轮时 loss 出现回升,最终定为 8 轮
save_steps50每 50 步保存一次 checkpoint
save_total_limit3最多保留 3 个 checkpoint,自动清理更早的
learning_rate1e-4学习率,LoRA 微调的常见量级
gradient_checkpointingTrue开启梯度检查点,用计算换显存,训练更长序列/更大 batch 的必要手段
gradient_checkpointing_kwargs{"use_reentrant": False}关闭 reentrant 模式,避免新版 PyTorch 的兼容警告
report_to"none"不向 transformers 默认的日志后端(如 wandb/tensorboard)上报,指标统一交给 SwanLab 回调收集

训练流程与产物保存

trainer = Trainer( model=peft_model, args=args, train_dataset=train_dataset, eval_dataset=eval_dataset, data_collator=Qwen3VLDataCollator(tokenizer=tokenizer), callbacks=[swanlab_callback], ) trainer.train()

训练结束后,脚本做了三件事(见 train_qwen3_vl.py):

  1. trainer.state.log_history中提取带loss的日志,用 matplotlib 绘制Step → Loss曲线并保存为output_dir/training_loss.png(注意代码中标题仍沿用 30B 的命名,仅影响图内文字,不影响数据);
  2. trainer.model.save_pretrained(output_dir)保存 LoRA 适配器权重;
  3. tokenizer.save_pretrained(output_dir)processor.save_pretrained(output_dir)同步保存分词器与处理器——这对推理阶段保证 chat template 与词表一致至关重要。

微调前后模型输出对比:推理评估脚本解析

训练完成后,我们需要客观评估微调效果。对比推理脚本 compare_qwen3_vl_infer.py 实现了对基础模型与 LoRA 微调模型在同一批测试图片上的输出对比。

模型加载与 LoRA 内存合并

BASE_MODEL_ID = "/root/autodl-tmp/Qwen3-VL-4B-Instruct" PEFT_DIR = "/root/autodl-tmp/Qwen3-VL-4B" MERGE_LORA_IN_MEMORY = True NUM_TEST_SAMPLES = 5
  • load_backbone:与训练脚本相同的动态架构加载方式,按设备选择device_map="auto"(CUDA)或None(CPU),精度按设备选择 bfloat16 或 float32;
  • load_lora_model:先加载基座模型,再PeftModel.from_pretrained(base_model, peft_dir)挂载 LoRA;若MERGE_LORA_IN_MEMORY=True,则调用peft_model.merge_and_unload()把 LoRA 权重在内存中直接合并回主干(不落盘),合并失败时回退到未合并的 PeftModel 继续运行;
  • tokenizer 与 processor优先从 LoRA 输出目录读取,保证 chat template 与词表与训练阶段完全一致;
  • 加载后显式model.eval(),并关闭 gradient checkpointing、开启use_cache=True,为生成做准备。

生成逻辑与输出规范化

generate_answer使用@torch.inference_mode()装饰,生成参数固定为max_new_tokens=512do_sample=False(贪心解码)、use_cache=True,并把attention_maskimage_grid_thw等按需传给model.generate。生成后通过截断 prompt 长度来提取新增 token,再用tokenizer.decode(..., skip_special_tokens=True)还原文本。

ensure_block_dollars函数用于统一输出格式:若模型输出已是$$...$$块级公式则原样返回;若是$...$行内公式则剥掉单美元符号后包成块级;否则直接包裹$$...$$,从而让 GT、Base、LoRA 三列以一致的 LaTeX 块级格式输出对比。

对比主流程

ds = load_dataset("linxy/LaTeX_OCR", "synthetic_handwrite") ds = ds.shuffle(seed=222) test_split = ds["test"].select(range(NUM_TEST_SAMPLES))

主流程加载synthetic_handwrite子集的测试划分,shuffle(seed=222,与训练脚本保持一致)后取前 5 个样本,对每个样本依次打印GT(真实标签)、Base(基础模型输出)、LoRA(微调模型输出)。若 LoRA 目录不存在或加载失败,脚本会捕获异常并降级为"仅对基础模型推理对比",保证评估流程不中断。

微调效果分析与调参经验

Qwen3-VL-30B-A3B-Instruct 的训练曲线

使用 batch size 为 8 训练Qwen3-VL-30B-A3B-Instruct的监控图表如下。从图中可以看到 loss 基本处于稳定下降状态,grad_norm 初期快速下降后趋于平缓,learning_rate 随调度线性衰减,证明训练在稳定拟合数据集。

Qwen3-VL-4B-Instruct 不同 batch size 的对比

作者分别用 batch size 为 1 和 8 训练了 4B 模型。对比微调前后效果可以发现:batch size 为 1 训练出的模型提取效果较差(疑似过拟合),batch size 为 8 训练出的模型效果明显更好。下图是 4B 模型 batch size 为 8 时的微调前后输出对比示例——LoRA 微调后模型对公式符号、变量与张量指标的还原显著更接近 GT,而基础模型则存在符号写错、变量错乱等问题。

训练经验总结与过拟合讨论

从微调前后效果对比可以看出,30B-A3B 模型在部分示例上微调后有明显提升,但作者也发现模型在微调后出现了一些问题——偶尔有示例不如微调前的模型,推测是过拟合所致,因为训练曲线显示训练轮次设置得有些偏多。

作者实际进行了多轮实验,调参过程非常有参考价值:

  1. 训练轮次:最初只设置 1 轮微调,效果不佳——微调前后模型输出几乎一模一样;2 轮也类似。随后逐步调大轮次,当轮次到 9 时,loss 不再持续下降、部分指标反而回升,因此最终确定训练轮次为 8;
  2. 数据集选择:原本想使用手写公式识别数据集,但训练过程中模型拟合不佳——手写数据集中同一个字符的写法千变万化,仅用少量数据训练很难收敛,于是换回非手写(印刷体合成)公式;
  3. batch size 的影响:batch size 对训练结果影响显著。从 4B 模型可看出,batch size 为 1 时效果较差(过拟合),batch size 为 8 时效果更好。

感兴趣的读者可以在此基础上继续探索其他参数组合,例如修改lora_ranklora_alpha、学习率、batch_size等,并在 SwanLab 中对比不同实验的指标差异。

常见错误与解决办法

在复现过程中如果遇到如下报错:

pyarrow.lib.ArrowTypeError: Did not pass numpy.dtype object

该错误通常由 numpy 版本问题引起,可执行以下命令修复:

pip install --upgrade numpy

然后重新运行代码即可。仓库 requirements.txt 中也注明了"numpy 如果报错可以尝试对 numpy 进行更新"。

总结

本文完整复现了 Qwen3-VL 系列视觉语言模型在 LaTeX OCR 任务上的 LoRA 微调全流程:从硬件评估、依赖安装、数据集准备、ModelScope 模型下载,到 SwanLab 监控集成、LoRA 原理与参数配置、process_func多模态样本构造、自定义 DataCollator、动态模型架构加载与训练超参数调优,再到微调前后模型的推理对比评估。整个过程全部代码已开源在仓库的 05-Qwen3-VL-30B-A3B-Instruct Lora 可视化微调案例 - LaTexOCR 目录中,训练脚本为train_qwen3_vl.py、对比推理脚本为compare_qwen3_vl_infer.py,可基于自身显卡条件选择 4B 或 30B-A3B 模型直接复用,也可参照 Qwen3-VL-MoE-模型结构解析-Blog 进一步理解模型架构设计。

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询