1 什么是微调:
大模型微调(Fine-tuning of large models)是一种通过对已经训练好的大规模预训练模型进行进一步训练的技术,以便让模型在特定任务或特定领域上表现得更好。微调通常是在已经有了一个通用的、经过大规模数据训练的基础模型(如GPT-4、BERT等)之后,通过少量的特定数据进一步优化模型的参数,使其在某一特定任务上达到较高的精度或性能。
微调的核心步骤:
选择预训练模型:选择一个适合任务的预训练模型。这个模型通常已经在大规模数据集(如网页、百科等)上进行了初始训练,具备了基本的语言理解和生成能力。
准备特定领域的数据集:收集与目标任务或领域相关的特定数据集,用于微调。这些数据通常比原始大规模训练数据小得多,但更加针对性。
冻结部分模型层(可选):为了避免过度训练和计算资源的浪费,有时可以冻结一些模型的层,尤其是底层的特征提取层,只对顶层进行微调。
微调过程:使用特定任务的数据集对预训练模型进行训练,这个过程通常会通过调整模型的权重来适应特定任务的数据。
评估与测试:在微调完成后,需要在验证集或测试集上评估模型的表现,以确保其在特定任务上的有效性。
微调的优势:
节省资源:相比从头训练一个新模型,微调利用了预训练模型已有的知识,减少了计算资源和时间的消耗。
领域适应性强:通过微调,模型能够更好地适应某个特定的领域或任务(如医学、法律、金融等),使其在这些领域的表现更加出色。
减少数据需求:因为预训练模型已经具备了大量通用知识,微调过程通常只需要相对较小的特定领域数据。
应用场景:
文本分类:将预训练语言模型微调用于情感分析、新闻分类等任务。
对话生成:通过微调,预训练模型可以生成符合特定领域语言风格的对话,如法律顾问、医疗咨询等。
翻译:微调模型可以提高特定语言对的翻译质量。
通过微调技术,模型能够更好地在特定领域或任务上发挥作用,并提升特定问题的解决效果。
2为什么要微调:
1. 提高模型在特定任务或领域的表现
预训练的大模型(如GPT、BERT等)通常在大规模的通用数据集上训练,因此具备广泛的语言理解能力。但这些模型在特定领域或任务上(如法律、医疗、金融等)可能并不具备足够的专业知识或针对性。通过微调,可以让模型在特定领域的数据上进一步优化,从而提高其在该领域的表现。
例如:
微调一个预训练语言模型,使其能够在医学对话场景下表现得更加专业,生成更精准的医疗建议。
2. 减少训练时间和计算资源
从头开始训练一个大模型(尤其是数十亿参数的模型)需要大量的计算资源和时间。而微调通过在预训练模型的基础上进行小范围的调整,显著减少了重新训练整个模型的时间和计算成本。
微调只需要使用相对较小的特定领域数据进行训练,不必像预训练阶段那样需要处理海量的通用数据。这样可以在保持模型原有知识的基础上,快速适应新的任务或领域。
3. 克服数据不足问题
在很多情况下,领域特定的数据往往是有限的。例如,医学、法律等专业领域的标注数据可能难以获取。预训练的大模型已经通过大量通用数据学习了语言结构和基本常识,通过微调,只需要少量领域特定数据,模型就能较好地适应特定任务。微调通过利用已有的大量预训练知识,降低了对大规模领域数据的依赖。
4. 增强领域适应性
预训练模型尽管具备广泛的语言理解能力,但未必能理解特定领域的术语、行话或特有的语境。微调可以让模型学习特定领域的数据,从而适应该领域的语言风格和表达习惯。例如:
法律文本通常用词严谨且带有固定格式,通过微调,模型可以学习这些特点,使其在处理法律文本时更加精准。
5. 避免过拟合
从头训练模型很容易导致在小数据集上的过拟合问题,即模型在训练集上表现良好,但在新数据上效果较差。微调可以利用预训练模型的泛化能力,避免从零开始训练时常见的过拟合现象,只在少量数据上针对性地优化模型。
6. 针对特定用户需求进行个性化调整
每个应用场景的需求可能不同,例如某些对话系统需要模型展现不同的语气或风格。通过微调,可以根据具体需求对模型进行个性化调整,让它更符合特定用户或场景的要求。
例如:
微调一个客服聊天机器人,使其语气更加友好并适合特定公司的客户服务风格。
7. 提高模型的稳定性和可靠性
预训练的大模型可能会在特定任务中表现出不稳定或不准确的结果。例如,在医疗或金融领域,错误的输出可能会导致严重的后果。通过微调,可以确保模型在这些高风险任务中的表现更加可靠,减少出错的可能性
3.**微调(Fine-tuning)和外挂知识库(External Knowledge Base Integration)区别
两种让模型具备特定领域或任务知识的常见方法,但它们的原理和应用方式有所不同。
1. 定义和工作原理的区别
微调(Fine-tuning): 微调是通过在预训练模型的基础上,使用特定领域的训练数据进一步训练模型,使其在该领域或任务中表现得更好。这个过程会修改模型的内部参数,优化模型在特定任务上的表现。微调后的模型已经具备了特定领域的知识,在推理时可以直接依赖这些内部知识。
工作原理:
通过额外的训练数据,逐步调整预训练模型的参数,使其能够更好地理解和生成与特定任务或领域相关的内容。
训练完成后,微调后的模型不需要访问外部资源或知识库,就可以直接生成符合特定领域的结果。
外挂知识库(External Knowledge Base Integration): 外挂知识库是指将预训练模型与一个外部的知识库结合,使模型在生成或回答问题时,能实时查询或检索该知识库中的信息。模型本身的参数不被调整,而是通过调用外挂的知识库提供的结构化信息或文档内容,来补充模型原有的知识不足。
工作原理:
模型在生成或回答问题时,通过查询或访问外部的知识库(如数据库、文档、网页等)获取最新或领域相关的知识。
这些知识库可能是动态更新的,因此能够提供比模型预训练时更新、更丰富的知识。
2. 适用场景的区别
微调 适用于模型需要内置领域知识的情况,尤其是当这些知识可以通过大量训练数据学习到的情况下。常见的应用包括情感分析、文本分类、对话生成等场景。
适合静态或相对稳定的知识需求,例如特定领域的语言风格或问题类型。
使用微调的模型在推理时可以快速响应,因为不需要访问外部数据。
外挂知识库 更适合需要动态或最新信息的场景,尤其是知识快速变化或者信息量大到难以通过模型内部学习到的情况。例如新闻搜索、实时问答系统、法律条文查询等场景。
适合频繁更新的知识或难以通过模型参数直接记忆的大规模知识库。
常见的例子包括搜索引擎支持的问答系统、结合公司内网数据库的业务咨询系统。
3. 优缺点比较
4. 结合使用的可能性
在某些复杂的应用场景中,微调和外挂知识库可以结合使用:
例如,在构建一个医学问答系统时,可以通过微调让模型具备基本的医学知识和语言能力,同时通过外挂医学数据库或文献库,使模型在回答复杂或最新的医学问题时能够实时查询和获取详细的信息。
这种结合使用的方式能够在提升领域适应性的同时,保持对动态信息的及时获取,兼具微调的高效和外挂知识库的灵活性。
总结:
微调:通过修改模型内部参数来让模型具备特定领域的知识,适合静态知识需求。
外挂知识库:通过与外部知识库集成来实时获取信息,适合动态和大规模知识需求。
4.常见问题
1. 数据量与质量问题
数据量不足:微调通常需要一定量的领域特定数据,如果数据量太小,模型可能无法有效学习,导致微调效果不佳。尤其在稀缺领域(如医学、法律等),标注数据可能非常有限。
数据质量问题:低质量的训练数据(如带有偏见、噪声或错误标注的数据)可能会影响微调效果,导致模型生成的结果不准确或带有偏见。
2. 过拟合
当微调数据集较小时,模型可能会过度拟合这些数据,导致它在特定任务上表现出色,但在实际应用或新数据上表现不佳。过拟合会使模型的泛化能力降低,难以适应稍有变化的输入。
3. 灾难性遗忘
微调过程中,模型可能会“遗忘”之前预训练中学到的通用知识,特别是在使用单一的特定领域数据进行微调时。这种现象被称为灾难性遗忘,使得模型在应对原有领域或通用任务时表现变差。
例如,微调模型在金融领域,可能导致模型丧失对非金融问题的理解能力。
4. 训练成本与资源消耗
尽管微调比从头开始训练成本低,但对于大型预训练模型(如GPT-3、BERT等),微调仍然需要大量的计算资源,尤其是在高性能计算设备不足的情况下,训练时间和资源需求可能成为瓶颈。
微调需要GPU或TPU等高性能硬件,对于小型团队或个人开发者,可能面临资源有限的问题。
5. 模型参数选择
微调过程中,需要选择合适的超参数(如学习率、batch size、微调层数等)。不当的超参数选择可能导致训练失败或效果不佳。找到合适的超参数通常需要大量实验和调试,增加了微调的复杂性。
6. 如何处理小样本场景
对于许多领域,微调数据可能非常稀缺。在这种情况下,如何在小样本数据上有效微调模型成为挑战。**少样本学习(Few-shot Learning)和零样本学习(Zero-shot Learning)**可以帮助应对这一问题,但实现这些方法的技术门槛较高。
7. 模型偏见问题
大模型在预训练阶段可能已经学到了大量的数据偏见。微调过程中,如果数据集本身带有偏见,模型可能会进一步强化这些偏见,从而导致在特定任务中表现出不公正或不准确的结果。例如,微调模型用于招聘系统,如果训练数据中存在性别或种族偏见,模型可能也会表现出类似的偏见。
8. 评估难度
微调后的模型需要通过验证集或测试集评估其表现,但在某些领域,评估标准可能不够清晰,尤其是开放式生成任务(如对话生成)。这导致很难准确评估模型的好坏,并调整微调策略。
9.知识更新困难
微调后的模型包含的是在微调数据上学到的知识。随着时间推移,这些知识可能过时,无法反映最新的领域信息。要让模型更新知识,通常需要重新收集数据并进行微调,但这会增加维护成本和复杂性。
## glm4代码微调(lora ) :
import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
import os
# 初始化分布式环境
dist.init_process_group(backend="nccl")
# 获取当前进程的本地 GPU ID
local_rank = int(os.getenv("LOCAL_RANK", 0))
torch.cuda.set_device(local_rank)
# 将JSON文件转换为Dataset对象
df = pd.read_json('./huanhuan.json')
ds = Dataset.from_pandas(df)
# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained('./glm-4-9b-chat', use_fast=False, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
def process_func(example):
MAX_LENGTH = 384
instruction = tokenizer((f"[gMASK]<sop><|system|>\n假设你是皇帝身边的女人--甄嬛。<|user|>\n"
f"{example['instruction']+example['input']}<|assistant|>\n"
).strip(),
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
}
# 预处理数据
tokenized_id = ds.map(process_func, remove_columns=ds.column_names)
# 加载模型并移动到当前设备
model = AutoModelForCausalLM.from_pretrained('./glm-4-9b-chat', torch_dtype=torch.bfloat16, trust_remote_code=True)
model.enable_input_require_grads()
model = model.to(local_rank)
# 使用 DistributedDataParallel 包装模型
model = DDP(model, device_ids=[local_rank]).module
# LoRA 配置
from peft import LoraConfig, TaskType, get_peft_model
config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
target_modules=["query_key_value", "dense", "dense_h_to_4h", "dense_4h_to_h"],
inference_mode=False,
r=8,
lora_alpha=32,
lora_dropout=0.1
)
model = get_peft_model(model, config)
# 训练参数
args = TrainingArguments(
output_dir="./output/GLM4",
per_device_train_batch_size=1, # 继续使用最小 batch size
gradient_accumulation_steps=16, # 增加梯度累积
logging_steps=50,
num_train_epochs=2,
save_steps=100,
learning_rate=1e-5,
save_on_each_node=True,
gradient_checkpointing=True, # 保留梯度检查点,减少显存占用
fp16=True, # 启用混合精度
local_rank=local_rank
)
trainer = Trainer(
model=model,
args=args,
train_dataset=tokenized_id,
data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)
# 开始训练
trainer.train()
# 结束分布式进程
dist.destroy_process_group()
lora_path='./GLM4'
trainer.model.save_pretrained(lora_path)
tokenizer.save_pretrained(lora_path)
数据集地址:https://modelscope.cn/datasets/chenkangck50/huanhuan.json/resolve/master/huanhuan.json
gpu占用:
生成的过程文件:
训练文件:
测试:
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel
mode_path = './glm-4-9b-chat'
lora_path = './GLM4'
# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path, trust_remote_code=True)
# 加载模型
model = AutoModelForCausalLM.from_pretrained(mode_path, device_map="auto",torch_dtype=torch.bfloat16, trust_remote_code=True).eval()
print(model.base_model) # 确认微调权重是否已正确应用
# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)
prompt = ""
inputs = tokenizer.apply_chat_template([{"role": "system", "content": "朕现在有事。"},{"role": "user", "content": prompt}],
add_generation_prompt=True,
tokenize=True,
return_tensors="pt",
return_dict=True
).to('cuda')
gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1}
with torch.no_grad():
outputs = model.generate(**inputs, **gen_kwargs)
outputs = outputs[:, inputs['input_ids'].shape[1]:]
print(tokenizer.decode(outputs[0], skip_special_tokens=True))