zi2zi 目前 L1 Loss 主要考慮像素差異,但可能會造成模糊結果, 利用增強感知損失(Perceptual Loss)讓輸出更加銳利

vgg16:

你可以在現有的 L1 Loss 基礎上,增加 感知損失(Perceptual Loss) 來改善輸出的銳利度。感知損失主要使用預訓練的神經網路(如 VGG)來提取高層特徵,並計算輸入與目標影像之間的特徵差異,而不只是像素差異。這樣可以讓模型學習到更符合人眼感知的結果。


步驟 1:加入 VGG16 感知損失

  1. 載入 VGG16(使用 ImageNet 預訓練權重)
  2. 取出中間層特徵(如 relu1_2, relu2_2, relu3_3
  3. 計算特徵差異的 L1 Loss

1.1 定義 VGG 感知損失

import torchvision.models as models

class PerceptualLoss(nn.Module):
    def __init__(self):
        super(PerceptualLoss, self).__init__()
        vgg = models.vgg16(pretrained=True).features
        self.slices = nn.ModuleList([
            vgg[:4],  # relu1_2
            vgg[4:9],  # relu2_2
            vgg[9:16],  # relu3_3
        ])
        for param in self.parameters():
            param.requires_grad = False  # 凍結 VGG 權重
        self.loss_fn = nn.L1Loss()

    def forward(self, input, target):
        loss = 0
        for slice in self.slices:
            input = slice(input)
            target = slice(target)
            loss += self.loss_fn(input, target)
        return loss

步驟 2:在訓練流程中加入感知損失

Zi2ZiModelsetup 方法中,新增 PerceptualLoss

def setup(self):
    self.netG = UNetGenerator(...)
    self.netD = Discriminator(...)

    self.perceptual_loss = PerceptualLoss().to(self.device)  # 加入感知損失

    self.optimizer_G = torch.optim.AdamW(self.netG.parameters(), lr=self.lr, betas=(self.beta1, 0.999), weight_decay=self.weight_decay)
    self.optimizer_D = torch.optim.AdamW(self.netD.parameters(), lr=self.lr, betas=(self.beta1, 0.999), weight_decay=self.weight_decay)

步驟 3:在 G 的 Loss 計算中加入 Perceptual Loss

def backward_G(self):
    fake_AB = torch.cat([self.real_A, self.fake_B], 1)
    real_AB = torch.cat([self.real_A, self.real_B], 1)

    # 生成對抗損失
    fake_D_logits, _ = self.netD(fake_AB)
    self.g_loss = -torch.mean(F.logsigmoid(fake_D_logits))

    # L1 損失
    self.l1_loss_value = self.l1_loss(self.fake_B, self.real_B) * self.L1_penalty

    # 感知損失(新加入)
    self.perceptual_loss_value = self.perceptual_loss(self.fake_B, self.real_B) * 0.1  # 0.1 為感知損失權重

    # 總損失
    self.loss_G = self.g_loss + self.l1_loss_value + self.perceptual_loss_value
    self.loss_G.backward()

這樣做的優勢: ✅ L1 Loss:確保基礎像素對齊
感知損失(Perceptual Loss):提高銳利度,避免模糊
對抗損失(GAN Loss):增強真實感


挑戰重問一次:

你的 Zi2ZiModel 目前使用 L1 Loss 來計算像素層面的差異,但這可能導致模糊結果。若要讓輸出的字型風格更加接近 Zen Maru Gothic,可以加入 VGG16 感知損失(Perceptual Loss)。這將幫助模型更關注高層次的紋理與風格特徵,而不只是單純的像素對齊。

1. 為什麼選擇 VGG16 而非 VGG19?

  • VGG16 的特徵提取層數較少,計算量比 VGG19 低,訓練更高效。
  • VGG16 低層特徵(如邊緣、紋理)對字型風格轉換更重要,而 VGG19 更適合高層語意理解。
  • 根據許多影像風格化的研究,VGG16 的特徵提取能力已足夠用於風格轉換。

2. 如何加入 VGG16 感知損失?

你可以定義一個 PerceptualLoss 類別,並在 Zi2ZiModel 中使用它來計算感知損失。以下是具體實作步驟:

(1) 定義 VGG16 感知損失

setup 方法中加入:

import torchvision.models as models

class PerceptualLoss(nn.Module):
    def __init__(self):
        super(PerceptualLoss, self).__init__()
        vgg = models.vgg16(pretrained=True).features
        self.slice1 = nn.Sequential(*list(vgg[:4]))   # Conv1_2
        self.slice2 = nn.Sequential(*list(vgg[4:9]))  # Conv2_2
        self.slice3 = nn.Sequential(*list(vgg[9:16])) # Conv3_3
        self.slice4 = nn.Sequential(*list(vgg[16:23]))# Conv4_3
        for param in self.parameters():
            param.requires_grad = False

    def forward(self, x, y):
        loss = nn.functional.l1_loss(self.slice1(x), self.slice1(y)) + \
               nn.functional.l1_loss(self.slice2(x), self.slice2(y)) + \
               nn.functional.l1_loss(self.slice3(x), self.slice3(y)) + \
               nn.functional.l1_loss(self.slice4(x), self.slice4(y))
        return loss

這個 PerceptualLoss 計算 conv1_2, conv2_2, conv3_3, conv4_3 層的 L1 Loss,以保留字型風格。


(2) 在 Zi2ZiModel 中使用

setup 方法中新增:

self.vgg_loss = PerceptualLoss().to(self.device)

backward_G 方法中加入 VGG16 感知損失:

perceptual_loss = self.vgg_loss(self.fake_B, self.real_B)
self.g_loss += 10 * perceptual_loss  # 設定感知損失的權重

(3) 調整損失權重

你可以調整 perceptual_loss 權重來影響結果:

self.g_loss += λ * perceptual_loss  # λ = 5~20,根據訓練效果調整

這樣,模型在進行字型風格轉換時,會更加考慮 Zen Maru Gothic 的細節,而不只是像素匹配,從而生成更清晰、更具風格的輸出。


gemini vgg16

好的,為了在您的字型風格轉換模型中加入 VGG16 感知損失,並提高輸出結果的銳利度,我們需要執行以下步驟:

1. 引入 VGG16 特徵提取器:

首先,我們需要引入預訓練的 VGG16 模型,用於提取圖像的感知特徵。

Python

import torchvision.models as models
import torch.nn as nn

class VGG16(nn.Module):
    def __init__(self):
        super(VGG16, self).__init__()
        features = models.vgg16(pretrained=True).features
        self.relu1_2 = nn.Sequential(*list(features.children())[:4])
        self.relu2_2 = nn.Sequential(*list(features.children())[4:9])
        self.relu3_3 = nn.Sequential(*list(features.children())[9:16])
        self.relu4_3 = nn.Sequential(*list(features.children())[16:23])
        self.relu5_3 = nn.Sequential(*list(features.children())[23:30])
        # 不需要梯度
        for param in self.parameters():
            param.requires_grad = False

    def forward(self, x):
        relu1_2 = self.relu1_2(x)
        relu2_2 = self.relu2_2(relu1_2)
        relu3_3 = self.relu3_3(relu2_2)
        relu4_3 = self.relu4_3(relu3_3)
        relu5_3 = self.relu5_3(relu4_3)
        out = {
            'relu1_2': relu1_2,
            'relu2_2': relu2_2,
            'relu3_3': relu3_3,
            'relu4_3': relu4_3,
            'relu5_3': relu5_3,
        }
        return out

2. 在 Zi2ZiModel 中加入 VGG16 和感知損失:

修改 Zi2ZiModel,加入 VGG16 模型和感知損失計算。

Python

class Zi2ZiModel:
    def __init__(self, input_nc=1, embedding_num=40, embedding_dim=128, ngf=64, ndf=64,
                 Lconst_penalty=10, Lcategory_penalty=1, L1_penalty=100,
                 schedule=10, lr=0.001, gpu_ids=None, save_dir='.', is_training=True,
                 image_size=256, self_attention=False, residual_block=False,
                 weight_decay=1e-5, final_channels=1, beta1=0.5, g_blur=False, d_blur=False, epoch=40,
                 perceptual_weight=10):  # 添加感知損失權重
        # ... (其他初始化程式碼)
        self.perceptual_weight = perceptual_weight
        self.vgg = VGG16().to(self.device)
        self.l1_loss = nn.L1Loss()
        self.mse = nn.MSELoss()

    def backward_G(self, no_target_source=False):
        # ... (其他程式碼)
        l1_loss = self.L1_penalty * self.l1_loss(self.fake_B, self.real_B)
        # 添加感知損失
        real_features = self.vgg(self.real_B)
        fake_features = self.vgg(self.fake_B)
        perceptual_loss = 0
        for key in real_features:
            perceptual_loss += self.l1_loss(real_features[key], fake_features[key])
        perceptual_loss *= self.perceptual_weight
        self.g_loss = g_loss_adv + l1_loss + fake_category_loss + const_loss + fm_loss + perceptual_loss
        return const_loss, l1_loss, g_loss_adv, fm_loss

    def optimize_parameters(self, use_autocast=False):
        self.forward()
        self.set_requires_grad(self.netD, True)
        self.optimizer_D.zero_grad()
        if use_autocast:
            with torch.amp.autocast(device_type='cuda'):
                category_loss = self.backward_D()
                scaled_d_loss = self.scaler_D.scale(self.d_loss)
                scaled_d_loss.backward()
                self.scaler_D.step(self.optimizer_D)
                self.scaler_D.update()
        else:
            category_loss = self.backward_D()
            self.d_loss.backward()
            self.optimizer_D.step()

        self.set_requires_grad(self.netD, False)
        self.optimizer_G.zero_grad()
        const_loss, l1_loss, cheat_loss, fm_loss = 0, 0, 0, 0

        if use_autocast:
            with torch.amp.autocast(device_type='cuda'):
                const_loss, l1_loss, cheat_loss, fm_loss = self.backward_G()
                scaled_g_loss = self.scaler_G.scale(self.g_loss)
                scaled_g_loss.backward()
                self.scaler_G.step(self.optimizer_G)
                self.scaler_G.update()
        else:
            const_loss, l1_loss, cheat_loss, fm_loss = self.backward_G()
            self.g_loss.backward()
            self.optimizer_G.step()

        self.forward()
        self.optimizer_G.zero_grad()

        if use_autocast:
            with torch.amp.autocast(device_type='cuda'):
                const_loss, l1_loss, cheat_loss, fm_loss = self.backward_G()
                scaled_g_loss = self.scaler_G.scale(self.g_loss)
                scaled_g_loss.backward()
                self.scaler_G.step(self.optimizer_G)
                self.scaler_G.update()
        else:
            const_loss, l1_loss, cheat_loss, fm_loss = self.backward_G()
            self.g_loss.backward()
            self.optimizer_G.step()
        return const_loss, l1_loss, cheat_loss, fm_loss

3. 調整感知損失權重:

您可以調整 perceptual_weight 的值,以控制感知損失對總體損失的影響。較高的權重會使模型更注重感知特徵,從而產生更銳利的結果。

注意事項:

  • 確保您的 GPU 有足夠的記憶體來運行 VGG16 模型。
  • 您可以根據您的具體需求,選擇 VGG16 中不同的特徵層來計算感知損失。
  • 實驗不同的感知損失權重,以找到最佳的平衡。

為什麼上面的 class VGG16(nn.Module) 之中, 沒有執行 nn.L1Loss() ?

class VGG16(nn.Module) 中沒有執行 nn.L1Loss() 的原因,是因為這個類別的目的是建立一個 VGG16 的特徵提取器,而不是計算損失。

以下是更詳細的解釋:

1. VGG16 的角色:特徵提取

  • VGG16(nn.Module) 這個類別被設計用來載入預訓練的 VGG16 模型,並提取圖像在不同層次的特徵。
  • 這些特徵(例如 relu1_2relu2_2 等)代表了圖像在不同抽象層次的感知內容。
  • 這些特徵將被用於感知損失的計算,但 VGG16 本身並不負責計算損失。

2. nn.L1Loss() 的角色:損失計算

  • nn.L1Loss() 是一個損失函數,用於計算兩個張量之間的 L1 距離(絕對值差異的平均值)。
  • 在字型風格轉換的例子中,nn.L1Loss() 將被用於計算真實圖像和生成圖像在 VGG16 特徵空間中的差異。
  • 這個損失計算發生在 Zi2ZiModelbackward_G() 方法中,而不是在 VGG16 類別中。

3. 分離關注點

  • 將 VGG16 的特徵提取和損失計算分離,可以使程式碼更具模組化和可讀性。
  • VGG16 類別專注於提供特徵,而 Zi2ZiModel 專注於使用這些特徵來計算損失和更新模型。
  • 這樣可以使程式碼更易於維護和擴展。

總結

  • VGG16(nn.Module) 是一個特徵提取器,負責提供圖像的感知特徵。
  • nn.L1Loss() 是一個損失函數,用於計算特徵之間的差異。
  • 損失計算發生在模型的訓練迴圈中,而不是在特徵提取器中。

好的,根據您提供的程式碼和 VGG16 的修改建議,我們需要修改 backward_Goptimize_parameters 方法,以加入 VGG16 感知損失。

修改後的 backward_G 方法:

Python

def backward_G(self, no_target_source=False):
    fake_AB = torch.cat([self.real_A, self.fake_B], 1)
    real_AB = torch.cat([self.real_A, self.real_B], 1)

    fake_D_logits, fake_category_logits = self.netD(fake_AB)
    real_D_logits, _ = self.netD(real_AB)

    const_loss = self.Lconst_penalty * self.mse(self.encoded_real_A, self.encoded_fake_B)
    l1_loss = self.L1_penalty * self.l1_loss(self.fake_B, self.real_B)

    fake_category_loss = self.Lcategory_penalty * self.category_loss(fake_category_logits, self.labels)
    g_loss_adv = -torch.mean(F.logsigmoid(fake_D_logits - real_D_logits))

    fm_loss = self.compute_feature_matching_loss(real_AB, fake_AB)

    # 添加感知損失
    real_features = self.vgg(self.real_B)
    fake_features = self.vgg(self.fake_B)
    perceptual_loss = 0
    for key in real_features:
        perceptual_loss += self.l1_loss(real_features[key], fake_features[key])
    perceptual_loss *= self.perceptual_weight

    self.g_loss = g_loss_adv + l1_loss + fake_category_loss + const_loss + fm_loss + perceptual_loss
    return const_loss, l1_loss, g_loss_adv, fm_loss, perceptual_loss # 返回感知损失

修改後的 optimize_parameters 方法:

Python

def optimize_parameters(self, use_autocast=False):
    self.forward()
    self.set_requires_grad(self.netD, True)
    self.optimizer_D.zero_grad()
    if use_autocast:
        with torch.amp.autocast(device_type='cuda'):
            category_loss = self.backward_D()
            scaled_d_loss = self.scaler_D.scale(self.d_loss)
            scaled_d_loss.backward()
            self.scaler_D.step(self.optimizer_D)
            self.scaler_D.update()
    else:
        category_loss = self.backward_D()
        self.d_loss.backward()
        self.optimizer_D.step()

    self.set_requires_grad(self.netD, False)
    self.optimizer_G.zero_grad()
    const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss = 0, 0, 0, 0, 0 # 添加感知损失

    if use_autocast:
        with torch.amp.autocast(device_type='cuda'):
            const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss = self.backward_G()
            scaled_g_loss = self.scaler_G.scale(self.g_loss)
            scaled_g_loss.backward()
            self.scaler_G.step(self.optimizer_G)
            self.scaler_G.update()
    else:
        const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss = self.backward_G()
        self.g_loss.backward()
        self.optimizer_G.step()

    self.forward()
    self.optimizer_G.zero_grad()

    if use_autocast:
        with torch.amp.autocast(device_type='cuda'):
            const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss = self.backward_G()
            scaled_g_loss = self.scaler_G.scale(self.g_loss)
            scaled_g_loss.backward()
            self.scaler_G.step(self.optimizer_G)
            self.scaler_G.update()
    else:
        const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss = self.backward_G()
        self.g_loss.backward()
        self.optimizer_G.step()
    return const_loss, l1_loss, cheat_loss, fm_loss, perceptual_loss # 返回感知损失

修改重點:

  1. backward_G 方法:
    • 計算感知損失:使用 self.vgg 提取真實圖像和生成圖像的特徵,然後計算 L1 損失。
    • 將感知損失加入總體生成器損失 self.g_loss
    • 返回感知損失 perceptual_loss
  2. optimize_parameters 方法:
    • 修改 optimize_parameters 使其返回感知損失。
    • optimize_parameters 內,每次呼叫 backward_G 時,將感知損失也接收回來。

這樣修改後,您的模型將會使用 VGG16 感知損失來提高生成圖像的銳利度。


class VGG16(nn.Module) 之中 nn.Sequential() 次數到5次, 是不是3次就夠了? 請比較次數增加的優缺點.

class VGG16(nn.Module) 中,使用 nn.Sequential() 的次數與提取特徵的層級有關。讓我們來比較使用 3 次和 5 次 nn.Sequential() 的優缺點:

使用 5 次 nn.Sequential() (目前程式碼):

  • 優點:
    • 能夠提取更細緻的特徵:5 次 nn.Sequential() 對應於 VGG16 的 5 個主要區塊(每個區塊包含一系列卷積層和一個最大池化層)。這樣可以提取不同抽象層次的特徵,從淺層的邊緣和紋理到深層的物件部分和語意資訊。
    • 提供更豐富的感知資訊:對於需要高度精確的風格轉換任務,使用更多層次的特徵可以提供更豐富的感知資訊,從而產生更細緻和真實的結果。
  • 缺點:
    • 增加計算成本:提取更多層次的特徵會增加計算量和記憶體需求。
    • 可能引入不必要的細節:對於某些簡單的風格轉換任務,提取過多的細節可能會導致過擬合或產生不必要的雜訊。

使用 3 次 nn.Sequential()

  • 優點:
    • 減少計算成本:提取較少層次的特徵可以顯著減少計算量和記憶體需求。
    • 適用於簡單任務:對於一些簡單的風格轉換任務,使用較少層次的特徵可能已經足夠捕捉必要的風格特徵。
  • 缺點:
    • 損失細節資訊:提取較少層次的特徵可能會損失一些細節資訊,從而導致風格轉換結果不夠細緻。
    • 可能無法捕捉複雜風格:對於複雜的風格轉換任務,使用較少層次的特徵可能無法捕捉到足夠的風格資訊,從而導致結果不理想。

選擇建議:

  • 對於字型風格轉換這類需要細緻特徵的任務,特別是 Noto Sans → Zen Maru Gothic 這種有明顯風格變化的情況,使用 5 次 nn.Sequential() 通常會提供更好的結果。
  • 如果您遇到計算資源限制或需要更快的處理速度,可以嘗試使用 3 次 nn.Sequential()。但是,請注意這可能會影響結果的視覺品質。
  • 您可以通過實驗來確定最適合您的任務的 nn.Sequential() 次數。嘗試使用不同的次數進行訓練,並比較結果的視覺品質和損失值。

總結:

  • nn.Sequential() 的次數越多,提取的特徵越細緻,但計算成本也越高。
  • 根據您的任務複雜度和計算資源,選擇合適的 nn.Sequential() 次數。
  • 進行實驗以找到最佳的平衡。

運行 VGG16 模型所需的額外記憶體取決於多個因素,包括:

  • 輸入圖像的尺寸:
    • 較大的圖像需要更多的記憶體。
  • 批次大小(batch size):
    • 較大的批次大小會顯著增加記憶體需求。
  • GPU 記憶體:
    • VGG16 模型本身相對較大,需要一定的 GPU 記憶體來加載模型權重和進行計算。
  • 框架和優化:
    • 使用的深度學習框架(例如 PyTorch 或 TensorFlow)以及優化技術(例如混合精度訓練)也會影響記憶體使用情況。

VGG16 的記憶體需求:

  • VGG16 模型本身約有 138 百萬個參數,這意味著它需要相當多的記憶體來存儲模型權重。
  • 在實際運行中,記憶體需求主要來自於中間特徵圖的存儲。這些特徵圖的大小取決於輸入圖像的尺寸和網路的深度。
  • 通常來說 VGG16是一個相對大的模型,所以需要一定的顯存來運行。

如何減少記憶體使用:

  • 減小圖像尺寸:
    • 如果可能,減小輸入圖像的尺寸可以顯著減少記憶體需求。
  • 減小批次大小:
    • 減小批次大小可以減少每次迭代所需的記憶體,但可能會增加訓練時間。
  • 使用混合精度訓練:
    • 混合精度訓練使用半精度浮點數(FP16)來存儲模型權重和中間特徵圖,從而減少記憶體使用。
  • 梯度累加:
    • 梯度累加可以讓你用小的顯存,來達到大的batch size的效果。
  • 模型量化:
    • 模型量化可以減少模型權重所佔用的記憶體。

總結:

  • VGG16 是一個相對較大的模型,因此需要足夠的 GPU 記憶體才能運行。
  • 記憶體需求取決於多個因素,包括輸入圖像尺寸、批次大小和優化技術。
  • 您可以通過減小圖像尺寸、批次大小或使用混合精度訓練等技術來減少記憶體使用。

建議:

  • 在開始訓練之前,監控您的 GPU 記憶體使用情況,以確保有足夠的記憶體。
  • 如果遇到記憶體不足的問題,請嘗試上述減少記憶體使用的方法。

gemini vgg19

好的,為了增強感知損失(Perceptual Loss)以提高字型風格轉換的銳利度,我們需要執行以下步驟:

1. 引入 VGG19 特徵提取器:

我們將使用預訓練的 VGG19 模型來提取圖像的高級特徵。這些特徵能夠捕捉圖像的感知內容,而不僅僅是像素差異。

Python

import torchvision.models as models
import torch.nn as nn

class VGG19(nn.Module):
    def __init__(self):
        super(VGG19, self).__init__()
        features = models.vgg19(pretrained=True).features
        self.relu1_1 = nn.Sequential()
        self.relu1_2 = nn.Sequential()
        self.relu2_1 = nn.Sequential()
        self.relu2_2 = nn.Sequential()
        self.relu3_1 = nn.Sequential()
        self.relu3_2 = nn.Sequential()
        self.relu3_3 = nn.Sequential()
        self.relu3_4 = nn.Sequential()
        self.relu4_1 = nn.Sequential()
        self.relu4_2 = nn.Sequential()
        self.relu4_3 = nn.Sequential()
        self.relu4_4 = nn.Sequential()
        self.relu5_1 = nn.Sequential()
        self.relu5_2 = nn.Sequential()
        self.relu5_3 = nn.Sequential()
        self.relu5_4 = nn.Sequential()
        for x in range(2):
            self.relu1_1.add_module(str(x), features[x])
        for x in range(2, 4):
            self.relu1_2.add_module(str(x), features[x])
        for x in range(4, 7):
            self.relu2_1.add_module(str(x), features[x])
        for x in range(7, 9):
            self.relu2_2.add_module(str(x), features[x])
        for x in range(9, 12):
            self.relu3_1.add_module(str(x), features[x])
        for x in range(12, 14):
            self.relu3_2.add_module(str(x), features[x])
        for x in range(14, 16):
            self.relu3_3.add_module(str(x), features[x])
        for x in range(16, 18):
            self.relu3_4.add_module(str(x), features[x])
        for x in range(18, 21):
            self.relu4_1.add_module(str(x), features[x])
        for x in range(21, 23):
            self.relu4_2.add_module(str(x), features[x])
        for x in range(23, 25):
            self.relu4_3.add_module(str(x), features[x])
        for x in range(25, 27):
            self.relu4_4.add_module(str(x), features[x])
        for x in range(27, 30):
            self.relu5_1.add_module(str(x), features[x])
        for x in range(30, 32):
            self.relu5_2.add_module(str(x), features[x])
        for x in range(32, 34):
            self.relu5_3.add_module(str(x), features[x])
        for x in range(34, 36):
            self.relu5_4.add_module(str(x), features[x])
        # 不需要梯度
        for param in self.parameters():
            param.requires_grad = False

    def forward(self, x):
        relu1_1 = self.relu1_1(x)
        relu1_2 = self.relu1_2(relu1_1)
        relu2_1 = self.relu2_1(relu1_2)
        relu2_2 = self.relu2_2(relu2_1)
        relu3_1 = self.relu3_1(relu2_2)
        relu3_2 = self.relu3_2(relu3_1)
        relu3_3 = self.relu3_3(relu3_2)
        relu3_4 = self.relu3_4(relu3_3)
        relu4_1 = self.relu4_1(relu3_4)
        relu4_2 = self.relu4_2(relu4_1)
        relu4_3 = self.relu4_3(relu4_2)
        relu4_4 = self.relu4_4(relu4_3)
        relu5_1 = self.relu5_1(relu4_4)
        relu5_2 = self.relu5_2(relu5_1)
        relu5_3 = self.relu5_3(relu5_2)
        relu5_4 = self.relu5_4(relu5_3)
        out = {
            'relu1_1': relu1_1,
            'relu1_2': relu1_2,
            'relu2_1': relu2_1,
            'relu2_2': relu2_2,
            'relu3_1': relu3_1,
            'relu3_2': relu3_2,
            'relu3_3': relu3_3,
            'relu3_4': relu3_4,
            'relu4_1': relu4_1,
            'relu4_2': relu4_2,
            'relu4_3': relu4_3,
            'relu4_4': relu4_4,
            'relu5_1': relu5_1,
            'relu5_2': relu5_2,
            'relu5_3': relu5_3,
            'relu5_4': relu5_4,
        }
        return out

2. 在 Zi2ZiModel 中加入感知損失:

修改 Zi2ZiModel,加入 VGG19 模型和感知損失計算。

Python

class Zi2ZiModel:
    def __init__(self, input_nc=1, embedding_num=40, embedding_dim=128, ngf=64, ndf=64,
                 Lconst_penalty=10, Lcategory_penalty=1, L1_penalty=100,
                 schedule=10, lr=0.001, gpu_ids=None, save_dir='.', is_training=True,
                 image_size=256, self_attention=False, residual_block=False,
                 weight_decay=1e-5, final_channels=1, beta1=0.5, g_blur=False, d_blur=False, epoch=40,
                 perceptual_weight=10):  # 添加感知損失權重
        # ... (其他初始化程式碼)
        self.perceptual_weight = perceptual_weight
        self.vgg = VGG19().to(self.device)
        self.l1_loss = nn.L1Loss()
        self.mse = nn.MSELoss()

    def backward_G(self, no_target_source=False):
        # ... (其他程式碼)
        l1_loss = self.L1_penalty * self.l1_loss(self.fake_B, self.real_B)
        # 添加感知損失
        real_features = self.vgg(self.real_B)
        fake_features = self.vgg(self.fake_B)
        perceptual_loss = 0
        for key in real_features:
            perceptual_loss += self.l1_loss(real_features[key], fake_features[key])
        perceptual_loss *= self.perceptual_weight
        self.g_loss = g_loss_adv + l1_loss + fake_category_loss + const_loss + fm_loss + perceptual_loss
        return const_loss, l1_loss, g_loss_adv, fm_loss, perceptual_loss
    def optimize_parameters(self, use_autocast=False):
        # ... (其他程式碼)
        if use_autocast:
            with torch.amp.autocast(device_type='cuda'):
                const_loss

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *