深度學習中的批處理與小批次訓練

深度學習中的批處理與小批次訓練

深度學習徹底改變了人工智慧領域,它使機器能夠從資料中獲取更深入的資訊。深度學習能夠做到這一點,因為它模擬了我們大腦透過神經元突觸邏輯運作的方式。訓練深度學習模型最關鍵的方面之一是如何在訓練過程中將資料輸入模型。批處理和小批次訓練正是為此而生。模型的訓練方式將影響模型投入生產後的整體效能。在本文中,我們將深入探討這些概念,比較它們的優缺點,並探索它們的實際應用。

深度學習訓練過程

訓練深度學習模型涉及最小化損失函式,該函式衡量每個週期後預測輸出與實際標籤之間的差異。換句話說,訓練過程是前向傳播和後向傳播之間的雙人舞蹈。這種最小化通常使用梯度下降來實現,這是一種最佳化演算法,它會朝著減少損失的方向更新模型引數。

深度學習訓練過程

因此,由於計算和記憶體限制,資料很少一次傳遞一個樣本或一次性傳遞所有樣本。相反,資料以稱為“批次”的塊形式傳遞。

資料以稱為“批次”的塊形式傳遞

Source: Medium

在機器學習和神經網路訓練的早期階段,有兩種常見的資料處理方法:

1. 隨機學習

這種方法每次使用單個訓練樣本更新模型權重。雖然它能夠提供最快的權重更新速度,並且在流資料應用中非常有用,但它也存在一些明顯的缺點:

  • 由於梯度噪聲,更新過程極不穩定。
  • 這可能導致收斂效果不佳,並延長整體訓練時間。
  • 不適合使用 GPU 並行處理。

2. 全批次學習

這種方法使用整個訓練資料集來計算梯度,並對模型引數執行單次更新。它具有非常穩定的梯度和收斂行為,這是它的巨大優勢。然而,說到它的缺點,以下是一些:

  • 記憶體佔用極高,尤其是在大型資料集的情況下。
  • 由於需要等待處理整個資料集,因此每次迭代的計算速度很慢。
  • 對於動態增長的資料集或線上學習環境來說,它缺乏靈活性。

隨著資料集越來越大,神經網路也越來越深,這些方法在實踐中被證明效率低下。記憶體限制和計算效率低下迫使研究人員和工程師尋找一個折中方案:小批次訓練。

現在,讓我們嘗試理解什麼是批處理和小批次處理。

什麼是批處理?

對於每個訓練步驟,整個資料集都會一次性輸入模型,這個過程稱為批處理。這項技術的另一個名稱是全批次梯度下降。

什麼是批處理?

Source: Medium

主要特點:

  • 使用整個資料集計算梯度。
  • 每個迭代包含一次前向和後向迭代。
  • 佔用大量記憶體。
  • 通常每個迭代速度較慢,但​​穩定。

使用場景:

  • 當資料集完全適合現有記憶體(適當擬合)時。
  • 當資料集較小時。

什麼是小批次訓練?

小批次訓練是批次梯度下降和隨機梯度下降之間的一種折衷方法。它使用資料的子集或部分,而不是整個資料集或單個樣本。

主要特點:

  • 將資料集拆分成更小的組,例如 32、64 或 128 個樣本。
  • 在每個小批次訓練後執行梯度更新。
  • 實現更快的收斂和更好的泛化。

使用場景:

  • 適用於大型資料集。
  • 當 GPU/TPU 可用時。

讓我們以表格形式總結上述演算法:

型別 批次大小 更新頻率 記憶體需求 收斂性 噪聲
全量批(Full-Batch) 整個資料集 每輪(epoch)一次 穩定、慢
小批次(Mini-Batch) 例如 32 / 64 / 128 每個批次後 平衡
隨機(Stochastic) 單樣本 每個樣本後 嘈雜、快

梯度下降的工作原理

梯度下降的工作原理是不斷迭代更新模型引數,以最小化損失函式。在每一步中,我們計算損失函式相對於模型引數的梯度,並朝著梯度的反方向移動。

梯度下降的工作原理

Source: Builtin

更新規則:θ = θ − η ⋅ ∇θJ(θ)

其中:

  • θ 為模型引數
  • η 為學習率
  • ∇θJ(θ) 為損失函式的梯度

簡單類比

想象一下,你蒙著眼睛,試圖滑到遊樂場滑梯的最低點。你用腳感受坡度,然後一步步小步地往下走。腳下坡度的陡峭程度決定了每一步的走向。由於我們是逐漸下降的,這類似於梯度下降。模型會朝著誤差減少最多的方向移動。

全批次下降類似於使用巨大的滑梯地圖來確定最佳行動方案。你問朋友你想去哪裡,然後在隨機下降中邁出一步。在行動之前,你會在小批次下降中與一小組人商量。

數學公式

設 X ∈ R n×d 為包含 n 個樣本和 d 個特徵的輸入資料。

全批次梯度下降法

全批次梯度下降法 

小批次梯度下降法

小批次梯度下降法 

實際示例

假設嘗試根據評論估算產品價格。

如果您在做出選擇之前閱讀了全部 1000 條評論,則為全批次。僅閱讀一條評論後做出決定是隨機的。小批次是指您只閱讀少量評論(例如 32 條或 64 條)然後估算價格。小批次在足夠可靠以做出明智決策和足夠快速以快速採取行動之間取得了良好的平衡。小批次提供了良好的平衡:它足夠快以快速採取行動,並且足夠可靠以做出明智的決策。

實際實施

我們將使用 PyTorch 演示批次處理和小批次處理之間的區別。透過這個實現,我們將能夠了解這兩種演算法如何幫助我們收斂到最優的全域性最小值。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# Create synthetic data
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)
# Define model architecture
def create_model():
return nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
# Loss function
loss_fn = nn.MSELoss()
# Mini-Batch Training
model_mini = create_model()
optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01)
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)
mini_batch_losses = []
for epoch in range(64):
epoch_loss = 0
for batch_X, batch_y in dataloader:
optimizer_mini.zero_grad()
outputs = model_mini(batch_X)
loss = loss_fn(outputs, batch_y)
loss.backward()
optimizer_mini.step()
epoch_loss += loss.item()
mini_batch_losses.append(epoch_loss / len(dataloader))
# Full-Batch Training
model_full = create_model()
optimizer_full = optim.SGD(model_full.parameters(), lr=0.01)
full_batch_losses = []
for epoch in range(64):
optimizer_full.zero_grad()
outputs = model_full(X)
loss = loss_fn(outputs, y)
loss.backward()
optimizer_full.step()
full_batch_losses.append(loss.item())
# Plotting the Loss Curves
plt.figure(figsize=(10, 6))
plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o')
plt.plot(full_batch_losses, label='Full-Batch Training', marker='s')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset import matplotlib.pyplot as plt # Create synthetic data X = torch.randn(1000, 10) y = torch.randn(1000, 1) # Define model architecture def create_model(): return nn.Sequential( nn.Linear(10, 50), nn.ReLU(), nn.Linear(50, 1) ) # Loss function loss_fn = nn.MSELoss() # Mini-Batch Training model_mini = create_model() optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01) dataset = TensorDataset(X, y) dataloader = DataLoader(dataset, batch_size=64, shuffle=True) mini_batch_losses = [] for epoch in range(64): epoch_loss = 0 for batch_X, batch_y in dataloader: optimizer_mini.zero_grad() outputs = model_mini(batch_X) loss = loss_fn(outputs, batch_y) loss.backward() optimizer_mini.step() epoch_loss += loss.item() mini_batch_losses.append(epoch_loss / len(dataloader)) # Full-Batch Training model_full = create_model() optimizer_full = optim.SGD(model_full.parameters(), lr=0.01) full_batch_losses = [] for epoch in range(64): optimizer_full.zero_grad() outputs = model_full(X) loss = loss_fn(outputs, y) loss.backward() optimizer_full.step() full_batch_losses.append(loss.item()) # Plotting the Loss Curves plt.figure(figsize=(10, 6)) plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o') plt.plot(full_batch_losses, label='Full-Batch Training', marker='s') plt.title('Training Loss Comparison') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() plt.grid(True) plt.tight_layout() plt.show()
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import matplotlib.pyplot as plt
# Create synthetic data
X = torch.randn(1000, 10)
y = torch.randn(1000, 1)
# Define model architecture
def create_model():
return nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
# Loss function
loss_fn = nn.MSELoss()
# Mini-Batch Training
model_mini = create_model()
optimizer_mini = optim.SGD(model_mini.parameters(), lr=0.01)
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)
mini_batch_losses = []
for epoch in range(64):
epoch_loss = 0
for batch_X, batch_y in dataloader:
optimizer_mini.zero_grad()
outputs = model_mini(batch_X)
loss = loss_fn(outputs, batch_y)
loss.backward()
optimizer_mini.step()
epoch_loss += loss.item()
mini_batch_losses.append(epoch_loss / len(dataloader))
# Full-Batch Training
model_full = create_model()
optimizer_full = optim.SGD(model_full.parameters(), lr=0.01)
full_batch_losses = []
for epoch in range(64):
optimizer_full.zero_grad()
outputs = model_full(X)
loss = loss_fn(outputs, y)
loss.backward()
optimizer_full.step()
full_batch_losses.append(loss.item())
# Plotting the Loss Curves
plt.figure(figsize=(10, 6))
plt.plot(mini_batch_losses, label='Mini-Batch Training (batch_size=64)', marker='o')
plt.plot(full_batch_losses, label='Full-Batch Training', marker='s')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

unnamed

在這裡,我們可以將兩種策略的訓練損失隨時間的變化視覺化,以觀察差異。我們可以觀察到:

  1. 小批次訓練通常表現出更平滑、更快的初始進度,因為它更新權重的頻率更高。

訓練損失隨時間的變化視覺化

  1. 全批次訓練可能更新較少,但其梯度更穩定。

在實際應用中,通常優先使用小批次,以獲得更好的泛化能力和計算效率。

如何選擇批次大小?

我們設定的批次大小是一個超引數,需要根據模型架構和資料集大小進行實驗。確定最佳批次大小的有效方法是實施交叉驗證策略。

下表可以幫助您做出此決定:

特性 全量批訓練(Full-Batch) 小批次訓練(Mini-Batch)
梯度穩定性
收斂速度
記憶體佔用
並行化程度
訓練時間 較低(最佳化)
泛化能力 可能過擬合 較好

注意:如上所述,batch_size 是一個超引數,需要根據模型訓練進行微調。因此,有必要了解較低和較高批次大小值的效能。

小批次大小

較小的批次大小值通常介於 1 到 64 之間。由於梯度更新頻率更高(每批次),模型可以更早地開始學習,並快速更新權重,因此更新速度更快。恆定的權重更新意味著一個週期需要更多次迭代,這會增加計算開銷,從而延長訓練時間。

梯度估計中的“噪聲”有助於避免尖銳的區域性最小值和過擬合,通常可以提高測試效能,從而展現出更好的泛化能力。此外,由於這些噪聲的存在,收斂可能會不穩定。如果學習率很高,這些噪聲梯度可能會導致模型超調併發散。

將小批次大小想象成朝著目標邁出頻繁但不穩定的步伐。你可能不會走直線,但你可能會發現一條總體上更好的路徑。

大批次

可以考慮使用 128 及以上的大批次。較大的批次可以實現更穩定的收斂,因為每個批次包含的樣本越多,平均梯度就越平滑,越接近損失函式的真實梯度。如果梯度平滑,模型可能無法擺脫平坦或尖銳的區域性最小值。

這樣,完成一個迭代週期所需的迭代次數就越少,從而可以加快訓練速度。大批次需要更多記憶體,這需要 GPU 來處理這些巨大的資料塊。雖然每個迭代週期的速度更快,但由於更新步長較小且梯度噪聲較少,可能需要更多迭代週期才能收斂。

大批次就像按照預先規劃好的步數穩步朝著目標前進,但有時你可能會因為沒有探索所有其他路徑而陷入困境。

整體差異

這裡有一個比較全批次和小批次訓練的綜合表格。

維度 全量批訓練(Full-Batch Training) 小批次訓練(Mini-Batch Training)
優點 Pros • 梯度穩定且精確
• 損失計算精確
• 由於頻繁更新,訓練速度更快
• 支援 GPU/TPU 並行
• 噪聲有助於提升泛化能力
缺點 Cons • 記憶體消耗高
• 每輪訓練速度慢
• 不適合大規模資料
• 梯度更新噪聲大
• 需要調節批大小
• 穩定性略差
適用場景 Use Cases • 記憶體可容納的小型資料集<br>• 需要高度可復現的實驗 • 大規模資料集
• 在 GPU/TPU 上的深度學習
• 即時/流式訓練管線

實用建議

在批次訓練和小批次訓練之間進行選擇時,請考慮以下幾點:

  • 如果資料集較小(少於 10,000 個樣本)且記憶體不成問題,則全批次梯度下降法可能是可行的,因為它具有穩定性和精確的收斂性。
  • 對於中大型資料集(例如,100,000 個以上樣本),批次大小在 32 到 256 之間的小批次訓練通常是最佳選擇。
  • 在小批次訓練的每個 epoch 之前使用 shuffle 操作,以避免學習資料順序模式。
  • 使用學習速率排程或自適應最佳化器(例如,Adam、RMSProp 等)來幫助緩解小批次訓練中的噪聲更新。

小結

批處理和小批次訓練是深度學習模型最佳化中必須掌握的基礎概念。雖然全批次訓練能夠提供最穩定的梯度,但正如本文開頭所述,由於記憶體和計算能力的限制,它很少適用於現代大規模資料集。另一方面,小批次訓練則實現了恰到好處的平衡,藉助 GPU/TPU 加速,提供了良好的速度、泛化能力和相容性。因此,它已成為大多數實際深度學習應用的事實上的標準。

選擇最佳批次大小並非一刀切的決策。它應該以資料集的大小以及現有的記憶體和硬體資源為指導。此外,還需要考慮最佳化器的選擇以及所需的泛化能力和收斂速度,例如 learning_rate 和 decay_rate。透過理解這些動態因素,並利用學習速率排程、自適應最佳化器(如 ADAM)和批次大小調整等工具,我們可以更快、更準確、更高效地建立模型。

評論留言