LoRA技術因應大語言模型的微調挑戰而生,它透過低秩矩陣近似全引數微調的更新,有效減少了訓練所需資源。本文將詳細介紹如何將LoRA應用於GPT-2模型,從模型初始化、LoRA層的設計與整合,到實際的訓練流程與結果分析,提供完整的實作。透過LoRA技術,我們可以在不顯著增加模型大小的情況下,提升GPT-2在特定任務上的表現,這對於資源有限的開發者來說尤為重要。
使用LoRA進行引數高效微調的GPT-2模型實作
本章節將介紹如何使用LoRA(Low-Rank Adaptation)技術對GPT-2模型進行引數高效微調。首先,我們將初始化GPT-2模型並檢查其是否能生成合理的文字。接著,我們將使用LoRA技術對模型進行微調,以提高其在分類別任務上的效能。
初始化GPT-2模型
首先,我們需要初始化GPT-2模型。我們定義了一個model_configs字典,用於儲存不同大小的GPT-2模型的組態引數。
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},
}
我們選擇一個特定的模型大小,例如"gpt2-small (124M)",並更新BASE_CONFIG字典。
BASE_CONFIG.update(model_configs[CHOOSE_MODEL])
model_size = CHOOSE_MODEL.split(" ")[-1].lstrip("(").rstrip(")")
settings, params = download_and_load_gpt2(model_size=model_size, models_dir="gpt2")
model = GPTModel(BASE_CONFIG)
load_weights_into_gpt(model, params)
model.eval()
內容解密:
model_configs字典定義了不同大小的GPT-2模型的組態引數,包括嵌入維度、層數和注意力頭數。- 我們選擇一個特定的模型大小,並更新
BASE_CONFIG字典,以確保模型初始化正確。 download_and_load_gpt2函式下載並載入預訓練的GPT-2模型權重。GPTModel類別初始化GPT-2模型,並使用load_weights_into_gpt函式載入預訓練權重。
檢查模型生成文字的能力
為了確保模型載入正確,我們檢查其生成文字的能力。
from chapter04 import generate_text_simple
from chapter05 import text_to_token_ids, token_ids_to_text
text_1 = "Every effort moves you"
token_ids = generate_text_simple(
model=model,
idx=text_to_token_ids(text_1, tokenizer),
max_new_tokens=15,
context_size=BASE_CONFIG["context_length"]
)
print(token_ids_to_text(token_ids, tokenizer))
內容解密:
generate_text_simple函式生成文字,輸入為初始文字的token IDs。text_to_token_ids函式將文字轉換為token IDs。token_ids_to_text函式將token IDs轉換迴文字。- 我們檢查生成的文字是否合理,以確保模型載入正確。
準備模型進行分類別微調
接下來,我們準備模型進行分類別微調,替換輸出層。
torch.manual_seed(123)
num_classes = 2
model.out_head = torch.nn.Linear(in_features=768, out_features=num_classes)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
內容解密:
- 我們設定隨機種子以確保結果可復現。
num_classes變數定義了分類別任務的類別數。- 我們替換模型的輸出層,以適應分類別任務。
- 將模型移動到可用的裝置(GPU或CPU)。
計算初始分類別準確率
在微調之前,我們計算模型的初始分類別準確率。
from chapter06 import calc_accuracy_loader
torch.manual_seed(123)
train_accuracy = calc_accuracy_loader(train_loader, model, device, num_batches=10)
val_accuracy = calc_accuracy_loader(val_loader, model, device, num_batches=10)
test_accuracy = calc_accuracy_loader(test_loader, model, device, num_batches=10)
print(f"Training accuracy: {train_accuracy*100:.2f}%")
print(f"Validation accuracy: {val_accuracy*100:.2f}%")
print(f"Test accuracy: {test_accuracy*100:.2f}%")
內容解密:
calc_accuracy_loader函式計算模型在指定資料載入器上的準確率。- 我們計算模型在訓練集、驗證集和測試集上的初始準確率。
- 由於模型尚未進行微調,預期準確率約為50%。
使用LoRA進行引數高效微調
LoRA技術透過引入低秩矩陣來近似權重更新矩陣,從而實作引數高效微調。
import math
class LoRALayer(torch.nn.Module):
def __init__(self, in_dim, out_dim, rank, alpha):
super().__init__()
self.A = torch.nn.Parameter(torch.empty(in_dim, rank))
torch.nn.init.kaiming_uniform_(self.A, a=math.sqrt(5))
self.B = torch.nn.Parameter(torch.zeros(rank, out_dim))
self.alpha = alpha
def forward(self, x):
x = self.alpha * (x @ self.A @ self.B)
return x
內容解密:
LoRALayer類別實作了LoRA技術,透過引入低秩矩陣A和B來近似權重更新矩陣。rank變數控制了低秩矩陣的內在維度,從而影響了可訓練引數的數量。alpha變數作為縮放因子,調節低秩適應對原始層輸出的影響程度。
LoRA層的整合
LoRA層可以整合到現有的模型層中,以實作引數高效微調。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title LoRA層的整合
rectangle "原始權重" as node1
rectangle "LoRA矩陣" as node2
rectangle "相加" as node3
node1 --> node2
node2 --> node3
@enduml此圖示展示了LoRA層如何與原始模型層整合。原始權重和LoRA矩陣的輸出相加,得到最終輸出。
圖表說明:
- LoRA層透過引入低秩矩陣來近似權重更新矩陣,從而實作引數高效微調。
- 原始權重和LoRA矩陣的輸出相加,得到最終輸出。
透過使用LoRA技術,我們可以在不顯著增加引數數量的情況下,提高模型的效能。這使得LoRA成為引數高效微調的一種有效方法。
低秩適應(LoRA)技術在GPT模型中的引數高效微調實作
簡介
本篇內容將探討如何利用低秩適應(LoRA,Low-Rank Adaptation)技術對GPT模型進行引數高效微調。LoRA是一種有效的技術,能夠在不大幅增加訓練引數的情況下提升模型的表現。
LoRA技術的核心概念
LoRA的核心思想是在原有的模型架構中加入低秩矩陣,以實作高效的引數微調。具體而言,我們將原有的Linear層替換為LinearWithLoRA層,該層結合了原有的Linear層和LoRA層。
LinearWithLoRA層的實作
class LinearWithLoRA(torch.nn.Module):
def __init__(self, linear, rank, alpha):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
def forward(self, x):
return self.linear(x) + self.lora(x)
內容解密:
LinearWithLoRA類別繼承自torch.nn.Module,用於結合原有的Linear層和LoRA層。- 在初始化過程中,我們儲存原有的Linear層,並建立一個新的LoRALayer例項。
- 在
forward方法中,我們將輸入x同時傳遞給原有的Linear層和LoRA層,並將兩者的輸出相加後傳回。 - 由於LoRA層的權重矩陣B初始化為零,因此在初始階段,LoRA層的輸出為零,不會影響原有的模型輸出。
將Linear層替換為LinearWithLoRA層
為了將GPT模型中的Linear層替換為LinearWithLoRA層,我們定義了一個遞迴函式replace_linear_with_lora:
def replace_linear_with_lora(model, rank, alpha):
for name, module in model.named_children():
if isinstance(module, torch.nn.Linear):
setattr(model, name, LinearWithLoRA(module, rank, alpha))
else:
replace_linear_with_lora(module, rank, alpha)
內容解密:
replace_linear_with_lora函式遞迴地遍歷模型的所有子模組。- 如果子模組是Linear層,則將其替換為
LinearWithLoRA層。 - 如果子模組不是Linear層,則遞迴地呼叫
replace_linear_with_lora函式處理其子模組。
應用LoRA技術進行引數高效微調
在將GPT模型中的Linear層替換為LinearWithLoRA層後,我們首先凍結原有的模型引數,然後計算可訓練引數的數量:
for param in model.parameters():
param.requires_grad = False
replace_linear_with_lora(model, rank=16, alpha=16)
total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"Total trainable LoRA parameters: {total_params:,}")
內容解密:
- 我們首先凍結原有的模型引數,將其
requires_grad屬性設為False。 - 然後呼叫
replace_linear_with_lora函式將Linear層替換為LinearWithLoRA層。 - 最後計算可訓練引數的數量,並印出結果。
實驗結果
透過應用LoRA技術,我們將可訓練引數的數量從1.244億減少到266萬,減少了近50倍。
Total trainable parameters before: 124,441,346
Total trainable parameters after: 0
Total trainable LoRA parameters: 2,666,528
使用LoRA進行引數高效微調的技術解析
在人工智慧與深度學習領域,模型的微調(fine-tuning)是一項至關重要的技術。隨著模型規模的不斷擴大,如何在有限的計算資源下實作高效的模型微調,成為了一個重要的研究課題。LoRA(Low-Rank Adaptation)技術應運而生,為解決這一問題提供了有效的方案。
LoRA技術的基本原理
LoRA是一種引數高效的微調方法,其核心思想是透過低秩矩陣來更新模型的權重。傳統的微調方法需要更新模型的所有引數,這不僅計算量大,而且對儲存資源的要求也非常高。LoRA透過引入兩個低秩矩陣A和B,並將它們的乘積新增到原有的權重矩陣中,從而實作了引數的高效更新。
程式碼解析
import torch
import torch.nn as nn
class LoRALayer(nn.Module):
def __init__(self, original_layer, rank):
super(LoRALayer, self).__init__()
self.original_layer = original_layer
self.rank = rank
self.lora_A = nn.Parameter(torch.randn(original_layer.weight.size(0), rank))
self.lora_B = nn.Parameter(torch.randn(rank, original_layer.weight.size(1)))
def forward(self, x):
original_output = self.original_layer(x)
lora_output = torch.matmul(self.lora_A, self.lora_B) @ x
return original_output + lora_output
內容解密:
- LoRALayer類別定義:繼承自
nn.Module,用於實作LoRA層。 __init__方法:初始化LoRA層,包括原始層、秩(rank)以及兩個低秩矩陣lora_A和lora_B。forward方法:定義前向傳播過程。首先計算原始層的輸出,然後計算LoRA層的輸出,最後將兩者相加得到最終輸出。
LoRA在模型微調中的應用
在實際應用中,LoRA可以被用來微調大型預訓練模型。透過將LoRA層新增到模型的特定層中,可以實作對模型的高效微調。
微調範例
import time
from chapter06 import train_classifier_simple
# 設定訓練引數
torch.manual_seed(123)
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5, weight_decay=0.1)
num_epochs = 5
# 開始訓練
start_time = time.time()
train_losses, val_losses, train_accs, val_accs, examples_seen = \
train_classifier_simple(
model, train_loader, val_loader, optimizer, device,
num_epochs=num_epochs, eval_freq=50, eval_iter=5,
tokenizer=tokenizer
)
end_time = time.time()
execution_time_minutes = (end_time - start_time) / 60
print(f"Training completed in {execution_time_minutes:.2f} minutes.")
內容解密:
- 訓練引數設定:設定隨機種子、最佳化器、學習率、權重衰減以及訓練輪數。
- 訓練過程:呼叫
train_classifier_simple函式進行模型訓練,並記錄訓練損失、驗證損失、訓練準確率、驗證準確率等指標。 - 時間記錄:記錄訓練完成的時間,並計算訓練所花費的時間(分鐘)。
實驗結果與分析
透過LoRA技術進行模型微調,可以在保持較高精確度的同時,大幅減少需要更新的引數數量。實驗結果表明,使用LoRA微調的模型在訓練集、驗證集和測試集上都取得了優異的效能。
結果展示
| 資料集 | 準確率 |
|---|---|
| 訓練集 | 100.00% |
| 驗證集 | 96.64% |
| 測試集 | 98.00% |
結果分析
- 高訓練準確率:模型在訓練集上達到了100%的準確率,表明模型對訓練資料有很好的擬合能力。
- 驗證與測試準確率:雖然驗證集和測試集的準確率略低於訓練集,但仍然保持在較高的水平,表明模型具有一定的泛化能力。