通义千问1.5-1.8B-Chat-GPTQ-Int4 WebUI Transformer架构解析与微调实践你是不是也好奇那些能和你流畅对话的AI模型比如通义千问内部到底是怎么工作的为什么给它一段文字它就能理解并生成回复今天我们就以通义千问1.5-1.8B这个轻量级模型为例掰开揉碎了讲讲它背后的核心技术——Transformer架构。更重要的是我们不止于“看”还要“动手”我会带你一起在已经部署好的WebUI基础上亲手对这个模型进行微调让它更懂你的特定需求。这就像你拿到了一台性能不错的通用电脑预训练好的模型我们现在要做的就是给它安装一些专业软件微调让它变成更适合你工作的专用工作站。整个过程我们会用尽量直白的话把复杂的原理讲清楚并用实际的代码和操作步骤让你能跟着做出来。1. 先聊聊TransformerAI理解语言的“核心引擎”在深入代码之前我们得先搞明白模型的大脑是如何运转的。Transformer架构就是当今绝大多数大语言模型的“心脏”。你可以把它想象成一个超级高效的信息处理工厂。传统的处理方式像是必须一个词一个词按顺序读而Transformer则允许模型“一眼”看到整个句子并动态地关注其中最重要的部分。这个“动态关注”的能力就是大名鼎鼎的注意力机制它是Transformer最核心的发明。1.1 注意力机制模型到底在“注意”什么想象你在读这句话“我把苹果放进了冰箱。” 当你理解“冰箱”这个词时你的大脑会瞬间关联到“放进了”这个动作以及“苹果”这个被放入的对象。模型在做同样的事情。在技术上注意力机制通过计算句子中每个词与其他所有词的“相关性分数”来工作。对于“冰箱”这个词它与“苹果”、“放进了”的分数会很高而与“我”的分数可能较低。模型就用这些分数来决定在理解或生成“冰箱”时应该从句子其他部分汲取多少信息。在通义千问这样的模型里使用的是“多头注意力”。这好比不是只有一个人在看这句话而是有多组专家同时从不同角度比如语法角度、语义角度、上下文角度来分析词与词之间的关系最后把大家的见解综合起来这样理解就更全面、更深刻。1.2 Transformer的组件看看工厂的流水线一个标准的Transformer“工厂”主要由两大车间构成编码器和解码器。像通义千问这类纯解码器模型例如类似GPT的结构则主要使用了解码器堆叠的架构。我们来看看这个流水线上的关键工位输入嵌入与位置编码首先每个词被转换成一个数字向量嵌入。但光这样不行因为模型需要知道词的顺序。位置编码就是给每个词向量加上一个表示其位置的独特信号这样模型就知道“苹果”在“冰箱”前面。多头注意力层这就是上面说的“专家组分析”环节计算词与词之间的关联。前馈神经网络层注意力层决定了关注哪些信息而前馈层则是一个小型的“消化吸收”网络负责对聚焦后的信息进行复杂的转换和处理。残差连接与层归一化这是保证训练稳定高效的“技巧”。残差连接允许信息直接跳过某一层防止信息在多层传递后消失层归一化则让每一层的数据分布保持稳定加速训练。通义千问1.5-1.8B模型就是由数十个这样的解码器层堆叠而成的。每一层都对输入信息进行一次深度的理解和提炼最终使得模型能够生成连贯、合理的文本。2. 环境准备在星图GPU上启动我们的微调实验室理论说得差不多了咱们动手吧。假设你已经在星图GPU平台上通过镜像一键部署好了通义千问1.5-1.8B-Chat-GPTQ-Int4的WebUI界面。这个WebUI让我们能方便地对话和测试模型但要微调我们需要进入它的“后台”。通常这类镜像会提供终端访问能力。我们通过终端连接到容器内部来准备微调环境。# 假设我们已经通过平台提供的SSH或Web终端进入了容器环境 # 首先确认我们当前的工作目录和Python环境 pwd python --version # 安装微调可能需要的额外依赖 # 注意基础镜像可能已包含大部分这里安装一些常用工具 pip install datasets scikit-learn微调的核心是使用高效的微调库。目前最流行的是PEFTParameter-Efficient Fine-Tuning库它提供了LoRA等方法。我们还需要Hugging Face的Transformers库来加载模型和分词器。pip install transformers peft accelerate安装完成后我们可以创建一个专门的工作目录来存放微调脚本和数据。mkdir -p /workspace/finetune_qwen cd /workspace/finetune_qwen3. 数据准备告诉模型我们想让它学什么微调成功的关键一半在于数据。我们需要准备一个格式正确的数据集。对于对话模型通常使用“指令-输出”对的形式。我们创建一个简单的JSON格式数据集文件finetune_data.jsonl每行一个JSON对象。{instruction: 写一首关于春天的五言绝句。, output: 春眠不觉晓处处闻啼鸟。夜来风雨声花落知多少。} {instruction: 将以下英文翻译成中文Artificial Intelligence is changing the world., output: 人工智能正在改变世界。} {instruction: 用友好的语气回复用户的投诉我收到的商品破损了。, output: 非常抱歉给您带来了不好的体验请您提供一下订单号和商品破损的照片我们会立即为您处理换货或退款。} {instruction: 总结Transformer注意力机制的核心思想。, output: 注意力机制的核心思想是让模型在处理序列中某个元素时能够动态地、差异化地关注序列中所有其他元素的信息通过计算相关性分数来决定信息聚合的权重从而捕捉长距离依赖关系。}数据不需要一开始就非常多几十到几百条高质量的样本就能对模型行为产生显著影响。关键是质量和代表性你的数据要能准确反映你希望模型学会的任务或风格。我们可以用Python快速检查一下数据格式并将其转换为训练所需的格式。import json from datasets import Dataset # 读取数据 data_path finetune_data.jsonl data_list [] with open(data_path, r, encodingutf-8) as f: for line in f: data_list.append(json.loads(line)) # 转换为Hugging Face Dataset格式 def format_instruction(example): # 构建模型输入的对话格式。这里需要匹配通义千问的对话模板。 # 通义千问1.5的典型对话格式是 # |im_start|system\n{system_message}|im_end|\n|im_start|user\n{instruction}|im_end|\n|im_start|assistant\n system_msg You are a helpful assistant. # 注意实际模板需查阅模型文档。这里是一个通用示例。 prompt f|im_start|system\n{system_msg}|im_end|\n|im_start|user\n{example[instruction]}|im_end|\n|im_start|assistant\n # 训练时我们需要的是“输入文本”和“目标文本”即期望模型生成的部分 return {prompt: prompt, completion: example[output]} formatted_data [format_instruction(item) for item in data_list] # 创建Dataset dataset Dataset.from_list(formatted_data) # 分割训练集和验证集这里简单按比例分割 split_dataset dataset.train_test_split(test_size0.1, seed42) train_dataset split_dataset[train] eval_dataset split_dataset[test] print(f训练集样本数{len(train_dataset)}) print(f验证集样本数{len(eval_dataset)})4. 使用LoRA进行高效微调只动“一小部分”参数现在来到了最关键的环节——微调。全参数微调需要动辄数十亿的参数计算成本极高。而LoRA是一种高效的微调方法它的思想很巧妙我们不去修改模型原有的庞大参数而是在原有结构旁添加一些小的、可训练的“适配器”模块。训练时只更新这些适配器的参数从而极大地减少了计算量和内存消耗。下面是一个使用PEFT库进行LoRA微调的示例脚本finetune_lora.pyimport torch from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq from peft import LoraConfig, get_peft_model, TaskType from datasets import load_from_disk import os # 1. 加载模型和分词器 model_name_or_path /path/to/your/qwen1.5-1.8b-chat-gptq-int4 # 替换为你的模型实际路径通常在容器内的某个目录如 /app/model tokenizer AutoTokenizer.from_pretrained(model_name_or_path, trust_remote_codeTrue) # 注意GPTQ量化模型加载可能需要特殊处理确保 transformers 版本支持。 # 有些镜像可能已提供适配的加载方式请参考镜像文档。 model AutoModelForCausalLM.from_pretrained( model_name_or_path, torch_dtypetorch.float16, # 使用半精度节省内存 device_mapauto, # 自动分配到GPU trust_remote_codeTrue ) tokenizer.pad_token tokenizer.eos_token # 设置填充token # 2. 处理数据集 def tokenize_function(examples): # 对输入prompt进行编码不添加标签 model_inputs tokenizer(examples[prompt], truncationTrue, paddingFalse, max_length512) # 对完整输出prompt completion进行编码用于计算损失 # 我们需要告诉模型只在“completion”部分计算损失 with tokenizer.as_target_tokenizer(): labels tokenizer(examples[completion], truncationTrue, paddingFalse, max_length256) # 将completion的token id作为标签对于prompt部分使用-100忽略其损失计算 # 首先将prompt的input_ids复制给labels model_inputs[labels] [-100] * len(model_inputs[input_ids]) # 然后将completion的token id拼接到labels的末尾假设是自回归生成这样处理是简化的实际需更严谨的对齐 # 更严谨的做法是将promptcompletion一起tokenize然后通过attention mask将prompt部分的loss mask掉。 # 这里为了简化演示采用一种常见策略将输入和输出分开处理再拼接需保证分词方式一致。 full_text [p c for p, c in zip(examples[“prompt”], examples[“completion”])] tokenized_full tokenizer(full_text, truncationTrue, paddingFalse, max_length768) # 创建labels将prompt部分设为-100 labels [] for i in range(len(tokenized_full[“input_ids”])): prompt_len len(tokenizer(examples[“prompt”][i], truncationTrue)[input_ids]) label [-100]*prompt_len tokenized_full[“input_ids”][i][prompt_len:] labels.append(label) model_inputs[“input_ids”] tokenized_full[“input_ids”] model_inputs[“attention_mask”] tokenized_full[“attention_mask”] model_inputs[“labels”] labels return model_inputs # 加载之前保存的数据集 train_dataset load_from_disk(“/workspace/finetune_qwen/formatted_train_data”) eval_dataset load_from_disk(“/workspace/finetune_qwen/formatted_eval_data”) tokenized_train train_dataset.map(tokenize_function, batchedTrue) tokenized_eval eval_dataset.map(tokenize_function, batchedTrue) # 3. 配置LoRA lora_config LoraConfig( task_typeTaskType.CAUSAL_LM, # 因果语言模型任务 r8, # LoRA的秩越小参数量越少通常8或16 lora_alpha32, # 缩放参数 lora_dropout0.1, # Dropout率 target_modules[q_proj, v_proj], # 针对Transformer的哪些模块应用LoRA。对于Qwen通常是query和value的投影层。 biasnone, ) # 将基础模型转换为PEFT模型 model get_peft_model(model, lora_config) model.print_trainable_parameters() # 打印可训练参数量会发现只占原模型的很小一部分通常1% # 4. 配置训练参数 training_args TrainingArguments( output_dir./qwen_lora_finetuned, num_train_epochs3, # 训练轮数根据数据量调整 per_device_train_batch_size4, # 根据GPU内存调整 per_device_eval_batch_size4, gradient_accumulation_steps4, # 梯度累积模拟更大batch size warmup_steps100, logging_steps50, evaluation_strategysteps, eval_steps200, save_strategysteps, save_steps200, learning_rate2e-4, # LoRA学习率通常可以设得大一些 fp16True, # 使用混合精度训练 load_best_model_at_endTrue, report_tonone, # 不在线报告本地运行 ) # 5. 创建Trainer并开始训练 trainer Trainer( modelmodel, argstraining_args, train_datasettokenized_train, eval_datasettokenized_eval, tokenizertokenizer, data_collatorDataCollatorForSeq2Seq(tokenizertokenizer, paddingTrue), ) trainer.train() # 6. 保存微调后的LoRA权重 model.save_pretrained(./qwen_lora_adapter)运行这个脚本你就可以启动微调过程了。在星图GPU提供的环境下训练速度会很快。通过观察控制台输出的损失值你可以判断模型是否在学习。5. 效果评估与使用看看微调后的模型表现如何训练完成后我们怎么知道模型有没有变好呢首先进行直观测试。我们可以写一个简单的加载和推理脚本对比微调前后模型对训练数据中指令的回复。from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline from peft import PeftModel # 加载基础模型 base_model_path /path/to/your/qwen1.5-1.8b-chat-gptq-int4 tokenizer AutoTokenizer.from_pretrained(base_model_path, trust_remote_codeTrue) base_model AutoModelForCausalLM.from_pretrained(base_model_path, device_mapauto, trust_remote_codeTrue) # 加载LoRA适配器权重 lora_path ./qwen_lora_adapter model PeftModel.from_pretrained(base_model, lora_path) # 切换到评估模式 model.eval() # 准备测试指令 test_instruction 写一首关于秋天的五言绝句。 prompt f|im_start|system\nYou are a helpful assistant.|im_end|\n|im_start|user\n{test_instruction}|im_end|\n|im_start|assistant\n # 使用基础模型生成可选用于对比 print( 基础模型回复 ) inputs tokenizer(prompt, return_tensorspt).to(model.device) with torch.no_grad(): outputs base_model.generate(**inputs, max_new_tokens50) print(tokenizer.decode(outputs[0], skip_special_tokensTrue)) print(\n 微调后模型回复 ) # 使用微调后模型生成 inputs tokenizer(prompt, return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens50) # 只打印助手的回复部分 full_output tokenizer.decode(outputs[0], skip_special_tokensTrue) # 简单分割提取‘assistant’后的内容 assistant_response full_output.split(|im_start|assistant\n)[-1] print(assistant_response)其次进行定量评估可选。如果准备了验证集可以计算模型在验证集上的困惑度等指标。更实用的方法是设计一组涵盖你微调目标的测试问题人工评估回复的相关性、准确性和风格是否符合预期。最后如何与WebUI结合微调后的LoRA权重是独立文件。一种方法是在WebUI的后端加载模型时动态加载这个LoRA适配器。具体方式取决于WebUI的实现框架如Text-Generation-WebUI, Open WebUI等通常支持LoRA加载。你可能需要查阅WebUI的文档将生成的adapter_model.bin和adapter_config.json文件放到指定目录并在界面上选择加载。6. 总结与下一步走完这一趟我们从Transformer架构的核心原理出发理解了通义千问这类模型如何通过注意力机制等组件处理语言。更重要的是我们完成了一次完整的轻量级微调实践。使用LoRA方法我们只训练了原模型极少的参数就实现了对模型行为的定向引导这大大降低了定制AI模型的门槛和成本。实际做下来你会发现数据质量真的至关重要几条高质量的数据样本效果可能胜过几百条粗糙的数据。另外微调不是一劳永逸的可能需要根据测试结果回头调整数据、修改提示词模板、或者调整LoRA的超参数比如秩r和学习率进行多轮迭代。如果你在星图平台的WebUI上操作接下来可以尝试用微调后的模型去处理你关心的具体任务比如客服话术优化、专业领域问答、或者特定的内容风格生成。这个过程本身就是深入理解并驾驭AI模型的最好方式。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。