電腦視覺 - 影像分割 - U-Net

電腦視覺 - 影像分割 - U-Net

除了知道圖片裡「有什麼」,我們還想知道它「在哪裡」與「是什麼形狀」。本篇將帶你了解影像分割的經典模型 U-Net,並透過 Python 與 PyTorch 實作,從零訓練一個模型來抓出圖片中的物件輪廓。


在之前的文章中,我們學習了 CNN (用來判斷整張圖片是什麼) 以及 YOLO (用來框出物件在哪裡)。但如果我們遇到的是醫學影像(例如找出腫瘤的精確邊界)或是自動駕駛(辨識哪裡是道路、哪裡是行人),只用一個「方框」是不夠的。

我們需要的是影像分割 (Image Segmentation),也就是為圖片中的「每一個像素 (Pixel)」進行分類。而在這領域中,最經典且強大的模型就是 U-Net


一、 影像分割的演進與模型發展史

要了解 U-Net 為什麼偉大,我們必須先回顧「影像分割」這個領域是如何演進的:

  1. FCN (全卷積神經網路, 2014)開山鼻祖。科學家首次把 CNN 最後的「全連接層 (Dense Layer)」拔掉,全部換成卷積層。這讓模型能輸出跟原圖一樣大小的「熱力圖 (Heatmap)」,而不只是一個分類標籤。缺點是輸出的邊界非常模糊。
  2. U-Net (2015)醫學影像的救星。為了解決 FCN 邊界模糊的問題,提出了「U 字型架構」與「跳躍連接 (Skip Connections)」,完美結合了深層語意與淺層細節,成為影像分割領域的黃金標準。
  3. Mask R-CNN (2017)實例分割 (Instance Segmentation) 的王者。U-Net 只能分出「哪些像素是人」,但 Mask R-CNN 不僅能分割,還能告訴你「這是路人 A,那是路人 B」。它將 YOLO 那樣的物件偵測與影像分割完美結合。
  4. SAM (Segment Anything Model, 2023)分割界的 ChatGPT。由 Meta 發表,這是一個基於 Transformer 的超大型基礎模型 (Foundation Model)。你只要點擊圖片上的任何物體,或是輸入文字,它就能「Zero-shot (零樣本)」直接完美分割出該物件。

了解了這段發展史,我們就來好好拆解這座影像分割領域的重要里程碑:U-Net。


二、 資料集來源:合成雜訊形狀資料集 (Synthetic Noisy Shapes)

為了解釋 U-Net 的威力,我們這次不使用龐大的真實資料集,而是自己用 Python 寫一個生成器,創造出一個「充滿雜訊的形狀資料集」。

資料集特色與欄位介紹:

這是一個 二元影像分割問題 (Binary Image Segmentation)

  • 目標 (Ground Truth Mask):一張黑白的遮罩圖片,白色的部分 (1) 代表物件,黑色的部分 (0) 代表背景。
  • 輸入特徵 (Input Image):我們在原圖上加入大量的隨機高斯雜訊 (Gaussian Noise),讓物件變得模糊難辨,用來測試模型是否能「看透」雜訊抓出真正的形狀。

三、 原理:為什麼叫 U-Net?

U-Net 發表於 2015 年,最初是為了解決醫學影像分割問題而誕生的。它的網路架構畫出來就像一個英文字母 "U",因此得名。

我們可以透過以下的 Mermaid 圖表,直觀地看看這個「U 字型」長什麼樣子:

Loading Diagram...

U-Net 核心由兩個部分組成:

  1. 編碼器 (Encoder / Downsampling):U 的左半邊。它像一般的 CNN 一樣,不斷地縮小圖片尺寸並萃取特徵(尋找「這是什麼」的語意資訊)。
  2. 解碼器 (Decoder / Upsampling):U 的右半邊。它將縮小後的特徵圖逐步放大,恢復到原本的圖片尺寸(重建「在哪裡」的空間資訊)。

跳躍連接 (Skip Connections):U-Net 的靈魂

許多初學者在這裡會不太好理解,為什麼我們需要「跳躍連接」?

讓我們用一個「畫地圖」的例子來想像這個 U 字型架構

  1. U 型的左半邊 (縮小/編碼):為了看懂地圖的「整體大局(例如:這裡有一座山、一條河)」,網路會把圖片不斷縮小。這時模型懂了「這是什麼」,但因為圖片變小了,河流確切的邊界細節已經糊掉、遺失了。
  2. U 型的右半邊 (放大/解碼):現在我們要把這張縮小、充滿大局觀的特徵圖,重新放大回原本的尺寸,並精準標出河流的位置。如果只憑剛剛模糊的印象來放大,畫出來的邊界一定很不精準。
  3. 跳躍連接的作用:它就像是個作弊橋樑!在你(右半邊)準備放大的每一個階段,它會把「左半邊還沒縮小前、邊界依然清晰的局部草圖」直接拷貝一份,透過一條捷徑貼到右半邊給模型當參考

如此一來,右半邊的網路在預測時,同時擁有了:

  • 從 U 型底部深層網路傳上來的 「宏觀理解」(我知道這整塊區域是一條河)。
  • 從左側跳躍連接傳過來的 「微觀細節」(我看著清晰的草圖,知道河流的邊緣確切在哪裡)。

兩者完美拼接在一起,這就是 U-Net 能夠畫出極度精準輪廓的真正秘密!


四、 實戰:PyTorch 程式碼實作

我們用 PyTorch 實作了一個迷你的 U-Net。你可以看到 forward 函數中,是如何完美體現「跳躍連接」的:

# 關鍵程式碼:U-Net 的網路架構與跳躍連接
import torch
import torch.nn as nn

class TinyUNet(nn.Module):
    # ... (初始化省略)
        
    def forward(self, x):
        # 1. Encoder (左半邊:向下萃取)
        conv1 = self.dconv_down1(x)    # 產生草稿
        x = self.maxpool(conv1)        # 縮小
        conv2 = self.dconv_down2(x)
        
        # 2. Decoder (右半邊:向上還原)
        x = self.up(conv2)             # 放大
        
        # 3. Skip Connection (跳躍連接:將左邊的草稿貼過來!)
        x = torch.cat([x, conv1], dim=1) 
        
        x = self.dconv_up1(x)
        
        # 輸出預測的遮罩 (Mask)
        out = torch.sigmoid(self.conv_last(x))
        return out

五、 模型評估:U-Net 實測結果

我們將這個迷你的 U-Net 訓練了 10 個 Epoch(因為模型很小,在 CPU 上只需幾秒鐘),並觀察 Loss 的變化:

  • Epoch 1: Loss: 0.5436
  • Epoch 10: Loss: 0.0169 (成功收斂!)

視覺化解析:模型看到了什麼?

我們隨機抽取一張沒見過的測試圖片讓模型預測,結果如下:

U-Net預測結果

圖片結果與說明:

  1. 左圖 (Input Image):這是模型看到的畫面,充滿了雪花般的雜訊,人類肉眼雖然能看出有個圓形,但邊界非常模糊。
  2. 中圖 (True Mask):這是我們期望模型輸出的標準答案(乾淨的圓形遮罩)。
  3. 右圖 (Predicted Mask):這是 U-Net 預測出的結果
    • 結果驚人:儘管輸入圖片充滿雜訊,U-Net 依然完美地過濾了背景干擾,精準地還原了圓形的平滑邊界。這就是「跳躍連接」保留空間細節的威力。

六、 總結

  1. 像素級的預測:影像分割 (Segmentation) 比物件偵測更進一步,它能告訴我們每一個像素屬於什麼分類。
  2. U 字型架構:左半邊負責理解「這是什麼」,右半邊負責還原「它在哪裡」。
  3. 跳躍連接 (Skip Connection):U-Net 的靈魂,完美融合了深層的語意資訊與淺層的邊界細節。

在醫學影像、衛星空拍圖分析、甚至現在最紅的 AI 繪圖 (Diffusion Model 內部也使用了 U-Net 架構!) 中,U-Net 都是不可或缺的核心基石。