Qwen1.5-1.8B GPTQ解析Transformer架构:原理与模型微调实战

张开发
2026/4/4 9:45:15 15 分钟阅读
Qwen1.5-1.8B GPTQ解析Transformer架构:原理与模型微调实战
Qwen1.5-1.8B GPTQ解析Transformer架构原理与模型微调实战你是不是也好奇那些能写文章、能对话、甚至能编程的大模型比如Qwen、ChatGPT它们内部到底是怎么工作的为什么输入一段文字就能输出一段有逻辑的回答今天我们就用一个非常具体、可操作的例子来揭开这层神秘面纱。我们会请出今天的主角——Qwen1.5-1.8B一个参数规模适中、非常适合学习和研究的开源大语言模型。我们将做两件非常酷的事把它当作“解剖标本”我们将深入Qwen1.5-1.8B的内部用大白话和代码一步步拆解其核心——Transformer架构。你会看到“注意力机制”到底在看什么“前馈网络”又在做什么。把它当作“可塑之材”理解了原理之后我们将手把手教你如何用你自己的数据比如医疗报告、法律条文、客服对话对这个模型进行轻量级微调LoRA让它变成你专属的领域专家。这不仅仅是一篇理论文章更是一份实战指南。跟着做下来你不仅能看懂大模型的“心脏”还能亲手让它学会新技能。1. 环境准备与模型初探在开始“解剖”和“改造”之前我们得先把工具和环境准备好。整个过程力求简单清晰让你能快速上手。1.1 基础环境搭建首先我们需要一个能运行Python和深度学习框架的环境。这里推荐使用Anaconda来管理环境避免包冲突。# 创建一个新的Python环境命名为qwen-tutorial conda create -n qwen-tutorial python3.10 -y conda activate qwen-tutorial # 安装PyTorch请根据你的CUDA版本去官网选择对应命令这里以CUDA 11.8为例 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装Transformers和Peft库前者用于加载模型后者用于LoRA微调 pip install transformers peft accelerate datasets # 安装bitsandbytes用于量化加载如果你的GPU显存有限这个非常有用 pip install bitsandbytes1.2 下载与加载Qwen1.5-1.8B GPTQ模型Qwen1.5-1.8B是一个1.8B参数量的模型对于学习和实验来说大小正合适。GPTQ是一种模型量化技术能在几乎不损失精度的情况下大幅减少模型对显存的占用让我们在消费级显卡上也能流畅运行。我们将使用transformers库来加载模型。这里我们加载一个社区提供的GPTQ量化版本。from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig import torch # 指定模型名称这里使用一个在Hugging Face Hub上的GPTQ量化版本 model_name TheBloke/Qwen1.5-1.8B-GPTQ # 配置4-bit量化加载极大节省显存 bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, bnb_4bit_use_double_quantTrue, bnb_4bit_quant_typenf4 ) # 加载分词器 tokenizer AutoTokenizer.from_pretrained(model_name) # 使用量化配置加载模型 model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_mapauto, # 自动分配模型层到可用的GPU/CPU上 trust_remote_codeTrue # Qwen模型可能需要这个参数 ) print(模型与分词器加载完毕) print(f模型架构{model.config.model_type})运行完这段代码模型就已经安静地躺在你的内存显存里了。你可以试着和它打个招呼看看它的基础能力。# 一个简单的对话测试 prompt 请用一句话介绍一下你自己。 inputs tokenizer(prompt, return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens50) response tokenizer.decode(outputs[0], skip_special_tokensTrue) print(response)如果一切顺利你会得到Qwen1.5-1.8B的一段自我介绍。好了我们的“标本”和“手术台”都已就位接下来开始深入内部。2. 深入核心拆解Transformer架构现在我们以刚刚加载的Qwen1.5-1.8B模型为例子来看看Transformer这个“大脑”到底由哪些关键部件构成。别担心我们会用最直观的方式来理解。2.1 Transformer的宏观视角编码器与解码器你可能听说过原始的Transformer由编码器Encoder和解码器Decoder组成像翻译模型那样。但像Qwen、LLaMA、GPT这类自回归语言模型通常只使用解码器部分。为什么因为它们的核心任务是根据已经生成的上文预测下一个最可能的词。这就像一个单向的、不断延续的创作过程。解码器架构天生适合这种“从左到右”的生成任务。在Qwen1.5里你看到的是一层层堆叠的Decoder Layer。2.2 核心组件一自注意力机制Self-Attention这是Transformer的灵魂。你可以把它想象成模型在阅读句子时大脑里进行的一场“信息关联派对”。传统RNN的问题阅读“猫坐在垫子上它很柔软”时读到“它”的时候需要费力地回想前面提到的“猫”和“垫子”才能确定“它”指代谁。距离越远回想越困难。自注意力的魔法处理“它”这个字时自注意力机制会瞬间让“它”与句子中的每一个字包括“猫”、“垫子”、“柔软”都进行一次“亲密沟通”计算出一个“注意力分数”。这个分数决定了在理解“它”的时候应该从“猫”、“垫子”、“柔软”各自汲取多少信息。让我们用代码从模型里“挖”出一层注意力权重来看看。我们不会手动实现数学公式而是直接观察模型内部的数据流。# 我们取模型的第一层Transformer块Decoder Layer为例 layer model.model.layers[0] # 准备一个简单的输入 test_text 人工智能正在改变世界。 inputs tokenizer(test_text, return_tensorspt).to(model.device) # 让模型进行一次前向传播并保留中间结果激活值 with torch.no_grad(): outputs model(**inputs, output_attentionsTrue) # 注意这里设置了output_attentionsTrue # 获取第一层的注意力权重 # attentions是一个元组包含了每一层的注意力权重 attention_weights outputs.attentions[0] # 形状通常是 (batch_size, num_heads, seq_len, seq_len) print(f注意力权重的形状{attention_weights.shape}) print(f这意味着有 {attention_weights.shape[1]} 个注意力头每个头都计算了一个 {attention_weights.shape[2]} x {attention_attention.shape[3]} 的关联矩阵。) # 我们可以简单看一下第一个注意力头对于第一个词“人工”的关注度分布 # 通常对角线上的值会比较高关注自己但我们也能看到它与其他词的联系 first_token_attention attention_weights[0, 0, 0, :].cpu().numpy() print(f\n第一个词‘{tokenizer.decode(inputs[‘input_ids’][0,0:1])}’对序列中所有词的注意力分数第一个头) for i, score in enumerate(first_token_attention): word tokenizer.decode(inputs[‘input_ids’][0, i:i1]) print(f - ‘{word}’: {score:.4f})通过这段代码你就能直观地看到模型在处理“人工”这个词时它的“注意力”是如何分配给上下文其他词的。多头注意力就是让模型同时进行多场这样的“派对”从不同角度例如语法、语义、指代关系理解词语间的关系。2.3 核心组件二前馈神经网络Feed-Forward Network注意力机制负责“收集信息”而前馈网络则负责“消化和转化信息”。每一层Transformer块里在自注意力之后都会跟一个前馈网络。你可以把它理解为一个“微型的大脑皮层”它对注意力层输出的、已经融合了上下文信息的每个词向量进行独立的、复杂的非线性变换。这个变换通常是两个线性层中间夹着一个激活函数如SiLU或ReLU目的是增强模型的表达能力学习更复杂的特征。# 继续使用第一层看看前馈网络部分 ffn layer.mlp # 在Qwen/LLaMA架构中前馈网络通常命名为mlp print(f前馈网络的结构{ffn}) # 通常会输出类似MLP( (gate_proj): Linear(...), (up_proj): Linear(...), (down_proj): Linear(...) ) # 这是一种特殊的结构如LLaMA的SwiGLU比标准的两个线性层更强大。2.4 其他关键“零件”层归一化LayerNorm像“稳定器”放在注意力层和前馈网络的前后确保数据分布稳定让训练更顺畅。残差连接Residual Connection像“高速公路”把一层的输入直接加到输出上。它的核心作用是防止网络在很深的时候信息传递不下去梯度消失让模型可以轻松地构建成百上千层。位置编码Positional Encoding因为自注意力本身不考虑词序所以需要额外告诉模型“哪个词在前面哪个在后面”。在Qwen等现代模型中常用的是旋转位置编码RoPE它非常巧妙地将位置信息融入到注意力计算中能更好地处理长文本。3. 赋予新能力使用LoRA进行模型微调实战理解了模型的构造我们就可以开始“改造”它了。全参数微调需要巨大的算力而LoRA技术让我们可以用极小的代价为模型注入新知识。3.1 LoRA原理简介一种“外科手术式”的微调想象一下预训练好的大模型是一个庞大的、训练有素的大脑。全参数微调相当于让这个大脑重新学习一切成本极高。LoRA则像是一种“脑部插件”手术。它的核心思想是冻结原始模型的所有参数不让它们更新只在一些关键层通常是注意力层的查询Q和键值V投影矩阵旁边插入一对小小的、可训练的“低秩适配器”矩阵A和B。在前向传播时原始矩阵的输出加上适配器A*B的输出。训练时只更新A和B这两个小矩阵的参数。这样做的好处是参数极少通常只增加原模型参数的0.1%~1%存储和训练开销极小。效果接近全微调多个任务上证明LoRA能达到接近全参数微调的效果。切换灵活可以为不同任务训练不同的LoRA适配器像换“技能卡”一样快速切换模型能力。3.2 准备你的领域数据假设我们想让Qwen1.5更擅长处理医疗问答。我们需要准备一个(问题答案)格式的数据集。这里我们创建一个极简的示例数据集。from datasets import Dataset # 示例数据简单的医疗问答对 medical_data [ {instruction: 什么是感冒, output: 感冒是一种常见的上呼吸道病毒感染主要症状包括流鼻涕、打喷嚏、喉咙痛和咳嗽。}, {instruction: 高血压患者平时要注意什么, output: 高血压患者应注意低盐饮食规律服用降压药定期监测血压保持适度运动避免情绪激动。}, {instruction: 如何预防糖尿病, output: 预防糖尿病需保持健康体重均衡饮食减少糖和精制碳水摄入定期进行体育锻炼并避免吸烟和过量饮酒。}, # ... 这里可以添加成百上千条你自己的数据 ] # 将数据转换为Hugging Face Dataset格式 def format_func(example): # 构建一个简单的提示模板 prompt f### 问题\n{example[‘instruction’]}\n\n### 回答\n{example[‘output’]} return {“text”: prompt} dataset Dataset.from_list(medical_data) dataset dataset.map(format_func) # 使用分词器处理整个数据集 def tokenize_function(examples): return tokenizer(examples[“text”], truncationTrue, padding“max_length”, max_length256) tokenized_dataset dataset.map(tokenize_function, batchedTrue) tokenized_dataset tokenized_dataset.remove_columns([“instruction”, “output”, “text”]) # 移除原始列只保留tokenized后的input_ids和attention_mask tokenized_dataset.set_format(“torch”) print(f“数据集处理完成样本数{len(tokenized_dataset)}”)3.3 配置与运行LoRA微调现在使用peft库来配置LoRA并开始训练。from peft import LoraConfig, TaskType, get_peft_model from transformers import TrainingArguments, Trainer # 1. 定义LoRA配置 lora_config LoraConfig( task_typeTaskType.CAUSAL_LM, # 因果语言模型任务 inference_modeFalse, # 训练模式 r8, # LoRA的秩rank越小参数量越少通常4,8,16 lora_alpha32, # 缩放参数 lora_dropout0.1, # Dropout概率防止过拟合 target_modules[“q_proj”, “v_proj”] # 指定要对哪些模块注意力层的QV矩阵添加LoRA适配器 ) # 2. 将基础模型转换为PEFT模型仅LoRA参数可训练 peft_model get_peft_model(model, lora_config) peft_model.print_trainable_parameters() # 打印可训练参数量你会发现它只占原模型的极小一部分 # 3. 配置训练参数 training_args TrainingArguments( output_dir“./qwen1.5-1.8b-medical-lora”, # 输出目录 per_device_train_batch_size4, # 根据你的GPU调整 gradient_accumulation_steps4, # 梯度累积模拟更大batch size num_train_epochs3, # 训练轮数 logging_steps10, save_steps100, learning_rate2e-4, # LoRA学习率可以设得稍大一点 fp16True, # 使用混合精度训练节省显存加速训练 push_to_hubFalse, # 如果希望上传到Hugging Face Hub可以设为True ) # 4. 创建Trainer并开始训练 trainer Trainer( modelpeft_model, argstraining_args, train_datasettokenized_dataset, data_collatorlambda data: {‘input_ids’: torch.stack([f[‘input_ids’] for f in data]), ‘attention_mask’: torch.stack([f[‘attention_mask’] for f in data]), ‘labels’: torch.stack([f[‘input_ids’] for f in data])}, # 语言模型训练标签就是输入本身 ) print(“开始LoRA微调训练...”) trainer.train() print(“训练完成”) # 5. 保存LoRA适配器权重 peft_model.save_pretrained(“./my_medical_lora_adapter”)训练完成后你只会得到一个很小的文件几MB到几十MB这就是你的“医疗技能卡”——LoRA适配器权重。3.4 加载与使用微调后的模型使用微调后的模型非常简单只需要加载基础模型和对应的LoRA适配器即可。from peft import PeftModel # 加载基础模型和之前一样 base_model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_map“auto”, trust_remote_codeTrue ) # 加载训练好的LoRA适配器 tuned_model PeftModel.from_pretrained(base_model, “./my_medical_lora_adapter”) tuned_model tuned_model.merge_and_unload() # 可选将LoRA权重合并到基础模型中加速推理 # 现在用微调后的模型进行医疗问答 medical_prompt “### 问题\n感冒了应该多喝水吗\n\n### 回答\n” inputs tokenizer(medical_prompt, return_tensors“pt”).to(tuned_model.device) with torch.no_grad(): outputs tuned_model.generate(**inputs, max_new_tokens100, temperature0.7) response tokenizer.decode(outputs[0], skip_special_tokensTrue) print(“微调后模型的回答”) print(response)你会发现模型的回答会更倾向于使用你训练数据中的医学表述风格和知识这就是LoRA微调的效果。4. 总结与进阶思考通过这篇长文我们完成了一次从理论到实践的深度旅程。我们不仅把Qwen1.5-1.8B模型当作一个黑盒来调用更是打开它看懂了其内部Transformer架构的核心工作原理——自注意力机制如何建立词与词之间的动态关联前馈网络又如何进行深层特征变换。更重要的是我们掌握了LoRA这种强大的轻量级微调技术能够以极低的成本将通用的预训练大模型定制成我们需要的领域专家。动手操作一遍后你对大模型的理解应该不再停留在表面。你可以尝试用更多的数据、不同的target_modules配置比如也加到ffn层、或者调整r秩的大小来观察对微调效果的影响。也可以尝试在其他开源模型上重复这个过程比如LLaMA 3、Gemma等。大模型技术正在快速 democratize民主化理解其原理并掌握微调技能无疑是当前开发者最具价值的竞争力之一。希望这篇结合了原理解析与实战代码的指南能成为你探索更广阔AI世界的一块坚实跳板。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章