深度學習中的轉移學習技術,藉由使用預訓練模型,能有效縮短模型訓練時間並提升效能。本文將以 PyTorch 和 Keras 兩種深度學習框架為例,示範如何使用 ResNet 和 MobileNet 等預訓練模型,快速建構影像分類別模型。我們會逐步講解如何載入預訓練模型,凍結部分權重以保留已學習的特徵,以及如何增加客製化層以適應新的影像分類別任務。文章中將使用 CIFAR-10 資料集作為訓練和評估的範例,並提供完整的程式碼實作。此外,我們也會探討特徵提取和微調等進階技巧,以及如何應用於物件偵測任務,例如使用 YOLO 架構。

轉移學習的基本步驟

  1. 載入預訓練模型:首先,載入一個預先訓練好的模型,這個模型通常是在大型資料集上訓練的,例如ImageNet。
  2. 凍結部分權重:根據需要,凍結模型中某些層的權重,以保留其學到的特徵提取能力。
  3. 增加新層:在預訓練模型的基礎上增加新層,以適應新任務的需求。
  4. 訓練新模型:使用新任務的資料集對新增加的層進行訓練。

PyTorch中的轉移學習例項

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

# 載入預訓練模型
model = torchvision.models.resnet50(pretrained=True)

# 凍結部分權重
for param in model.parameters():
    param.requires_grad = False

# 增加新層
model.fc = nn.Linear(model.fc.in_features, 10)

# 設定損失函式和最佳化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.fc.parameters(), lr=0.001)

# 訓練新模型
for epoch in range(5):
    # 訓練模型
    model.train()
    for inputs, labels in train_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
    
    # 測試模型
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for inputs, labels in test_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            test_loss += loss.item()
            _, predicted = torch.max(outputs, 1)
            correct += (predicted == labels).sum().item()
    
    accuracy = correct / len(test_loader.dataset)
    print(f'Epoch {epoch+1}, Test Accuracy: {accuracy:.2f}%')

Keras中的轉移學習例項

from tensorflow import keras
from tensorflow.keras import layers

# 載入預訓練模型
base_model = keras.applications.MobileNetV2(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 凍結部分權重
base_model.trainable = False

# 增加新層
x = base_model.output
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)

# 建立新模型
model = keras.Model(inputs=base_model.input, outputs=predictions)

# 編譯模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 訓練新模型
model.fit(train_data, train_labels, epochs=5, validation_data=(test_data, test_labels))

使用 Keras 進行轉移學習

在本文中,我們將使用 Keras 和 TensorFlow 來實作兩種轉移學習(Transfer Learning, TL)場景。這樣,我們可以比較這兩個函式庫。在這個例子中,我們將使用 MobileNetV3Small 架構。讓我們開始:

步驟 1:定義迷你批次和輸入影像大小

首先,我們需要定義迷你批次和輸入影像大小。影像大小由玄貓決定。

IMG_SIZE = 224
BATCH_SIZE = 50

步驟 2:載入 CIFAR-10 資料集

接下來,我們使用 TensorFlow datasets 載入 CIFAR-10 資料集。repeat() 方法允許我們重複使用資料集進行多個 epoch。

import tensorflow as tf
import tensorflow_datasets as tfds

data, metadata = tfds.load('cifar10', with_info=True, as_supervised=True)
raw_train, raw_test = data['train'].repeat(), data['test'].repeat()

步驟 3:定義資料轉換函式

現在,我們需要定義兩個函式:train_format_sampletest_format_sample。這些函式將原始影像轉換為適合 CNN 輸入的格式。輸入影像將經過以下轉換:

  • 尺寸重設為 224x224
  • 標準化到 (-1, 1) 區間
  • 標籤轉換為 one-hot 編碼
  • 隨機水平和垂直翻轉訓練影像

以下是實際的實作:

def train_format_sample(image, label):
    """轉換訓練資料"""
    image = tf.cast(image, tf.float32)
    image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_flip_up_down(image)
    label = tf.one_hot(label, 10)
    return image, label

內容解密:

在上述程式碼中,我們使用 tf.cast 將影像轉換為 float32 型別,然後使用 tf.image.resize 尺寸重設為 224x224。接下來,我們使用 tf.image.random_flip_left_righttf.image.random_flip_up_down 對影像進行隨機水平和垂直翻轉。最後,我們使用 tf.one_hot 將標籤轉換為 one-hot 編碼。

圖表翻譯:

  flowchart TD
    A[原始影像] --> B[尺寸重設]
    B --> C[標準化]
    C --> D[隨機翻轉]
    D --> E[one-hot 編碼]
    E --> F[輸出]

此圖表展示了影像轉換的過程,從原始影像到最終的輸出。

使用預訓練模型進行特徵提取

在深度學習中,預訓練模型可以大大加速我們的開發過程。這裡,我們將使用 MobileNetV3Small 這個預訓練模型作為特徵提取器。首先,我們需要載入這個預訓練模型,並且排除掉最後的全連線層(FC層)。

base_model = tf.keras.applications.MobileNetV3Small(
    weights='imagenet', include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3))

接下來,我們需要凍結預訓練模型的權重,以避免在訓練過程中修改它們。這是因為預訓練模型已經學習到了很多有用的特徵,我們不希望在訓練過程中破壞它們。

base_model.trainable = False

然後,我們需要在預訓練模型的輸出上增加一些額外的層,以便能夠輸出我們需要的特徵。這裡,我們增加了一個 GlobalAveragePooling2D 層,來對預訓練模型的輸出進行全域性平均池化。

x = base_model.output
x = tf.keras.layers.GlobalAveragePooling2D()(x)

最後,我們可以定義我們的特徵提取模型了。

fe_model = tf.keras.Model(inputs=base_model.input, outputs=x)

這個特徵提取模型可以用來從輸入影像中提取出有用的特徵。

完整程式碼

def build_fe_model():
    """Create feature extraction model from the pre-trained model MobileNetV3Small"""
    base_model = tf.keras.applications.MobileNetV3Small(
        weights='imagenet', include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3))
    base_model.trainable = False
    x = base_model.output
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    fe_model = tf.keras.Model(inputs=base_model.input, outputs=x)
    return fe_model

內容解密:

在這個程式碼中,我們首先載入了 MobileNetV3Small 這個預訓練模型,並且排除掉了最後的全連線層。然後,我們凍結了預訓練模型的權重,以避免在訓練過程中修改它們。接下來,我們增加了一個 GlobalAveragePooling2D 層,來對預訓練模型的輸出進行全域性平均池化。最後,我們定義了我們的特徵提取模型,並且傳回它。

圖表翻譯:

  graph LR
    A[輸入影像] --> B[預訓練模型]
    B --> C[GlobalAveragePooling2D]
    C --> D[特徵提取模型]
    D --> E[輸出特徵]

這個圖表展示了我們的特徵提取模型的架構。輸入影像首先被輸入到預訓練模型中,然後預訓練模型的輸出被輸入到 GlobalAveragePooling2D 層中。最後,GlobalAveragePooling2D 層的輸出被輸入到特徵提取模型中,然後輸出特徵。

進階電腦視覺應用

在深度學習中,預先訓練好的模型可以作為特徵提取器,幫助我們快速開發新的電腦視覺應用。下面,我們將介紹如何使用預先訓練好的 MobileNetV3Small 模型進行特徵提取和微調。

特徵提取模型

首先,我們需要定義一個特徵提取模型。這個模型使用預先訓練好的 MobileNetV3Small 作為基礎,然後在頂部增加了一個全連線層。以下是實作:

def build_fe_model():
    """建立特徵提取模型"""
    base_model = tf.keras.applications.MobileNetV3Small(
        input_shape=(IMG_SIZE, IMG_SIZE, 3),
        include_top=False,
        weights='imagenet',
        include_preprocessing=True
    )
    base_model.trainable = False  #凍結所有層
    
    model = tf.keras.Sequential([
        base_model,
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.Dense(
            metadata.features['label'].num_classes,
            activation='softmax'
        )
    ])
    return model

在這個模型中,我們首先載入預先訓練好的 MobileNetV3Small 模型,然後凍結所有層,以防止在訓練過程中更新權重。接著,我們在頂部增加了一個全連線層,使用 softmax 啟用函式進行多分類別任務。

微調模型

微調模型與特徵提取模型的主要區別在於,我們只凍結底層的預先訓練網路層,而不是所有層。以下是實作:

def build_ft_model():
    """建立微調模型"""
    base_model = tf.keras.applications.MobileNetV3Small(
        input_shape=(IMG_SIZE, IMG_SIZE, 3),
        include_top=False,
        weights='imagenet',
        include_preprocessing=True
    )
    fine_tune_at = 100  # 從這層開始微調
    
    # 凍結所有層,除了從 fine_tune_at 層開始的層
    for layer in base_model.layers[:fine_tune_at]:
        layer.trainable = False
    
    model = tf.keras.Sequential([
        base_model,
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.Dense(
            metadata.features['label'].num_classes,
            activation='softmax'
        )
    ])
    return model

在這個模型中,我們首先載入預先訓練好的 MobileNetV3Small 模型,然後凍結所有層,除了從 fine_tune_at 層開始的層。這樣,我們就可以在微調過程中更新這些層的權重,以適應新的任務。

內容解密:

在上面的程式碼中,我們使用 tf.keras.applications.MobileNetV3Small 載入預先訓練好的 MobileNetV3Small 模型。然後,我們使用 include_top=False 引數排除頂部的全連線層,因為我們要在頂部增加自己的全連線層。接著,我們使用 weights='imagenet' 引數載入預先訓練好的權重,並使用 include_preprocessing=True 引數啟用預處理。

在微調模型中,我們使用 fine_tune_at 變數控制從哪層開始微調。然後,我們使用 layer.trainable = False 凍結所有層,除了從 fine_tune_at 層開始的層。

圖表翻譯:

  flowchart TD
    A[載入預先訓練好的模型] --> B[凍結所有層]
    B --> C[增加全連線層]
    C --> D[定義微調模型]
    D --> E[凍結部分層]
    E --> F[增加全連線層]
    F --> G[定義微調模型]

在這個流程圖中,我們展示瞭如何建立特徵提取模型和微調模型。首先,我們載入預先訓練好的模型,然後凍結所有層。接著,我們增加全連線層以進行多分類別任務。在微調模型中,我們凍結部分層,然後增加全連線層。

實作深度學習模型的訓練和評估

在深度學習中,模型的訓練和評估是非常重要的步驟。以下是實作模型訓練和評估的步驟。

訓練模型

def train_model(model, epochs=5):
    """
    訓練模型。
    
    這個函式是共用的,適用於特徵提取(FE)和微調(FT)模式。
    
    Args:
        model: 要訓練的模型。
        epochs: 訓練的 epochs 數量,預設為 5。
    """
    
    # 組態模型為訓練模式
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )
    
    # 訓練模型
    history = model.fit(
        train_batches,
        epochs=epochs,
        steps_per_epoch=metadata.splits['train'].num_examples / BATCH_SIZE,
        validation_data=test_batches,
        validation_steps=metadata.splits['test'].num_examples / BATCH_SIZE
    )
    
    return history

評估模型

def evaluate_model(model):
    """
    評估模型。
    
    Args:
        model: 要評估的模型。
    
    Returns:
        評估結果。
    """
    
    # 評估模型
    loss, accuracy = model.evaluate(test_batches)
    
    return loss, accuracy

繪製準確率

def plot_accuracy(history):
    """
    繪製準確率。
    
    Args:
        history: 訓練歷史。
    """
    
    # 繪製準確率
    plt.plot(history.history['accuracy'], label='訓練準確率')
    plt.plot(history.history['val_accuracy'], label='驗證準確率')
    plt.xlabel('Epochs')
    plt.ylabel('準確率')
    plt.legend()
    plt.show()

物體偵測

def object_detection(model, image):
    """
    物體偵測。
    
    Args:
        model: 要使用的模型。
        image: 要偵測的影像。
    
    Returns:
       偵測結果。
    """
    
    # 預測
    predictions = model.predict(image)
    
    # 處理預測結果
    results = []
    for prediction in predictions:
        # 處理單個預測結果
        result = {
            'class': prediction['class'],
            'confidence': prediction['confidence'],
            'bbox': prediction['bbox']
        }
        results.append(result)
    
    return results

範例使用

# 建立模型
model = create_model()

# 訓練模型
history = train_model(model)

# 評估模型
loss, accuracy = evaluate_model(model)

# 繪製準確率
plot_accuracy(history)

# 物體偵測
image = ...
results = object_detection(model, image)

物體偵測技術概述

物體偵測是一種電腦視覺任務,旨在找到影像或影片中特定類別的物體例項,例如人、車輛和樹木。與影像分類別不同,物體偵測不僅能夠辨識物體的類別,還能夠定位物體在影像中的位置。

物體偵測演算法的輸出通常包括以下訊息:

  • 物體的類別(例如人、車輛、樹木等)
  • 物體存在的機率(或物體得分),範圍為[0, 1],表示演算法對物體存在的信心程度
  • 物體所在的矩形區域(稱為邊界盒)

物體偵測方法

目前,物體偵測方法主要分為三類別:

  1. 經典滑動視窗法:此方法使用一個普通的分類別網路(分類別器)對影像中的每個位置進行分類別,以判斷是否存在物體。這種方法可以使用任何型別的分類別演算法,但相對較慢且容易出錯。
    • 建立影像金字塔:將同一影像以不同尺寸進行表示,以便能夠檢測到不同大小的物體。
    • 將分類別器滑動於整個影像中:使用影像中的每個位置作為分類別器的輸入,判斷該位置是否存在物體。
    • 合併多個重疊的邊界盒:使用一些啟發式方法將多個邊界盒合併為單一預測。
  2. 兩階段檢測方法:這種方法非常準確,但相對較慢。如其名稱所示,兩階段檢測方法包括兩個步驟:
    • 區域提案網路(RPN):掃描影像並提出可能包含物體的邊界盒(或稱為區域興趣),但不進行物體類別的判斷。
    • 物體類別判斷:將提出的區域送入第二階段進行物體類別的判斷。
  3. 單階段(或單次)檢測方法:這種方法使用單一的卷積神經網路(CNN)同時預測物體類別和邊界盒。單階段方法通常比兩階段方法更快,但準確度稍低。

YOLO(You Only Look Once)物體偵測演算法

YOLO是一種流行的單階段物體偵測演算法,其名稱反映了其單階段的特性。自最初釋出以來,YOLO已經經歷了多個版本的更新,包括YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5、YOLOv7等。每個版本都在前一個版本的基礎上進行了改進和最佳化。

YOLO的優點在於其高效和實時的物體偵測能力,使其在各種應用領域中得到廣泛使用。然而,YOLO的準確度可能不如兩階段檢測方法,但其速度和效率使其成為了一種非常有競爭力的物體偵測演算法。

YOLO的演進

  • YOLOv1:最初的YOLO版本,由Joseph Redmon、Santosh Divvala、Ross Girshick和Ali Farhadi提出。
  • YOLOv2:由Joseph Redmon和Ali Farhadi提出。
  • YOLOv3:由Joseph Redmon和Ali Farhadi提出。
  • YOLOv4:由Alexey Bochkovskiy、Chien-Yao Wang和Hong-Yuan Mark Liao提出。
  • YOLOv5:由Glenn Jocher提出。
  • YOLOv7:由玄貓、Alexey Bochkovskiy和Hong-Yuan Mark Liao提出,達到了當時的最先進水平。

YOLOv7的提出標誌著YOLO系列演算法又一次取得了重大突破,進一步提高了物體偵測的準確度和效率。

YOLO的應用

YOLO在各種領域中得到廣泛應用,包括:

  • 安全監控:使用YOLO進行實時物體偵測,可以快速回應安全事件。
  • 人工智慧交通:YOLO可以用於車輛、行人和其他交通參與者的偵測,提高交通安全性。
  • 工業檢測:YOLO可以用於產品的品品檢測,自動化生產流程。
  • 醫學影像分析:YOLO可以用於醫學影像中的物體偵測,例如腫瘤的檢測。

YOLO的高效和準確使其成為了一種非常有用的工具,在各種領域中得到廣泛應用和研究。

物件偵測技術:YOLO架構

YOLO(You Only Look Once)是一種實時物件偵測系統,能夠快速地偵測影像中的物件。YOLO的架構主要分為三個部分:Backbone、Neck和Head。

Backbone

Backbone是YOLO架構中的第一部分,負責從輸入影像中提取特徵。通常,Backbone是一個預先訓練好的卷積神經網路(CNN),例如DarkNet-53。這個部分的輸出將被傳遞給Neck進行進一步的處理。

綜觀電腦視覺領域的發展趨勢,轉移學習已成為加速模型訓練、提升效能的關鍵技術。本文從PyTorch和Keras框架的程式碼範例出發,深入剖析了轉移學習的實作步驟,並以MobileNetV3Small模型在CIFAR-10資料集上的應用為例,闡明瞭特徵提取和微調策略的實際操作。此外,本文還探討了YOLO物件偵測技術的演進歷程及其核心架構,展現了其在物件偵測領域的優勢。然而,轉移學習並非萬靈丹,模型選擇、超引數調整以及資料集特性等因素都會影響最終效能。對於追求極致效能的應用場景,仍需針對特定任務進行模型最佳化和客製化調整。展望未來,隨著模型架構的持續創新和訓練資料的指數級增長,預期轉移學習將在更多電腦視覺任務中扮演更重要的角色,並朝向更自動化、更精準的方向發展。玄貓認為,深入理解轉移學習的原理和實務技巧,將有助於開發者構建更強大、更有效率的電腦視覺應用。