成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

大模型預訓練代碼實戰教程

發布于 2025-6-6 07:05
瀏覽
0收藏

任務介紹

本文使用一個簡單的數據集,展示大模型預訓練與有監督微調過程。無論是大模型的預訓練還是有監督微調,其損失值的計算過程都是與下一個要預測的詞計算損失。

預訓練損失值的計算,即從第一個字開始每個字都與下一個字計算損失;

有監督微調與預訓練唯一不同的點,便是不對指令與用戶的輸入文本計算損失,實際操作就是把用戶輸入文本在訓練過程中遮罩掉,把對應的 label 的值設置為-100。這是因為不希望大模型學會,如何生成的用戶的問題。

當前文章介紹預訓練,下篇文章介紹有監督微調

本文不使用 llamafactory 等,大模型微調工具,上述工具把大模型微調的過程都封裝到底層了。只使用 transformers庫的AutoTrain實現大模型的微調。

開源地址:

??https://github.com/JieShenAI/csdn/tree/main/25/02/pre_train??

原始數據集

將使用下述5條數據微調大模型,對比一下,預訓練與有監督微調的區別。

[
  {
    "instruct": "請你給哪吒寫一首詩:",
    "input": "哪吒降世,意氣飛揚。\n逆天改命,破障沖霄。",
    "label": "紅綾纏腕,風火踏浪。\n不屈不悔,笑傲蒼茫。"
  },
  {
    "instruct": "請你給敖丙寫一首詩:",
    "input": "碧海生龍子,云中舞雪霜。",
    "label": "恩仇難兩忘,何處是家鄉?"
  },
  {
    "instruct": "請你給殷夫人寫一首詩:",
    "input": "十月懷胎盼子生,柔心鐵骨兩相承。",
    "label": "甘將慈愛護天地,不懼風雷不懼征。"
  },
  {
    "instruct": "請你給太乙真人寫一首詩:",
    "input": "仙風道骨,騎獸遨游。",
    "label": "爐中煉術,指點神童。"
  },
  {
    "instruct": "請你給申公豹寫一首詩:",
    "input": "陰謀藏心,步步為營。\n狂傲不羈,志向高冥。",
    "label": "欲翻天命,終難遂行。\n困局自招,悔恨難平。"
  }
]

下述是標準的有監督微調的數據格式,使用 ??apply_chat_template?? 方法,告知模型哪些是系統提示詞、用戶問題、模型的回答。

d = {
    "instruct": "請你給哪吒寫一首詩:",
    "input": "哪吒降世,意氣飛揚。\n逆天改命,破障沖霄。",
    "label": "紅綾纏腕,風火踏浪。\n不屈不悔,笑傲蒼茫。",
}
messages = [
    {
        "role": "system",
        "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant.",
    },
    {
        "role": "user",
        "content": d["instruct"] + d["input"],
    },
    {
        "role": "assistant",
        "content": d["label"],
    },
]
text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    # add_generation_prompt=True
)
print(text)

輸出:

<|im_start|>system
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.<|im_end|>
<|im_start|>user
請你給哪吒寫一首詩:哪吒降世,意氣飛揚。
逆天改命,破障沖霄。<|im_end|>
<|im_start|>assistant
紅綾纏腕,風火踏浪。
不屈不悔,笑傲蒼茫。<|im_end|>

上述是數據 template的構造,每個大模型的template不一樣,但很多大模型微調工具(llamafactory等)都會自動構造template,無需太擔心。

本文是大模型預訓練與有監督微調的手搓簡化版本,數據構造不使用template,設置預訓練和有監督微調的輸入文本一樣,都是把 ??instruct + input + label?? 拼接起來,在結尾添加一個結束符號。

instruct + input + label + tokenizer.eos_token

在結尾需要添加 ??tokenizer.eos_token?? 停止符號,這是為了讓大模型學會停止文本生成。不然在大模型推理的時候,大模型就會一直往后生成文本,直到達到模型最大的生成的長度才會停止。

預訓練代碼實戰

from typing import List, Dict, Sequence
import torch
import transformers
from transformers import TrainingArguments, Trainer
from torch.utils.data import Dataset
from dataclasses import dataclass

IGNORE_INDEX = -100
device = "cuda:0" if torch.cuda.is_available() else "cpu"

??IGNORE_INDEX??? -100, 在 ??label?? 中被標注為-100表示不參與 loss 計算。

from transformers import AutoModelForCausalLM, AutoTokenizer

model_dir = r"Qwen/Qwen2.5-0.5B"

model = AutoModelForCausalLM.from_pretrained(model_dir)
model = model.to("cuda:0")

tokenizer = AutoTokenizer.from_pretrained(model_dir, padding_side="right")

大模型預訓練代碼實戰教程-AI.x社區

據上圖所示,發現 Qwen 模型 文本填充與文本結束符 是同一個符號。這給后續計算文本停止符號的 loss計算 帶來了麻煩。

這里的討論可以忽略,如果想加深對 填充符號、文本停止符號、generate停止符的理解,可以閱讀下述文本:

如果 文本填充與文本結束符 是同一個符號,那么在 label 中,就不能把全部的填充符號都設置為-100,因為模型的填充符號與文本生成的停止符號是同一個字符。如果全部設置為-100,都不計算 loss,會導致模型學不會生成文本結束符號。當然也可以選擇對所有的文本填充符號都計算 loss,這會導致模型學會在生成填充符號之后,下一個字符繼續生成填充符號。

踩坑經歷:我曾經在微調模型的時候,遇到一種情況,大模型在經過微調后,文本生成結束了還在一直輸出??[PAD]???符號。這個原因就是沒有把填充符號??[PAD]???的 label 設置為-100,導致大模型學會了在遇到[PAD]之后,下一個詞依然輸出[PAD]。同時也沒有把??[PAD]??,作為停止符號,添加到generate方法的停止詞中,這才導致了一直生成[PAD]的情況出現。

總而言之,Qwen的填充符與停止符是同一個符號沒有問題。在模型調用generate方法生成文本時,雖然模型會一直生成填充符號,但是填充符號同時也是停止符號,模型也會停止文本生成。

由于本文不使用框架訓練模型,可以更自由一點,故自定義填充符為??[PAD]??:

tokenizer.add_special_tokens({
    "pad_token": "[PAD]"
})

tokenizer.pad_token, tokenizer.pad_token_id

輸出:

('[PAD]', 151665)

自定義數據集 

class PreTrainDataset(Dataset):

    def __init__(self, data: List):
        super().__init__()
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx) -> List[Dict]:
        item = self.data[idx]
        text = item["instruct"] + item["input"] + item["label"] + tokenizer.eos_token
        return text

dataset = PreTrainDataset(data)
dataset[0]

輸出:

'請你給哪吒寫一首詩:哪吒降世,意氣飛揚。\n逆天改命,破障沖霄。紅綾纏腕,風火踏浪。\n不屈不悔,笑傲蒼茫。<|endoftext|>'

很多人都喜歡在自定義數據集里面完成 tokenizer,但我把這個操作留到了 ??DataCollator?? 中。

  • 如果在數據集中完成tokenizer,那么就需要在 ??DataCollator??? 對 ??input_ids??? 和 ??attention_mask?? 進行手動填充。
  • 如果在 ??DataCollator??? 完成 tokenizer,便無需再對 ??input_ids??? 和 ??attention_mask?? 手動填充。tokenizer 會默認把這個batch的數據處理完成。只需要手動處理 label。

@dataclass
class DataCollatorForPretrainDataset(object):
    tokenizer: transformers.PreTrainedTokenizer

    def __call__(self, items: Sequence[Dict]) -> Dict[str, torch.Tensor]:

        prompt = [item for item in items]

        prompt_tokenizer = tokenizer(
            prompt,
            return_tensors="pt",
            padding="longest",
            max_length=tokenizer.model_max_length,
            truncatinotallow=True,
        )

        labels = prompt_tokenizer["input_ids"].clone()

        # 不對 pad 計算 loss
        pad_idx = labels.eq(tokenizer.pad_token_id)
        labels[pad_idx] = -100

        prompt_tokenizer["labels"] = labels
        return prompt_tokenizer
  • ??padding="longest"?? 把數據填充到這個 batch中數據的最大長度;
  • ??max_length=tokenizer.model_max_length?? 最大長度是 tokenizer中模型是最大長度

大模型預訓練的 ??label??很簡單,就是input_ids,做一個復制操作就行。

在進行模型訓練之前,測試一下, DataCollatorForPretrainDataset 處理數據:

tokenizer.eos_token_id, tokenizer.pad_token_id,

輸出:

(151643, 151665)

data_collator = DataCollatorForPretrainDataset(tokenizer=tokenizer)
prompt_tokenizer = data_collator([dataset[0], dataset[1]])
prompt_tokenizer

輸出:

{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
'input_ids': tensor([[112720,  89012,  99459, 122157,  61443, 108462, 100045,   5122,  99459,
         122157,  99457,  99244,   3837,  36589,  99180, 115449,   8997, 100531,
          35727,  22418,  50509,   3837,  99577,  99884,  99907, 109564,   1773,
          99425, 120827, 103073, 103610,   3837,  99208,  79599, 100875,  99964,
           8997,  16530, 102683,  16530, 103020,   3837,  48738, 102744, 102635,
         100619,   1773, 151643],
        [112720,  89012, 113735, 106980,  61443, 108462, 100045,   5122, 102461,
          55135,  21287,  99465,  44729,   3837,  99718,  15946, 100066, 100167,
         105401,   1773, 100697, 100956,  99349,  77540,  99980,   3837, 114216,
          20412, 105686,  11319, 151643, 151665, 151665, 151665, 151665, 151665,
         151665, 151665, 151665, 151665, 151665, 151665, 151665, 151665, 151665,
         151665, 151665, 151665]]),
'labels': tensor([[112720,  89012,  99459, 122157,  61443, 108462, 100045,   5122,  99459,
         122157,  99457,  99244,   3837,  36589,  99180, 115449,   8997, 100531,
          35727,  22418,  50509,   3837,  99577,  99884,  99907, 109564,   1773,
          99425, 120827, 103073, 103610,   3837,  99208,  79599, 100875,  99964,
           8997,  16530, 102683,  16530, 103020,   3837,  48738, 102744, 102635,
         100619,   1773, 151643],
        [112720,  89012, 113735, 106980,  61443, 108462, 100045,   5122, 102461,
          55135,  21287,  99465,  44729,   3837,  99718,  15946, 100066, 100167,
         105401,   1773, 100697, 100956,  99349,  77540,  99980,   3837, 114216,
          20412, 105686,  11319, 151643,   -100,   -100,   -100,   -100,   -100,
           -100,   -100,   -100,   -100,   -100,   -100,   -100,   -100,   -100,
           -100,   -100,   -100]])}

??151643??? 是文本結束符號,??151665?? 是文本填充符號。

attention_mask 為1的代表有意義的文本,需要參與到向量嵌入計算中。attention_mask 為 0的一般都是填充的符號。

在 decode 模型中, labels 的shape乃至內容,一般都與input_ids 一樣。-100代表該位置的值不參與 loss 計算。(眾所周知 decode 模型與下一個詞計算loss。labels 需要左移一位并在尾部填充-100,這個操作用戶無需關心,此操作由transformers包根據數據集中的labels自動轉換)

模型訓練 

args = TrainingArguments(
    output_dir=r"C:\Users\username\Desktop\train_model_output\Qwen2.5-0.5B\CLM_output",
    num_train_epochs=10,
    per_device_train_batch_size=2,
    save_safetensors=True,
    logging_strategy="epoch",
    # fp16=True,
)

utput_dir:模型的保存地址,我的C盤是固態硬盤,加載訓練完成后的模型會快一點。


trainer = Trainer(
    model=model,
    processing_class=tokenizer,
    args=args,
    train_dataset=dataset,
    eval_dataset=None,
    data_collator=DataCollatorForSupervisedDataset(tokenizer=tokenizer),
)

參數量估算

我選擇 ??Qwen/Qwen2.5-0.5B?? 這個模型,因為這個模型參數少,可以更快看到結果。

上述模型微調是全參數微調,沒有使用LoRA,會導致顯存占用很大。

下述是顯存占用的粗略估算的過程:

1.全精度,fp32:
1B  = 10^9個參數 = 10^9 x 4Byte = 4GB
由于我們是全參數微調,那么最終占用的顯存是: (模型參數 x1 + 梯度 x1 + Adam優化器 x2)

0.5 x 4GB x (4) = 8GB

8 GB + batch的中間變量內存

2.半精度, fp161B  = 10^9個參數 = 10^9 x 2Byte = 2GB
由于我們是全參數微調,那么最終占用的顯存是: (模型參數 x1 + 梯度 x1 + Adam優化器 x2)

0.5 x 2GB x (4) = 4GB

4 GB + batch的中間變量內存

模型推理

使用上述訓練完成的模型,在訓練集的數據上進行推理。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

device = "cuda:0"if torch.cuda.is_available() else"cpu"

train_model = r"C:\Users\1\Desktop\train_model_output\Qwen2.5-0.5B\CLM_output"

model = AutoModelForCausalLM.from_pretrained(train_model)
model = model.to(device)
tokenizer = AutoTokenizer.from_pretrained(train_model, padding_side="right")

def infer(text):
    input_ids = tokenizer(text, return_tensors="pt").to(model.device)

    generated_ids = model.generate(**input_ids)
    generated_ids = [
        output_ids[len(input_ids) :]
        for input_ids, output_ids in zip(input_ids.input_ids, generated_ids)
    ]

    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    return response

text = "請你給哪吒寫一首詩:"
infer(text)

輸出:

'哪吒降世,意氣飛揚。\n逆天改命,破障沖霄。紅綾纏腕,風火踏浪。\n不屈不悔,笑傲蒼茫。'

通過模型的推理結果,驗證了大模型的預訓練是有效果的。

參考資料??https://github.com/huggingface/transformers/blob/main/examples/pytorch/language-modeling/run_clm.py??

本文轉載自??AI悠閑區??,作者:AI悠閑區

已于2025-6-6 14:48:43修改
收藏
回復
舉報
回復
相關推薦
主站蜘蛛池模板: 黄色网址大全在线观看 | 一区二区av | 综合一区二区三区 | 一区二区精品在线 | 成人av网站在线观看 | 国产精品3区 | 亚洲一区二区三区在线播放 | 亚洲性视频 | 久久99精品国产99久久6男男 | 成人小视频在线观看 | 91综合网 | 欧美成年黄网站色视频 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 欧美日韩不卡合集视频 | 免费黄色大片 | 国产一区二区三区视频 | 在线观看中文字幕 | 日韩精品在线观看一区二区三区 | 成人久久久久 | 国产精品免费一区二区三区四区 | 午夜资源 | 天天干天天操天天看 | 日韩视频在线播放 | 国产精品毛片一区二区三区 | 黑人性hd| 成人在线小视频 | 一级国产精品一级国产精品片 | 国产有码| 毛片在线看片 | 盗摄精品av一区二区三区 | 欧美日韩在线一区二区 | 国内自拍偷拍 | 在线观看视频一区二区三区 | 精品欧美一区二区久久久伦 | 四虎永久在线精品免费一区二 | 黄色三级免费 | 2020国产在线 | 久久99精品久久久久久国产越南 | 久久久久久国产精品 | 午夜免费观看网站 | 午夜精品一区二区三区在线视 |