导入库
import json
import os
# 指定使用的GPU设备
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1,3,"
from datasets import Dataset, DatasetDict
import torch
# ---- Tokenize处理 ----
from transformers import AutoTokenizer, TrainingArguments, AutoModelForCausalLM ,BitsAndBytesConfig
# ---- 第二部分:模型加载与LoRA配置 ----
# ---- 第三部分:训练配置 ----
from trl import SFTTrainer
from peft import LoraConfig, get_peft_model, AutoPeftModelForCausalLM
加载数据
file_path = "/idas/users/licong/deepseek/data.json"
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
train_data = data.get("train", [])
# 转换为 Dataset 对象
train_dataset = Dataset.from_list(train_data)
train_dataset[0]
{‘text’: ‘学习强国>>党史研究遵义会议与长征的胜利2025-01-15来源:《百年潮》2025年第1期作者:高中华播报普通话在中国工农红军第五次反“围剿”失败和长征初期严重受挫的历史紧要关头,中共中央在贵州遵义召开了政治局扩大会议。’,
‘metadata’: {‘category’: ‘遵义会议与长征的胜利’}}
数据预处理
if "text" in train_dataset.column_names:
# 对 text 列进行映射操作
train_dataset = train_dataset.map(
lambda examples: {"processed_text": [t + "<|endoftext|>" for t in examples["text"]]},
batched=True,
num_proc=1 # 使用全部 CPU 核心加速处理
)
# 如果需要,将处理后的数据集放回 DatasetDict 中
dataset = DatasetDict()
dataset["train"] = train_dataset
else:
print("列 'text' 不存在于数据集中")
print("Before tokenization:", dataset)
Map: 100%|██████████| 30/30 [00:00<00:00, 3475.37 examples/s]
Before tokenization: DatasetDict({
train: Dataset({
features: [‘text’, ‘metadata’, ‘processed_text’],
num_rows: 30
})})
dataset
DatasetDict({
train: Dataset({
features: [‘text’, ‘metadata’, ‘processed_text’],
num_rows: 30
})
})
加载分词器
tokenizer = AutoTokenizer.from_pretrained(
"/idas/users/licong/deepseek7b/",
use_fast=True,
pad_token="<|endoftext|>",
)
数据预处理tokenize,包括使用滑动窗口处理过长文本,避免truncation
def split_and_tokenize(text, tokenizer, max_seq_length, split_stride):
tokens = tokenizer(text, add_special_tokens=False, return_tensors="pt")["input_ids"].squeeze().tolist()
if not isinstance(tokens, list):
tokens = [tokens]
token_chunks = []
for i in range(0, len(tokens), split_stride):
chunk = tokens[i:i + max_seq_length]
# 强制填充每个分块到 max_seq_length
padding_length = max_seq_length - len(chunk)
input_ids = chunk + [tokenizer.pad_token_id] * padding_length
attention_mask = [1] * len(chunk) + [0] * padding_length
token_chunks.append({
"input_ids": input_ids,
"attention_mask": attention_mask
})
return token_chunks
def tokenize_function(examples):
all_input_ids = []
all_attention_masks = []
for text in examples["processed_text"]:
tokenized = split_and_tokenize(
text,
tokenizer,
max_seq_length=max_seq_length,
split_stride=split_stride
)
# 遍历分词后的结果,将 input_ids 和 attention_mask 分别添加到对应的列表中
for item in tokenized:
all_input_ids.append(item["input_ids"])
all_attention_masks.append(item["attention_mask"])
return {
"input_ids": all_input_ids,
"attention_mask": all_attention_masks
}
# 参数设置
max_seq_length = 512
split_stride = 256 # 滑动窗口步长
# 对数据集应用分词函数
dataset = dataset.map(
tokenize_function,
batched=True,
num_proc=4,
remove_columns=dataset["train"].column_names, # 移除原始文本列
batch_size=2 # 根据内存调整批大小
)
dataset
DatasetDict({
train: Dataset({
features: [‘input_ids’, ‘attention_mask’],
num_rows: 296
})
})
加载模型以及设置量化参数
model = AutoModelForCausalLM.from_pretrained(
"/idas/users/licong/deepseek7b/",
device_map="auto", # 自动分配模型到 GPU
torch_dtype=torch.float16, # 使用浮点16位以减少显存占用
)
quantization_config = BitsAndBytesConfig(
load_in_8bit=True, # 或者 load_in_8bit=True
# 其他量化配置参数
)
使用LORA微调大模型
# 配置 LoRA
config = LoraConfig(
r=8, # LoRA 秩
lora_alpha=16, # LoRA 缩放因子
target_modules=["q_proj", "v_proj"], # 要应用 LoRA 的目标模块
lora_dropout=0.1, # LoRA 丢弃率
bias="none", # 不调整偏置
task_type="CAUSAL_LM" # 任务类型
)
加载模型
model = get_peft_model(model, config)
model.print_trainable_parameters()
配置训练器
trainer = SFTTrainer(
model=model,
train_dataset=dataset["train"],
dataset_text_field='processed_text', # 与预处理后的字段名一致
max_seq_length=max_seq_length,
tokenizer=tokenizer,
args=TrainingArguments(
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
warmup_ratio=0.1,
num_train_epochs=1,
learning_rate=2e-5,
fp16=not torch.cuda.is_bf16_supported(),
bf16=torch.cuda.is_bf16_supported(),
logging_steps=10,
optim="adamw_8bit",
weight_decay=0.01,
lr_scheduler_type="cosine",
output_dir="./outputs",
report_to="none",
save_strategy="epoch",
save_steps=10, # 每10个epoch保存一次
),
packing=False, # 关闭自动打包(适用于完整文本训练)
)
开始训练
trainer.train()
model.save_pretrained("./lora_adapter")
合并LORA参数到原模型
merged_model = AutoPeftModelForCausalLM.from_pretrained(
"./lora_adapter",
device_map="auto",
torch_dtype=torch.float16
)
merged_model = merged_model.merge_and_unload()
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
print("训练完成!模型已保存至./merged_model")