初次使用transformers進行大模型微調
環境:
電腦配置:
筆記本電腦:I5(6核12線程) + 16G + RTX3070(8G顯存)
需要自行解決科學上網
Python環境:
python版本:3.8.8
大模型:microsoft/DialoGPT-medium
(微軟的對話大模型,模型小,筆記本也能學習微調)
數據集:daily_dialog
(日常對話數據集)
其他:
模型及數據集:使用來源于抱抱臉
微調大模型
準備工作:
下載模型:
找到自己想要的模型:
-
打開抱抱臉官網——點擊Model:
-
輸入要搜索的模型(這里以DialoGPT-medium為例):
-
復制名稱到代碼中替換要下載的模型名稱:
模型下載:
import os
from transformers import AutoModel, AutoTokenizer# 因為使用了科學上網,需要進行處理
os.environ["HTTP_PROXY"] = "http://127.0.0.1:xxxx"
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:xxxx"if __name__ == '__main__':# model_name = 'google-t5/t5-small' # 要下載的模型名稱model_name = 'microsoft/DialoGPT-medium' # 要下載的模型名稱 需要到抱抱臉進行復制cache_dir = r'xxxx' # 模型保存位置# 加載模型時指定下載路徑model = AutoModel.from_pretrained(model_name, cache_dir=cache_dir)
下載數據集:
找到自己想要的模型:
- 打開抱抱臉官網——點擊Datasets:
- 輸入要搜索的內容,點擊對應數據集進入:
- 找到適合用的模型后,點擊復制
開始微調訓練
代碼示例:
# 系統模塊
import os# 第三方庫
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer
from datasets import load_dataset# 設置代理(注意:可能需要根據實際網絡環境調整或移除)
os.environ["HTTP_PROXY"] = "http://127.0.0.1:xxxx" # HTTP代理設置
os.environ["HTTPS_PROXY"] = "http://127.0.0.1:xxxx" # HTTPS代理設置if __name__ == '__main__':# 數據準備階段 --------------------------------------------------------------# 加載完整數據集(daily_dialog包含日常對話數據集)full_dataset = load_dataset("daily_dialog", trust_remote_code=True)# 創建子數據集(僅使用訓練集前500條樣本,用于快速實驗)dataset = {"train": full_dataset["train"].select(range(500)) # select保持數據集結構}# 模型加載階段 --------------------------------------------------------------# 模型配置參數model_name = "microsoft/DialoGPT-medium" # 使用微軟的對話生成預訓練模型cache_dir = r'xxx' # 本地模型緩存路徑# 加載分詞器(重要:設置填充token與EOS token一致)tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir=cache_dir)tokenizer.pad_token = tokenizer.eos_token # 將填充token設置為與EOS相同# 加載預訓練模型(使用因果語言模型結構)model = AutoModelForCausalLM.from_pretrained(model_name, cache_dir=cache_dir)# 數據預處理階段 ------------------------------------------------------------def tokenize_function(examples):"""將對話數據轉換為模型輸入格式的預處理函數"""# 將多輪對話用EOS token連接,并在結尾添加EOSdialogues = [tokenizer.eos_token.join(dialog) + tokenizer.eos_tokenfor dialog in examples["dialog"]]# 對文本進行分詞處理tokenized = tokenizer(dialogues,truncation=True, # 啟用截斷max_length=512, # 最大序列長度padding="max_length" # 填充到最大長度(靜態填充))# 創建標簽(對于因果語言模型,標簽與輸入相同)tokenized["labels"] = tokenized["input_ids"].copy()return tokenized# 應用預處理(保留數據集結構)tokenized_dataset = {"train": dataset["train"].map(tokenize_function,batched=True, # 批量處理提升效率batch_size=50, # 每批處理50個樣本remove_columns=["dialog", "act", "emotion"] # 移除原始文本列)}# 數據驗證(檢查預處理結果)print("Sample keys:", tokenized_dataset["train"][0].keys()) # 應包含input_ids, attention_mask, labelsprint("Input IDs:", tokenized_dataset["train"][0]["input_ids"][:5]) # 檢查前5個token# 訓練配置階段 --------------------------------------------------------------training_args = TrainingArguments(output_dir="./dialo_finetuned", # 輸出目錄per_device_train_batch_size=2, # 每個設備的批次大小(根據顯存調整)gradient_accumulation_steps=8, # 梯度累積步數(模擬更大batch size)learning_rate=1e-5, # 初始學習率(可調超參數)num_train_epochs=3, # 訓練輪次(根據需求調整)fp16=True, # 啟用混合精度訓練(需要GPU支持)logging_steps=10, # 每10步記錄日志# 可添加的優化參數:# evaluation_strategy="steps", # 添加驗證策略# save_strategy="epoch", # 保存策略# warmup_steps=100, # 學習率預熱步數)# 創建訓練器trainer = Trainer(model=model,args=training_args,train_dataset=tokenized_dataset["train"], # 訓練數據集# 可擴展功能:# eval_dataset=tokenized_dataset["validation"], # 添加驗證集# data_collator=..., # 自定義數據整理器# compute_metrics=..., # 添加評估指標)# 訓練執行階段 --------------------------------------------------------------trainer.train() # 啟動訓練# 模型保存階段 --------------------------------------------------------------model.save_pretrained("./dialo_finetuned") # 保存模型權重tokenizer.save_pretrained("./dialo_finetuned") # 保存分詞器# 推薦使用以下方式統一保存:trainer.save_model("./dialo_finetuned") # 官方推薦保存方式
微調后使用
代碼:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
from transformers import TextStreamer
from collections import deque
import torchdef optimized_generation(text, tokenizer, model):inputs = tokenizer(text, return_tensors="pt").to(model.device)outputs = model.generate(**inputs,max_new_tokens=150,temperature=0.9, # 越高越有創意 (0-1)top_k=50, # 限制候選詞數量top_p=0.95, # 核采樣閾值repetition_penalty=1.2, # 抑制重復num_beams=3, # 束搜索寬度early_stopping=True,do_sample=True)return tokenizer.decode(outputs[0], skip_special_tokens=True)# 單輪對話
def simple_chat(model_path, text, max_length=100):"""單輪對話:param text::param max_length::return:"""# 加載模型和分詞器tokenizer = AutoTokenizer.from_pretrained(model_path)model = AutoModelForCausalLM.from_pretrained(model_path)# 確保pad_token設置正確tokenizer.pad_token = tokenizer.eos_token# inputs = tokenizer(text + tokenizer.eos_token, return_tensors="pt")# outputs = model.generate(# inputs.input_ids,# max_length=max_length,# pad_token_id=tokenizer.eos_token_id,# temperature=0.7,# do_sample=True# )# response = tokenizer.decode(outputs[0], skip_special_tokens=True)response = optimized_generation(text + tokenizer.eos_token, tokenizer, model)return response[len(text):] # 去除輸入文本# 多輪對話
class DialogueBot:def __init__(self, model_path, max_history=3):self.tokenizer = AutoTokenizer.from_pretrained(model_path)self.model = AutoModelForCausalLM.from_pretrained(model_path).to("cuda")self.max_history = max_historyself.history = deque(maxlen=max_history * 2) # 每輪包含用戶和機器人各一條# 確保pad_token設置if self.tokenizer.pad_token is None:self.tokenizer.pad_token = self.tokenizer.eos_tokendef generate_response(self, user_input):# 添加用戶輸入(帶EOS)self.history.append(f"User: {user_input}{self.tokenizer.eos_token}")# 構建prompt并編碼prompt = self._build_prompt()inputs = self.tokenizer(prompt,return_tensors="pt",max_length=512,truncation=True).to(self.model.device)# 流式輸出# streamer = TextStreamer(self.tokenizer)# 生成回復outputs = self.model.generate(inputs.input_ids,attention_mask=inputs.attention_mask,max_new_tokens=150,temperature=0.85,top_p=0.95,eos_token_id=self.tokenizer.eos_token_id,pad_token_id=self.tokenizer.eos_token_id,do_sample=True,# streamer=streamer,early_stopping=True)# 解碼并處理回復full_response = self.tokenizer.decode(outputs[0][inputs.input_ids.shape[-1]:],skip_special_tokens=True)# 清理無效內容(按第一個EOS截斷)clean_response = full_response.split(self.tokenizer.eos_token)[0].strip()# 添加機器人回復到歷史(帶EOS)self.history.append(f"Bot: {clean_response}{self.tokenizer.eos_token}")return clean_responsedef _build_prompt(self):return "".join(self.history)if __name__ == '__main__':# 指定模型路徑model_path = "./dialo_finetuned"# 測試單輪對話print(simple_chat(model_path, "Hello, how are you?"))# 使用示例 多輪對話# bot = DialogueBot(model_path)# while True:# user_input = input("You: ")# if user_input.lower() == "exit":# break# print("Bot:", bot.generate_response(user_input))