PyTorch 提供了強大的資料載入器機制,能有效率地將資料批次化,方便模型訓練。尤其在處理大語言模型(LLM)的微調任務時,一個設計良好的資料載入器至關重要。本篇將著重於如何利用 PyTorch 的 DataLoader 和自定義 collate_fn 函式來構建高效的資料載入器,並探討如何將其應用於 LLM 的微調流程。過程中,我們會設定 allowed_max_length 引數來限制輸入序列長度,並根據硬體環境選擇 “cpu”、“cuda” 或 “mps” 作為運算裝置。同時,我們也會使用 functools 函式庫中的 partial 函式來簡化 collate_fn 的設定,預先填入裝置和最大長度等引數。最後,我們會結合實際程式碼片段示範如何設定訓練、驗證和測試資料集的載入器,並說明如何檢查批次資料的維度,確保資料處理流程的正確性。

進階指令評估與最佳化

在前一節中,我們已經將多個指令例子組合成一個批次。現在,我們將建立 PyTorch 資料載入器,以便對大語言模型(LLM)進行微調。

為了確保資料載入器的正確性,我們需要設定自定義的 collate 函式(custom_collate_fn)。這個函式包含將輸入和目標張量移動到指定裝置(例如,CPU 或 NVIDIA GPU)的程式碼。裝置設定可以是 “cpu”、“cuda”(適用於 NVIDIA GPU)或 “mps”(適用於蘋果公司的 Apple Silicon 晶片)。

注意,使用 “mps” 裝置可能會導致與本章內容不同的數值差異,因為 PyTorch 對 Apple Silicon 的支援仍然是實驗性的。在之前的程式碼中,我們在主訓練迴圈中將資料移到目標裝置(例如,當裝置為 “cuda” 時,將資料移到 GPU 記憶體)。但是,將此過程作為背景過程外部化,可以防止它在模型訓練期間阻塞 GPU。

以下程式碼初始化 device 變數:

if torch.backends.mps.is_available():
    device = "mps"
else:
    device = "cuda" if torch.cuda.is_available() else "cpu"
print("Device:", device)

這將根據您的機器列印 “Device: cpu” 或 “Device: cuda”。

接下來,為了在 PyTorch DataLoader 類別中重用所選定的裝置設定,我們使用 Python 的 functools 標準函式庫中的 partial 函式建立 custom_collate_fn 的新版本,其裝置引數預先填充。另外,我們設定 allowed_max_length 為 1024,這將截斷資料到玄貓支援的最大上下文長度:

from functools import partial

customized_collate_fn = partial(
    custom_collate_fn,
    device=device,
    allowed_max_length=1024
)

現在,我們可以設定資料載入器,如同之前所做,但這次我們將使用自定義的 collate 函式進行批次處理:

train_dataloader = DataLoader(
    train_dataset,
    batch_size=batch_size,
    collate_fn=customized_collate_fn,
    shuffle=True
)

eval_dataloader = DataLoader(
    eval_dataset,
    batch_size=batch_size,
    collate_fn=customized_collate_fn,
    shuffle=False
)

內容解密:

  • custom_collate_fn 是一個自定義的 collate 函式,負責將輸入和目標張量移動到指定裝置。
  • device 變數根據您的機器設定為 “cpu”、“cuda” 或 “mps”。
  • allowed_max_length 設定為 1024,以截斷資料到玄貓支援的最大上下文長度。
  • DataLoader 類別使用自定義的 collate 函式進行批次處理。

圖表翻譯:

  graph LR
    A[資料載入] --> B[自定義 collate 函式]
    B --> C[裝置設定]
    C --> D[批次處理]
    D --> E[模型訓練]

這個圖表展示了資料載入、自定義 collate 函式、裝置設定、批次處理和模型訓練之間的流程關係。

建立指令資料集的資料載入器

為了高效地訓練模型,我們需要建立資料載入器(Data Loader),以便批次地從指令資料集中載入資料。以下是建立資料載入器的步驟:

首先,我們需要匯入必要的函式庫,包括 torch.utils.data 中的 DataLoader 類別。然後,我們定義了一些重要的引數,例如 num_workers(設為 0,表示不使用多執行緒載入資料)和 batch_size(設為 8,表示每批次載入 8 個樣本)。

from torch.utils.data import DataLoader

num_workers = 0
batch_size = 8

接下來,我們需要建立指令資料集的例項,分別對於訓練、驗證和測試資料。這裡,我們使用 InstructionDataset 類別,並傳入相應的資料和分詞器(tokenizer)。

torch.manual_seed(123)
train_dataset = InstructionDataset(train_data, tokenizer)
val_dataset = InstructionDataset(val_data, tokenizer)
test_dataset = InstructionDataset(test_data, tokenizer)

然後,我們建立資料載入器的例項,分別對於訓練、驗證和測試資料。這裡,我們使用 DataLoader 類別,並傳入相應的資料集、批次大小、自定義的拼接函式(collate_fn)、是否打亂資料(shuffle)和是否丟棄最後一個批次(drop_last)。

train_loader = DataLoader(
    train_dataset,
    batch_size=batch_size,
    collate_fn=customized_collate_fn,
    shuffle=True,
    drop_last=True,
    num_workers=num_workers
)

val_loader = DataLoader(
    val_dataset,
    batch_size=batch_size,
    collate_fn=customized_collate_fn,
    shuffle=False,
    drop_last=False,
    num_workers=num_workers
)

test_loader = DataLoader(
    test_dataset,
    batch_size=batch_size,
    collate_fn=customized_collate_fn,
    shuffle=False,
    drop_last=False,
    num_workers=num_workers
)

最後,我們可以檢查訓練資料載入器生成的批次輸入和目標資料的維度。

print("Train loader:")
for inputs, targets in train_loader:
    print(inputs.shape, targets.shape)

輸出結果如下(為了節省空間,輸出結果被截斷):

Train loader:
torch.Size([8, 61]) torch.Size([8, 61])

這表明,每批次輸入和目標資料的維度分別為 (8, 61) 和 (8, 61),其中 8 是批次大小,61 是序列長度。

自然語言模型微調之旅

在深度學習的世界中,自然語言模型(LLM)是一種強大的工具,可以用於各種自然語言處理任務。然而,要讓這些模型真正發揮作用,我們需要對它們進行微調,以適應特定的任務和資料集。在這篇文章中,我們將探討如何微調一個預訓練的LLM,以使其遵循指令。

資料準備:批次處理

當我們準備好資料後,下一步就是建立批次以供模型訓練使用。批次大小是決定模型效能的關鍵因素,因為它直接影響了模型的計算效率和記憶使用量。透過控制批次大小,我們可以在計算資源和模型效能之間取得平衡。

import torch

# 定義批次大小
batch_size = 8

# 建立資料載入器
data_loader = torch.utils.data.DataLoader(
    dataset,
    batch_size=batch_size,
    collate_fn=custom_collate_fn
)

在上面的程式碼中,我們定義了一個批次大小為8的資料載入器,並使用了一個自定義的collate_fn函式來處理批次資料。這個函式允許我們建立不同長度的批次,這對於自然語言處理任務非常重要,因為輸入序列的長度可能會有所不同。

載入預訓練模型

在開始微調之前,我們需要載入一個預訓練的LLM模型。這個模型將作為我們微調過程的基礎。與其使用最小的124百萬引數模型,我們這裡選擇載入一個中等大小的355百萬引數模型。這個選擇是根據實驗結果,表明小型模型可能無法提供足夠的容量來完成複雜的任務。

import torch.nn as nn

# 載入預訓練模型
model = nn.Transformer(
    d_model=512,
    nhead=8,
    num_encoder_layers=6,
    num_decoder_layers=6,
    dim_feedforward=2048,
    dropout=0.1
)

在上面的程式碼中,我們定義了一個Transformer模型作為我們的LLM。這個模型具有512個嵌入維度、8個注意力頭、6個編碼器層和6個解碼器層,以及2048個前向神經元和0.1的dropout率。

微調流程

微調流程可以分為三個階段:資料準備、批次處理和模型微調。在這裡,我們已經完成了資料準備和批次處理階段。下一步就是開始微調過程了。

  flowchart TD
    A[資料準備] --> B[批次處理]
    B --> C[模型微調]

在上面的Mermaid圖表中,我們展示了微調流程的三個階段。首先,我們需要準備好資料,包括下載和預處理。然後,我們建立批次以供模型訓練使用。最後,我們開始微調過程,以適應特定的任務和資料集。

圖表翻譯:

這個Mermaid圖表展示了微調流程的三個階段:資料準備、批次處理和模型微調。每個階段都對應著一個特定的步驟,在微調過程中扮演著重要的角色。透過這個圖表,我們可以清晰地看到微調流程的邏輯順序,以及每個階段之間的關係。

內容解密:

在這篇文章中,我們探討瞭如何微調一個預訓練的LLM,以使其遵循指令。首先,我們需要準備好資料,包括下載和預處理。然後,我們建立批次以供模型訓練使用,使用了一個自定義的collate_fn函式來處理批次資料。接下來,我們載入了一個預訓練的LLM模型,並定義了一個Transformer模型作為我們的LLM。最後,我們展示了微調流程的三個階段,包括資料準備、批次處理和模型微調。

透過這篇文章,我們希望能夠提供一個清晰的,教導讀者如何微調一個預訓練的LLM,以使其遵循指令。同時,我們也希望能夠提供一些有用的見解和建議,幫助讀者更好地理解微調流程和LLM模型的工作原理。

建立高效能語言模型的步驟

在深入探討語言模型的建立之前,瞭解整個流程至關重要。以下是建立和評估高效能語言模型的主要步驟:

步驟1:資料準備

資料是語言模型的基本。首先,需要收集和預處理大量的文字資料,以便模型能夠學習語言的結構和語義。

步驟2:建立資料載入器

資料載入器(Data Loader)是負責將預處理的資料餵給模型的元件。它們確保資料以批次的形式提供給模型,從而加速訓練過程。

步驟3:載入預訓練模型

使用預訓練的大語言模型(LLM)可以節省時間和資源。這些模型已經在大量文字資料上進行了預訓練,可以作為我們自己的模型的基礎。

步驟4:微調預訓練模型

雖然預訓練模型提供了良好的起點,但它們可能不完全符合我們特定的需求。透過對預訓練模型進行微調,可以使其更好地適應我們的具體任務和資料。

步驟5:評估模型回應

評估模型生成的回應是確保其品質和相關性的關鍵步驟。這涉及檢查回應是否符合預期,並根據需要進行調整。

步驟6:檢視模型損失

模型損失是評估模型效能的重要指標。透過檢視損失,可以瞭解模型在哪些方面需要改進,並進行相應的調整。

步驟7:提取回應

一旦模型被微調和評估,下一步就是提取它生成的回應。這些回應可以用於各種應用,從聊天機器人到文字摘要。

步驟8:評估模型

評估模型的效能是最後但同樣重要的步驟。這涉及使用各種指標來評估模型的準確性、相關性和整體效能。

透過遵循這些步驟,可以建立一個高效能的語言模型,該模型能夠生成高品質的文字並執行各種自然語言處理任務。

內容解密:

以上步驟展示了建立高效能語言模型的過程。從資料準備到模型評估,每一步都對最終結果至關重要。透過瞭解和遵循這些步驟,開發人員可以建立出色的語言模型,以滿足他們的需求。

圖表翻譯:

  flowchart TD
    A[資料準備] --> B[建立資料載入器]
    B --> C[載入預訓練模型]
    C --> D[微調預訓練模型]
    D --> E[評估模型回應]
    E --> F[檢視模型損失]
    F --> G[提取回應]
    G --> H[評估模型]

此圖表展示了建立高效能語言模型的步驟之間的流程和關係。每一步都是一個連續的過程,最終目的是建立一個高效能的語言模型。

進階LLM微調的質性評估

在進行大語言模型(LLM)的微調時,瞭解模型的容量和其對於高品質指令跟隨任務的影響至關重要。較小的模型可能缺乏必要的容量來學習和保留複雜的模式和細膩的行為,這些是高品質指令跟隨任務所必需的。

載入預先訓練的LLM

要載入預先訓練的模型,我們需要使用與預訓練和微調時相同的程式碼,唯一的差異是指定模型的大小。在這裡,我們選擇了「gpt2-medium (355M)」作為我們的模型大小。這個選擇意味著我們將下載一個中等大小的GPT模型,約佔用1.42 GB的儲存空間,大約是小模型所需儲存空間的三倍。

from gpt_download import download_and_load_gpt2
from chapter04 import GPTModel
from chapter05 import load_weights_into_gpt

# 基礎組態
BASE_CONFIG = {
    "vocab_size": 50257,  # 詞彙大小
    "context_length": 1024,  # 上下文長度
    "drop_rate": 0.0,  # Dropout率
    "qkv_bias": True  # Query-key-value偏差
}

# 不同模型的組態
model_configs = {
    "gpt2-small (124M)": {"emb_dim": 768, "n_layers": 12, "n_heads": 12},
    "gpt2-medium (355M)": {"emb_dim": 1024, "n_layers": 24, "n_heads": 16},
    "gpt2-large (774M)": {"emb_dim": 1280, "n_layers": 36, "n_heads": 20},
    "gpt2-xl (1558M)": {"emb_dim": 1600, "n_layers": 48, "n_heads": 25},
}

# 選擇模型
CHOOSE_MODEL = "gpt2-medium (355M)"

# 更新基礎組態
BASE_CONFIG.update(model_configs[CHOOSE_MODEL])

# 取得模型大小
model_size = CHOOSE_MODEL.split(" ")[-1].lstrip("(").rstrip(")")

質性評估的重要性

進行質性評估可以幫助我們瞭解模型在不同任務上的表現,特別是在指令跟隨任務中。透過分析模型輸出的品質和一致性,我們可以判斷模型是否已經學習到必要的模式和行為,以完成高品質的指令跟隨任務。

內容解密:

上述程式碼片段展示瞭如何載入預先訓練的LLM模型,包括指定模型大小和更新基礎組態。這一步驟對於微調模型以適應特定任務至關重要。透過選擇合適的模型大小和組態,我們可以最佳化模型的效能並提高其在指令跟隨任務上的表現。

圖表翻譯:

  flowchart TD
    A[開始] --> B[載入預先訓練模型]
    B --> C[指定模型大小]
    C --> D[更新基礎組態]
    D --> E[進行微調]
    E --> F[評估模型表現]

此圖表展示了從載入預先訓練模型到進行微調和評估模型表現的整個過程。每一步驟都對於最終的質性評估結果至關重要。透過這個流程圖,我們可以清晰地看到如何系統地對LLM進行微調和評估,以達到最佳的指令跟隨任務表現。

轉換主動句為被動句

首先,我們需要了解什麼是主動句和被動句。主動句是指句子的主語是執行動作的那一方,而被動句則是指句子的主語是接受動作的一方。

給定的例子是「The chef cooks the meal every day.」這是一個主動句,因為主語「The chef」是執行動作「cooks」的那一方。

要轉換這個句子為被動句,我們需要將主語變為接受動作的一方,也就是「the meal」。因此,被動句應該是「The meal is cooked by the chef every day.」

這樣的轉換保留了原句的意思,但改變了句子的結構,讓接受動作的一方成為主語。

使用預訓練模型評估指令跟隨任務

使用預訓練的語言模型(LLM)來評估其在指令跟隨任務上的效能是很重要的。這可以幫助我們瞭解模型在沒有額外訓練的情況下如何處理特定任務。

首先,我們需要準備好輸入文字,這包括了指令和相關的上下文資訊。然後,我們使用預訓練模型生成回應。

生成回應後,我們需要從生成的文字中分離出模型真正產生的回應內容。這通常涉及到移除輸入指令本身,以便我們能夠專注於模型產生的內容。

最後,評估模型產生的回應與預期結果之間的差異,可以幫助我們瞭解模型在指令跟隨任務上的強項和弱項。

微調預訓練模型

微調預訓練模型是指在預訓練模型的基礎上,使用特定的任務相關資料進行額外的訓練,以提高模型在該任務上的效能。

這個過程涉及到調整模型的引數,以便它能夠更好地適應特定的任務需求。透過這種方式,可以顯著提高模型在指令跟隨任務上的效能。

在進行微調之前,評估預訓練模型的基礎效能是非常重要的。這樣可以為我們提供一個基準,幫助我們瞭解微調之後的改善程度。

實作細節

  • 資料準備:確保輸入文字和相關的上下文資訊正確無誤。
  • 模型生成:使用預訓練模型生成回應,並確保生成的文字完整且有意義。
  • 回應分離:從生成的文字中分離出模型真正產生的回應內容。
  • 評估:評估模型產生的回應與預期結果之間的差異,找出需要改善的地方。
  • 微調:根據評估結果,對預訓練模型進行微調,以提高其在指令跟隨任務上的效能。

透過這些步驟,可以有效地提高預訓練模型在指令跟隨任務上的效能,並使其更好地適應特定的任務需求。

將動詞轉換為被動語態

原句:The chef cooks the meal every day. 被動語態:The meal is cooked by the chef every day.

指令轉換實踐

在上述範例中,我們將原來的主動語態句子「The chef cooks the meal every day.」轉換為被動語態「The meal is cooked by the chef every day.」。這個過程涉及到重新排列句子的結構,強調的是動作的接受者(the meal),而不是執行動作的主體(the chef)。

實踐步驟

  1. 識別主動語態句子:首先,我們需要確定原句子的主動語態結構,即「主體 + 動詞 + 受詞」的順序。
  2. 轉換為被動語態:然後,我們將句子的結構轉換為被動語態,即「受詞 + 被動語態動詞 + by + 主體」。在這個例子中,「the meal」成為了新的主體,「is cooked」是被動語態動詞,而「by the chef」表示執行動作的主體。
  3. 檢查語法正確性:最後,我們需要檢查轉換後的句子是否語法正確且易於理解。

應用於指令理解

在指令理解的背景下,能夠正確地將主動語態轉換為被動語態對於遵循指令至關重要。這不僅展示了對語法結構的掌握,也體現了對指令的理解和執行能力。透過這種轉換,我們可以更好地適應不同的語言表達方式,提高溝通的效率和準確性。

結合Mermaid圖表進行視覺化

  graph LR
    A[主動語態] -->|轉換|> B[被動語態]
    B --> C[受詞 + 被動語態動詞 + by + 主體]
    C --> D[檢查語法正確性]
    D --> E[最終結果]

圖表翻譯:

上述Mermaid圖表描述了從主動語態到被動語態的轉換過程。首先,我們從主動語態(A)開始,然後進行轉換(|轉換|),得到被動語態(B)。被動語態的結構由受詞、被動語態動詞、by和主體組成(C)。最後,我們需要檢查轉換後句子的語法正確性(D),以確保最終結果(E)的準確性和可讀性。這個過程透過視覺化的方式呈現,使得理解和實踐主動語態到被動語態的轉換更加直觀。

從技術架構視角來看,構建高效的資料載入器對於大語言模型(LLM)的微調至關重要。分析custom_collate_fn函式的設計,可以發現它透過將資料移動到指定裝置(CPU、GPU 或 MPS)的操作外部化,有效避免了在訓練過程中阻塞 GPU,從而提升了訓練效率。同時,設定allowed_max_length引數,可以有效控制輸入序列長度,避免超出模型的上下文限制。然而,需要注意的是,PyTorch 對 MPS 裝置的支援仍處於實驗階段,可能存在與 CUDA 等成熟裝置的效能差異。整合functools.partial函式預先設定custom_collate_fn的引數,展現了程式碼設計的簡潔性和可維護性。對於追求極致效能的應用,可以進一步探討非同步資料載入機制,以最大限度地壓榨硬體效能。玄貓認為,隨著硬體和軟體生態的持續發展,LLM 訓練效率的提升將持續推動其在更多場景的落地應用。