为什么自动编码器中的解码器在最后一层使用 sigmoid?
Why is the decoder in an autoencoder uses a sigmoid on the last layer?
我正在查看 this 正在工作的变分自动编码器。
主要class
class VAE(nn.Module):
def __init__(self):
super(VAE, self).__init__()
self.fc1 = nn.Linear(784, 400)
self.fc21 = nn.Linear(400, 20)
self.fc22 = nn.Linear(400, 20)
self.fc3 = nn.Linear(20, 400)
self.fc4 = nn.Linear(400, 784)
def encode(self, x):
h1 = F.relu(self.fc1(x))
return self.fc21(h1), self.fc22(h1)
def reparametrize(self, mu, logvar):
std = logvar.mul(0.5).exp_()
if torch.cuda.is_available():
eps = torch.cuda.FloatTensor(std.size()).normal_()
else:
eps = torch.FloatTensor(std.size()).normal_()
eps = Variable(eps)
return eps.mul(std).add_(mu)
def decode(self, z):
h3 = F.relu(self.fc3(z))
return F.sigmoid(self.fc4(h3))
def forward(self, x):
mu, logvar = self.encode(x)
z = self.reparametrize(mu, logvar)
return self.decode(z), mu, logvar
有
def decode(self, z):
h3 = F.relu(self.fc3(z))
return F.sigmoid(self.fc4(h3))
我无法向自己解释为什么最后一层在返回之前要通过一个 sigmoid。
请说明。
编辑:
我只是在没有乙状结肠的情况下检查过。结果还是不错的。
现在不知道有没有必要
如果我没记错的话,它会将结果转换为概率,表示为 0 到 1 之间的实数。
正如 Jim J 在回答中提到的,sigmoid 强制输出范围为 [0, 1]。在这种情况下,并不是因为我们要将输出解释为概率,而是强制将输出解释为灰度图像的像素强度。
如果移除 sigmoid,NN 将不得不学习所有输出都应在 [0, 1] 范围内。 sigmoid 可能有助于使学习过程更加稳定。
这是因为图片来自
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.ToTensor()),
batch_size=args.batch_size, shuffle=True, **kwargs)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.ToTensor()),
batch_size=args.batch_size, shuffle=True, **kwargs)
有像素取值范围[0,1],可以在这里加上print('data[0]: ', data[0])
;
def test(epoch):
model.eval()
test_loss = 0
with torch.no_grad():
for i, (data, _) in enumerate(test_loader):
data = data.to(device)
print('data[0]: ', data[0])
...
查看打印输出,您会发现这些值的范围是 0 到 1。
顺便说一句,torchvision.utils.save_image()
的第一个参数也取像素值范围为0到1的张量,因为在该函数内部,它会在保存到图像之前乘以255。
我正在查看 this 正在工作的变分自动编码器。
主要class
class VAE(nn.Module):
def __init__(self):
super(VAE, self).__init__()
self.fc1 = nn.Linear(784, 400)
self.fc21 = nn.Linear(400, 20)
self.fc22 = nn.Linear(400, 20)
self.fc3 = nn.Linear(20, 400)
self.fc4 = nn.Linear(400, 784)
def encode(self, x):
h1 = F.relu(self.fc1(x))
return self.fc21(h1), self.fc22(h1)
def reparametrize(self, mu, logvar):
std = logvar.mul(0.5).exp_()
if torch.cuda.is_available():
eps = torch.cuda.FloatTensor(std.size()).normal_()
else:
eps = torch.FloatTensor(std.size()).normal_()
eps = Variable(eps)
return eps.mul(std).add_(mu)
def decode(self, z):
h3 = F.relu(self.fc3(z))
return F.sigmoid(self.fc4(h3))
def forward(self, x):
mu, logvar = self.encode(x)
z = self.reparametrize(mu, logvar)
return self.decode(z), mu, logvar
有
def decode(self, z):
h3 = F.relu(self.fc3(z))
return F.sigmoid(self.fc4(h3))
我无法向自己解释为什么最后一层在返回之前要通过一个 sigmoid。
请说明。
编辑: 我只是在没有乙状结肠的情况下检查过。结果还是不错的。 现在不知道有没有必要
如果我没记错的话,它会将结果转换为概率,表示为 0 到 1 之间的实数。
正如 Jim J 在回答中提到的,sigmoid 强制输出范围为 [0, 1]。在这种情况下,并不是因为我们要将输出解释为概率,而是强制将输出解释为灰度图像的像素强度。
如果移除 sigmoid,NN 将不得不学习所有输出都应在 [0, 1] 范围内。 sigmoid 可能有助于使学习过程更加稳定。
这是因为图片来自
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.ToTensor()),
batch_size=args.batch_size, shuffle=True, **kwargs)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.ToTensor()),
batch_size=args.batch_size, shuffle=True, **kwargs)
有像素取值范围[0,1],可以在这里加上print('data[0]: ', data[0])
;
def test(epoch):
model.eval()
test_loss = 0
with torch.no_grad():
for i, (data, _) in enumerate(test_loader):
data = data.to(device)
print('data[0]: ', data[0])
...
查看打印输出,您会发现这些值的范围是 0 到 1。
顺便说一句,torchvision.utils.save_image()
的第一个参数也取像素值范围为0到1的张量,因为在该函数内部,它会在保存到图像之前乘以255。