【发布时间】:2019-10-05 14:15:09
【问题描述】:
我正在尝试在 Pytorch 中创建一个 contractive autoencoder。我找到了this thread 并据此尝试。这是我根据提到的线程写的sn-p:
import datetime
import numpy as np
import torch
import torchvision
from torchvision import datasets, transforms
from torchvision.utils import save_image, make_grid
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
%matplotlib inline
dataset_train = datasets.MNIST(root='MNIST',
train=True,
transform = transforms.ToTensor(),
download=True)
dataset_test = datasets.MNIST(root='MNIST',
train=False,
transform = transforms.ToTensor(),
download=True)
batch_size = 128
num_workers = 2
dataloader_train = torch.utils.data.DataLoader(dataset_train,
batch_size = batch_size,
shuffle=True,
num_workers = num_workers,
pin_memory=True)
dataloader_test = torch.utils.data.DataLoader(dataset_test,
batch_size = batch_size,
num_workers = num_workers,
pin_memory=True)
def view_images(imgs, labels, rows = 4, cols =11):
imgs = imgs.detach().cpu().numpy().transpose(0,2,3,1)
fig = plt.figure(figsize=(8,4))
for i in range(imgs.shape[0]):
ax = fig.add_subplot(rows, cols, i+1, xticks=[], yticks=[])
ax.imshow(imgs[i].squeeze(), cmap='Greys_r')
ax.set_title(labels[i].item())
# now let's view some
imgs, labels = next(iter(dataloader_train))
view_images(imgs, labels,13,10)
class Contractive_AutoEncoder(nn.Module):
def __init__(self):
super().__init__()
self.encoder = nn.Linear(784, 512)
self.decoder = nn.Linear(512, 784)
def forward(self, input):
# flatten the input
shape = input.shape
input = input.view(input.size(0), -1)
output_e = F.relu(self.encoder(input))
output = F.sigmoid(self.decoder(output_e))
output = output.view(*shape)
return output_e, output
def loss_function(output_e, outputs, imgs, device):
output_e.backward(torch.ones(output_e.size()).to(device), retain_graph=True)
criterion = nn.MSELoss()
assert outputs.shape == imgs.shape ,f'outputs.shape : {outputs.shape} != imgs.shape : {imgs.shape}'
imgs.grad.requires_grad = True
loss1 = criterion(outputs, imgs)
print(imgs.grad)
loss2 = torch.mean(pow(imgs.grad,2))
loss = loss1 + loss2
return loss
epochs = 50
interval = 2000
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = Contractive_AutoEncoder().to(device)
optimizer = optim.Adam(model.parameters(), lr =0.001)
for e in range(epochs):
for i, (imgs, labels) in enumerate(dataloader_train):
imgs = imgs.to(device)
labels = labels.to(device)
outputs_e, outputs = model(imgs)
loss = loss_function(outputs_e, outputs, imgs,device)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i%interval:
print('')
print(f'epoch/epoechs: {e}/{epochs} loss : {loss.item():.4f} ')
为了简洁起见,我只为编码器和解码器使用了一层。显然,无论其中任何一个的层数如何,它都应该可以工作!
但是这里的问题是,除了我不知道这是否是正确的方法(计算相对于输入的梯度)之外,我得到一个错误,这使得前一个解决方案错误/不适用。
即:
imgs.grad.requires_grad = True
产生错误:
AttributeError:“NoneType”对象没有“requires_grad”属性
我还尝试了该线程中建议的第二种方法,如下所示:
class Contractive_Encoder(nn.Module):
def __init__(self):
super().__init__()
self.encoder = nn.Linear(784, 512)
def forward(self, input):
# flatten the input
input = input.view(input.size(0), -1)
output_e = F.relu(self.encoder(input))
return output_e
class Contractive_Decoder(nn.Module):
def __init__(self):
super().__init__()
self.decoder = nn.Linear(512, 784)
def forward(self, input):
# flatten the input
output = F.sigmoid(self.decoder(input))
output = output.view(-1,1,28,28)
return output
epochs = 50
interval = 2000
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model_enc = Contractive_Encoder().to(device)
model_dec = Contractive_Decoder().to(device)
optimizer = optim.Adam([{"params":model_enc.parameters()},
{"params":model_dec.parameters()}], lr =0.001)
optimizer_cond = optim.Adam(model_enc.parameters(), lr = 0.001)
criterion = nn.MSELoss()
for e in range(epochs):
for i, (imgs, labels) in enumerate(dataloader_train):
imgs = imgs.to(device)
labels = labels.to(device)
outputs_e = model_enc(imgs)
outputs = model_dec(outputs_e)
loss_rec = criterion(outputs, imgs)
optimizer.zero_grad()
loss_rec.backward()
optimizer.step()
imgs.requires_grad_(True)
y = model_enc(imgs)
optimizer_cond.zero_grad()
y.backward(torch.ones(imgs.view(-1,28*28).size()))
imgs.grad.requires_grad = True
loss = torch.mean([pow(imgs.grad,2)])
optimizer_cond.zero_grad()
loss.backward()
optimizer_cond.step()
if i%interval:
print('')
print(f'epoch/epoechs: {e}/{epochs} loss : {loss.item():.4f} ')
但我面临错误:
RuntimeError: invalid gradient at index 0 - got [128, 784] but expected shape compatible with [128, 512]
我应该如何在 Pytorch 中解决这个问题?
【问题讨论】:
-
首先,检查您的数据是否正确加载(可能使用
image, label = next(iter(train_loader)),然后使用matplotlib检查image)。另一件事是您将imgs转换为cuda,但我认为您需要先将其转换为 PyTorch 张量。因此,您应该使用imgs = torch.autograd.Variable(imgs)执行此操作,然后您可以执行.to(device)。 -
关于加载等一切都是正确的。默认情况下,imgs 和标签是张量。因为在数据集中,它们使用 ToTensor() 转换转换为张量。张量从 0.4 开始与 Variable 合并(也就是说,它们可以具有梯度并被跟踪),并且 Variable 已经被弃用了相当长的一段时间。 torch.Tensor 几乎可以用于任何事情!
-
尝试打印出
imgs.grad或它的形状并检查其中是否有东西。如果您想进行双重反向传递(这就是为什么要设置imgs的梯度的requires_grad参数),那么首先检查imgs.grad是否是一个带有一些值的张量(而不是NoneType如错误中所述)。 -
问题是,它仍然是 NoneType ,我似乎找不到让它工作的方法。这就是我的问题的要点!我添加了 google colab 链接,您可以根据需要使用它来运行代码。
-
我想我理解这个问题,虽然我不知道如何解决它,因为我不熟悉这种网络。问题是
imgs.grad将保持NoneType直到您在计算图中具有imgs的东西上调用backward。现在,您确实在output_e上向后调用,但这不能正常工作。我认为你应该在 PyTorch 论坛上问这个问题。
标签: python deep-learning pytorch autoencoder autograd