返回文章列表

深度學習資料增強技術與模型最佳化

本文探討深度學習中的資料增強技術,包含 PyTorch 的 torchvision.transforms 模組提供的 RandomAffine、Lambda

深度學習 影像處理

深度學習模型的效能提升,除了仰賴模型架構設計,資料增強和訓練策略也扮演著至關重要的角色。本文介紹如何利用 PyTorch 的 torchvision.transforms 模組進行資料增強,包含仿射變換、色彩空間轉換和新增高斯噪聲等技巧,並提供自定義變換的實作方法,讓讀者能根據實際需求調整資料增強策略。此外,文章也探討了逐步提升影像尺寸的訓練技巧,以及透過整合學習方法結合多個模型的預測結果以提升整體效能。最後,文章也深入淺出地介紹了迴圈神經網路(RNN),特別是 LSTM 和其變體 GRU 與 biLSTM 的應用,以及詞嵌入技術的應用,這些技術在自然語言處理領域中扮演著重要的角色。

資料增強技術在深度學習中的應用

在深度學習領域,資料增強(Data Augmentation)是一種透過對原始資料進行多樣化轉換,從而增加訓練資料量和多樣性的技術。這種技術可以有效提升模型的泛化能力,減少過擬合的風險。在影像處理領域,資料增強尤其重要,因為它可以模擬不同的拍攝條件、角度和光線,從而使模型更加穩健。

隨機仿射變換(RandomAffine)

PyTorch 的 torchvision.transforms 模組提供了 RandomAffine 變換,可以對影像進行隨機的仿射變換,包括縮放、旋轉、平移和剪下等操作。以下是一個使用 RandomAffine 的範例:

torchvision.transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0)

其中,degrees 引數控制旋轉的角度範圍,可以是單一數值或一個範圍。translate 引數控制平移的範圍,是一個包含水平和垂直平移倍率的元組。scale 引數控制縮放的範圍,是一個包含最小和最大縮放倍率的元組。shear 引數控制剪下的角度範圍,可以是單一數值或一個範圍。

程式碼範例

import torchvision.transforms as transforms

# 定義 RandomAffine 變換
transform = transforms.RandomAffine(degrees=10, shear=50)

# 對影像進行變換
image = ...  # 載入影像
transformed_image = transform(image)

內容解密:

  1. degrees=10:設定旋轉角度範圍為 -10 到 10 度之間。
  2. shear=50:設定剪下角度範圍為 -50 到 50 度之間。
  3. transform(image):對輸入影像進行隨機仿射變換。

自定義變換

除了使用 PyTorch 提供的內建變換外,我們還可以透過 transforms.Lambda 或自定義變換類別來實作特定的資料增強邏輯。例如,將影像轉換到不同的色彩空間(如 HSV),或新增高斯噪聲。

Lambda 變換範例

def _random_colour_space(x):
    output = x.convert("HSV")
    return output

colour_transform = transforms.Lambda(lambda x: _random_colour_space(x))
random_colour_transform = torchvision.transforms.RandomApply([colour_transform])

內容解密:

  1. _random_colour_space(x):將影像轉換到 HSV 色彩空間。
  2. transforms.Lambda:將自定義函式包裝成變換。
  3. RandomApply:以一定的機率應用指定的變換。

自定義變換類別範例

class Noise:
    """Adds gaussian noise to a tensor."""
    def __init__(self, mean, std):
        self.mean = mean
        self.std = std
    
    def __call__(self, tensor):
        noise = torch.normal(self.mean, self.std, tensor.shape)
        return tensor + noise
    
    def __repr__(self):
        return f"Noise(mean={self.mean}, std={self.std})"

# 使用自定義 Noise 變換
transform = transforms.Compose([
    transforms.ToTensor(),
    Noise(0.1, 0.05),
])

內容解密:

  1. __init__:初始化噪聲的均值和標準差。
  2. __call__:對輸入張量新增高斯噪聲。
  3. __repr__:傳回變換的字串表示,用於除錯。

探討資料擴增與模型最佳化技術

自定義轉換操作:Noise 類別實作

在深度學習的影像處理中,資料擴增是一種常見的技術,用於提升模型的泛化能力。以下是一個自定義的 Noise 類別,用於在影像資料中新增雜訊:

def __init__(self, mean, stddev):
    self.mean = mean
    self.stddev = stddev

def __call__(self, tensor):
    noise = torch.zeros_like(tensor).normal_(self.mean, self.stddev)
    return tensor.add_(noise)

def __repr__(self):
    repr = f"{self.__class__.__name__}(mean={self.mean}, stddev={self.stddev})"
    return repr

內容解密:

  1. __init__ 方法:初始化 Noise 類別,設定雜訊的平均值 (mean) 和標準差 (stddev)。
  2. __call__ 方法:使該類別例項可被呼叫,新增符合指定平均值和標準差的正態分佈雜訊至輸入的張量 (tensor)。
  3. __repr__ 方法:提供類別例項的字串表示,顯示其平均值和標準差,方便除錯和記錄。

將此 Noise 類別新增到轉換流程中,可以觀察到 __repr__ 方法被呼叫的結果:

transforms.Compose([Noise(0.1, 0.05)])
# 輸出:Compose(Noise(mean=0.1, stddev=0.05))

從小開始,逐步擴充套件

在訓練模型時,一個有效的策略是從較小的影像尺寸開始(如64×64),逐步增加到目標尺寸(如256×256)。這種方法可以使模型先學習影像的整體結構,再細化細節,從而獲得更好的效能。

動態調整影像尺寸

使用 torchvision.transforms.Resize 函式,可以在訓練過程中動態調整影像尺寸:

resize = transforms.Compose([
    transforms.Resize(64),
    # 其他資料擴增轉換...
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

內容解密:

  1. transforms.Resize(64):將影像調整至64×64尺寸。
  2. transforms.ToTensor():將影像轉換為張量格式。
  3. transforms.Normalize:對影像進行標準化處理,使用指定的均值和標準差。

整合學習(Ensemble)

整合多個模型的預測結果,可以提升整體的預測準確性。以下是一個簡單的整合方法,透過平均多個模型的預測結果來獲得最終輸出:

predictions = [model(input) for model in models]
avg_prediction = torch.stack(predictions).mean(0).argmax()

內容解密:

  1. predictions = [model(input) for model in models]:對每個模型進行預測,將結果儲存在列表中。
  2. torch.stack(predictions):將預測結果張量堆積疊在一起,形成一個新的張量。
  3. .mean(0):計算各個模型預測結果的平均值,沿著第0維度進行。
  4. .argmax():找出平均預測結果中機率最高的分佈索引。

進一步閱讀

若希望深入瞭解影像領域的相關技術,可參考以下資源:

  • fast.ai 課程,由 Jeremy Howard、Rachel Thomas 和 Sylvain Gugger 提供。
  • Torchvision 檔案
  • PIL/Pillow 檔案
  • “Cyclical Learning Rates for Training Neural Networks” by Leslie N. Smith (2015)
  • “ColorNet: Investigating the Importance of Color Spaces for Image Classification” by Shreyank N. Gowda and Chun Yuan (2019)

第5章:文字分類別

我們暫時放下影像,轉而關注深度學習在自然語言處理(NLP)領域取得的重大進展。Google Translate就是一個很好的例子。最初,負責翻譯的程式碼是一個龐大的50萬行程式碼系統。而現在根據TensorFlow的新系統大約只有500行程式碼,並且效能優於舊方法。

近年來,在將遷移學習(第4章中介紹過)應用於NLP問題方面也取得了突破。像Transformer架構這樣的新架構,催生了像OpenAI的GPT-2這樣的網路。GPT-2的較大版本生成的文字品質幾乎與人類寫作無異(事實上,OpenAI並未發布該模型的權重,因為擔心它被惡意使用)。

本章將快速介紹迴圈神經網路(RNN)和嵌入技術。然後,我們將探討torchtext函式庫以及如何使用它進行根據LSTM的文字處理。

迴圈神經網路

回顧我們迄今為止使用CNN架構的方式,我們可以看到它們一直是在處理一個完整時間快照。但考慮以下兩個句子片段: “The cat sat on the mat.” 和 “She got up and impatiently climbed on the chair, meowing for food.”

如果你將這兩個句子一個接一個地輸入到CNN中,並詢問“貓在哪裡?”,你會遇到問題,因為網路沒有記憶的概念。這在處理具有時間域的資料(如文字、語音、影片和時間序列資料)時非常重要。

迴圈神經網路(RNN)透過隱藏狀態為神經網路提供了記憶功能。

RNN的工作原理

RNN的工作原理可以這樣理解:“想象一個神經網路與一個for迴圈的結合。” 圖5-1展示了經典RNN結構的示意圖。

在每個時間步t,我們向RNN輸入資料,並獲得一個隱藏輸出狀態ht,同時該輸出也被反饋到RNN中,用於下一個時間步。我們可以展開這個網路,以更深入地瞭解其工作原理,如圖5-2所示。

RNN的展開與反向傳播

在展開檢視中,我們可以看到RNN可以被視為一連串全連線層的管道,連續的輸入被送入序列中的下一個層(當中插入了像ReLU這樣的非線性函式)。當我們得到完整的預測序列後,我們需要將誤差反向傳播透過RNN。由於這涉及到逐步回溯網路的步驟,這個過程被稱為“時間反向傳播”。誤差在整個序列上計算,然後網路按照圖5-2的方式展開,並為每個時間步計算梯度,最後將這些梯度合併以更新網路的分享引數。可以將其視為對各個網路進行反向傳播,並將所有梯度加總起來。

長短期記憶網路(LSTM)

在實踐中,RNN特別容易受到第2章中提到的梯度消失問題,或者更糟糕的梯度爆炸問題(誤差趨向無窮大)。這兩種情況都不好,因此RNN無法被有效地應用於許多原本被認為適合的問題上。這種情況在1997年Sepp Hochreiter和Jürgen Schmidhuber引入LSTM(長短期記憶)變體後得到了改變。

LSTM結構

圖5-3展示了一個LSTM層的結構。雖然看起來很複雜,但實際上並不難理解。關鍵在於思考三個門(輸入門、輸出門和遺忘門)。在標準RNN中,我們“記住”所有事情永遠。但這並不符合我們大腦的工作方式(遺憾的是!),LSTM的遺忘門允許我們模擬這種情況:隨著輸入鏈的繼續,鏈的開始變得越來越不重要。而LSTM遺忘多少是透過訓練學習到的,因此如果網路的最佳利益是遺忘更多資訊,遺忘門引數就會這樣做。

單元最終成為了網路層的“記憶”;輸入門、輸出門和遺忘門將決定資料如何在層中流動。資料可能只是簡單地透過,可能“寫入”單元,並且這些資料可能(或可能不!)透過輸出門流向下一層。

這種元件的組合足以解決梯度消失問題,並且具有圖靈完備性,因此理論上,你可以用它來進行任何可以在電腦上進行的計算。

但事情並沒有就此停止。當然,自LSTM以來,RNN領域又發生了一些發展,我們將在接下來的章節中介紹其中一些主要的發展。

import torch
import torch.nn as nn
import torch.optim as optim

# 定義一個簡單的LSTM模型
class SimpleLSTM(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleLSTM, self).__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        h0 = torch.zeros(1, x.size(0), hidden_size).to(x.device)
        c0 = torch.zeros(1, x.size(0), hidden_size).to(x.device)

        out, _ = self.lstm(x, (h0, c0))
        out = self.fc(out[:, -1, :])
        return out

#### 內容解密:
# 1. SimpleLSTM類別定義了一個簡單的LSTM模型,繼承自nn.Module。
# 2. 在__init__方法中,初始化LSTM層和全連線層。
#    - `nn.LSTM(input_size, hidden_size, batch_first=True)`:建立一個LSTM層,其中input_size是輸入特徵的維度,hidden_size是隱藏狀態的維度,batch_first=True表示輸入資料的第一維是批次大小。
#    - `nn.Linear(hidden_size, output_size)`:建立一個全連線層,將LSTM最後的隱藏狀態對映到輸出維度。
# 3. 在forward方法中,定義了前向傳播過程。
#    - 初始化h0和c0,分別代表LSTM的初始隱藏狀態和初始單元狀態,它們的形狀需要與LSTM層的要求相匹配。
#    - `out, _ = self.lstm(x, (h0, c0))`:將輸入x和初始狀態(h0, c0)傳入LSTM層,獲得輸出out。
#    - `out = self.fc(out[:, -1, :])`:取LSTM輸出的最後一個時間步的結果,透過全連線層得到最終輸出。

LSTM的優勢與應用

LSTM透過引入遺忘門、輸入門和輸出門,有效地解決了傳統RNN中的梯度消失和梯度爆炸問題,使得網路能夠學習長期依賴關係。這使得LSTM在處理具有時間序列特性的資料(如語音、文字、影片等)時表現出色,在諸如語言建模、機器翻譯、語音識別等領域取得了廣泛應用。

迴圈神經網路變體與詞嵌入技術

Gated Recurrent Units(GRU)

自1997年以來,許多LSTM網路的變體被提出,其中一個在2014年出現的變體——Gated Recurrent Unit(GRU),因其在某些領域的流行而值得關注。GRU的主要特點是將LSTM中的遺忘門(forget gate)和輸出門(output gate)合併,這使得GRU相比LSTM具有更少的引數,因此訓練速度更快,執行時佔用的資源更少。雖然GRU本質上可以作為LSTM的直接替代品,但由於其簡化了門控機制,理論上其能力不如LSTM。因此,在實際應用中,建議嘗試使用GRU和LSTM,並根據具體表現選擇合適的架構。

GRU架構分析

GRU的設計哲學在於簡化LSTM的複雜度,同時盡量保留其處理序列資料的能力。其核心改進包括:

  • 合併遺忘門和輸出門,減少引數數量
  • 更快的訓練速度和更低的資源佔用
  • 保持對序列資料的有效建模能力

Bidirectional LSTM(biLSTM)

另一個常見的LSTM變體是雙向LSTM(biLSTM)。傳統的LSTM只能根據過去的資訊進行預測,但在某些應用場景中(如機器翻譯、手寫識別等),未來的資訊同樣重要。biLSTM透過兩個堆積疊的LSTM來解決這個問題:一個處理正向輸入序列,另一個處理反向輸入序列。這種架構使模型能夠同時考慮過去和未來的資訊,從而提高預測準確性。

biLSTM實作要點

在PyTorch中建立biLSTM非常簡單,只需在建立LSTM()單元時傳入bidirectional=True引數即可。biLSTM的優勢在於能夠更全面地理解序列資料的上下文資訊。

詞嵌入(Embeddings)

在處理自然語言處理(NLP)任務時,如何將單詞表示為網路可接受的形式是一個基本問題。最簡單的方法是獨熱編碼(one-hot encoding),但這種方法存在兩個主要缺陷:

  1. 向量維度過高且稀疏,導致計算效率低下。
  2. 無法表示單詞之間的語義關係。

詞嵌入矩陣

為瞭解決獨熱編碼的侷限性,研究者提出了使用詞嵌入矩陣的方法。其核心思想是將高維的獨熱向量對映到一個低維的稠密向量空間中。在這個空間中,語義相似的單詞會被對映到相近的位置,例如「cat」和「kitty」可能會被表示為相近的向量。這種表示方法能夠更好地捕捉單詞之間的語義關係。

詞嵌入的工作原理

詞嵌入層與神經網路中的其他層類別似,其引數在訓練過程中被更新,以使語義相似的單詞在向量空間中聚集在一起。常見的距離度量方法(如歐氏距離或餘弦相似度)可用於衡量單詞之間的相似性。

實務建議

  1. 選擇適合的RNN架構:根據具體任務需求選擇LSTM、GRU或biLSTM。
  2. 使用詞嵌入技術:透過詞嵌入矩陣捕捉單詞之間的語義關係,提高模型的表現。
  3. 嘗試不同的模型組態:在實際應用中,應嘗試不同的模型架構和引陣列態,以找到最優解。