一、核心定义
大模型微调(Fine-tuning)指在通用预训练模型(如GPT、LLaMA等)基础上,通过特定领域或任务的标注数据对模型参数进行二次优化,使其从通用能力向垂直场景的专业能力迁移的技术过程。
核心价值:解决预训练模型“通用性强但专业精度不足”的痛点,避免从零训练的高成本问题。
技术本质:通过调整模型参数(部分或全部),使模型“永久性”掌握特定领域知识或任务能力。
二、大型模型微调的技术手段
大型模型的全面微调(Fine-tuning)涉及调整所有层和参数,以适配特定任务。此过程通常采用较小的学习率和特定任务的数据,可以充分利用预训练模型的通用特征,但可能需要更多计算资源。
参数高效微调(Parameter-Efficient Fine-Tuning,PEFT)旨在通过最小化微调参数数量和计算复杂度,提升预训练模型在新任务上的表现,从而减轻大型预训练模型的训练负担。
即使在计算资源受限的情况下,PEFT技术也能够利用预训练模型的知识快速适应新任务,实现有效的迁移学习。因此,PEFT不仅能提升模型效果,还能显著缩短训练时间和计算成本,使更多研究者能够参与到深度学习的研究中。
PEFT包括LoRA、QLoRA、适配器调整(Adapter Tuning)、前缀调整(Prefix Tuning)、提示调整(Prompt Tuning)、P-Tuning及P-Tuning v2等多种方法。
以下图表示了7种主流微调方法在Transformer网络架构中的作用位置及其简要说明,接下来将详细介绍每一种方法。
三、微调预训练模型的方法
微调所有层:将预训练模型的所有层都参与微调,以适应新的任务。
微调顶层:只微调预训练模型的顶层,以适应新的任务。
冻结底层:将预训练模型的底层固定不变,只对顶层进行微调。
逐层微调:从底层开始,逐层微调预训练模型,直到所有层都被微调。
迁移学习:将预训练模型的知识迁移到新的任务中,以提高模型性能。这种方法通常使用微调顶层或冻结底层的方法。
四、Qwen大模型
1.简介
Qwen2是通义千问团队最近开源的大语言模型, 以Qwen2作为基座大模型,通过指令微调的方式做高精度的命名实体识别(NER),入门学习LLM微调、建立对大模型微调的认知。
命名实体识别(Named Entity Recognition,简称 NER)是自然语言处理中的一项重要任务。其主要目的是从文本中识别出具有特定意义的实体,这些实体可以包括人名、地名、组织机构名、时间、日期、货币金额等。
2.环境配置
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple swanlab modelscope transformers datasets peft pandas accelerate
3.数据集
数据集来自HuggingFace上的chinese_ner_sft,该数据集被用于训练命名实体识别模型。
将ccfbdci.jsonl文件下载到与python文件同一目录下即可
4.加载模型
使用modelscope下载Qwen2-1.5B-Instruct模型
from modelscope import snapshot_download, AutoTokenizer
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq
model_id = "qwen/Qwen2-1.5B-Instruct"
model_dir = "./qwen/Qwen2-1___5B-Instruct"
# 在modelscope上下载Qwen模型到本地目录下
model_dir = snapshot_download(model_id, cache_dir="./", revision="master")
# Transformers加载模型权重
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
5.可视化工具
使用SwanLab来监控整个训练过程,并评估最终的模型效果。
from swanlab.integration.huggingface import SwanLabCallback
swanlab_callback = SwanLabCallback(...)
trainer = Trainer(
...
callbacks=[swanlab_callback],
)
6.train.py
全部的完整代码如下
import json
import pandas as pd
import torch
from datasets import Dataset
from modelscope import snapshot_download, AutoTokenizer
from swanlab.integration.huggingface import SwanLabCallback
from peft import LoraConfig, TaskType, get_peft_model
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForSeq2Seq
import os
import swanlab
def dataset_jsonl_transfer(origin_path, new_path):
"""
将原始数据集转换为大模型微调所需数据格式的新数据集
"""
messages = []
# 读取旧的JSONL文件
with open(origin_path, "r") as file:
for line in file:
# 解析每一行的json数据
data = json.loads(line)
input_text = data["text"]
entities = data["entities"]
match_names = ["地点", "人名", "地理实体", "组织"]
entity_sentence = ""
for entity in entities:
entity_json = dict(entity)
entity_text = entity_json["entity_text"]
entity_names = entity_json["entity_names"]
for name in entity_names:
if name in match_names:
entity_label = name
break
entity_sentence += f"""{{"entity_text": "{entity_text}", "entity_label": "{entity_label}"}}"""
if entity_sentence == "":
entity_sentence = "没有找到任何实体"
message = {
"instruction": """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如 {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体". """,
"input": f"文本:{input_text}",
"output": entity_sentence,
}
messages.append(message)
# 保存重构后的JSONL文件
with open(new_path, "w", encoding="utf-8") as file:
for message in messages:
file.write(json.dumps(message, ensure_ascii=False) + "\n")
def process_func(example):
"""
将数据集进行预处理
"""
MAX_LENGTH = 384
input_ids, attention_mask, labels = [], [], []
system_prompt = """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如 {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体"."""
instruction = tokenizer(
f"<|im_start|>system\n{system_prompt}<|im_end|>\n<|im_start|>user\n{example['input']}<|im_end|>\n<|im_start|>assistant\n",
add_special_tokens=False,
)
response = tokenizer(f"{example['output']}", add_special_tokens=False)
input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
attention_mask = (
instruction["attention_mask"] + response["attention_mask"] + [1]
)
labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
if len(input_ids) > MAX_LENGTH: # 做一个截断
input_ids = input_ids[:MAX_LENGTH]
attention_mask = attention_mask[:MAX_LENGTH]
labels = labels[:MAX_LENGTH]
return {"input_ids": input_ids, "attention_mask": attention_mask, "labels": labels}
def predict(messages, model, tokenizer):
device = "cuda" #若没有cuda,则用cpu
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
model_inputs = tokenizer([text], return_tensors="pt").to(device)
generated_ids = model.generate(
model_inputs.input_ids,
max_new_tokens=512
)
generated_ids = [
output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response)
return response
model_id = "qwen/Qwen2-1.5B-Instruct"
model_dir = "./qwen/Qwen2-1___5B-Instruct"
# 在modelscope上下载Qwen模型到本地目录下
model_dir = snapshot_download(model_id, cache_dir="./", revision="master")
# Transformers加载模型权重
tokenizer = AutoTokenizer.from_pretrained(model_dir, use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", torch_dtype=torch.bfloat16)
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
# 加载、处理数据集和测试集
train_dataset_path = "ccfbdci.jsonl"
train_jsonl_new_path = "ccf_train.jsonl"
if not os.path.exists(train_jsonl_new_path):
dataset_jsonl_transfer(train_dataset_path, train_jsonl_new_path)
# 得到训练集
total_df = pd.read_json(train_jsonl_new_path, lines=True)
train_df = total_df[int(len(total_df) * 0.1):]
train_ds = Dataset.from_pandas(train_df)
train_dataset = train_ds.map(process_func, remove_columns=train_ds.column_names)
config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
inference_mode=False, # 训练模式
r=8, # Lora 秩
lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
lora_dropout=0.1, # Dropout 比例
)
model = get_peft_model(model, config)
args = TrainingArguments(
output_dir="./output/Qwen2-NER",
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
gradient_accumulation_steps=4,
logging_steps=10,
num_train_epochs=2,
save_steps=100,
learning_rate=1e-4,
save_on_each_node=True,
gradient_checkpointing=True,
report_to="none",
)
swanlab_callback = SwanLabCallback(
project="Qwen2-NER-fintune",
experiment_name="Qwen2-1.5B-Instruct",
description="使用通义千问Qwen2-1.5B-Instruct模型在NER数据集上微调,实现关键实体识别任务。",
config={
"model": model_id,
"model_dir": model_dir,
"dataset": "qgyd2021/chinese_ner_sft",
},
)
trainer = Trainer(
model=model,
args=args,
train_dataset=train_dataset,
data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
callbacks=[swanlab_callback],
)
trainer.train()
# 用测试集的随机20条,测试模型
# 得到测试集
test_df = total_df[:int(len(total_df) * 0.1)].sample(n=20)
test_text_list = []
for index, row in test_df.iterrows():
instruction = row['instruction']
input_value = row['input']
messages = [
{"role": "system", "content": f"{instruction}"},
{"role": "user", "content": f"{input_value}"}
]
response = predict(messages, model, tokenizer)
messages.append({"role": "assistant", "content": f"{response}"})
result_text = f"{messages[0]}\n\n{messages[1]}\n\n{messages[2]}"
test_text_list.append(swanlab.Text(result_text, caption=response))
swanlab.log({"Prediction": test_text_list})
swanlab.finish()
7.查看训练结果
到SwanLab上查看最终的训练结果:
经历了两个epoch后,微调后qwen2的loss值降低了很多,并趋于稳定的水平,同时在一些测试样例上,可得知微调后的qwen2能够给出准确的实体抽取结果, 至此,我们完成了qwen2在NER任务上的指令微调训练。
8.推理测试
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
def predict(messages, model, tokenizer):
device = "cuda"
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
model_inputs = tokenizer([text], return_tensors="pt").to(device)
generated_ids = model.generate(model_inputs.input_ids, max_new_tokens=512)
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
return response
# 加载原下载路径的tokenizer和model
tokenizer = AutoTokenizer.from_pretrained("./qwen/Qwen2-1___5B-Instruct/", use_fast=False, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("./qwen/Qwen2-1___5B-Instruct/", device_map="auto", torch_dtype=torch.bfloat16)
# 加载训练好的Lora模型,将下面的[checkpoint-XXX]替换为实际的checkpoint文件名名称
model = PeftModel.from_pretrained(model, model_id="./output/Qwen2-NER/checkpoint-1700")
input_text = "国会外有大约200名警察驻守,防止抗议人群闯入国会。"
test_texts = {
"instruction": """你是一个文本实体识别领域的专家,你需要从给定的句子中提取 地点; 人名; 地理实体; 组织 实体. 以 json 格式输出, 如; {"entity_text": "南京", "entity_label": "地理实体"} 注意: 1. 输出的每一行都必须是正确的 json 字符串. 2. 找不到任何实体时, 输出"没有找到任何实体". """,
"input": f"文本:{input_text}"
}
instruction = test_texts['instruction']
input_value = test_texts['input']
messages = [
{"role": "system", "content": f"{instruction}"},
{"role": "user", "content": f"{input_value}"}
]
response = predict(messages, model, tokenizer)
print(response)
AI不会淘汰人类,但会淘汰不会用AI的人
这不是科幻电影,而是2025年全球职场加速“AI化”的缩影。从最新数据看,全球已有23%的知识型岗位因AI大模型缩减规模,而在编程、翻译、数据分析等领域,替代率更飙升至40%以上。当AI开始撰写法律合同、设计建筑图纸、甚至独立完成新药分子结构预测时,一个残酷的真相浮出水面:人类与AI的竞争,已从辅助工具升级为生存战争。
留给人类的时间窗口正在关闭。学习大模型已不是提升竞争力的可选项,而是避免被淘汰的必选项。正如谷歌CEO桑达尔·皮查伊所说:“未来只有两种人:创造AI的人,和解释自己为什么不需要AI的人。”你,选择成为哪一种?
1.AI大模型学习路线汇总
L1阶段-AI及LLM基础
L2阶段-LangChain开发
L3阶段-LlamaIndex开发
L4阶段-AutoGen开发
L5阶段-LLM大模型训练与微调
L6阶段-企业级项目实战
L7阶段-前沿技术扩展
2.AI大模型PDF书籍合集
3.AI大模型视频合集
4.LLM面试题和面经合集
5.AI大模型商业化落地方案
📣朋友们如果有需要的话,可以V扫描下方二维码联系领取~