返回文章列表

CLIP模型實作與擴散模型介紹

本文介紹CLIP模型的實作與應用,並探討擴散模型的原理和實作步驟。CLIP模型能有效橋接視覺與文字資料,實作影像與文字描述的匹配。文章以Google

機器學習 深度學習

CLIP 模型能有效地將影像和文字描述聯絡起來,透過理解搜尋詞彙與影像之間的語義內容,提升搜尋的準確性和效率。本文以 Google Colab 為例,示範如何使用預訓練的 CLIP 模型,並以一張黑白毛色的狗的圖片進行測試,觀察不同描述句子的匹配程度。程式碼部分包含安裝 transformers 函式庫、載入模型和處理器、預處理影像和文字輸入,以及計算相似度分數等步驟。除了 CLIP 模型,本文也介紹了擴散模型,一種根據逐步新增和去除噪聲來生成影像的模型。文章詳細說明瞭如何從零開始實作一個擴散模型,包含安裝必要的函式庫、預處理 MNIST 資料集(篩選數字 7),以及定義模型訓練所需的函式。此外,文章也介紹了 U-Net 架構,一種常用於影像分割的卷積神經網路,並說明其如何作為擴散模型的基礎元件。

CLIP 模型實作與擴散模型介紹

CLIP 模型的實際應用與實作

CLIP(Contrastive Language-Image Pre-training)是一種能夠有效橋接視覺與文字資料之間差距的模型。其核心能力在於能夠理解搜尋詞彙與影像目錄之間的語義內容,從而確保匹配結果在視覺與上下文上都合適。這種應用不僅提升了使用者經驗,使產品搜尋更加直觀和高效,也展現了 CLIP 在處理複雜文字描述與大量視覺資料之間的潛力。

在 Google Colab 中實作 CLIP 模型

本文將在 Google Colab Notebook 中實作 CLIP 模型,並探討其如何有效地連線視覺與文字資料。我們將使用一張具有獨特黑白毛皮的狗的影像(圖 2-4)作為測試物件。同時,我們將輸入一系列句子,每個句子描述狗的潛在特徵。CLIP 的優勢在於能夠根據影像上下文評估這些句子,提供機率分數以指示每個描述與視覺資訊的匹配程度。

步驟 1:安裝必要的函式庫與載入資料

首先,我們需要安裝必要的函式庫並載入所需的資料。

pip install transformers

上述命令安裝了由 Hugging Face 開發的 transformers 函式庫,該函式庫提供了廣泛的預訓練模型來執行不同模態的任務。

from PIL import Image
import os
from transformers import CLIPProcessor, CLIPModel
import pandas as pd
import torch

接下來,我們將連線到 Google Drive 以存取將作為輸入的影像。

from google.colab import drive
drive.mount('/content/drive')
os.chdir("/content/drive/My Drive/Colab Notebooks")
image_path = 'benali_image.jpg'
Image.open(image_path)

內容解密:

  1. 安裝 transformers 函式庫:使用 pip install transformers 命令安裝 Hugging Face 的 transformers 函式庫,為後續使用 CLIP 模型做準備。
  2. 載入必要的 Python 函式庫:匯入處理影像、資料處理和深度學習所需的函式庫,包括 PILostransformerspandastorch
  3. 連線 Google Drive:使用 google.colabdrive 模組掛載 Google Drive,以便存取儲存在雲端的影像檔案。
  4. 指定影像路徑:設定影像檔案的路徑並開啟影像,準備進行後續處理。

步驟 2:資料預處理

接下來,我們進行資料預處理。

model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
inputs = processor(text=["a photo of a cat", "a photo of a dog with black and white fur", "a photo of a boy", "baseball field with a lot of people cheering"], images=image, return_tensors="pt", padding=True)

內容解密:

  1. 載入 CLIP 模型:使用 CLIPModel.from_pretrained 方法載入預訓練的 CLIP 模型,這裡使用的是 “openai/clip-vit-base-patch32” 版本。
  2. 建立 CLIP 處理器:使用 CLIPProcessor.from_pretrained 方法建立一個 CLIP 處理器例項,同樣根據 “openai/clip-vit-base-patch32” 版本,用於預處理文字和影像資料。
  3. 預處理輸入資料:使用處理器對輸入的文字描述和影像進行預處理,將它們轉換為 PyTorch 張量,並進行填充以確保所有文字輸入具有統一的長度。

步驟 3:模型推斷

在步驟 3 中,我們進入模型推斷階段,將 CLIP 模型應用於預處理後的資料,從而提取文字與影像之間的洞察和相關性。

outputs = model(**inputs)
logits_per_image = outputs.logits_per_image
probs = logits_per_image.softmax(dim=1)
probs_percentage = probs.detach().numpy() * 100
text_inputs = ["a photo of a cat", "a photo of a dog with black and white fur", "a photo of a boy", "baseball field with a lot of people cheering"]
df = pd.DataFrame({
    'text_input': text_inputs,
    'similarity_with_image (%)': probs_percentage[0]
})
print(df)

內容解密:

  1. 將預處理後的輸入饋送給 CLIP 模型:使用模型的 forward 方法(透過解封裝 inputs 字典)對預處理後的文字和影像進行評估。
  2. 計算影像-文字相似度分數:從模型的輸出中提取影像-文字相似度分數,並使用 softmax 函式將其轉換為機率。
  3. 將機率轉換為百分比形式:將得到的機率乘以 100 以轉換為百分比形式,便於解讀。
  4. 建立資料框以展示結果:使用 pandas 建立一個資料框,展示每個文字輸入與影像之間的相似度百分比。

輸出結果如下:

                  text_input  similarity_with_image (%)
0           a photo of a cat             1.893156
1  a photo of a dog with black...            62.819950
2           a photo of a boy            35.285812
3  baseball field with a lot ...             0.001074

從輸出結果中可以明顯看出,「a photo of a dog with black and white fur」這個句子與輸入影像具有最高的相似度,準確地匹配了我們的輸入影像。令人印象深刻的是,CLIP 在短短幾秒內就達到了這一結果。

擴散模型簡介

擴散模型是生成式 AI 領域的一項創新技術,標誌著人工智慧在建立複雜資料(如影像、音訊或文字)方面的新里程碑。這些模型的運作原理是首先向資料樣本中引入隨機性或噪聲,然後有條不紊地學習逆轉這一過程。這一過程透過兩個階段展開:在前向階段,模型逐步破壞原始資料;在反向階段,模型則仔細地從噪聲狀態重建原始資料。這種機制使擴散模型能夠生成高度細緻和逼真的輸出。

從零實作擴散模型

本章節將探討如何從零開始實作擴散模型。我們將逐步改善一張完全雜訊的影像,使其變成清晰的影像。為了實作這一點,我們的深度學習模型將依賴兩個關鍵輸入:輸入影像(即需要處理的雜訊影像)和時間戳(告知模型目前的雜訊狀態)。時間戳在引導模型的學習過程中扮演著關鍵角色,使模型更容易理解和逆轉每個階段的雜訊新增過程。這種方法使我們能夠建立一個能夠增強影像品質並提供對擴散模型中影像轉換動態過程的洞察的模型。

步驟1:安裝必要的函式庫

在步驟1中,我們開始實作擴散模型,首先安裝必要的函式庫並載入資料,為我們的生成之旅奠定初始基礎。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import transforms
from tqdm.auto import trange, tqdm
import matplotlib.pyplot as plt

image_size = 32
batch_size = 64
number_of_timesteps = 30
time_scale_array = 1 - np.linspace(0, 1.0, number_of_timesteps + 1)

device = torch.device('cuda')

內容解密:

  • 首先,我們匯入所有必要的函式庫。我們將使用PyTorch進行實作,並使用來自torchvision函式庫的MNIST資料集。
  • image_size設為32,將MNIST影像縮放到32x32畫素。
  • batch_size設為64,表示訓練時的批次大小。
  • number_of_timesteps表示將雜訊影像轉換為清晰影像的步驟數。
  • time_scale_array建立一個從1到0的線性間隔陣列,用於控制每個步驟中資料中的雜訊量。
  • device設為GPU(若可用),以加速深度學習模型的運算。

步驟2:資料預處理

步驟2專注於資料預處理,這是一個關鍵階段,我們在此階段調整資料集以與擴散模型相容。

mnist_image_transform = transforms.Compose([
    transforms.Resize((32, 32)),
    transforms.ToTensor(),
    transforms.Lambda(lambda x: x.repeat(3, 1, 1)),
    transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
])

dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=mnist_image_transform)

index_for_7 = [i for i, (img, label) in enumerate(dataset) if label == 7]
subset_training = torch.utils.data.Subset(dataset, index_for_7)
traindata_loader = torch.utils.data.DataLoader(subset_training, batch_size=batch_size, shuffle=True, num_workers=4)

內容解密:

  • 我們使用PyTorch的torchvision函式庫匯入MNIST資料集,並對其進行多項轉換,包括縮放、轉換為張量、重複通道以模擬RGB影像,以及標準化。
  • index_for_7儲存了所有標籤為7的影像索引,我們只對這些影像進行訓練。
  • subset_training是原始MNIST資料集的一個子集,僅包含標籤為7的影像。
  • traindata_loader設定了一個DataLoader,用於在訓練過程中迭代這個子集,具有指定的批次大小、洗牌和平行載入設定。

步驟3:模型訓練

在步驟3中,我們探討模型訓練的核心,擴散模型在此階段從我們準備好的資料中學習。

def add_stepwise_forward_noise(x_img, t_step):
    p = time_scale_array[t_step]
    q = time_scale_array[t_step + 1]
    noise = np.random.normal(size=x_img.shape)
    p = p.reshape((-1, 1, 1, 1))
    q = q.reshape((-1, 1, 1, 1))
    img_p = x_img * (1 - p) + noise * p
    img_q = x_img * (1 - q) + noise * q
    return img_p, img_q

def generate_random_timesteps(num):
    return np.random.randint(0, number_of_timesteps, size=num)

def display_image_grid(image):
    image = image.permute([0, 2, 3, 1])
    image = image - image.min()
    image = (image/image.max())
    return image.numpy().astype(np.float32)

內容解密:

  • add_stepwise_forward_noise函式根據給定的時間步t_step,在輸入影像x_img上新增控制量的雜訊,模擬擴散過程中的前向過程。
  • generate_random_timesteps函式生成一組隨機的時間步,這在訓練根據擴散的生成模型時至關重要。
  • display_image_grid函式用於顯示影像網格,將影像張量轉換為適當的格式並進行標準化,以便視覺化。

重點整理

透過本章節的介紹,我們瞭解瞭如何從零開始實作一個擴散模型,包括必要的函式庫安裝、資料預處理和模型訓練的基本步驟。未來,我們可以進一步探討如何最佳化模型引數、改善生成影像的品質,以及將擴散模型應用於更複雜的任務和資料集上。

U-Net架構與擴散模型基礎

在探討擴散模型的實作之前,瞭解U-Net架構是非常重要的。U-Net是一種特殊的卷積神經網路,設計用於影像分割任務。其架構特點是對稱的「U」形結構,如圖2-7所示。這個結構由收縮路徑和擴張路徑組成,收縮路徑用於擷取上下文資訊,而擴張路徑則能夠實作精確的定位。

U-Net架構解析

  • 收縮路徑(Contracting Path): 這部分由一系列的卷積層和最大池化層組成。卷積層負責從影像中提取和學習特徵,而最大池化層則降低影像的空間維度,從而增加特徵圖的深度。這一過程對於理解影像的上下文至關重要。
  • 擴張路徑(Expansive Path): 在收縮路徑之後,擴張路徑使用轉置卷積來上取樣特徵圖,逐漸增加其空間解析度以重建分割圖。U-Net的特別之處在於其使用了跳躍連線,將收縮路徑的層與擴張路徑的對應層連線起來。這些連線對於結合高層次的上下文資訊和低層次的空間資訊非常重要,從而促進了精確分割圖的生成。

建立擴散模型的基礎元件

首先,我們來定義一個基本的建構區塊(building_block),它是U-Net模型的基本組成部分。

class building_block(nn.Module):
    def __init__(self, in_channels=128, size=32):
        super(building_block, self).__init__()
        self.image_feature_conv = nn.Conv2d(in_channels=in_channels, out_channels=128, kernel_size=3, padding=1)
        self.output_conv = nn.Conv2d(in_channels=in_channels, out_channels=128, kernel_size=3, padding=1)
        self.timestep_dense = nn.Linear(192, 128)
        self.normalized_layer = nn.LayerNorm([128, size, size])

    def forward(self, image, timestep):
        image_feature = F.relu(self.image_feature_conv(image))
        timestep_feature = F.relu(self.timestep_dense(timestep))
        timestep_feature = timestep_feature.view(-1, 128, 1, 1)
        conditioned_image_feature = image_feature * timestep_feature
        final_output = self.output_conv(image)
        final_output = final_output + conditioned_image_feature
        final_output = F.relu(self.normalized_layer(final_output))
        return final_output

內容解密:

  • building_block類別定義了一個神經網路模組,用於處理輸入資料。
  • __init__方法初始化了多個神經網路層,包括卷積層(image_feature_convoutput_conv)、全連線層(timestep_dense)和層歸一化(normalized_layer)。
  • forward方法定義了輸入資料的前向傳遞過程。它首先對影像進行特徵提取,然後處理時間步資訊,並將這兩者結合起來以調節影像特徵。
  • 最終輸出是經過調節的特徵圖,並且透過層歸一化和ReLU啟用函式進行處理。

TimeStepUNet模型定義

class TimeStepUNet(nn.Module):
    def __init__(self):
        super(TimeStepUNet, self).__init__()
        self.timestep_processor = nn.Sequential(
            nn.Linear(1, 192),
            nn.LayerNorm([192]),
            nn.ReLU(),
        )
        self.downsample_x32 = building_block(in_channels=3, size=32)
        self.downsample_x16 = building_block(size=16)
        self.downsample_x8 = building_block(size=8)
        self.downsample_x4 = building_block(size=4)
        self.combined_feature_processor = nn.Sequential(
            nn.Linear(2240, 128),
            nn.LayerNorm([128]),
            nn.ReLU(),
            nn.Linear(128, 32 * 4 * 4),
        )

內容解密:

  • TimeStepUNet類別定義了一個U-Net模型,專門用於處理帶有時間步資訊的影像資料。
  • timestep_processor是一個順序容器,用於處理時間步資訊,將其從1維轉換為192維,並進行層歸一化和ReLU啟用。
  • 多個building_block例項(downsample_x32downsample_x16downsample_x8downsample_x4)被用來處理不同解析度的影像特徵。
  • combined_feature_processor是用於處理組合特徵的順序容器,將特徵維度從2240降低到128,然後再到32*4*4。

圖表翻譯:

此圖示展示了U-Net架構及其在擴散模型中的應用。U-Net透過其對稱的「U」形結構,有效地擷取上下文資訊並實作精確的定位。在擴散模型中,U-Net被用來處理帶有時間步資訊的影像資料,透過調節影像特徵來生成精確的分割圖或重建影像。