大模型微调+LORA+滑动窗口

导入库

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") 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值