1. AI PC本地微调LLM:无需独显实现工具调用能力
在AI技术快速发展的今天,大语言模型(LLM)的应用越来越广泛。传统上,微调这些模型需要昂贵的GPU资源或云端计算能力,但AI PC的出现改变了这一局面。AI PC配备了强大的端侧计算组合(CPU/iGPU/NPU),使得开发者可以在本地完成模型微调,无需依赖独立显卡或云服务。
我最近成功在一台配备Intel处理器的AI PC上,使用Unsloth框架对Llama-3.2-3B-Instruct模型进行了LoRA微调,使其具备了Function Calling(工具调用)能力。整个过程完全在本地完成,没有使用任何独立显卡或云服务。下面我将详细分享这个项目的完整实现过程。
2. 环境准备与工具链配置
2.1 硬件与基础软件要求
要进行本地微调,首先需要确保你的AI PC满足以下基本要求:
- 操作系统:Windows 10/11 64位
- 处理器:第11代或更新的Intel Core处理器(推荐i7或更高)
- 内存:至少16GB(推荐32GB)
- 存储:至少50GB可用空间(用于模型和数据集)
- 集成显卡:Intel Iris Xe或更高
提示:虽然这个方案不需要独立显卡,但如果你有Intel Arc独立显卡,性能会更好。
2.2 开发环境搭建步骤
2.2.1 安装Visual Studio C++工具链
首先需要安装Visual Studio Build Tools,这是编译Python扩展的必要组件:
- 访问Visual Studio官网下载页面
- 下载并安装Visual Studio Build Tools或Community版本
- 安装时勾选以下组件:
- Desktop development with C++
- MSVC v143 toolset
- Windows 10/11 SDK
安装完成后,在命令提示符中验证编译器是否可用:
bash复制call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
where cl
如果能看到cl.exe的路径,说明安装成功。
2.2.2 安装Intel oneAPI基础工具包
Intel oneAPI提供了对Intel硬件的优化支持:
- 下载Intel oneAPI Base Toolkit 2024.1版本
- 运行安装程序,选择默认安装选项
- 安装完成后初始化环境变量:
bash复制call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat"
2.2.3 启用Windows长路径支持
某些Python包需要长路径支持,需要执行:
bash复制powershell -Command "Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1"
3. 模型微调实现过程
3.1 创建Python环境并安装依赖
建议使用conda创建独立的Python环境:
bash复制conda create -n aipc-finetune python=3.11 -y
conda activate aipc-finetune
然后安装Unsloth框架(Intel XPU版本):
bash复制git clone https://github.com/unslothai/unsloth.git
cd unsloth
pip install -e .[intel-gpu-torch290]
验证XPU是否可用:
python复制import torch
print(torch.__version__) # 应该输出2.9.0+xpu
print(hasattr(torch, "xpu") and torch.xpu.is_available()) # 应该输出True
3.2 配置Level Zero SDK
Triton Intel后端需要Level Zero SDK:
- 下载level-zero-win-sdk-1.20.2.zip
- 解压到本地目录,例如:C:\level-zero-win-sdk-1.20.2
- 设置环境变量:
bash复制set ZE_PATH=C:\level-zero-win-sdk-1.20.2
3.3 准备数据集
本项目使用两个数据集:
- 训练集:hiyouga/glaive-function-calling-v2-sharegpt
- 评测集:Salesforce/xlam-function-calling-60k(需要申请权限)
对于评测集,需要先登录Hugging Face并申请访问权限:
bash复制set HF_TOKEN=你的HuggingFace令牌
huggingface-cli login
然后访问数据集页面手动申请访问权限。
4. 训练代码解析与实现
4.1 主训练脚本分析
以下是训练脚本的核心部分:
python复制import datetime
from datasets import load_dataset
import torch
from unsloth import FastLanguageModel
from trl import SFTTrainer, SFTConfig
from unsloth.chat_templates import get_chat_template
def prepare_dataset(tokenizer, dataset_name, dataset_size, dataset_seed):
system_prompt = "You are a helpful assistant with access to the following functions..."
def formatting_prompts_func(example):
texts = []
for messages, tools in zip(example["conversations"], example["tools"]):
messages.insert(0, {"from": "system", "value": system_prompt.replace("__TOOL_DESCRIPTION__", tools)})
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False
)
texts.append(text)
return {"text": texts}
dataset = load_dataset(dataset_name, split="train").select(range(dataset_size)).shuffle(seed=dataset_seed)
dataset = dataset.map(formatting_prompts_func, batched=True)
return dataset
def main():
# 模型和训练参数配置
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="meta-llama/Llama-3.2-3B-Instruct",
max_seq_length=2048,
dtype=torch.bfloat16,
device_map="xpu:0"
)
# LoRA配置
model = FastLanguageModel.get_peft_model(
model,
r=16, # LoRA秩
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_alpha=16,
lora_dropout=0
)
# 准备数据集
dataset = prepare_dataset(tokenizer, "hiyouga/glaive-function-calling-v2-sharegpt", 2000, 3407)
# 训练配置
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=2048,
args=SFTConfig(
per_device_train_batch_size=2,
gradient_accumulation_steps=1,
num_train_epochs=1,
learning_rate=2e-4,
fp16=False,
bf16=True,
logging_steps=50,
optim="adamw_torch"
)
)
# 开始训练
trainer_stats = trainer.train()
print("Total Time:", str(datetime.timedelta(seconds=int(trainer_stats.metrics["train_runtime"]))))
if __name__ == "__main__":
main()
4.2 关键参数说明
-
LoRA配置:
r=16:LoRA的秩,影响模型微调的参数数量target_modules:应用LoRA的模型层lora_alpha=16:LoRA缩放因子lora_dropout=0:LoRA层的dropout率
-
训练参数:
per_device_train_batch_size=2:每个设备的批次大小gradient_accumulation_steps=1:梯度累积步数num_train_epochs=1:训练轮数learning_rate=2e-4:学习率bf16=True:使用bfloat16精度
注意事项:在Intel硬件上,使用bfloat16通常比float16更稳定,性能也更好。
5. 推理与评测实现
5.1 推理脚本实现
训练完成后,可以使用以下脚本进行推理:
python复制from unsloth import FastLanguageModel
import json
def inference(model, tokenizer, query, tools):
messages = [{"role": "user", "content": query}]
input_ids = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
tools=tools,
return_tensors="pt"
).to("xpu")
output = model.generate(
input_ids=input_ids,
max_new_tokens=150,
do_sample=False
)
generated_tokens = output[:, input_ids.shape[1]:]
return tokenizer.decode(generated_tokens[0], skip_special_tokens=True)
# 加载模型和适配器
model, tokenizer = FastLanguageModel.from_pretrained(
"meta-llama/Llama-3.2-3B-Instruct",
device_map="xpu:0"
)
model.load_adapter("outputs/checkpoints/checkpoint-1000")
# 示例工具
tools = [{
"name": "get_vector_sum",
"description": "Calculates the sum of two vectors",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "array", "items": {"type": "number"}},
"b": {"type": "array", "items": {"type": "number"}}
},
"required": ["a", "b"]
}
}]
# 执行推理
query = "Find the sum of a = [1, -1, 2] and b = [3, 0, -4]"
response = inference(model, tokenizer, query, tools)
print(response)
5.2 评测脚本实现
为了评估模型性能,可以使用以下评测脚本:
python复制from datasets import load_dataset
from evaluate import load as load_metric
from tqdm import tqdm
def evaluate_model(model, tokenizer, dataset_name, num_samples=50):
# 加载评测集
dataset = load_dataset(dataset_name, split="train")
samples = dataset.shuffle(seed=42).select(range(num_samples))
# 加载评测指标
exact_match = load_metric("exact_match")
rouge = load_metric("rouge")
predictions = []
references = []
for sample in tqdm(samples, desc="Evaluating"):
# 生成预测
prediction = inference(model, tokenizer, sample["query"], json.loads(sample["tools"]))
predictions.append(prediction)
# 获取参考输出
reference = json.loads(sample["answers"])[0]
references.append(reference)
# 计算指标
em_score = exact_match.compute(predictions=predictions, references=references)
rouge_score = rouge.compute(predictions=predictions, references=references)
print(f"Exact Match: {em_score['exact_match']}")
print(f"ROUGE-L: {rouge_score['rougeL']}")
# 执行评测
evaluate_model(model, tokenizer, "Salesforce/xlam-function-calling-60k")
6. 实际效果与性能分析
6.1 工具调用示例
经过微调后,模型能够正确理解和执行工具调用。以下是几个实际案例:
- 向量求和:
json复制{"name": "get_vector_sum", "parameters": {"a": [1, -1, 2], "b": [3, 0, -4]}}
模型输出:
code复制The sum of vectors a and b is [4, -1, -2].
- 设置提醒:
json复制{"name": "set_reminder", "parameters": {"reminder_text": "Call John", "time": "3 PM tomorrow"}}
模型输出:
code复制Okay, I have set a reminder to call John at 3 PM tomorrow.
- 生成密码:
json复制{"name": "generate_password", "parameters": {"length": 12, "include_numbers": true, "include_symbols": true}}
模型输出:
code复制The generated password for you is :JlgWf0G8PXa.
6.2 性能指标
在50个样本的评测中,模型表现如下:
| 指标 | 得分 |
|---|---|
| Tool Call Accuracy | 0.78 |
| Exact Match | 0.76 |
| ROUGE-L | 0.953 |
| BLEU | 0.96 |
| METEOR | 0.958 |
这些结果表明,即使只训练了1个epoch(约1000步),模型已经具备了相当不错的工具调用能力。
7. 常见问题与解决方案
7.1 环境配置问题
问题1:安装Unsloth时出现编译错误
- 原因:通常是因为缺少Visual C++组件或环境变量未正确设置
- 解决方案:
- 确保安装了正确的Visual Studio组件
- 运行
vcvars64.bat初始化环境 - 重新安装Unsloth
问题2:XPU不可用
- 原因:Intel驱动或oneAPI未正确安装
- 解决方案:
- 更新Intel显卡驱动
- 重新安装oneAPI
- 验证
torch.xpu.is_available()返回True
7.2 训练过程中的问题
问题1:训练速度慢
- 原因:批次大小太小或硬件性能不足
- 解决方案:
- 尝试增大
per_device_train_batch_size - 启用梯度累积(
gradient_accumulation_steps) - 确保使用
bf16精度
- 尝试增大
问题2:内存不足
- 原因:模型或批次太大
- 解决方案:
- 减小
max_seq_length - 减小批次大小
- 使用
gradient_checkpointing
- 减小
7.3 推理与评测问题
问题1:工具调用格式不正确
- 原因:训练数据格式或模板不匹配
- 解决方案:
- 检查
formatting_prompts_func是否正确 - 确保评测数据与训练数据格式一致
- 检查
问题2:长输出被截断
- 原因:
max_new_tokens设置太小 - 解决方案:
- 增大
max_new_tokens - 实现流式输出或分块处理
- 增大
8. 优化建议与扩展方向
8.1 性能优化
-
使用Intel Extension for PyTorch:
安装intel-extension-for-pytorch可以进一步提升Intel硬件上的性能:bash复制
pip install intel-extension-for-pytorch -
调整LoRA参数:
- 尝试不同的
r值(8, 32, 64) - 调整
lora_alpha(通常设为r的2倍) - 尝试不同的
target_modules组合
- 尝试不同的
-
数据增强:
- 增加训练数据量
- 添加更多样化的工具调用示例
- 包含边缘案例(如无效输入、缺失参数等)
8.2 功能扩展
-
多工具组合调用:
扩展模型能力,使其能够按顺序调用多个工具完成复杂任务。 -
动态工具发现:
实现工具的动态注册和发现机制,而不是硬编码在系统提示中。 -
工具调用验证:
在模型调用工具前,添加参数验证和安全性检查。 -
对话历史支持:
扩展模型以支持多轮对话中的工具调用。
8.3 部署优化
-
使用OpenVINO优化:
将训练好的模型转换为OpenVINO格式,获得更好的推理性能:python复制from openvino.tools import mo ov_model = mo.convert_model(model, input_shape=[1, 2048]) -
量化部署:
对模型进行8位或4位量化,减少内存占用和提高推理速度。 -
本地API服务:
使用FastAPI创建本地工具调用API服务,方便其他应用集成。
通过这个项目,我验证了在普通AI PC上微调中等规模LLM的可行性。这种方法特别适合需要快速迭代、数据隐私敏感或预算有限的场景。虽然性能无法与高端GPU或云端相比,但对于许多实际应用已经足够。
