iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0

  昨天 Day19 我們用 Transformers + DeepSpeed 完成一個模型的預訓練,接下來要讓模型理解自然語言,所以需要SFT。

Pretrian 與 SFT 的差異

我們先複習一下之前有介紹過的,大型語言模型(LLM)的三個階段訓練流程

  • Pretrain(預訓練):
      使用海量的無標註文本,進行自監督的 Causal Language Modeling(CLM),即用於訓練預測下一個 token,模型在此階段學習語言結構、語義關係與世界知識。

  • SFT(有監督微調):
      使用「指令:回應」成對的資料進行訓練,讓模型能夠依據人類指令進行規劃與輸出,核心任務仍為 CLM,但 Loss 僅對回應(Assistant 輸出)計算,不計算指令部分。

因此,SFT 與 Pretrain 的主要區別不在於建模方式,而在於 資料格式與 Loss 計算區域。

Chat Template:如何把對話轉換成訓練樣本?

  要做 SFT,我們需要設計一個 Chat Template,把多輪對話拼成一個文字序列,例如在 Qwen 模型中,對話會被標註成三種角色:

  • System:模型的初始提示(例如 "You are a helpful assistant.")
  • User (human):使用者輸入的問題 / 指令
  • Assistant:模型要生成的回覆

Chat Template 範例:

<|im_start|>system
You are a helpful assistant.<|im_end|>

<|im_start|>human
請翻譯:今天天氣真好<|im_end|>

<|im_start|>assistant
The weather is nice today.<|im_end|>

在 SFT 中:

  • User 部分:不需要計算 loss,模型不用學會「我們怎麼提問」。
  • Assistant 部分:需要計算 loss,模型要學會正確回答。

資料處理流程

我們用 BelleGroup 指令數據集來做範例

定義特殊 Token

# BOS / EOS / PAD
im_start = tokenizer("<|im_start|>").input_ids
im_end = tokenizer("<|im_end|>").input_ids
IGNORE_TOKEN_ID = -100   # 不計算 loss 的 token

# 角色標籤
_system = tokenizer("system").input_ids
_user = tokenizer("human").input_ids
_assistant = tokenizer("assistant").input_ids

拼接多回合對話

def preprocess(sources, tokenizer, max_len=2048):
    input_ids, targets, attention_masks = [], [], []

    for source in sources:  # 每個 source 是一段多回合對話
        # system prompt
        system = im_start + _system + tokenizer("You are a helpful assistant.").input_ids + im_end
        input_id = system
        target   = [IGNORE_TOKEN_ID] * len(system)

        for sentence in source:
            role = sentence["from"]  # "human" 或 "assistant"
            content = tokenizer(sentence["value"]).input_ids + im_end

            if role == "human":
                # User → 不計算 loss
                input_id += _user + content
                target   += [IGNORE_TOKEN_ID] * (len(_user) + len(content))
            elif role == "assistant":
                # Assistant → 要學習
                input_id += _assistant + content
                target   += [IGNORE_TOKEN_ID] * len(_assistant) + content

        # padding & mask
        input_id = input_id[:max_len]
        target   = target[:max_len]

        attention_mask = [1 if t != tokenizer.pad_token_id else 0 for t in input_id]

        input_ids.append(input_id)
        targets.append(target)
        attention_masks.append(attention_mask)

    return {
        "input_ids": torch.tensor(input_ids),
        "labels": torch.tensor(targets),
        "attention_mask": torch.tensor(attention_mask)
    }

包裝成 Dataset

from torch.utils.data import Dataset

class SupervisedDataset(Dataset):
    def __init__(self, raw_data, tokenizer, max_len=2048):
        self.data = preprocess(raw_data, tokenizer, max_len)

    def __len__(self):
        return len(self.data["input_ids"])

    def __getitem__(self, i):
        return dict(
            input_ids=self.data["input_ids"][i],
            labels=self.data["labels"][i],
            attention_mask=self.data["attention_mask"][i],
        )

這樣 SFT 的 Dataset 就準備好了!

使用 Trainer 開始 SFT

接下來的步驟幾乎和 Pretrain 一模一樣!

from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
import torch, json

# 1. 載入模型與 tokenizer
model_name = "Qwen/Qwen1.5-1.5B"
model = AutoModelForCausalLM.from_pretrained(model_name, trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)

# 2. 載入資料
with open("belle_train.json", "r", encoding="utf-8") as f:
    raw_data = [json.loads(line) for line in f]

train_dataset = SupervisedDataset(raw_data, tokenizer)

# 3. 訓練設定
training_args = TrainingArguments(
    output_dir="./sft-output",
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    num_train_epochs=1,
    logging_steps=10,
    save_steps=500,
    fp16=True,
)

# 4. 建立 Trainer 並開始訓練
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    tokenizer=tokenizer
)

trainer.train()

參考連結:
https://datawhalechina.github.io/happy-llm/#/


上一篇
[Day19] 用 Transformers 實現 LLM 的預訓練!
系列文
從上下文工程到 Agent:30 天生成式 AI 與 LLM 學習紀錄20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言