一直以来更加把精力放在AI的应用上,但是随着时间的推移发现模型微调也是AI应用一个无法迈过的坎,学习的路还很长,写文章也是督促自己继续下去的动力
环境安装
首先我们要做的就是微调模型的环境安装,这里我列出一下我使用的版本
CUDA版本
pytorch版本
依赖包
ini
pip install modelscope==1.16.1
pip install transformers==4.43.1
pip install accelerate==0.32.1
pip install peft==0.11.1
pip install datasets==2.20.0
如果发现下载缓慢可以更换pip的仓库源
arduino
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
数据准备
这里我们可以从魔塔中寻找自己感兴趣的训数据集

李火旺的数据集一下就进入我是视野,这里有意思的数据集好像也不多
寻找数据集时需要注意找我们的训练方式可以直接使用的数据集,我这边使用的训练方式是SFT,火子哥这个数据集是DPO方式训练的,我不能直接使用,但是找到了另一个甄嬛的数据集,也是挺有意思的
数据集的数据结构
这里只介绍两种数据集的结构,下面这种我们决定使用的数据集就是甄嬛传的数据集
SFT(监督式微调,Supervised Fine-Tuning)
- instruction 提示语/用户输入/上下文
- input 可选补充信息,如果为空表示没有附加输入
- output 模型应该生成的答案(监督信号)
DPO 训练是一种"偏好对比学习"
- prompt 用户输入,给模型看的提示语
- chosen 一个好的(偏好更高的)模型回答
- rejected 一个差的、被拒绝的回答
这是chatgpt给出的区别解释
项目 | SFT(有监督微调) | DPO(直接偏好优化) |
---|---|---|
🔧 训练目标 | 学会输出"标准答案" | 学会偏好"更好"的回答 |
📊 数据需求 | 单条 input + output | 多条 input + (chosen, rejected) 对比数据 |
📦 数据难度 | 易收集(只需一个好答案) | 难收集(要构造两个回答并打分优劣) |
🧠 模型训练方式 | 监督学习 | 近似偏好学习(强化学习的简化版) |
💬 学到的能力 | 更加规范、模板化 | 更符合人类偏好,更自然、人性化 |
🚧 训练稳定性 | 高,收敛快 | 依赖对比样本质量,调参更敏感 |
⚠️ 错误传播风险 | 输出错误直接作为学习目标,会放大数据缺陷 | 错误样本影响小,因训练的是倾向 |
✅ 模型偏好优化 | 弱 | 强,适用于对齐阶段(如RLHF) |
🤖 生成多样性 | 低,偏模板化 | 高,鼓励多样表达 |
训练代码
引入必要依赖
javascript
from datasets import Dataset
import pandas as pd
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
from peft import LoraConfig, TaskType, get_peft_model
- datasets.Dataset 用于构造训练数据集
- pandas 用于读取和处理 json 数据
- transformers 加载预训练模型、分词器、训练参数等
- peft 进行 LoRA 低秩适配训练
数据处理函数
ini
def process_func(example):
MAX_LENGTH = 384 # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
input_ids, attention_mask, labels = [], [], []
instruction = tokenizer(f"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nCutting Knowledge Date: December 2023\nToday Date: 26 Jul 2024\n\n现在你要扮演皇帝身边的女人--甄嬛<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n{example['instruction'] + example['input']}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n", add_special_tokens=False) # add_special_tokens 不在开头加 special_tokens
response = tokenizer(f"{example['output']}<|eot_id|>", 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] # 因为eos token咱们也是要关注的所以 补充为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
}
训练代码
ini
if __name__ == "__main__":
model = AutoModelForCausalLM.from_pretrained('./LLM-Research/Meta-Llama-3___1-8B-Instruct', device_map="auto",torch_dtype=torch.bfloat16)
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
tokenizer = AutoTokenizer.from_pretrained('./LLM-Research/Meta-Llama-3___1-8B-Instruct', use_fast=False, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
# 将JSON文件转换为CSV文件
df = pd.read_json('huanhuan.json')
ds = Dataset.from_pandas(df)
tokenized_id = ds.map(process_func, remove_columns=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)
model.print_trainable_parameters() # 打印总训练参数
args = TrainingArguments(
output_dir="./output/llama3_1_instruct_lora",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
logging_steps=10,
num_train_epochs=3,
save_steps=100,
learning_rate=1e-4,
save_on_each_node=True,
gradient_checkpointing=True
)
trainer = Trainer(
model=model,
args=args,
train_dataset=tokenized_id,
data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)
trainer.train() # 开始训练
训练代码的补充解释
加载模型和分词器
ini
model = AutoModelForCausalLM.from_pretrained(...)
tokenizer = AutoTokenizer.from_pretrained(...)
读取数据集并转换格式
ini
df = pd.read_json('huanhuan.json')
ds = Dataset.from_pandas(df)
tokenized_id = ds.map(process_func, remove_columns=ds.column_names)
LORA配置并应用到模型
ini
config = LoraConfig(...)
model = get_peft_model(model, config)
训练参数配置
ini
args = TrainingArguments(...)
创建 Trainer 并开始训练
scss
trainer = Trainer(...)
trainer.train()
模型选择
模型我们选择一个8B的小模型,也是在modelscope下载就可以
Meta-Llama-3-8B-Instruct
可以使用下面的代码下载
java
import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
model_dir = snapshot_download('LLM-Research/Meta-Llama-3-8B-Instruct', cache_dir='/root/autodl-tmp', revision='master')
下载时间有点长,可能要耐心等待一下

耗时2个小时左右,漫长的等待,都是钱啊

开始训练
我设置了进行3轮训练
整个流程耗时20分钟左右,最大的耗时还是模型下载
微调后对话

就写到这里,后续有什么新的内容再分享吧,微调学习才刚起步还需要多多实践,还有数据集制作等很多内容要学习