我有一个用pytorch实现的简单网络,比如,
class network: def __init__(self): self.device = device #这些是3个卷积突触;相同的卷积; self.layer = sequential( conv2d(3, 3, (23), padding=11), batch_norm_2d(3), Swish(), conv2d(3, 3, (11), padding=5), batch_norm_2d(3), Swish(), conv2d(3, 3, (5), padding=2), batch_norm_2d(3), Swish(), conv2d(3, 4, (3), padding=15, stride=2), batch_norm_2d(4), Swish(), conv2d(4, 8, (3), padding=15, stride=2), batch_norm_2d(8), Swish(), conv2d(8, 4, (1)), batch_norm_2d(4), Swish(), conv2d(4, 8, (3), padding=15, stride=2), batch_norm_2d(8), Swish(), conv2d(8, 16, (3), padding=15, stride=2), batch_norm_2d(16), Swish(), conv2d(16, 8, (1)), batch_norm_2d(8), Swish(), conv2d(8, 16, (3), padding=15, stride=2), batch_norm_2d(16), Swish(), conv2d(16, 32, (3), padding=15, stride=2), batch_norm_2d(32), Swish(), conv2d(32, 16, (1)), batch_norm_2d(16), Swish(), conv2d(16, 32, (3), padding=15, stride=2), batch_norm_2d(32), Swish(), conv2d(32, 64, (3), padding=15, stride=2), batch_norm_2d(64), Swish(), conv2d(64, 32, (1)), batch_norm_2d(32), Swish(), conv2d(32, 64, (3), padding=15, stride=2), batch_norm_2d(64), Swish(), conv2d(64, 128, (3), padding=15, stride=2), batch_norm_2d(128), Swish(), conv2d(128, 64, (1)), batch_norm_2d(64), Swish(), conv2d(64, 128, (3), padding=15, stride=2), batch_norm_2d(128), Swish(), conv2d(128, 256, (3), padding=15, stride=2), batch_norm_2d(256), Swish(), conv2d(256, 128, (1)), batch_norm_2d(128), Swish(), flatten(1, -1), linear(128*29*29, 8*8*2*5), batch_norm_1d(8*8*2*5), Swish() ) #损失和优化器函数用于ethirun self.Loss_1 = IoU_Loss() #用于边界框的损失函数 self.Loss_2 = tor.nn.SmoothL1Loss(reduction='mean') #优化器 self.Optimizer = tor.optim.AdamW(self.parameters())#tor.optim.SGD(self.parameters(), lr=1e-2, momentum=0.9, weight_decay=1e-5, nesterov=True) self.Scheduler = tor.optim.lr_scheduler.StepLR(self.Optimizer, 288, gamma=0.5) self.sizes = tor.tensor(range(0, 5), dtype=tor.int64, device=self.device) def forward(self, input): return self.layer(input) def backprop(self, preds, lbls, val_or_trn): #接受预测和标签并计算误差并反向传播 mask = tor.index_select(lbls, -1, self.sizes[0]) preds.register_hook(lambda grad: grad * mask.float()) error = self.Loss_2(preds, lbls) if val_or_trn == 1: #反向传播 error.backward() self.Optimizer.step() self.Scheduler.step() #将梯度归零 self.Optimizer.zero_grad() return error.detach()model = network()
其中的输入、输出和通道都是任意的。假设我创建一个随机输入张量,如下所示,
input_data = torch.randn(1, 3, 256, 256)
然后我在这些数据中进行预测,如下所示,
model(input_data)
假设我还通过多次初始化torch.randn
命令来改变input_data
变量,同时保持模型不变。也就是说,不重新初始化model=network()
命令。
我得到了这个错误,
训练时每个通道期望超过1个值,得到的输入大小为torch.Size([1, some_value])
因此,我尝试通过使用model.eval()
函数以评估模式运行它,如下所示,
model.eval()with tor.no_grad() pred = model(input_data)model.train()
这样做没有错误。然而,无论我如何改变input_data
变量,我在pred
中总是得到相同的值。但是,如果我重新初始化模型的参数,我会得到一个新的pred
,它再次不会随着不同的输入而改变。除非我再次使用model=network()
重新初始化模型。我做错了什么?
编辑:为了提供更多关于我的问题的信息,我正在尝试从头开始创建一个类似yolo的网络。这是我正在使用的数据集 https://www.kaggle.com/devdgohil/the-oxfordiiit-pet-dataset
回答:
基本上这就是Batchnorm在做的事情。你使用Batchnorm来使训练不太容易过拟合,但在评估中不要使用batchnorm,以便你能得到正确的结果。Dropout也是如此。
每个带有批量归一化和/或dropout的CNN模型都会这样做。在训练和评估期间,相同输入的输出会有所不同
这正是Pytorch具有model.eval()
的原因。在推理期间关闭这些层以获得正确输出。
编辑
问题出在输出处的激活和批量归一化。
只使用那些能使结果与真实值相似的函数。比如,当你希望输出在0到1的范围内时使用sigmoid
,当希望在-1到1范围内时使用tanh
,当希望在轴上概率分布时使用softmax
。
想象一下relu
函数(它基本上是swish
和softplus
的简化版本)。它会将所有低于0的值变成0。而你可能需要一些输出低于0的值,所以你的模型根本不会收敛。