1.分組
'1. 兩種訓練方法'
從零開始預訓練:BV1xhYtewEum 4分30秒
二次特定預訓練:BV1xhYtewEum
2.創建LLM
'1. 創建模型核心流程'
(1)加載模型->(2)將模型放入顯卡->(3)輸入文本并分詞->
(4)將分詞放入顯卡->(5)設置label->(6)模型輸出->
(7)獲取模型loss->(8)更新參數->(9)保存模型
'2. 創建模型核心代碼'
#加載模型
model_path = '/data04/llama3/Meta-Llama-3.1-8B-Instruct'
tokenizer = AutoTokenizer.from_pretrained(model_path)
#將模型放入顯卡
model.to("cuda")
optimizer = torch.optim.AdamW(model.parameters())
#輸入文本,文本分詞
text = "今天天氣不錯。"
input = tokenizer(text, return_tensors="pt")
input = {k: v.to("cuda") for k, v in input.items()}
#設置labels和inputs一致
input["labels"] = input["input_ids"].clone()
output = model(**input)
#獲取模型的loss
loss = output.loss
loss.backward()
optimizer.step()
optimizer.zero_grad()
#保存模型
model.save_pretrained("output_dir")
'3. 大模型計算內部Loss'
step1. LLM獲取“今天”,輸出概率最高的詞是“天氣”
step2. LLM輸出“天氣”,根據“今天”、“天氣”選擇輸出概率最高的“不錯”
step3. LLM輸出“不錯”,根據“今天”、“天氣”、“不錯”選擇輸出概率最高的“。”
step4. LLM輸出“。”,由于輸入的內容已經學習完畢,LLM便丟棄最后一個預測
'4. llama的loss計算'
#丟棄預測的最后一個token;
#logits是預測結果,形狀為 (batch_size, seq_len, classes)
#labels是真實標簽,形狀為 (batch_size, seq_len)
shift_logits = logits[...,;-1,:].contiguous()
shift_labels = labels[...,1:].contiguous()
#創建交叉熵損失函數
loss_fct = nn.CroassEntropyLoss()
#調整形狀
##logits原始形狀:(batch_size, seq_len-1, vocab_size)
##logits目標形狀:(batch_size*(seq_len-1), vocab_size)
shift_logits = shift_logits.view(-1,self.config.vocab_size)
##labels原始形狀:(batch_size, seq_len-1)
##labels目標形狀:(batch_size*(seq_len-1))
shift_labels = shift_labels.view(-1)
#確保 logits和 labels在同一設備計算
shift_labels = shift_labels.to(shift_logits.device)
#損失計算?
loss = loss_fct(shift_logits,shift_labels)
'5. 縮小顯存需求-量化加載'
# 4bit load
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
'6. 縮小顯存需求-LoRA'
# 4bit load
peft_config = LoraConfig(
r=8,
target_modules=["q_proj",
"v_proj",
"k_proj",
"o_proj",
"gate_proj",
"down_proj",
"up_proj"
],
task_type=TaskType.CAUSAL_LM,
lora_alpha=16,
lora_dropout=0.05
)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
3.訓練LLM
'1. 數據處理'
step1 將數據保存為txt文件
step2 在文件的各個節點添加<|begin_of_text|>、<|start_header_id|>、
<|end_header_id|>、<|end_header_id|>
'2. 參數初始化'
根據需要選擇“驗證策略”、“優化器”、“學習率”等參數
# 定義一個參數類
@dataclass
class CustomArguments(transformers.TrainingArguments):
# LoRA_r
lora_r: int = field(default=8)
# 數據處理時的并行進程數
num_proc: int = field(default=1)
# 使用類
parser = transformers.HfArgumentParser(CustomArguments)
training_args, = parser.parse_args_into_dataclasses()
'3. 處理輸入數據'
# 加載數據
train_dataset = load_dataset("text", data_dir="/train_data", split="train")
eval_dataset = load_dataset("text", data_dir="/eval_data", split="train")
# 定義分詞函數:對傳入的數據分詞處理
#example是數據集中單條樣本的字典,必須包含text字段
def tokenization(example):
return tokenizer(example["text"])
# 對所以樣本進行分詞
# main_process_first保證分布式訓練只在主進程處理
with training_args.main_process_first(desc="dataset map tokenization"):
train_dataset = train_dataset.map(tokenization, remove_columns=["text"], num_proc=training_args.num_proc)
eval_dataset = eval_dataset.map(tokenization, remove_columns=["text"], num_proc=training_args.num_proc)
'4. 分組函數'
# 定義分組函數
def group_texts(examples):
# 拼接所有樣本序列,首位相連
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
# 計算總長度
total_length = len(concatenated_examples[list(examples.keys())[0]])
# 確保總長度是max_seq_length的整數倍,剩余的丟棄
total_length = (total_length // training_args.max_seq_length) * training_args.max_seq_length
# 分塊處理:將連續序列按固定長度切分
result = {
k: [t[i: i + training_args.max_seq_length] for i in range(0, total_length, training_args.max_seq_length)]
for k, t in concatenated_examples.items()
}
# 標簽對齊
result["labels"] = result["input_ids"].copy()
return result
# 使用分組函數
with training_args.main_process_first(desc="dataset map tokenization"):
train_dataset = train_dataset.map(group_texts, num_proc=training_args.num_proc, batched=True)
eval_dataset = eval_dataset.map(group_texts, num_proc=training_args.num_proc, batched=True)
4.完整代碼
from accelerate import PartialState
from datasets import load_dataset
from peft import TaskType, LoraConfig, get_peft_model
from transformers import Trainer
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from dataclasses import dataclass, field
import transformers
from itertools import chain
import torch
import warnings
warnings.filterwarnings("ignore")
@dataclass
class CustomArguments(transformers.TrainingArguments):
# LoRA_r
lora_r: int = field(default=8)
# 數據處理時的并行進程數
num_proc: int = field(default=1)
# 最大序列長度
max_seq_length: int = field(default=32)
# 驗證策略,如不想進行驗證,可以設置為 ‘no’
eval_strategy: str = field(default="steps")
# 每多少步進行一次驗證
eval_steps: int = field(default=100)
# 隨機種子
seed: int = field(default=0)
# 優化器
optim: str = field(default="adamw_torch")
# 訓練epoch數
num_train_epochs: int = field(default=2)
# 每個設備上的批量大小
per_device_train_batch_size: int = field(default=1)
# 學習率
learning_rate: float = field(default=5e-5)
# 權重衰減
weight_decay: float = field(default=0)
# 預熱步數
warmup_steps: int = field(default=10)
# 學習率規劃期類型
lr_scheduler_type: str = field(default="linear")
# 是否使用梯度檢查點
gradient_checkpointing: bool = field(default=False)
# 是否使用bf16作為混合精度訓練類型
bf16: bool = field(default=True)
# 梯度累加步數
gradient_accumulation_steps: int = field(default=1)
# 日志記錄的步長頻率
logging_steps: int = field(default=3)
# checkpoint保存策略
save_strategy: str = field(default="steps")
# checkpoint保存的步長頻率
save_steps: int = field(default=3)
# 總的保存checkpoint的數量
save_total_limit: int = field(default=2)
# 使用CustomArguments
parser = transformers.HfArgumentParser(CustomArguments)
training_args, = parser.parse_args_into_dataclasses()
# 導入模型參數
model_path = '/data04/llama3/Meta-Llama-3.1-8B-Instruct'
# 4b量化加載
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_path)
# 創建模型
model = AutoModelForCausalLM.from_pretrained(
model_path,
low_cpu_mem_usage=True,
quantization_config=bnb_config,
device_map={"": PartialState().process_index}
)
# 將模型導入Lora中
peft_config = LoraConfig(
r=training_args.lora_r,
target_modules=["q_proj",
"v_proj",
"k_proj",
"o_proj",
"gate_proj",
"down_proj",
"up_proj"
],
task_type=TaskType.CAUSAL_LM,
lora_alpha=16,
lora_dropout=0.05
)
# 微調模型
model = get_peft_model(model, peft_config)
# 計算模型中的可訓練參數數量
model.print_trainable_parameters()
# 加載數據
train_dataset = load_dataset("text", data_dir="/home/xuepeng/pretrain_test/train_data", split="train")
eval_dataset = load_dataset("text", data_dir="/home/xuepeng/pretrain_test/eval_data", split="train")
# 輸入數據分詞
def tokenization(example):
return tokenizer(example["text"])
with training_args.main_process_first(desc="dataset map tokenization"):
train_dataset = train_dataset.map(tokenization, remove_columns=["text"], num_proc=training_args.num_proc)
eval_dataset = eval_dataset.map(tokenization, remove_columns=["text"], num_proc=training_args.num_proc)
# 輸入數據分組
def group_texts(examples):
# 將數據相連
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
# 計算總長度
total_length = len(concatenated_examples[list(examples.keys())[0]])
# 按定義最大長度分組,剩余token舍棄
total_length = (total_length // training_args.max_seq_length) * training_args.max_seq_length
# 分塊
result = {
k: [t[i: i + training_args.max_seq_length] for i in range(0, total_length, training_args.max_seq_length)]
for k, t in concatenated_examples.items()
}
# 標簽對齊
result["labels"] = result["input_ids"].copy()
return result
with training_args.main_process_first(desc="dataset map tokenization"):
train_dataset = train_dataset.map(group_texts, num_proc=training_args.num_proc, batched=True)
eval_dataset = eval_dataset.map(group_texts, num_proc=training_args.num_proc, batched=True)
# 利用transformers包里面的Trainer做訓練
if __name__ == '__main__':
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset
)
trainer.train()
trainer.save_model("/data04/xuepeng/test_train")
學習視頻:【大模型預訓練看這個視頻就夠了】 https://www.bilibili.com/video/BV1xhYtewEum/?share_source=copy_web&vd_source=050ab764db52d186ab224170392c4055
浙公網安備 33010602011771號