vgg16:
你可以在現有的 L1 Loss 基礎上,增加 感知損失(Perceptual Loss) 來改善輸出的銳利度。感知損失主要使用預訓練的神經網路(如 VGG)來提取高層特徵,並計算輸入與目標影像之間的特徵差異,而不只是像素差異。這樣可以讓模型學習到更符合人眼感知的結果。
步驟 1:加入 VGG16 感知損失
- 載入 VGG16(使用 ImageNet 預訓練權重)
- 取出中間層特徵(如
relu1_2
,relu2_2
,relu3_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:在訓練流程中加入感知損失
在 Zi2ZiModel
的 setup
方法中,新增 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_2
、relu2_2
等)代表了圖像在不同抽象層次的感知內容。 - 這些特徵將被用於感知損失的計算,但 VGG16 本身並不負責計算損失。
2. nn.L1Loss()
的角色:損失計算
nn.L1Loss()
是一個損失函數,用於計算兩個張量之間的 L1 距離(絕對值差異的平均值)。- 在字型風格轉換的例子中,
nn.L1Loss()
將被用於計算真實圖像和生成圖像在 VGG16 特徵空間中的差異。 - 這個損失計算發生在
Zi2ZiModel
的backward_G()
方法中,而不是在VGG16
類別中。
3. 分離關注點
- 將 VGG16 的特徵提取和損失計算分離,可以使程式碼更具模組化和可讀性。
VGG16
類別專注於提供特徵,而Zi2ZiModel
專注於使用這些特徵來計算損失和更新模型。- 這樣可以使程式碼更易於維護和擴展。
總結
VGG16(nn.Module)
是一個特徵提取器,負責提供圖像的感知特徵。nn.L1Loss()
是一個損失函數,用於計算特徵之間的差異。- 損失計算發生在模型的訓練迴圈中,而不是在特徵提取器中。
好的,根據您提供的程式碼和 VGG16 的修改建議,我們需要修改 backward_G
和 optimize_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 # 返回感知损失
修改重點:
backward_G
方法:- 計算感知損失:使用
self.vgg
提取真實圖像和生成圖像的特徵,然後計算 L1 損失。 - 將感知損失加入總體生成器損失
self.g_loss
。 - 返回感知損失
perceptual_loss
。
- 計算感知損失:使用
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 個主要區塊(每個區塊包含一系列卷積層和一個最大池化層)。這樣可以提取不同抽象層次的特徵,從淺層的邊緣和紋理到深層的物件部分和語意資訊。 - 提供更豐富的感知資訊:對於需要高度精確的風格轉換任務,使用更多層次的特徵可以提供更豐富的感知資訊,從而產生更細緻和真實的結果。
- 能夠提取更細緻的特徵: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