在圖像到圖像翻譯任務(例如您的字體風格轉換)中,判別器輸入通道的選擇取決於您希望判別器學習區分什麼。通常,使用 2 個通道 作為判別器的輸入是更常見且更合理的方法,原因如下:
使用 2 個通道 (拼接 A 和 B):
- 學習聯合分佈: 判別器接收真實的 (input_A, target_B) 對和生成的 (input_A, fake_B) 對。它學習區分這兩種聯合分佈。這使得判別器能夠判斷生成的圖像
fake_B
是否不僅看起來像目標風格,而且是否與輸入圖像input_A
在風格上是一致的。 - 更強的約束: 判別器可以學習輸入和輸出之間的依賴關係,這對於生成與輸入內容相關的風格化輸出非常重要。例如,在字體風格轉換中,判別器可以學習生成的目標字體風格應該保留輸入字體的結構。
- 更符合任務目標: 您的目標是生成一個具有目標風格
B
,但內容來自輸入A
的圖像。因此,判別器需要評估生成的fake_B
在給定A
的情況下是否真實。
使用 1 個通道 (僅 B 或 fake_B):
- 僅判斷圖像的真實性: 如果判別器只接收單個圖像(真實的
B
或生成的fake_B
),它只能學習判斷該圖像是否看起來像目標風格的真實樣本。它無法學習輸入A
和輸出B
/fake_B
之間的關係。 - 可能導致模式崩潰: 生成器可能會生成看起來逼真但與輸入
A
完全不相關的fake_B
,因為判別器只關注fake_B
的風格真實性。 - 忽略了條件資訊: 在條件生成任務中(以
A
為條件生成B
),忽略條件資訊(A
)通常會導致生成結果不佳。
在您的代碼中:
您的 backward_D
方法中正確地將 real_A
和 real_B
拼接在一起作為真實樣本的輸入 (real_AB = torch.cat([self.real_A, self.real_B], 1)
),並將 real_A
和生成的 fake_B
拼接在一起作為假樣本的輸入 (fake_AB = torch.cat([self.real_A, self.fake_B.detach()], 1)
)。這表明您的意圖是讓判別器學習聯合分佈。
因此,將判別器的第一個卷積層的輸入通道設置為 2 是正確且推薦的做法,以匹配您拼接後的輸入。這允許判別器學習輸入和輸出之間的關係,並更好地指導生成器生成符合任務要求的結果。
總結:
對於您的字體風格轉換任務,使用 2 個通道 作為判別器的輸入(通過拼接輸入圖像 A
和目標/生成圖像 B
/fake_B
)是更好的選擇,因為它可以讓判別器學習輸入和輸出之間的聯合分佈,並生成與輸入內容風格一致的結果。您在代碼中所做的拼接操作也支持了這一點。
好的,根據您提供的包含 Discriminator
類別的完整腳本,我們需要修改 Discriminator
的 __init__
方法,將第一個卷積層的 in_channels
參數設置為 2。
找到 Discriminator
類別的 __init__
方法,並修改其第一個 nn.Conv2d
層的初始化:
Python
class Discriminator(nn.Module):
def __init__(self, input_nc, embedding_num, ndf=64, norm_layer=nn.BatchNorm2d, blur=False):
super(Discriminator, self).__init__()
use_bias = norm_layer != nn.BatchNorm2d
kw = 5
padw = 2
sequence = [
nn.utils.spectral_norm(nn.Conv2d(2, ndf, kernel_size=kw, stride=2, padding=padw)), # 將 input_nc 改為 2
nn.LeakyReLU(0.2, True)
]
nf_mult = 1
for n in range(1, 3):
nf_mult_prev = nf_mult
nf_mult = min(2 ** n, 8)
sequence += [
nn.utils.spectral_norm(nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=2, padding=padw, bias=use_bias)),
norm_layer(ndf * nf_mult),
nn.LeakyReLU(0.2, True)
]
sequence += [
nn.utils.spectral_norm(nn.Conv2d(ndf * nf_mult, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias)),
norm_layer(ndf * nf_mult),
nn.LeakyReLU(0.2, True)
]
self.model = nn.Sequential(*sequence)
self.global_pool = nn.AdaptiveAvgPool2d((4, 4)) # 自適應池化
final_features = ndf * nf_mult * 4 * 4
self.binary = nn.Linear(final_features, 1)
self.category = nn.Linear(final_features, embedding_num)
self.blur = blur
if blur:
self.gaussian_blur = T.GaussianBlur(kernel_size=3, sigma=1.0)
def forward(self, input):
if self.blur:
input = self.gaussian_blur(input)
features = self.model(input)
features = self.global_pool(features)
features = features.view(input.shape[0], -1) # 展平成 batch x final_features
binary_logits = self.binary(features)
category_logits = self.category(features)
return binary_logits, category_logits
修改說明:
在 Discriminator
的 __init__
方法中,將創建第一個卷積層的地方:
Python
nn.utils.spectral_norm(nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw)),
修改為:
Python
nn.utils.spectral_norm(nn.Conv2d(2, ndf, kernel_size=kw, stride=2, padding=padw)),
這樣,判別器的第一個卷積層就會期望接收 2 個輸入通道,與您在 backward_D
方法中拼接 real_A
和 real_B
後得到的 real_AB
的通道數相匹配,從而解決了 RuntimeError
。
請確保您在訓練腳本中使用的 Discriminator
類別是這個修改後的版本。