【发布时间】:2020-11-09 15:57:50
【问题描述】:
我正在处理一个奇怪的问题,即反向传递后的渐变具有不同的形状,具体取决于使用的是 CUDA 还是 CPU。使用的模型比较简单:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.pool2 = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
self.relu1 = nn.ReLU()
self.relu2 = nn.ReLU()
self.relu3 = nn.ReLU()
self.relu4 = nn.ReLU()
def forward(self, x):
x = self.pool1(self.relu1(self.conv1(x)))
x = self.pool2(self.relu2(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = self.relu3(self.fc1(x))
x = self.relu4(self.fc2(x))
x = self.fc3(x)
return x
输入张量的形状为(1, 3, 32, 32),相关部分代码如下,其中generate_gradients方法尤为重要:
class VanillaBackprop():
"""
Produces gradients generated with vanilla back propagation from the image
"""
def __init__(self, model):
self.model = model
self.gradients = None
# Put model in evaluation mode
self.model.eval()
# Hook the first layer to get the gradient
self.hook_layers()
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model.to(self.device)
def hook_layers(self):
def hook_function(module, grad_in, grad_out):
self.gradients = grad_in[0]
# Register hook to the first layer
try:
first_layer = list(self.model.features._modules.items())[0][1]
except:
first_layer = list(self.model._modules.items())[0][1]
first_layer.register_backward_hook(hook_function)
def generate_gradients(self, input_image, target_class):
# Forward
model_output = self.model(input_image.to(self.device))
# Zero grads
self.model.zero_grad()
# Target for backprop
one_hot_output = torch.FloatTensor(1, model_output.size()[-1]).zero_()
one_hot_output[0][target_class] = 1
# Backward pass
model_output.backward(gradient=one_hot_output.to(self.device))
# Convert Pytorch variable to numpy array
gradients_as_arr = self.gradients.data.cpu().numpy()[0]
return gradients_as_arr
在 CPU 上,self.gradients 的形状为 (1, 3, 32, 32),而在 CUDA 上,它的形状为 (1, 6, 28, 28)。这怎么可能,我该如何解决?非常感谢任何帮助。
【问题讨论】:
-
hook_layers 中的 try-except 看起来很可疑。它应该处理什么异常?
-
try-except 用于处理非torchvision 模型。大多数torchvision模型由
model.features(conv + activation + pooling blocks)和model.classifier(最后的全连接层)组成。你会注意到一个基本的卷积网络(比如我创建的那个或任何 nn.Sequential 模型)缺少这两个子组件。本质上,我正在迭代层,对于 torchvision 模型,您需要迭代model.features._modules,而对于非 Torchvision 模型,您需要self.model._modules。这不是万无一失的,但对于我的目的来说已经足够了。 -
好的。
(1, 6, 28, 28)看起来像第一个卷积层的输出。你能检查一下第一层实际上是两种情况下的第一层吗? -
确实,在这两种情况下,第一层实际上是第一层,并且通过 forward 方法迭代会导致两者在每一层之后的形状相同。例如,
x = self.pool1(self.relu1(self.conv1(x)))在 CUDA 和 CPU 上输出x,形状为(1, 6, 14, 14),这是预期的行为。