Qwen3‑VL‑8B 是 Qwen 系列中功能最强且使用最广泛的视觉‑语言模型,旨在统一理解文本、图像和视频。
它在文本生成、视觉推理、空间感知、长上下文处理和智能体交互等方面带来显著提升,适合在边缘与云端环境中开展研究与实际部署。
在本教程中,我们将 微调 Qwen3‑VL‑8B‑Instruct,以电子原理图为对象。通过训练模型识别原理图符号、连线与空间关系,我们让它能够准确理解电路设计,并判断在真实电路中需要补充哪些电子元件。
您将学到:
从 Hugging Face 加载并清洗数据集。
为训练创建多模态对话模板。
搭建 Qwen3-VL 视觉‑语言流水线。
配置 LoRA 以进行内存高效训练。
微调模型并保存检查点。
将训练好的适配器发布到 Hugging Face。
对比微调前后的结果。
如果您还不熟悉 Hugging Face,可以先学习 Hugging Face 基础 技能路径!
1. 微调 Qwen3-VL-8B 的环境搭建
在使用 视觉–语言模型 时,GPU 显存往往成为关键限制。高分辨率图像与多模态编码器会迅速消耗显存,因此强烈建议使用显存充足的 GPU。
在本教程中,我们将启动一台 RunPod A100(80GB)实例 ,并使用最新版的 PyTorch 镜像。该配置可为训练提供充足的显存余量,并在微调过程中避免不必要的内存瓶颈。
向下滚动至 Pod Template 部分。在这里选择 最新的 PyTorch 镜像 。您也可以在此调整存储设置并添加环境变量。
点击 Edit ,然后进行如下修改:
容器磁盘大小: 设置为 40 GB
卷磁盘大小: 设置为 40 GB
环境变量: 添加 HF_TOKEN,其值为您的Hugging Face 访问令牌 (可在 Hugging Face 设置 中生成)
完成后,保存模板并部署该 pod。
当 pod 启动后:
打开 JupyterLab。
创建一个新的 Python 笔记本。
安装所需依赖。
要安装依赖,请运行以下代码单元。在 Jupyter 中,行首的感叹号表示以 Shell 命令而非 Python 代码执行。
!pip -q install -U accelerate datasets pillow sentencepiece safetensors peft
!pip install --quiet "transformers==5.0.0rc1"
!pip install --quiet --no-deps trl
!pip install --no-cache-dir flash-attn --no-build-isolation技术支持
接下来,设置固定随机种子以保证可复现性,并启用针对 A100 的性能优化。
import torch
from transformers import set_seed
set_seed( 42 )
torch. backends. cuda. matmul. allow_tf32 = True
torch. backends. cudnn. allow_tf32 = True
print ( "CUDA:" , torch. cuda. is_available( ) , torch. cuda. get_device_name( 0 ) if torch. cuda. is_available( ) else None )
print ( "bf16 supported:" , torch. cuda. is_available( ) and torch. cuda. is_bf16_supported( ) ) 技术支持
CUDA: True NVIDIA A100 80GB PCIe
bf16 supported: True技术支持
2. 从 Hugging Face 下载 Open Schematics 数据集
现在我们将从 Hugging Face Hub 加载 Open Schematics 数据集。该数据集包含电子原理图片像以及描述每个电路的丰富元数据,非常适合用于视觉–语言训练。
import torch
from datasets import load_dataset
DATASET_ID = "bshada/open-schematics"
ds_all = load_dataset( DATASET_ID, split= "train" )
print ( ds_all) 技术支持
Dataset({
features: ['schematic', 'image', 'components_used', 'json', 'yaml', 'name', 'description', 'type'],
num_rows: 84470
})技术支持
该数据集包含 8.4 万余个样本,每个样本将一张原理图与结构化信息(如元件列表以及机器可读格式 JSON 和 YAML)配对。
3. 探索数据集结构
我们先检查一个样本,以更好地理解数据集结构。
ex = ds_all[ 0 ]
print ( "\nSample keys:" , ex. keys( ) )
print ( "name:" , ex. get( "name" ) )
print ( "type:" , ex. get( "type" ) )
print ( "components_used:" , ( ex. get( "components_used" ) or [ ] ) [ : 10 ] )
print ( "has schematic:" , bool ( ex. get( "schematic" ) ) )
print ( "has json/yaml:" , bool ( ex. get( "json" ) ) , bool ( ex. get( "yaml" ) ) )
print ( "image:" , ex. get( "image" ) ) 技术支持
Sample keys: dict_keys(['schematic', 'image', 'components_used', 'json', 'yaml', 'name', 'description', 'type'])
name: TiebeDeclercq/Uart-programmer
type: .kicad_sch
components_used: ['Conn_01x01_Pin', 'Conn_01x06_Pin', 'USB_A', 'Conn_02x05_Odd_Even', 'Conn_01x06_MountingPin', 'C', 'Fuse_Small', 'LED', 'R', 'CH340C']
has schematic: True
has json/yaml: True True
image: <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=1123x794 at 0x7FBC6FD060F0>技术支持
这表明每个样本都包含高分辨率原理图、一份元件列表,以及电路的结构化表示。
我们可以直接在 Jupyter 笔记本中渲染原理图片像。
ex. get( "image" ) 技术支持
最后,我们查看该原理图所用元件的完整列表。
print ( ex. get( "components_used" ) ) 技术支持
['Conn_01x01_Pin', 'Conn_01x06_Pin', 'USB_A', 'Conn_02x05_Odd_Even', 'Conn_01x06_MountingPin', 'C', 'Fuse_Small', 'LED', 'R', 'CH340C', 'Jumper_3_Open', 'MountingHole', 'AMS1117-3.3', 'MMBT3904', '1N5819HW-7-F', 'LESD5D5.0CT1G', '+3.3V', '+5V', 'GND', 'VCC']技术支持
该元件列表为原理图片像与电路中的电子元件之间提供了清晰的对应关系。
4. 清洗与筛选数据集
在训练前,我们对数据集进行清洗与筛选,以确保每个样本都包含视觉–语言学习所需的最少信息。具体而言,我们保留具有有效元件标注且对应有原理图片像的样本。
首先,检查有多少样本的 components_used 缺失、为空或无效。
need_cols = [ c for c in [ "components_used" , "schematic" , "name" , "type" ] if c in ds_all. column_names]
ds_small = ds_all. select_columns( need_cols)
missing_key = none_components = empty_components = missing_any = 0
has_schematic_but_missing = 0
for ex in ds_small:
if "components_used" not in ex:
missing_key += 1
missing_any += 1
if ex. get( "schematic" ) :
has_schematic_but_missing += 1
continue
cu = ex[ "components_used" ]
bad = ( cu is None ) or ( isinstance ( cu, list ) and len ( cu) == 0 )
if cu is None :
none_components += 1
elif isinstance ( cu, list ) and len ( cu) == 0 :
empty_components += 1
if bad:
missing_any += 1
if ex. get( "schematic" ) :
has_schematic_but_missing += 1
print ( "\n=== Missing components report ===" )
print ( "Total:" , len ( ds_all) )
print ( "Missing key:" , missing_key)
print ( "None:" , none_components)
print ( "Empty list:" , empty_components)
print ( "Missing (any):" , missing_any)
if "schematic" in need_cols:
print ( "Has schematic but missing components:" , has_schematic_but_missing) 技术支持
=== Missing components report ===
Total: 84470
Missing key: 0
None: 47558
Empty list: 8
Missing (any): 47566
Has schematic but missing components: 47566技术支持
下方汇总显示,大量样本虽包含原理图数据,但缺少可用的元件标注。
为避免筛选时的不必要内存占用,我们显式禁用图像解码。这样在应用过滤器时,Hugging Face 不会将图像加载到内存。
from datasets. features import Image as HFImage
ds_all = ds_all. cast_column( "image" , HFImage( decode= False ) ) 技术支持
接着,我们定义一个过滤器,仅保留具有非空元件列表且图像引用有效的样本。
def keep_components_and_image ( components_used, image) :
if not ( isinstance ( components_used, list ) and len ( components_used) > 0 ) :
return False
if image is None :
return False
if isinstance ( image, dict ) :
return bool ( image. get( "path" ) ) or bool ( image. get( "bytes" ) )
return True 技术支持
应用该过滤器后,数据集显著收敛为高质量且可直接使用的样本。
ds_clean = ds_all. filter (
keep_components_and_image,
input_columns= [ "components_used" , "image" ] ,
)
print ( "Original:" , len ( ds_all) )
print ( "Clean:" , len ( ds_clean) )
print ( "Dropped:" , len ( ds_all) - len ( ds_clean) ) 技术支持
Original: 84470
Clean: 33275
Dropped: 51195技术支持
筛选后,我们保留了 3.3 万余个干净样本,既包含有效原理图片像,也有明确的元件标注。该清洗后的数据集为后续预处理与模型训练打下可靠基础。
5. 加载 Qwen3-VL-8B 视觉‑语言模型
现在加载 Qwen3-VL-8B-Instruct 模型及其对应的处理器。该大规模视觉‑语言模型能够联合推理图像与文本,非常适合原理图理解任务。
我们以 bfloat16 精度加载模型,以降低内存占用并保持数值稳定。同时启用 Flash Attention 2,以在 A100 上实现更快且更省内存的注意力计算。device_map="auto" 会自动将模型层分配到可用 GPU 上。
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
MODEL_ID = "Qwen/Qwen3-VL-8B-Instruct"
model = Qwen3VLForConditionalGeneration. from_pretrained(
MODEL_ID,
dtype= torch. bfloat16,
device_map= "auto" ,
attn_implementation= "flash_attention_2" ,
)
processor = AutoProcessor. from_pretrained( MODEL_ID) 技术支持
6. 面向电路分析的提示词设计
此步骤定义用于准备多模态训练提示、目标与图像的轻量实用函数。通过切换 TASK 变量(components、YAML、JSON 或原理图重建),一个流水线可复用在多项任务上。
我们应用基础安全限制来控制目标长度与图像大小,帮助训练稳定并节省内存。
from PIL import Image
TASK = "components"
MAX_TARGET_CHARS = 5000
MAX_IMAGE_SIDE = 1024
MAX_IMAGE_PIXELS = 1024 * 1024 技术支持
构建提示词
build_prompt() 函数构造传给模型的指令文本。它利用数据集元数据提供上下文,并施加严格的输出约束,以减少幻觉并在不同任务间保持一致的监督信号。
def build_prompt ( example) :
name = example. get( "name" ) or "Unknown project"
ftype = example. get( "type" ) or "unknown format"
if TASK == "components" :
return (
f"Project: { name} \nFormat: { ftype} \n"
"From the schematic image, extract all component labels and identifiers exactly as shown "
"(part numbers, values, footprints, net labels like +5V/GND).\n"
"Output only a comma-separated list. Do not generalize or add extra text."
)
if TASK == "yaml" :
return (
f"Project: { name} \nFormat: { ftype} \n"
"From the schematic image, produce YAML metadata for the design.\n"
"Return valid YAML only. No markdown, no explanations."
)
if TASK == "json" :
return (
f"Project: { name} \nFormat: { ftype} \n"
"From the schematic image, produce a JSON representation of the schematic structure.\n"
"Return valid JSON only. No markdown, no explanations."
)
if TASK == "schematic" :
return (
f"Project: { name} \nFormat: { ftype} \n"
"From the schematic image, reconstruct the raw KiCad schematic content.\n"
"Return only the schematic text. No markdown, no explanations."
)
raise ValueError( "Unknown TASK" ) 技术支持
构建目标
build_target() 函数直接从数据集中为所选任务提取真实标签。内容按原样返回,使模型学习精确复现,而非意译。
def build_target ( example) :
if TASK == "components" :
comps = example. get( "components_used" ) or [ ]
return ", " . join( comps)
if TASK == "yaml" :
return ( example. get( "yaml" ) or "" ) . strip( )
if TASK == "json" :
return ( example. get( "json" ) or "" ) . strip( )
if TASK == "schematic" :
return ( example. get( "schematic" ) or "" ) . strip( )
raise ValueError( "Unknown TASK" ) 技术支持
clamp_text() 函数对目标施加硬性字符上限,避免过大的 JSON、YAML 或原理图文件在训练中引发内存问题。
def clamp_text ( s: str , max_chars: int = MAX_TARGET_CHARS) - > str :
s = ( s or "" ) . strip( )
return s if len ( s) <= max_chars else s[ : max_chars] . rstrip( ) 技术支持
调整图像尺寸
_resize_pil() 函数在处理前对原理图片像进行标准化与缩放。它同时限制最大边长与总像素数,在保留视觉细节的同时确保可预测的 GPU 内存占用。
def _resize_pil ( pil: Image. Image, max_side: int = MAX_IMAGE_SIDE, max_pixels: int = MAX_IMAGE_PIXELS) - > Image. Image:
pil = pil. convert( "RGB" )
w, h = pil. size
scale_side = min ( 1.0 , max_side / float ( max ( w, h) ) )
scale_area = ( max_pixels / float ( w * h) ) ** 0.5 if ( w * h) > max_pixels else 1.0
scale = min ( scale_side, scale_area)
if scale < 1.0 :
nw, nh = max ( 1 , int ( w * scale) ) , max ( 1 , int ( h * scale) )
pil = pil. resize( ( nw, nh) , resample= Image. BICUBIC)
return pil技术支持
7. 设置多模态聊天模板
在本步骤中,我们将每个清洗后的样本转换为可被 Qwen 视觉‑语言模型直接消费的多模态对话格式。该格式将原理图片像与文本指令及其对应目标输出显式对齐。
def to_messages ( example) :
prompt = build_prompt( example)
target = clamp_text( build_target( example) )
example[ "messages" ] = [
{
"role" : "user" ,
"content" : [
{ "type" : "image" } ,
{ "type" : "text" , "text" : prompt} ,
] ,
} ,
{
"role" : "assistant" ,
"content" : [ { "type" : "text" , "text" : target} ] ,
} ,
]
return example技术支持
我们随机打乱数据集以消除排序偏差,并选择一个小子集用于初步实验。
随后将数据集映射至 to_messages() 以生成多模态训练样本。最后重新启用图像解码,使图像仅在训练时加载,从而让预处理更轻量、内存更友好。
train_ds = ds_clean. shuffle( seed= 42 ) . select( range ( min ( 800 , len ( ds_clean) ) ) ) . map ( to_messages)
train_ds = train_ds. cast_column( "image" , HFImage( decode= True ) ) 技术支持
8. 评估微调前的 Qwen3-VL-8B
在微调前,我们先评估 Qwen3‑VL 8B Instruct 在本任务上的开箱即用表现。该基线有助于了解预训练模型在未做任务适配时,从原理图片像中提取信息的能力。
run_inference() 函数对一个样本进行单次前向推理,使用与训练阶段相同的提示与图像预处理逻辑。
import torch
def run_inference ( model_, example, max_new_tokens= 256 ) :
prompt = build_prompt( example)
messages = [
{
"role" : "user" ,
"content" : [
{ "type" : "image" , "image" : _resize_pil( example[ "image" ] ) } ,
{ "type" : "text" , "text" : prompt} ,
] ,
}
]
inputs = processor. apply_chat_template(
messages,
tokenize= True ,
add_generation_prompt= True ,
return_dict= True ,
return_tensors= "pt" ,
) . to( model_. device)
with torch. inference_mode( ) :
out = model_. generate( ** inputs, max_new_tokens= max_new_tokens, do_sample= False )
gen = out[ 0 ] [ inputs[ "input_ids" ] . shape[ 1 ] : ]
return processor. decode( gen, skip_special_tokens= True )
baseline_ex = train_ds. shuffle( seed= 120 ) . select( range ( 1 ) ) [ 0 ] 技术支持
我们先在训练集随机选取一个样本进行评估。
print ( "\n--- BASELINE OUTPUT ---\n" , run_inference( model, baseline_ex) )
print ( "\n--- TARGET (dataset) ---\n" , clamp_text( build_target( baseline_ex) , 1500 ) ) 技术支持
--- BASELINE OUTPUT ---
J1,Conn_02x11_Odd_Even,CINT6,CINT5,CINT4,CINT3,CINT2,CINT1,CINT0,CINT15,CINT14,CINT13,CINT12,CINT11,CINT10,CINT9,CINT8,CINT7,CINT16,CINT17,CINT18,CINT19,CINT20,CINT21,CINT22,CINT23,CINT24,CINT25,CINT26,CINT27,CINT28,CINT29,CINT30,CINT31,CINT32,CINT33,CINT34,CINT35,CINT36,CINT37,CINT38,CINT39,CINT40,CINT41,CINT42,CINT43,CINT44,CINT45,CINT46,CINT47,CINT48,CINT49,CINT50,CINT51,CINT52,CINT53,CINT54,CINT55,CINT56,CINT57,CINT58,CINT59,CINT60,CINT61,CINT62
--- TARGET (dataset) ---
Conn_02x11_Odd_Even, R_Pack04, GND技术支持
在此示例中,模型能正确识别部分结构要素,但过度生成了针脚与信号名称,同时未能还原数据集中使用的精确元件标识。对更多样本评估也观察到了同样的模式。
总体而言,基线结果显示:尽管模型具备较强的一般视觉与文本理解能力,但与数据集特定元件标注的对齐不足。这一现象凸显出需要通过微调来降低幻觉输出并提升精度。
9. 构建用于训练的视觉‑语言数据整合器
本节定义一个自定义数据整合器,用于准备多模态对话风格的小批量数据。它将文本与图像联合编码为模型可用的张量,并确保仅在助手的回复上计算损失。
整合器会构建两份聊天文本:完整版本(提示+目标)用于输入编码,和仅提示版本用于计算提示长度。借助这些长度信息,我们会在标签中屏蔽提示与填充的 token,使只有助手输出参与损失计算。图像进行一致的缩放,并施加固定的最大序列长度以控制内存。
from typing import List, Dict, Any
import torch
MAX_LEN = 1500
def collate_fn ( batch: List[ Dict[ str , Any] ] ) :
full_texts = [
processor. apply_chat_template(
ex[ "messages" ] ,
tokenize= False ,
add_generation_prompt= False ,
)
for ex in batch
]
prompt_texts = [
processor. apply_chat_template(
ex[ "messages" ] [ : - 1 ] ,
tokenize= False ,
add_generation_prompt= True ,
)
for ex in batch
]
images = [ _resize_pil( ex[ "image" ] ) for ex in batch]
enc = processor(
text= full_texts,
images= images,
return_tensors= "pt" ,
padding= True ,
truncation= True ,
max_length= MAX_LEN,
)
input_ids = enc[ "input_ids" ]
pad_id = processor. tokenizer. pad_token_id
prompt_ids = processor. tokenizer(
prompt_texts,
return_tensors= "pt" ,
padding= True ,
truncation= True ,
max_length= MAX_LEN,
add_special_tokens= False ,
) [ "input_ids" ]
prompt_lens = ( prompt_ids != pad_id) . sum ( dim= 1 )
labels = input_ids. clone( )
bs, seqlen = labels. shape
for i in range ( bs) :
pl = int ( prompt_lens[ i] . item( ) )
pl = min ( pl, seqlen)
labels[ i, : pl] = - 100
labels[ labels == pad_id] = - 100
enc[ "labels" ] = labels
return enc技术支持
该整合器可实现高效且正确的视觉‑语言微调监督。
10. 配置 LoRA 以高效微调 Qwen3-VL-8B
我们将配置 LoRA (低秩适配)来高效微调 Qwen3‑VL,而无需更新全部权重。LoRA 将可训练的低秩矩阵注入到选定的投影层,在保证性能的同时大幅降低内存占用。
from peft import LoraConfig, TaskType, get_peft_model
lora = LoraConfig(
r= 16 ,
lora_alpha= 32 ,
lora_dropout= 0.05 ,
bias= "none" ,
task_type= TaskType. CAUSAL_LM,
target_modules= [
"q_proj" , "k_proj" , "v_proj" , "o_proj" ,
"gate_proj" , "up_proj" , "down_proj"
] ,
) 技术支持
随后使用 SFTConfig 定义训练配置,设置适合在 A100 上稳定微调的批大小、学习率、精度与日志选项。
from trl import SFTTrainer, SFTConfig
args = SFTConfig(
output_dir= f"qwen3vl-open-schematics- { TASK} -lora" ,
num_train_epochs= 1 ,
per_device_train_batch_size= 2 ,
gradient_accumulation_steps= 4 ,
gradient_checkpointing= False ,
learning_rate= 1e-4 ,
warmup_steps= 10 ,
weight_decay= 0.01 ,
max_grad_norm= 1.0 ,
bf16= True ,
fp16= False ,
lr_scheduler_type= "cosine" ,
logging_steps= 10 ,
report_to= "none" ,
remove_unused_columns= False ,
) 技术支持
最后,我们初始化 SFTTrainer,将模型、数据集、自定义整合器与 LoRA 配置结合起来,开始有监督微调。
trainer = SFTTrainer(
model= model,
args= args,
train_dataset= train_ds,
data_collator= collate_fn,
peft_config= lora
) 技术支持
11. 在 Open Schematics 上微调 Qwen3‑VL-8B
现在启动微调流程。
trainer. train( ) 技术支持
训练开始后,您可以在 RunPod 的遥测面板中查看。以 A100 80GB 实例为例,流程通常占用约 40–45 GB 显存,并接近满负载利用 GPU,表明硬件使用高效。
随着训练推进,您应当看到训练损失逐步下降并趋于稳定。在我们的实验中,损失收敛并在约 6.5 左右平台化,这可作为模型已适应原理图元件提取任务的基准信号。
至此,LoRA 适配器已成功微调,准备进入评估与导出阶段。
12. 将微调后的模型发布到 Hugging Face Hub
微调完成后,先将训练好的 LoRA 适配器与处理器保存到本地。
out_dir = trainer. args. output_dir
trainer. save_model( out_dir)
processor. save_pretrained( out_dir) 技术支持
接着,将微调模型发布到 Hugging Face Hub,便于在推理或进一步微调中复用适配器与处理器。
import os
repo_id = "kingabzpro/qwen3vl-open-schematics-lora"
trainer. model. push_to_hub(
repo_id,
token= os. getenv( "HF_TOKEN" ) ,
)
processor. push_to_hub(
repo_id,
token= os. getenv( "HF_TOKEN" ) ,
) 技术支持
上传完成后,微调的 LoRA 适配器与处理器会在 指定仓库 中公开可用。
12. 评估微调后的 Qwen3-VL-8B 模型
微调完成后,我们直接从 Hugging Face Hub 重新加载模型与处理器。这样可确保评估使用的正是导出的 LoRA 适配器,与真实推理场景完全一致。
model = Qwen3VLForConditionalGeneration. from_pretrained(
repo_id,
dtype= torch. bfloat16,
device_map= "auto" ,
attn_implementation= "flash_attention_2" ,
)
processor = AutoProcessor. from_pretrained( repo_id)
```
We now repeat the same inference procedure used during the baseline evaluation, allowing for a direct comparison between pre- fine- tuning and post- fine- tuning behavior.
```python
baseline_ex = train_ds. shuffle( seed= 120 ) . select( range ( 1 ) ) [ 0 ]
print ( "\n--- FINETUNED OUTPUT ---\n" , run_inference( model, baseline_ex) )
print ( "\n--- TARGET (dataset) ---\n" , clamp_text( build_target( baseline_ex) , 1500 ) ) 技术支持
--- FINETUNED OUTPUT ---
Conn_02x11_0dd_Even, P3.3V
--- TARGET (dataset) ---
Conn_02x11_Odd_Even, R_Pack04, GND技术支持
与基线相比,微调后的模型输出更短、更聚焦,避免了对针脚名称和推断信号的大规模过度生成。
尽管预测仍不完整且存在小错误,但已明显朝着与数据集中元件标识一致的方向改进。
我们再评估第二个示例以验证这一行为。
baseline_ex = train_ds. shuffle( seed= 170 ) . select( range ( 1 ) ) [ 0 ]
print ( "\n--- FINETUNED OUTPUT ---\n" , run_inference( model, baseline_ex) )
print ( "\n--- TARGET (dataset) ---\n" , clamp_text( build_target( baseline_ex) , 1500 ) ) 技术支持
--- FINETUNED OUTPUT ---
ATMEGA328P-PU, +5V, GND, R, C, C16MHz, SERVO_A, SERVO_B, SERVO_C, SERVO_D, SERVO_E, SERVO_F, SERVO_G
--- TARGET (dataset) ---
+5V, 7.62MM-3P, 7.62MM-3P_1, 7.62MM-3P_2, 7.62MM-3P_3, 7.62MM-3P_4, 7.62MM-3P_5, 7.62MM-3P_6, ATMEGA328P-PU, ATMEGA328P-PU_1, GND, MBB02070C1002FCT00, MBB02070C1002FCT00_1, Unknown_0_-806, X49SD16MSD2SC, Y5P102K2KV16CC0224, Y5P102K2KV16CC0224_1, Y5P102K2KV16CC0224_2技术支持
在这里,微调模型能正确识别核心组件(如微控制器与电源网络),并显著减少无关的信号级幻觉。然而,它仍将部分元件抽象或泛化,未完全复现数据集中的精确标识。
总体来看,微调有效抑制了过度生成,并提升了与原理图层面元件提取的一致性。尽管可以通过增加轮数、更大的训练集或更严格的输出约束进一步提高精度,但与基线相比,微调后的表现已实现清晰且可度量的改进。
如果您在运行本教程代码时遇到问题,请参阅 配套笔记本 。
结语
视觉–语言模型与纯文本模型在本质上不同,将二者一概而论几乎总会带来不理想的结果。
如果不加留意,即使批大小为 1 也很容易遇到显存不足,或者微调过程看似正常却无法真正学会任务。这是我用实践“踩坑”得到的教训。
最终带来改变的是对多模态训练中特定关键细节的关注。
将图像缩放到安全范围、清洗数据集以移除损坏或不可用样本,并确保只将有效的图文对传给模型,都是必不可少的步骤。忽略任意一步都可能迅速导致不稳定或浪费算力。
在建模层面,仅使用相关的 LoRA 目标层帮助训练更高效、更聚焦;而对训练参数的细致调优,则在不增加内存压力的情况下改善了收敛表现。
针对 A100 进行优化、启用 Flash Attention、并使用 bfloat16,使训练在显著降低运行时长的同时保持稳定。 在实践中,这些优化几乎将训练时间减半,而不牺牲质量。
最终结果表明,即便是强大的预训练视觉‑语言模型,也能从特定领域的微调中受益匪浅。通过合适的预处理、配置与面向硬件的优化,可以可靠且高效地适配大型多模态模型。
如果您希望进一步练习微调,推荐学习 使用 Llama 3 进行微调 课程。
Qwen3‑VL‑8B 是 Qwen 系列中功能最强且使用最广泛的视觉‑语言模型,旨在统一理解文本、图像和视频。
它在文本生成、视觉推理、空间感知、长上下文处理和智能体交互等方面带来显著提升,适合在边缘与云端环境中开展研究与实际部署。
在本教程中,我们将微调 Qwen3‑VL‑8B‑Instruct,以电子原理图为对象。通过训练模型识别原理图符号、连线与空间关系,我们让它能够准确理解电路设计,并判断在真实电路中需要补充哪些电子元件。
您将学到:
如果您还不熟悉 Hugging Face,可以先学习Hugging Face 基础技能路径!
1. 微调 Qwen3-VL-8B 的环境搭建
在使用视觉–语言模型时,GPU 显存往往成为关键限制。高分辨率图像与多模态编码器会迅速消耗显存,因此强烈建议使用显存充足的 GPU。
在本教程中,我们将启动一台RunPod A100(80GB)实例,并使用最新版的PyTorch镜像。该配置可为训练提供充足的显存余量,并在微调过程中避免不必要的内存瓶颈。
向下滚动至Pod Template部分。在这里选择最新的 PyTorch 镜像。您也可以在此调整存储设置并添加环境变量。
点击Edit,然后进行如下修改:
HF_TOKEN,其值为您的Hugging Face 访问令牌(可在 Hugging Face 设置中生成)完成后,保存模板并部署该 pod。
当 pod 启动后:
要安装依赖,请运行以下代码单元。在 Jupyter 中,行首的感叹号表示以 Shell 命令而非 Python 代码执行。
接下来,设置固定随机种子以保证可复现性,并启用针对 A100 的性能优化。
2. 从 Hugging Face 下载 Open Schematics 数据集
现在我们将从 Hugging Face Hub 加载 Open Schematics 数据集。该数据集包含电子原理图片像以及描述每个电路的丰富元数据,非常适合用于视觉–语言训练。
该数据集包含 8.4 万余个样本,每个样本将一张原理图与结构化信息(如元件列表以及机器可读格式 JSON 和 YAML)配对。
3. 探索数据集结构
我们先检查一个样本,以更好地理解数据集结构。
这表明每个样本都包含高分辨率原理图、一份元件列表,以及电路的结构化表示。
我们可以直接在 Jupyter 笔记本中渲染原理图片像。
最后,我们查看该原理图所用元件的完整列表。
该元件列表为原理图片像与电路中的电子元件之间提供了清晰的对应关系。
4. 清洗与筛选数据集
在训练前,我们对数据集进行清洗与筛选,以确保每个样本都包含视觉–语言学习所需的最少信息。具体而言,我们保留具有有效元件标注且对应有原理图片像的样本。
首先,检查有多少样本的
components_used缺失、为空或无效。下方汇总显示,大量样本虽包含原理图数据,但缺少可用的元件标注。
为避免筛选时的不必要内存占用,我们显式禁用图像解码。这样在应用过滤器时,Hugging Face 不会将图像加载到内存。
接着,我们定义一个过滤器,仅保留具有非空元件列表且图像引用有效的样本。
应用该过滤器后,数据集显著收敛为高质量且可直接使用的样本。
筛选后,我们保留了 3.3 万余个干净样本,既包含有效原理图片像,也有明确的元件标注。该清洗后的数据集为后续预处理与模型训练打下可靠基础。
5. 加载 Qwen3-VL-8B 视觉‑语言模型
现在加载Qwen3-VL-8B-Instruct模型及其对应的处理器。该大规模视觉‑语言模型能够联合推理图像与文本,非常适合原理图理解任务。
我们以 bfloat16 精度加载模型,以降低内存占用并保持数值稳定。同时启用 Flash Attention 2,以在 A100 上实现更快且更省内存的注意力计算。
device_map="auto"会自动将模型层分配到可用 GPU 上。6. 面向电路分析的提示词设计
此步骤定义用于准备多模态训练提示、目标与图像的轻量实用函数。通过切换
TASK变量(components、YAML、JSON 或原理图重建),一个流水线可复用在多项任务上。我们应用基础安全限制来控制目标长度与图像大小,帮助训练稳定并节省内存。
构建提示词
build_prompt()函数构造传给模型的指令文本。它利用数据集元数据提供上下文,并施加严格的输出约束,以减少幻觉并在不同任务间保持一致的监督信号。构建目标
build_target()函数直接从数据集中为所选任务提取真实标签。内容按原样返回,使模型学习精确复现,而非意译。clamp_text()函数对目标施加硬性字符上限,避免过大的 JSON、YAML 或原理图文件在训练中引发内存问题。调整图像尺寸
_resize_pil()函数在处理前对原理图片像进行标准化与缩放。它同时限制最大边长与总像素数,在保留视觉细节的同时确保可预测的 GPU 内存占用。7. 设置多模态聊天模板
在本步骤中,我们将每个清洗后的样本转换为可被 Qwen 视觉‑语言模型直接消费的多模态对话格式。该格式将原理图片像与文本指令及其对应目标输出显式对齐。
我们随机打乱数据集以消除排序偏差,并选择一个小子集用于初步实验。
随后将数据集映射至
to_messages()以生成多模态训练样本。最后重新启用图像解码,使图像仅在训练时加载,从而让预处理更轻量、内存更友好。8. 评估微调前的 Qwen3-VL-8B
在微调前,我们先评估 Qwen3‑VL 8B Instruct 在本任务上的开箱即用表现。该基线有助于了解预训练模型在未做任务适配时,从原理图片像中提取信息的能力。
run_inference()函数对一个样本进行单次前向推理,使用与训练阶段相同的提示与图像预处理逻辑。我们先在训练集随机选取一个样本进行评估。
在此示例中,模型能正确识别部分结构要素,但过度生成了针脚与信号名称,同时未能还原数据集中使用的精确元件标识。对更多样本评估也观察到了同样的模式。
总体而言,基线结果显示:尽管模型具备较强的一般视觉与文本理解能力,但与数据集特定元件标注的对齐不足。这一现象凸显出需要通过微调来降低幻觉输出并提升精度。
9. 构建用于训练的视觉‑语言数据整合器
本节定义一个自定义数据整合器,用于准备多模态对话风格的小批量数据。它将文本与图像联合编码为模型可用的张量,并确保仅在助手的回复上计算损失。
整合器会构建两份聊天文本:完整版本(提示+目标)用于输入编码,和仅提示版本用于计算提示长度。借助这些长度信息,我们会在标签中屏蔽提示与填充的 token,使只有助手输出参与损失计算。图像进行一致的缩放,并施加固定的最大序列长度以控制内存。
该整合器可实现高效且正确的视觉‑语言微调监督。
10. 配置 LoRA 以高效微调 Qwen3-VL-8B
我们将配置LoRA(低秩适配)来高效微调 Qwen3‑VL,而无需更新全部权重。LoRA 将可训练的低秩矩阵注入到选定的投影层,在保证性能的同时大幅降低内存占用。
随后使用
SFTConfig定义训练配置,设置适合在 A100 上稳定微调的批大小、学习率、精度与日志选项。最后,我们初始化
SFTTrainer,将模型、数据集、自定义整合器与 LoRA 配置结合起来,开始有监督微调。11. 在 Open Schematics 上微调 Qwen3‑VL-8B
现在启动微调流程。
训练开始后,您可以在 RunPod 的遥测面板中查看。以 A100 80GB 实例为例,流程通常占用约 40–45 GB 显存,并接近满负载利用 GPU,表明硬件使用高效。
随着训练推进,您应当看到训练损失逐步下降并趋于稳定。在我们的实验中,损失收敛并在约 6.5 左右平台化,这可作为模型已适应原理图元件提取任务的基准信号。
至此,LoRA 适配器已成功微调,准备进入评估与导出阶段。
12. 将微调后的模型发布到 Hugging Face Hub
微调完成后,先将训练好的 LoRA 适配器与处理器保存到本地。
接着,将微调模型发布到 Hugging Face Hub,便于在推理或进一步微调中复用适配器与处理器。
上传完成后,微调的 LoRA 适配器与处理器会在指定仓库中公开可用。
12. 评估微调后的 Qwen3-VL-8B 模型
微调完成后,我们直接从 Hugging Face Hub 重新加载模型与处理器。这样可确保评估使用的正是导出的 LoRA 适配器,与真实推理场景完全一致。
与基线相比,微调后的模型输出更短、更聚焦,避免了对针脚名称和推断信号的大规模过度生成。
尽管预测仍不完整且存在小错误,但已明显朝着与数据集中元件标识一致的方向改进。
我们再评估第二个示例以验证这一行为。
在这里,微调模型能正确识别核心组件(如微控制器与电源网络),并显著减少无关的信号级幻觉。然而,它仍将部分元件抽象或泛化,未完全复现数据集中的精确标识。
总体来看,微调有效抑制了过度生成,并提升了与原理图层面元件提取的一致性。尽管可以通过增加轮数、更大的训练集或更严格的输出约束进一步提高精度,但与基线相比,微调后的表现已实现清晰且可度量的改进。
如果您在运行本教程代码时遇到问题,请参阅配套笔记本。
结语
视觉–语言模型与纯文本模型在本质上不同,将二者一概而论几乎总会带来不理想的结果。
如果不加留意,即使批大小为 1 也很容易遇到显存不足,或者微调过程看似正常却无法真正学会任务。这是我用实践“踩坑”得到的教训。
最终带来改变的是对多模态训练中特定关键细节的关注。
将图像缩放到安全范围、清洗数据集以移除损坏或不可用样本,并确保只将有效的图文对传给模型,都是必不可少的步骤。忽略任意一步都可能迅速导致不稳定或浪费算力。
在建模层面,仅使用相关的 LoRA 目标层帮助训练更高效、更聚焦;而对训练参数的细致调优,则在不增加内存压力的情况下改善了收敛表现。
针对 A100 进行优化、启用 Flash Attention、并使用 bfloat16,使训练在显著降低运行时长的同时保持稳定。 在实践中,这些优化几乎将训练时间减半,而不牺牲质量。
最终结果表明,即便是强大的预训练视觉‑语言模型,也能从特定领域的微调中受益匪浅。通过合适的预处理、配置与面向硬件的优化,可以可靠且高效地适配大型多模态模型。
如果您希望进一步练习微调,推荐学习使用 Llama 3 进行微调课程。