深度學習模型的訓練往往需要大量的資料和計算資源。透過微調預訓練模型,我們可以利用現有的模型架構和權重,針對特定任務進行調整,從而減少訓練時間和資源消耗。微調的關鍵在於調整學習率、批次大小、迭代次數等超引數,並監控訓練過程中的損失和準確度變化,以便及時調整策略。同時,評估模型的泛化能力也是至關重要的,這可以透過驗證集和測試集的效能指標來衡量。
def train_classifier(model, train_loader, val_loader, optimizer, device, num_epochs, eval_freq):
train_losses, val_losses, train_accs, val_accs = [], [], [], []
examples_seen, global_step = 0, -1
for epoch in range(num_epochs):
model.train()
for input_batch, target_batch in train_loader:
optimizer.zero_grad()
loss = calc_loss_batch(input_batch, target_batch, model, device)
loss.backward()
optimizer.step()
examples_seen += input_batch.shape[0]
global_step += 1
if global_step % eval_freq == 0:
pass
進階分類別模型調整
在深度學習中,模型的微調(fine-tuning)是一種重要的技術,尤其是在分類別任務中。透過微調預訓練模型,可以使其更好地適應特定的分類別任務,從而提高模型的準確率。下面,我們將探討如何對一個簡單的分類別器進行微調。
微調流程
首先,我們需要定義一個訓練函式,該函式可以接收多個引數,包括模型、訓練資料載入器、驗證資料載入器、最佳化器、裝置、epoch數、評估頻率和評估次數等。這個函式將傳回訓練過程中的損失和準確率。
def train_classifier(model, train_loader, val_loader, optimizer, device, num_epochs, eval_freq):
# 初始化列表來儲存損失和準確率
train_losses, val_losses, train_accs, val_accs = [], [], [], []
# 初始化已見示例數和全域性步數
examples_seen, global_step = 0, -1
# 進行epoch次訓練
for epoch in range(num_epochs):
# 設定模型為訓練模式
model.train()
# 進行批次訓練
for input_batch, target_batch in train_loader:
# 清空最佳化器梯度
optimizer.zero_grad()
# 計算批次損失
loss = calc_loss_batch(input_batch, target_batch, model, device)
# 反向傳播
loss.backward()
# 更新模型引數
optimizer.step()
# 更新已見示例數和全域性步數
examples_seen += input_batch.shape[0]
global_step += 1
# 如果達到評估頻率,進行評估
if global_step % eval_freq == 0:
# 在這裡加入評估程式碼
pass
評估和微調
在上述程式碼中,我們每隔一定步數(由eval_freq
決定)就會進行一次評估。評估的目的是計算模型在驗證集上的損失和準確率,以便我們可以觀察到模型的效能變化。透過這些資訊,我們可以對模型進行微調,例如調整學習率、修改模型結構等,以使模型更好地適應特定的分類別任務。
flowchart TD A[開始訓練] --> B[載入批次資料] B --> C[計算批次損失] C --> D[反向傳播] D --> E[更新模型引數] E --> F[檢查評估頻率] F -->|達到頻率|> G[進行評估] F -->|未達到頻率|> B G --> H[更新評估結果] H --> I[繼續下一批次]
圖表翻譯:
此圖表示了模型訓練和評估的流程。首先,模型開始訓練,然後載入批次資料。接下來,模型計算批次損失,進行反向傳播和更新模型引數。然後,模型檢查是否達到評估頻率,如果達到,就進行評估並更新評估結果。如果未達到,就繼續下一批次的訓練。
微調策略
在進行微調時,我們需要考慮多個因素,例如學習率、batch大小、epoch數等。透過調整這些超引數,我們可以使模型更好地適應特定的分類別任務。
# 示例:調整學習率
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
深度學習模型微調流程
在進行模型微調的過程中,評估模型的效能是一個非常重要的步驟。以下是微調模型以進行垃圾郵件分類別的示例程式碼:
train_loss, val_loss = evaluate_model(
model, train_loader, val_loader, device, eval_iter
)
train_losses.append(train_loss)
val_losses.append(val_loss)
print(f"Epoch {epoch+1} (Step {global_step:06d}): "
f"Train loss {train_loss:.3f}, "
f"Val loss {val_loss:.3f}"
)
train_accuracy = calc_accuracy_loader(
train_loader, model, device, num_batches=eval_iter
)
val_accuracy = calc_accuracy_loader(
val_loader, model, device, num_batches=eval_iter
)
print(f"Training accuracy: {train_accuracy*100:.2f}% | ", end="")
print(f"Validation accuracy: {val_accuracy*100:.2f}%")
train_accs.append(train_accuracy)
val_accs.append(val_accuracy)
return train_losses, val_losses, train_accs, val_accs, examples_seen
evaluate_model
函式
evaluate_model
函式用於評估模型在訓練集和驗證集上的效能。其實作如下:
def evaluate_model(model, train_loader, val_loader, device, eval_iter):
model.eval()
with torch.no_grad():
# 進行模型評估的程式碼
pass
這個函式將模型設為評估模式(model.eval()
),並關閉梯度計算(with torch.no_grad():
),以避免不必要的計算。
微調流程
在微調流程中,我們需要跟蹤訓練損失、驗證損失、訓練準確率和驗證準確率等指標。為此,我們定義了幾個列表來儲存這些指標的歷史資料:
train_losses = []
val_losses = []
train_accs = []
val_accs = []
每次迭代後,我們會計算當前的訓練損失、驗證損失、訓練準確率和驗證準確率,並將這些值追加到對應的列表中。
圖表翻譯:
flowchart TD A[開始] --> B[初始化列表] B --> C[計算損失和準確率] C --> D[追加資料到列表] D --> E[列印結果] E --> F[傳回歷史資料]
內容解密:
上述程式碼的主要目的是評估模型在訓練集和驗證集上的效能,並跟蹤相關指標的歷史資料。這些指標對於評估模型的效能和調整超引數至關重要。
在 evaluate_model
函式中,model.eval()
用於設定模型為評估模式,這會關閉批次歸一化和 dropout 等層的隨機性,以保證評估結果的一致性。with torch.no_grad():
用於關閉梯度計算,以節省計算資源和加速評估過程。
微調流程中,我們會不斷迭代地計算和更新指標,並將這些資料儲存起來,以便之後進行分析和視覺化。這些指標的變化可以幫助我們瞭解模型的學習情況和最佳化過程。
訓練迴圈解析
在深度學習模型的訓練過程中,訓練迴圈(training loop)扮演著至關重要的角色。它負責對模型進行訓練,讓模型能夠從資料中學習並改進其預測能力。下面,我們將詳細解析訓練迴圈的每一個步驟,以便更好地理解這個過程。
訓練模式設定
首先,模型需要被設定為訓練模式(training mode)。這一步驟告訴模型,它即將接受新的資料並根據這些資料更新其引數。這與評估模式(evaluation mode)不同,在評估模式中,模型使用既有的引數進行預測,而不進行任何更新。
重置損失梯度
在開始新的訓練迴圈之前,模型會重置來自上一次批次(batch)計算的損失梯度。這是必要的,因為我們希望每次訓練都從一個乾淨的狀態開始,避免前一次計算的梯度影響新的更新。
損失梯度計算
接下來,模型會計算當前批次資料的損失梯度。損失梯度表示模型引數相對於損失函式的梯度,告訴我們如何調整模型引數以最小化損失。這一步驟對於模型學習至關重要,因為它提供了模型改進的方向。
模型引數更新
使用計算出的損失梯度,模型會更新其引數。這通常透過最佳化演算法(如梯度下降法、Adam等)實作,最佳化演算法根據梯度資訊調整模型引數以最小化損失。
跟蹤例子
在某些情況下,特別是在自然語言處理任務中,模型可能需要跟蹤例子(examples)而不是token。這意味著模型關注的是整個輸入序列或檔案,而不是個別的詞彙或字元。
可選評估步驟
作為訓練過程的一部分,可能會包含可選的評估步驟。在這一步驟中,模型會在一個獨立的評估資料集上進行評估,以計算其在未見資料上的效能。這通常用於監控模型的泛化能力,並根據評估結果調整模型或訓練過程。
準確率計算
最後,模型會計算其在當前批次或評估資料集上的準確率。準確率是評估模型效能的一個重要指標,它表示模型正確預測的例項佔總例項數的比例。透過跟蹤準確率的變化,可以評估模型在訓練過程中的學習情況和改進空間。
內容解密:
上述過程描述了深度學習模型的一個基本訓練迴圈。每一步驟都對於模型的學習和改進至關重要。透過設定訓練模式、重置損失梯度、計算損失梯度、更新模型引數、跟蹤例子、進行可選評估和計算準確率,模型可以不斷學習和改進,以達到最佳的效能。
圖表翻譯:
flowchart TD A[開始] --> B[設定訓練模式] B --> C[重置損失梯度] C --> D[計算損失梯度] D --> E[更新模型引數] E --> F[跟蹤例子] F --> G[可選評估] G --> H[計算準確率] H --> I[結束]
此圖表展示了訓練迴圈的流程,從開始到結束,每一步驟都清晰地展示出來,有助於理解和實作深度學習模型的訓練過程。
微調模型的訓練流程
在進行微調之前,我們需要計算訓練集和驗證集上的損失函式。這可以透過 calc_loss_loader
函式實作,該函式接受資料載入器、模型、裝置和批次數作為輸入引數。
train_loss = calc_loss_loader(train_loader, model, device, num_batches=eval_iter)
val_loss = calc_loss_loader(val_loader, model, device, num_batches=eval_iter)
接下來,我們需要將模型設定為訓練模式,並初始化最佳化器。最佳化器的選擇對於模型的訓練結果有著重要的影響。在這裡,我們選擇使用 AdamW 最佳化器,學習率設定為 5e-5,權重衰減設定為 0.1。
model.train()
torch.manual_seed(123)
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5, weight_decay=0.1)
設定好最佳化器後,我們需要定義訓練的 epochs 數量。在這個例子中,我們選擇訓練 5 個 epochs。
num_epochs = 5
最後,我們可以使用 train_classifier_simple
函式進行模型的訓練。這個函式接受模型、訓練資料載入器、驗證資料載入器、最佳化器、裝置、epochs 數量、評估頻率和評估批次數作為輸入引數。
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
)
內容解密:
在上述程式碼中,calc_loss_loader
函式用於計算給定資料載入器上的損失函式。model.train()
用於將模型設定為訓練模式。torch.optim.AdamW
用於初始化 AdamW 最佳化器,學習率和權重衰減分別設定為 5e-5 和 0.1。train_classifier_simple
函式用於進行模型的訓練,傳回訓練損失、驗證損失、訓練準確率、驗證準確率和已經看到的樣本數。
圖表翻譯:
flowchart TD A[計算訓練損失] --> B[計算驗證損失] B --> C[初始化最佳化器] C --> D[設定模型為訓練模式] D --> E[進行模型訓練] E --> F[傳回訓練結果]
在這個流程圖中,我們首先計算訓練損失和驗證損失,然後初始化最佳化器和設定模型為訓練模式。接下來,我們進行模型的訓練,最後傳回訓練結果。
訓練過程分析
在訓練過程中,我們可以看到訓練損失(Train loss)和驗證損失(Val loss)隨著時間的推移而下降。這表明模型正在學習並改善其預測能力。同時,訓練準確率(Training accuracy)和驗證準確率(Validation accuracy)也在提高,表明模型的效能正在改善。
執行時間
訓練過程花費了約5.65分鐘,這是一個相對較短的時間,表明模型的訓練效率較高。
損失函式繪製
使用Matplotlib繪製損失函式,可以更直觀地看到訓練和驗證損失的變化趨勢。這有助於我們瞭解模型的學習過程和效能改善的情況。
程式碼分析
以下是繪製損失函式的程式碼:
import matplotlib.pyplot as plt
def plot_values(
epochs_seen, examples_seen, train_values, val_values,
label="loss"
):
fig, ax1 = plt.subplots(figsize=(5, 3))
ax1.plot(epochs_seen, train_values, label=f"Training {label}")
ax1.plot(
epochs_seen, val_values, linestyle="-.",
label=f"Validation {label}"
)
ax1.set_xlabel("Epochs")
ax1.set_ylabel(label.capitalize())
ax1.legend()
ax2 = ax1.twiny()
ax2.plot(examples_seen, train_values, alpha=0)
ax2.set_xlabel("Examples seen")
fig.tight_layout()
plt.savefig(f"{label}-plot.pdf")
這個程式碼使用Matplotlib建立了一個繪圖區域,然後繪製了訓練和驗證損失的曲線。同時,也增加了軸標籤、圖例和儲存圖片的功能。
圖表翻譯
此圖表展示了訓練和驗證損失隨著時間的推移而下降的趨勢。這有助於我們瞭解模型的學習過程和效能改善的情況。透過分析這個圖表,我們可以得出以下結論:
- 訓練損失和驗證損失都在下降,表明模型正在學習並改善其預測能力。
- 訓練準確率和驗證準確率都在提高,表明模型的效能正在改善。
- 訓練過程花費了約5.65分鐘,這是一個相對較短的時間,表明模型的訓練效率較高。
訓練過程中的損失曲線分析
在深度學習模型的訓練過程中,觀察損失曲線對於理解模型的學習進展和泛化能力至關重要。損失曲線是指在訓練過程中,模型在訓練集和驗證集上的損失值隨著訓練epoch的變化而繪製出的圖形。
損失曲線的繪製
利用Python的matplotlib函式庫,可以輕鬆地繪製出損失曲線。首先,需要定義訓練epoch和對應的損失值資料。然後,使用matplotlib的plot函式即可繪製出損失曲線。
import matplotlib.pyplot as plt
import torch
# 定義訓練epoch和損失值資料
num_epochs = 5
train_losses = [0.8, 0.6, 0.4, 0.2, 0.1]
val_losses = [0.7, 0.5, 0.3, 0.2, 0.1]
# 繪製損失曲線
epochs_tensor = torch.linspace(0, num_epochs, len(train_losses))
examples_seen_tensor = torch.linspace(0, 1000, len(train_losses))
# 定義繪製函式
def plot_values(x1, x2, y1, y2):
plt.plot(x1, y1, label='訓練損失')
plt.plot(x2, y2, label='驗證損失')
plt.xlabel('訓練epoch')
plt.ylabel('損失值')
plt.title('訓練和驗證損失曲線')
plt.legend()
plt.show()
plot_values(epochs_tensor, examples_seen_tensor, train_losses, val_losses)
損失曲線分析
透過觀察損失曲線,可以得出以下結論:
- 訓練損失和驗證損失在前幾個epoch中迅速下降,表明模型正在有效地學習訓練資料。
- 隨著訓練的進行,損失值逐漸穩定,表明模型的學習進展趨於平穩。
- 訓練損失和驗證損失之間的差距相對較小,表明模型具有良好的泛化能力,可以有效地應用於未見的資料。
綜上所述,損失曲線是評估模型學習進展和泛化能力的一個重要工具。透過觀察損失曲線,可以對模型的效能有更深入的瞭解,並對模型的訓練過程進行調整和最佳化。
6.7 微調模型的監督資料
根據圖 6.16 中的陡峭下降曲線,我們可以看到模型從訓練資料中學習得很好,並且沒有明顯的過度擬合跡象,即訓練集和驗證集的損失之間沒有明顯的差距。現在,讓我們使用相同的 plot_values
函式來繪製分類別準確率:
epochs_tensor = torch.linspace(0, num_epochs, len(train_accs))
examples_seen_tensor = torch.linspace(0, examples_seen, len(train_accs))
plot_values(
epochs_tensor,
examples_seen_tensor,
train_accs,
val_accs,
label="accuracy"
)
圖 6.17 顯示了準確率的結果。模型在第 4 和 5 個 epoch 後達到相對較高的訓練和驗證準確率。重要的是,我們之前設定了 eval_iter=5
。
選擇 epoch 數量
早些時候,當我們啟動訓練時,我們將 epoch 數量設定為 5。epoch 數量取決於資料集和任務的難度,並且沒有通用的解決方案或建議,儘管 5 個 epoch 通常是一個好的起點。如果模型在前幾個 epoch 後過度擬合(見圖 6.16),您可能需要減少 epoch 數量。相反,如果趨勢線表明驗證損失可以透過進一步訓練來改善,您應該增加 epoch 數量。在這個具體案例中,5 個 epoch 是一個合理的數量,因為沒有早期過度擬合的跡象,且驗證損失接近 0。
圖 6.17 顯示了訓練準確率(實線)和驗證準確率(虛線)在早期 epoch 中大幅增加,然後達到平臺,幾乎達到完美的準確率分數 1.0。兩條線在整個 epoch 中的接近程度表明模型沒有過度擬合訓練資料。
當使用 train_classifier_simple
函式時,我們必須計算訓練、驗證和測試集在整個資料集上的效能指標,這次不設定 eval_iter
值:
train_accuracy = calc_accuracy_loader(train_loader, model, device)
val_accuracy = calc_accuracy_loader(val_loader, model, device)
test_accuracy = calc_accuracy_loader(test_loader, model, device)
print(f"訓練準確率:{train_accuracy*100:.2f}%")
print(f"驗證準確率:{val_accuracy*100:.2f}%")
print(f"測試準確率:{test_accuracy*100:.2f}%")
結果準確率值為:
訓練準確率:97.21% 驗證準確率:97.32% 測試準確率:95.67%
訓練集和測試集的效能幾乎相同。訓練集和測試集準確率之間的輕微差異表明模型對訓練資料的過度擬合很小。通常,驗證集的準確率會稍微高於測試集的準確率,因為模型開發通常涉及調整超引數以在驗證集上表現良好,這可能不能很好地推廣到測試集。這種情況很常見,但透過調整超引數(例如增加丟棄率)可以盡量減少這種差距。
使用大語言模型進行垃圾郵件分類別
在對模型進行微調和評估後,我們現在可以使用它來分類別垃圾郵件了。讓我們使用我們微調過的根據GPT的垃圾郵件分類別模型。以下的classify_review
函式遵循與之前實作的SpamDataset
類別似的資料預處理步驟。然後,在將文書處理為令牌ID後,該函式使用模型預測一個整數類別標籤,類別似於我們在第6.6節中實作的內容,然後傳回相應的類別名稱。
def classify_review(
text, model, tokenizer, device, max_length=None, pad_token_id=50256
):
model.eval()
input_ids = tokenizer.encode(text)
supported_context_length = model.pos_emb.weight.shape[1]
input_ids = input_ids[:min(max_length, supported_context_length)]
input_ids += [pad_token_id] * (max_length - len(input_ids))
input_tensor = torch.tensor(input_ids, device=device).unsqueeze(0)
with torch.no_grad():
# 使用模型進行預測
outputs = model(input_tensor)
# 取得預測結果
_, predicted = torch.max(outputs.scores, dim=1)
# 將預測結果轉換為類別名稱
class_name = "垃圾郵件" if predicted.item() == 1 else "正常郵件"
return class_name
內容解密:
在上述程式碼中,我們首先將模型設定為評估模式(model.eval()
),然後對輸入文字進行編碼,得到令牌ID。接著,我們計算出支援的上下文長度,確保輸入序列不超過模型能夠處理的最大長度。然後,我們對輸入序列進行填充,確保所有序列都具有相同的長度。接下來,我們將輸入序列轉換為張量,並將其傳入模型進行預測。最後,我們取得預測結果,並根據預測結果傳回相應的類別名稱。
圖表翻譯:
以下是使用Mermaid語法繪製的流程圖,展示了垃圾郵件分類別過程:
flowchart TD A[輸入文字] --> B[編碼] B --> C[填充] C --> D[模型預測] D --> E[取得預測結果] E --> F[傳回類別名稱]
在這個流程圖中,我們可以看到垃圾郵件分類別過程的各個步驟,從輸入文字開始,到傳回類別名稱為止。每個步驟都對應著程式碼中的特定部分,展示了整個過程的邏輯流程。
文字分類別模型的實作
在進行文字分類別任務時,需要將預訓練好的模型應用於新的文字資料上。以下是使用模型對新文字進行分類別的步驟:
步驟1:模型輸出處理
當我們使用模型對新文字進行預測時,需要處理模型的輸出結果。假設我們的模型輸出是一個logits張量,我們可以使用torch.argmax
函式來獲得預測標籤。
logits = model(input_tensor)[:, -1, :]
predicted_label = torch.argmax(logits, dim=-1).item()
步驟2:分類別結果判斷
根據預測標籤,我們可以判斷新文字是否為垃圾郵件(spam)。如果預測標籤為1,則判斷為垃圾郵件,否則判斷為非垃圾郵件。
return "spam" if predicted_label == 1 else "not spam"
資料準備階段
在進行文字分類別任務之前,需要準備好資料集。以下是資料準備階段的步驟:
- 下載資料集:下載所需的文字資料集。
- 預處理資料集:對資料集進行預處理,例如_tokenization_、_stopword_移除等。
- 建立資料載入器:建立資料載入器,以便於批次讀取資料。
- 初始化模型:初始化預訓練好的模型。
- 載入預訓練權重:載入預訓練好的模型權重。
- 修改模型:對模型進行修改,以便於fine-tuning。
- 實作評估工具:實作評估工具,以便於評估模型的效能。
內容解密:
上述步驟中,logits
張量代表模型的輸出結果,predicted_label
代表預測的標籤。torch.argmax
函式用於獲得預測標籤,item()
方法用於獲得標籤的值。
圖表翻譯:
以下是使用Mermaid語法繪製的流程圖:
flowchart TD A[文字輸入] --> B[模型輸出] B --> C[預測標籤] C --> D[分類別結果] D --> E[輸出結果]
圖表展示了文字分類別的流程,從文字輸入到模型輸出,然後到預測標籤,最終到分類別結果和輸出結果。
從商業價值視角來看,本文探討的進階分類別模型微調技術,能有效提升垃圾郵件識別等商業應用場景的效率。分析段落中提供的程式碼範例與圖表,清晰地展現了模型訓練、評估、微調的完整流程,以及如何透過損失曲線和準確度指標來判斷模型效能。技術限制深析部分指出,epoch 數量、學習率等超引數的調整需要根據具體任務和資料集特性進行,沒有通用的最佳設定。文章也提供實務建議,例如透過觀察訓練損失和驗證損失的差距來判斷是否過度擬合,並據此調整超引數。展望未來,隨著大語言模型的持續發展,預期微調技術將在更多特定領域的文字分類別任務中發揮關鍵作用,例如客戶服務、市場分析等,進一步提升商業決策的效率和準確性。玄貓認為,掌握微調技術對於提升模型效能至關重要,企業應積極探索並將其整合至現有系統中,以最大化商業價值。