ERNIE-4.5-0.3B-PT模型微调指南:适配特定领域任务
1. 引言
如果你手头有一个通用的语言模型,但想让它在你的业务场景里表现得更专业、更懂行,那么模型微调就是你需要掌握的关键技能。今天咱们要聊的,就是如何给ERNIE-4.5-0.3B-PT这个轻量级模型“开小灶”,让它专门为你服务。
ERNIE-4.5-0.3B-PT是个挺有意思的模型,参数只有3亿左右,算是大模型里的“小个子”。但别小看它,这个模型在中文处理上表现不错,而且因为体积小,微调起来对硬件要求没那么高,普通的工作站甚至配置好点的个人电脑都能跑起来。
想象一下这样的场景:你需要一个能理解医疗术语的客服助手,或者一个能写专业法律文书的写作助手,又或者是一个能分析金融报告的智能工具。直接用通用模型,效果可能差强人意,但经过微调后,模型就能在这些特定领域里游刃有余。
这篇文章就是带你一步步走完这个“开小灶”的过程。我会用最直白的方式,从数据准备开始,到训练参数怎么设,再到怎么评估效果,把每个环节都讲清楚。即使你之前没怎么接触过模型微调,跟着做下来也能有个清晰的思路。
2. 环境准备与模型获取
在开始微调之前,咱们得先把环境搭好,把模型拿到手。这个过程其实不复杂,跟着步骤走就行。
2.1 基础环境配置
首先,你需要一个Python环境。我建议用Python 3.8到3.10之间的版本,太新或太旧的版本可能会有兼容性问题。如果你用的是Anaconda,可以创建一个专门的环境:
conda create -n ernie-finetune python=3.9 conda activate ernie-finetune接下来安装必要的库。PyTorch是必须的,版本建议在1.12以上。如果你有GPU,记得安装对应CUDA版本的PyTorch。transformers库是Hugging Face提供的,咱们微调模型主要就靠它:
pip install torch torchvision torchaudio pip install transformers datasets accelerate peft pip install wandb # 可选,用于训练过程可视化这些库装好后,基础环境就差不多了。transformers负责加载和保存模型,datasets用来处理训练数据,accelerate能让训练过程更高效,peft则是后面要用到的参数高效微调工具。
2.2 获取ERNIE-4.5-0.3B-PT模型
模型可以从Hugging Face上直接下载。ERNIE-4.5-0.3B-PT这个版本用的是PyTorch格式的权重,用起来比较方便。下载模型很简单,几行代码就能搞定:
from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "baidu/ERNIE-4.5-0.3B-PT" tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True) print(f"模型加载完成,参数量:{model.num_parameters():,}")运行这段代码,它会自动从Hugging Face下载模型和分词器。第一次运行可能会花点时间,取决于你的网络速度。下载完成后,模型就保存在本地的缓存目录里了,下次再用就不用重新下载。
这里有个细节需要注意:trust_remote_code=True这个参数是必须的,因为ERNIE模型有些自定义的代码需要从远程加载。如果你在下载过程中遇到网络问题,可以尝试设置代理,或者用国内的镜像源。
模型下载好后,咱们可以先简单测试一下它能不能正常工作:
# 测试模型基础功能 prompt = "人工智能是" inputs = tokenizer(prompt, return_tensors="pt") outputs = model.generate(**inputs, max_new_tokens=50) result = tokenizer.decode(outputs[0], skip_special_tokens=True) print(f"生成结果:{result}")如果能看到模型生成的文本,说明模型加载成功了。不过这时候生成的文本可能还比较通用,这正是咱们要通过微调来改进的地方。
3. 数据准备:为模型准备“教材”
模型微调就像教学生,教材质量直接决定学习效果。你的训练数据就是模型的“教材”,这部分工作做得好不好,直接影响微调后的模型表现。
3.1 数据格式与要求
对于ERNIE-4.5-0.3B-PT这样的因果语言模型,训练数据通常采用对话格式。每条数据包含系统提示、用户输入和模型应有的回答。举个例子,如果你要微调一个医疗问答助手,数据可能是这样的:
{ "conversations": [ {"role": "system", "content": "你是一个专业的医疗助手,用中文回答用户的健康咨询问题。"}, {"role": "user", "content": "感冒了应该吃什么药?"}, {"role": "assistant", "content": "感冒是自限性疾病,通常不需要特殊药物治疗。建议多休息、多喝水,如果症状严重可以咨询医生。不建议自行用药。"} ] }如果你要做的是文本生成任务,比如写法律文书,数据格式会更简单一些:
{ "instruction": "根据以下事实写一份起诉状", "input": "原告:张三,被告:李四,事由:借款纠纷,金额:10万元,借款时间:2023年1月1日", "output": "民事起诉状\n原告:张三,男,1980年1月1日出生...(完整的法律文书)" }数据量方面,对于ERNIE-4.5-0.3B-PT这样规模的模型,有个几百到几千条高质量数据通常就能看到明显效果。当然,数据越多越好,但质量比数量更重要。十条精心标注的高质量数据,可能比一百条粗糙的数据效果更好。
3.2 数据预处理实战
假设你已经收集了一些原始数据,现在需要把它们转换成模型能理解的格式。咱们用一个实际的例子来看看怎么处理。
假设你收集了一些客服对话记录,原始数据可能是CSV格式的:
import pandas as pd from datasets import Dataset # 读取原始数据 df = pd.read_csv("customer_service_data.csv") print(f"原始数据条数:{len(df)}") print(df.head()) # 转换格式 def convert_to_conversation(row): return { "conversations": [ {"role": "system", "content": "你是一个专业的客服助手,用中文回答用户的问题。"}, {"role": "user", "content": row["question"]}, {"role": "assistant", "content": row["answer"]} ] } # 应用转换 converted_data = [convert_to_conversation(row) for _, row in df.iterrows()] # 创建Hugging Face数据集 dataset = Dataset.from_list(converted_data) # 划分训练集和验证集 split_dataset = dataset.train_test_split(test_size=0.1, seed=42) train_dataset = split_dataset["train"] eval_dataset = split_dataset["test"] print(f"训练集:{len(train_dataset)}条,验证集:{len(eval_dataset)}条")数据划分时,我一般会留出10%左右作为验证集,用来在训练过程中评估模型表现。随机种子设成固定值,这样每次划分的结果都一样,方便复现。
3.3 数据质量检查
在开始训练前,花点时间检查数据质量是很有必要的。一些常见的问题包括:
- 格式不一致:有的对话有系统提示,有的没有
- 内容错误:回答里有事实性错误
- 长度问题:有的回答太短(比如就一两个字),有的又太长
- 重复数据:完全相同或高度相似的对话
你可以写个简单的脚本来检查这些问题:
def check_data_quality(dataset): issues = [] for i, item in enumerate(dataset): conversations = item["conversations"] # 检查格式 if len(conversations) != 3: issues.append(f"第{i}条数据:对话轮数不是3轮") continue # 检查角色顺序 roles = [conv["role"] for conv in conversations] if roles != ["system", "user", "assistant"]: issues.append(f"第{i}条数据:角色顺序不正确") # 检查内容长度 assistant_content = conversations[2]["content"] if len(assistant_content) < 10: issues.append(f"第{i}条数据:回答太短({len(assistant_content)}字符)") elif len(assistant_content) > 1000: issues.append(f"第{i}条数据:回答太长({len(assistant_content)}字符)") if issues: print(f"发现{len(issues)}个问题:") for issue in issues[:5]: # 只显示前5个问题 print(f" - {issue}") if len(issues) > 5: print(f" ... 还有{len(issues)-5}个问题") else: print("数据质量检查通过!") return issues # 运行检查 issues = check_data_quality(train_dataset)发现问题后及时修正,能避免很多训练过程中的麻烦。好的数据是成功微调的一半,这句话在模型训练里特别适用。
4. 微调实战:让模型学习你的数据
数据准备好了,环境也搭好了,现在可以开始真正的微调了。我会介绍两种常用的微调方法:全参数微调和LoRA微调,你可以根据实际情况选择。
4.1 全参数微调
全参数微调就是调整模型的所有参数,这种方法效果通常比较好,但对计算资源要求也高。对于ERNIE-4.5-0.3B-PT这样3亿参数的模型,如果有张显存大点的GPU(比如16GB以上),全参数微调是可行的。
先来看看完整的训练代码:
from transformers import TrainingArguments, Trainer from transformers import DataCollatorForLanguageModeling import torch # 数据预处理函数 def preprocess_function(examples): # 把对话格式转换成文本 texts = [] for conv in examples["conversations"]: # 把对话拼接成文本 text = "" for turn in conv: if turn["role"] == "system": text += f"<|system|>\n{turn['content']}\n" elif turn["role"] == "user": text += f"<|user|>\n{turn['content']}\n" elif turn["role"] == "assistant": text += f"<|assistant|>\n{turn['content']}\n" texts.append(text) # 分词 tokenized = tokenizer( texts, truncation=True, padding="max_length", max_length=512, return_tensors="pt" ) # 设置标签(对于因果语言模型,标签就是输入本身) tokenized["labels"] = tokenized["input_ids"].clone() return tokenized # 应用预处理 tokenized_train = train_dataset.map(preprocess_function, batched=True) tokenized_eval = eval_dataset.map(preprocess_function, batched=True) # 设置训练参数 training_args = TrainingArguments( output_dir="./ernie-finetuned", num_train_epochs=3, # 训练轮数 per_device_train_batch_size=4, # 批次大小 per_device_eval_batch_size=4, warmup_steps=100, # 预热步数 weight_decay=0.01, # 权重衰减 logging_dir="./logs", logging_steps=50, evaluation_strategy="steps", eval_steps=200, save_strategy="steps", save_steps=500, load_best_model_at_end=True, metric_for_best_model="eval_loss", greater_is_better=False, push_to_hub=False, # 不上传到Hugging Face Hub report_to="wandb", # 可选:使用wandb记录训练过程 ) # 创建Trainer trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_train, eval_dataset=tokenized_eval, data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False), ) # 开始训练 trainer.train()这里有几个关键参数需要根据你的实际情况调整:
- num_train_epochs:训练轮数。对于小数据集(几百条),3-5轮通常够了;大数据集(几千条以上)可能1-2轮就行。
- per_device_train_batch_size:批次大小。这个值受GPU显存限制,ERNIE-4.5-0.3B-PT在16GB显存上大概能跑batch size 4-8。
- learning_rate:学习率。全参数微调一般用比较小的学习率,比如1e-5到5e-5之间。
训练过程中,你可以通过日志观察loss的变化。正常情况下,训练loss应该逐渐下降,验证loss也应该同步下降。如果验证loss开始上升,可能是过拟合了,这时候可以考虑提前停止训练。
4.2 LoRA微调:更高效的方案
如果你的GPU显存不够大,或者想更快地完成微调,LoRA(Low-Rank Adaptation)是个很好的选择。它只训练模型的一小部分参数,大大减少了计算量和内存占用。
用LoRA微调ERNIE-4.5-0.3B-PT的代码是这样的:
from peft import LoraConfig, get_peft_model, TaskType from transformers import Trainer # 配置LoRA lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, # 因果语言模型任务 r=8, # LoRA的秩,一般8或16 lora_alpha=32, # 缩放参数 lora_dropout=0.1, # Dropout率 target_modules=["q_proj", "v_proj"], # 对哪些模块应用LoRA bias="none", # 不训练偏置 ) # 应用LoRA到模型 model = get_peft_model(model, lora_config) model.print_trainable_parameters() # 打印可训练参数数量 # 训练参数可以设得更大一些,因为要训练的参数少了 training_args = TrainingArguments( output_dir="./ernie-lora-finetuned", num_train_epochs=5, # LoRA可以多训练几轮 per_device_train_batch_size=8, # 批次可以设大一些 per_device_eval_batch_size=8, learning_rate=1e-4, # 学习率可以大一些 warmup_steps=100, weight_decay=0.01, logging_steps=50, evaluation_strategy="steps", eval_steps=200, save_strategy="steps", save_steps=500, load_best_model_at_end=True, metric_for_best_model="eval_loss", greater_is_better=False, ) # 创建Trainer(和全参数微调一样) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_train, eval_dataset=tokenized_eval, data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False), ) # 开始训练 trainer.train() # 保存LoRA权重 model.save_pretrained("./ernie-lora-weights")LoRA的关键参数是r(秩)和target_modules。r越小,要训练的参数量越少,但能力可能越受限。对于ERNIE-4.5-0.3B-PT,r=8是个不错的起点。target_modules指定对模型的哪些部分应用LoRA,通常选择注意力机制中的查询(q_proj)和值(v_proj)投影层。
用LoRA微调后,你得到的其实是一个小的适配器权重文件,只有几MB大小。使用时需要加载原始模型和这个适配器:
from peft import PeftModel # 加载基础模型 base_model = AutoModelForCausalLM.from_pretrained("baidu/ERNIE-4.5-0.3B-PT", trust_remote_code=True) # 加载LoRA权重 model = PeftModel.from_pretrained(base_model, "./ernie-lora-weights")这样既节省存储空间,又能在不同任务间快速切换。
5. 训练技巧与参数调优
微调不只是把数据扔进去训练就完事了,有些技巧能让训练过程更顺利,效果更好。
5.1 学习率调度策略
学习率是训练中最重要的超参数之一。一开始用大一点的学习率快速收敛,后面用小学习率精细调整,这种策略通常效果不错。transformers的TrainingArguments已经内置了学习率调度,但你可以通过一些参数来调整:
training_args = TrainingArguments( # ... 其他参数 learning_rate=2e-5, # 初始学习率 lr_scheduler_type="cosine", # 余弦退火调度 warmup_ratio=0.1, # 前10%的步数用于热身 warmup_steps=100, )常用的调度策略有:
- linear:线性衰减,简单可靠
- cosine:余弦退火,在后期能更好地跳出局部最优
- cosine_with_restarts:带重启的余弦退火,适合训练时间比较长的情况
对于ERNIE-4.5-0.3B-PT的微调,我一般用cosine,配合warmup_ratio=0.1。
5.2 梯度累积与混合精度训练
如果你的GPU显存不够大,无法设置想要的batch size,可以用梯度累积来模拟更大的batch:
training_args = TrainingArguments( # ... 其他参数 per_device_train_batch_size=2, # 实际batch size gradient_accumulation_steps=4, # 累积4步,相当于batch size=8 fp16=True, # 使用混合精度训练,节省显存 )梯度累积的原理是:先在小batch上计算梯度,但不立即更新参数,而是累积多个batch的梯度后再更新。gradient_accumulation_steps=4意味着每4个batch更新一次参数,相当于batch size扩大了4倍。
混合精度训练(fp16=True)用半精度浮点数存储大部分参数和梯度,能显著减少显存占用,通常还能加快训练速度。不过要注意,有些模型用混合精度训练可能会不稳定,如果发现loss变成NaN,可以关掉这个选项试试。
5.3 早停与模型保存
训练过程中要监控验证集上的表现,避免过拟合。早停(early stopping)是个实用的技巧:
from transformers import EarlyStoppingCallback training_args = TrainingArguments( # ... 其他参数 load_best_model_at_end=True, metric_for_best_model="eval_loss", greater_is_better=False, ) trainer = Trainer( # ... 其他参数 callbacks=[EarlyStoppingCallback(early_stopping_patience=3)], # 连续3次验证loss不下降就停止 )early_stopping_patience=3表示如果验证loss连续3次评估都没有下降,就停止训练。这个值可以根据你的数据集大小调整,数据少可以设小一点(比如2),数据多可以设大一点(比如5)。
模型保存也很重要,我建议同时保存最后几个检查点和最佳检查点:
training_args = TrainingArguments( # ... 其他参数 save_strategy="steps", save_steps=500, # 每500步保存一次 save_total_limit=3, # 只保留最近3个检查点 load_best_model_at_end=True, )这样既不会占用太多磁盘空间,又能在需要时回退到之前的某个检查点。
6. 评估与测试:看看模型学得怎么样
训练完成后,不能只看训练loss就认为模型学好了,还得实际测试一下。评估分为定量评估和定性评估两部分。
6.1 定量评估指标
对于文本生成任务,常用的评估指标有困惑度(Perplexity)、BLEU、ROUGE等。不过对于领域适配的微调,我更喜欢用一些更贴近实际应用的指标。
首先可以计算模型在验证集上的困惑度:
import math from transformers import Trainer # 在训练时已经设置了评估,可以直接用trainer评估 eval_results = trainer.evaluate() print(f"验证集困惑度:{math.exp(eval_results['eval_loss']):.2f}")困惑度越低,说明模型对数据的拟合越好。但要注意,困惑度太低可能意味着过拟合。
你还可以自定义一些评估函数,比如计算生成文本与参考文本的相似度:
from rouge import Rouge def evaluate_generation(model, tokenizer, eval_dataset, num_samples=20): rouge = Rouge() scores = {"rouge-1": [], "rouge-2": [], "rouge-l": []} # 随机采样一些数据 import random samples = random.sample(list(eval_dataset), min(num_samples, len(eval_dataset))) for sample in samples: # 获取输入 prompt = sample["conversations"][1]["content"] # 用户输入 reference = sample["conversations"][2]["content"] # 参考回答 # 生成回答 inputs = tokenizer(prompt, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=200, temperature=0.7, do_sample=True, top_p=0.9 ) generated = tokenizer.decode(outputs[0], skip_special_tokens=True) # 计算ROUGE分数 try: rouge_scores = rouge.get_scores(generated, reference)[0] for key in scores.keys(): scores[key].append(rouge_scores[key]["f"]) except: continue # 计算平均分 avg_scores = {key: sum(values)/len(values) for key, values in scores.items()} return avg_scores # 使用微调后的模型进行评估 model.eval() scores = evaluate_generation(model, tokenizer, eval_dataset) print(f"ROUGE-1: {scores['rouge-1']:.3f}") print(f"ROUGE-2: {scores['rouge-2']:.3f}") print(f"ROUGE-L: {scores['rouge-l']:.3f}")ROUGE分数越高,说明生成文本与参考文本越相似。但要注意,这只是一个参考指标,不是绝对的评判标准。
6.2 定性评估:人工检查
定量指标很重要,但最终还是要看模型生成的内容质量如何。我建议准备一个测试集,包含一些典型的用例,然后人工检查模型的输出。
你可以写个简单的交互脚本,手动测试模型:
def test_model_interactive(model, tokenizer, system_prompt): print("开始测试模型(输入'退出'结束)") print(f"系统提示:{system_prompt}") print("-" * 50) while True: user_input = input("\n你的问题:") if user_input.lower() in ["退出", "exit", "quit"]: break # 构建对话 messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input} ] # 格式化输入 text = "" for msg in messages: if msg["role"] == "system": text += f"<|system|>\n{msg['content']}\n" else: text += f"<|user|>\n{msg['content']}\n" # 生成回答 inputs = tokenizer(text, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=300, temperature=0.7, do_sample=True, top_p=0.9, pad_token_id=tokenizer.eos_token_id ) # 解码并显示 response = tokenizer.decode(outputs[0], skip_special_tokens=True) # 提取助手的回答 if "<|assistant|>" in response: assistant_response = response.split("<|assistant|>")[-1].strip() else: assistant_response = response.split("<|user|>")[-1].strip() if "<|user|>" in response else response print(f"\n模型回答:{assistant_response}") # 使用微调后的模型 test_model_interactive(model, tokenizer, "你是一个专业的医疗助手,用中文回答用户的健康咨询问题。")通过这种方式,你能直观地感受模型在具体问题上的表现。注意观察:
- 回答是否专业、准确
- 语言是否流畅、自然
- 有没有出现事实性错误
- 有没有重复或无关的内容
6.3 对比测试:微调前后的差异
要真正看出微调的效果,最好做个对比测试,看看微调前后的模型在相同问题上的表现有什么不同:
def compare_models(original_model, finetuned_model, tokenizer, test_questions): print("模型对比测试") print("=" * 60) for i, question in enumerate(test_questions, 1): print(f"\n问题 {i}: {question}") for model_name, model in [("原始模型", original_model), ("微调后", finetuned_model)]: # 生成回答 text = f"<|system|>\n你是一个专业的医疗助手。\n<|user|>\n{question}\n<|assistant|>\n" inputs = tokenizer(text, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=200, temperature=0.7, do_sample=True, top_p=0.9 ) response = tokenizer.decode(outputs[0], skip_special_tokens=True) if "<|assistant|>" in response: answer = response.split("<|assistant|>")[-1].strip() else: answer = response print(f"\n{model_name}: {answer[:100]}..." if len(answer) > 100 else f"\n{model_name}: {answer}") print("-" * 60) # 准备测试问题 test_questions = [ "感冒了应该吃什么药?", "高血压患者平时要注意什么?", "如何预防糖尿病?" ] # 加载原始模型做对比 original_model = AutoModelForCausalLM.from_pretrained("baidu/ERNIE-4.5-0.3B-PT", trust_remote_code=True) # 运行对比测试 compare_models(original_model, model, tokenizer, test_questions)通过这样的对比,你能清楚地看到微调带来的改进。通常微调后的模型在专业领域的问题上回答会更准确、更专业,而在通用问题上可能略有下降,这是正常的,因为模型把能力集中到了特定领域。
7. 部署与应用:让模型真正用起来
模型训练好了,评估也通过了,接下来就是把它用起来。这里介绍几种常见的部署方式。
7.1 本地API服务
最简单的部署方式就是启动一个本地的API服务。用FastAPI可以快速搭建:
from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn from typing import List, Optional app = FastAPI(title="ERNIE微调模型API") class ChatRequest(BaseModel): messages: List[dict] max_tokens: Optional[int] = 200 temperature: Optional[float] = 0.7 top_p: Optional[float] = 0.9 class ChatResponse(BaseModel): response: str usage: dict @app.post("/chat", response_model=ChatResponse) async def chat_completion(request: ChatRequest): try: # 格式化消息 text = "" for msg in request.messages: if msg["role"] == "system": text += f"<|system|>\n{msg['content']}\n" elif msg["role"] == "user": text += f"<|user|>\n{msg['content']}\n" elif msg["role"] == "assistant": text += f"<|assistant|>\n{msg['content']}\n" # 添加assistant标记开始生成 text += "<|assistant|>\n" # 生成回答 inputs = tokenizer(text, return_tensors="pt").to(model.device) outputs = model.generate( **inputs, max_new_tokens=request.max_tokens, temperature=request.temperature, do_sample=True, top_p=request.top_p, pad_token_id=tokenizer.eos_token_id ) # 解码 full_response = tokenizer.decode(outputs[0], skip_special_tokens=True) assistant_response = full_response.split("<|assistant|>")[-1].strip() # 计算token使用量 input_tokens = len(tokenizer.encode(text)) output_tokens = len(tokenizer.encode(assistant_response)) return ChatResponse( response=assistant_response, usage={ "input_tokens": input_tokens, "output_tokens": output_tokens, "total_tokens": input_tokens + output_tokens } ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": # 启动服务 uvicorn.run(app, host="0.0.0.0", port=8000)启动服务后,你就可以通过HTTP请求来调用模型了:
curl -X POST "http://localhost:8000/chat" \ -H "Content-Type: application/json" \ -d '{ "messages": [ {"role": "system", "content": "你是一个专业的医疗助手"}, {"role": "user", "content": "感冒了怎么办?"} ], "max_tokens": 150, "temperature": 0.7 }'7.2 使用vLLM加速推理
如果你需要更高的推理速度,或者要处理大量并发请求,可以考虑用vLLM来部署。vLLM是一个专门优化大语言模型推理的库,能显著提升吞吐量。
首先安装vLLM:
pip install vllm然后用vLLM加载微调后的模型:
from vllm import LLM, SamplingParams # 加载模型(如果是LoRA微调,需要先合并权重) if hasattr(model, "merge_and_unload"): # 如果是PeftModel model = model.merge_and_unload() # 保存合并后的模型 model.save_pretrained("./ernie-merged") tokenizer.save_pretrained("./ernie-merged") # 用vLLM加载 llm = LLM( model="./ernie-merged", trust_remote_code=True, max_model_len=2048, # 根据你的需求调整 gpu_memory_utilization=0.9 # GPU内存使用率 ) # 准备采样参数 sampling_params = SamplingParams( temperature=0.7, top_p=0.9, max_tokens=200 ) # 批量生成 prompts = [ "感冒了应该注意什么?", "如何预防高血压?", "糖尿病患者饮食要注意什么?" ] # 添加系统提示 formatted_prompts = [] for prompt in prompts: formatted_prompts.append( f"<|system|>\n你是一个专业的医疗助手。\n<|user|>\n{prompt}\n<|assistant|>\n" ) outputs = llm.generate(formatted_prompts, sampling_params) for output in outputs: generated_text = output.outputs[0].text print(f"输入:{output.prompt[:50]}...") print(f"输出:{generated_text[:100]}...") print("-" * 50)vLLM的优势在于它的连续批处理和PagedAttention技术,能同时处理多个请求,大大提升吞吐量。如果你的应用场景需要服务多个用户,或者有较高的并发要求,vLLM是个不错的选择。
7.3 模型量化与优化
如果要在资源受限的环境(比如没有GPU的服务器)上部署,可以考虑对模型进行量化。量化能减少模型大小,降低内存占用,虽然可能会损失一点精度,但通常影响不大。
用transformers自带的量化功能:
from transformers import BitsAndBytesConfig import torch # 配置4-bit量化 bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4" ) # 加载量化后的模型 quantized_model = AutoModelForCausalLM.from_pretrained( "./ernie-merged", # 或者你的模型路径 quantization_config=bnb_config, device_map="auto", # 自动分配设备 trust_remote_code=True )量化后的模型大小会大大减少,ERNIE-4.5-0.3B-PT用4-bit量化后大概只有原来的1/4大小。推理速度也会有所提升,特别是在CPU上。
不过要注意,量化模型在生成质量上可能会有轻微下降,而且有些操作(比如继续训练)可能不支持。所以通常是在部署阶段才做量化,训练阶段还是用全精度。
8. 总结
走完这一整套流程,你应该对ERNIE-4.5-0.3B-PT的微调有了比较全面的了解。从数据准备到训练调优,再到评估部署,每个环节都有需要注意的地方。
实际做下来,我觉得最重要的还是数据质量。花时间整理一批高质量、有代表性的训练数据,比盲目增加数据量或者调整超参数要有效得多。特别是在领域适配的场景下,数据要能覆盖这个领域的典型问题和回答方式。
训练过程中要多观察loss曲线,既要看训练loss,也要看验证loss。如果发现过拟合(训练loss继续下降但验证loss开始上升),可以尝试减少训练轮数、增加正则化、或者扩充训练数据。
评估的时候不能只看指标,要实际用用看。准备一些真实场景的问题,看看模型的回答是不是你想要的。有时候指标很好,但生成的内容就是不对劲,这时候可能需要调整训练数据或者提示词格式。
部署方面,根据你的需求选择合适的方式。如果只是内部测试或者小规模使用,用FastAPI搭个简单的API服务就够了。如果需要服务大量用户,或者对响应速度要求很高,可以考虑用vLLM这样的专业推理引擎。
最后想说,模型微调是个需要耐心和反复尝试的过程。第一次可能不会那么顺利,可能会遇到各种问题,比如显存不够、训练不收敛、生成效果不好等等。但每解决一个问题,你就积累了一次经验。多试几次,慢慢就能找到感觉,知道什么样的数据、什么样的参数适合你的任务。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。