已解决!!!有bug不要放弃一定要细心追根溯源,花点时间很正常的。
1:bug出现的地方
根据报错的信息,我们可以定位在损失函数losses = loss_function_train(pred_scales, target_scales),还有在损失函数的原函数处class CrossEntropyLoss2d(nn.Module):
2:什么原因导致的bug:
这是由于维度不匹配导致的,那是什么维度不匹配?,以及那两个维度不匹配的呢?。
①:在网上冲浪了大半天,大部分都是因为view函数使用错误,导致nn.linear函数的输入和输出不匹配。因此需要回模型检查view函数前的维度,通过print函数检查view函数输入前的维度,经过我认真检查维度,对每一个层都进行print后发现模型维度没有任何的错误,所以这个方法不适用于我,但是还把链接放在这里大家检查一下自己的模型batch维度不匹配。
②:然后我就在losses处前面加上print,即打印pred_scales,target_scales的shape。
# print(pred_scales.shape) #torch.Size([8, 40, 448, 448])
# print(target_scales.shape) #torch.Size([8, 448, 448])
losses = loss_function_train(pred_scales, target_scales)
这里还有一个小插曲,刚开始target_scales的size还打印不出来,是因为target_scales是一个列表,里面是totch,经过分析把target_scales旁边的中括号去掉就可以打印了。
这里我们看一下pred_scales,target_scales到底是啥:
pred_scales = model(image, depth)
if modality in ['rgbd', 'rgb']:
image = sample['image'].to(device)
# print(image.shape) #torch.Size([8, 3, 448, 448])
batch_size = image.data.shape[0]
if modality in ['rgbd', 'depth']:
depth = sample['depth'].to(device)
# print(depth.shape) #torch.Size([8, 1, 448, 448])
batch_size = depth.data.shape[0]
# print(batch_size) # 8
target_scales = sample['label'].to(device)
model是我们实例化后的模型,这里将rgb和depth输入,pred_scales就是我们的模型输出,这里是(8,40,448,448),target_scales是标签。这里我们可以看出target_scales是sample列表中['label']索引对应的数据,同理image和depth也是rgb和depth索引对应的数据。
而sample是什么呢?
train_data = Dataset(
data_dir=args.dataset_dir,
split='train',
depth_mode=depth_mode,
with_input_orig=with_input_orig,
**dataset_kwargs)
train_loader = DataLoader(train_data,
batch_size=args.batch_size,
num_workers=args.workers,
drop_last=True,
shuffle=True)
train_loader, valid_loader = data_loaders
for i, sample in enumerate(train_loader):
我们看一下数据传递的流程,首先获取data路径,经过dataset获得图片,然后经过dataloader取一个batch的数据得到trainloader,遍历trainloader的列表,得到索引i和数据sample。因为trainloader取的一个batch=8的数据,所以samle里面包含了image,depth,label他们的大小分别为torch.Size([8, 3, 448, 448]),torch.Size([8, 1, 448, 448]),torch.Size([8, 448, 448])。即
pred_scales大小为(8,40,448,448),我们有40个类别,target_scales大小为torch.Size([8, 448, 448])。
这里延伸一下pytorch如何进行损失函数计算 参考:
标签没有通道,每一个像素代表一个类别,且大小和图片的输入相同,为什么不需要one-hot编码是因为pytorch自动进行编码了。这里有一个坑:预测值和标签进行损失计算,他们两个都必须有batch,否则是不能计算成功的。
下面一个例子演示一下:
inputs_scales = torch.rand(8,40,448,448)
targets_scales = torch.rand(8,448,448)
for inputs, targets in zip(inputs_scales, targets_scales):
# inputs = inputs.unsqueeze(0)
# targets = targets.unsqueeze(0)
print(inputs.shape)
print(targets.shape)
loss2 = nn.CrossEntropyLoss()
result2 = loss2(inputs, targets.long())
print(result2)
torch.Size([40, 448, 448])
torch.Size([448, 448])
Traceback (most recent call last):
File "/tmp/pycharm_project_346/kong.py", line 816, in
result2 = loss2(inputs, targets.long())
File "/home/software/anaconda3/envs/pycharm329/lib/python3.7/site-packages/torch/nn/modules/module.py", line 1130, in _call_impl
return forward_call(*input, **kwargs)
File "/home/software/anaconda3/envs/pycharm329/lib/python3.7/site-packages/torch/nn/modules/loss.py", line 1166, in forward
label_smoothing=self.label_smoothing)
File "/home/software/anaconda3/envs/pycharm329/lib/python3.7/site-packages/torch/nn/functional.py", line 3014, in cross_entropy
return torch._C._nn.cross_entropy_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index, label_smoothing)
ValueError: Expected input batch_size (40) to match target batch_size (448).
类似于题目中的bug是吧!
我们增加batch维度后:batch为8,所以遍历八次,每次都做损失计算。
inputs_scales = torch.rand(8,40,448,448)
targets_scales = torch.rand(8,448,448)
for inputs, targets in zip(inputs_scales, targets_scales):
inputs = inputs.unsqueeze(0)
targets = targets.unsqueeze(0)
print(inputs.shape)
print(targets.shape)
loss2 = nn.CrossEntropyLoss()
result2 = loss2(inputs, targets.long())
print(result2)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7298)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7283)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7302)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7282)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7296)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7296)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7289)
torch.Size([1, 40, 448, 448])
torch.Size([1, 448, 448])
tensor(3.7292)
在损失的定义中:
inputs_scales和 targets_scales的维度分别为torch.Size([8, 40, 448, 448]),torch.Size([8, 448, 448]),遍历inputs_scales和 targets_scales,他们的维度就是如下,他们是不能进行损失计算的。
print(targets.shape) torch.Size([448, 448])
print(inputs.shape) torch.Size([40, 448, 448])
class CrossEntropyLoss2d(nn.Module):
def __init__(self, device, weight):
super(CrossEntropyLoss2d, self).__init__()
self.weight = torch.tensor(weight).to(device)
self.num_classes = len(self.weight) + 1 # +1 for void
if self.num_classes < 2**8:
self.dtype = torch.uint8
else:
self.dtype = torch.int16
self.ce_loss = nn.CrossEntropyLoss(
torch.from_numpy(np.array(weight)).float(),
reduction='none',
ignore_index=-1
)
self.ce_loss.to(device)
def forward(self, inputs_scales, targets_scales):
losses = []
for inputs, targets in zip(inputs_scales, targets_scales):
# mask = targets > 0
# 返回一个和源张量同shape、dtype和device的张量,与源张量不共享数据内存,但提供梯度的回溯
targets_m = targets.clone()
targets_m -= 1
print(inputs.size())
print(targets_m.size())
loss_all = self.ce_loss(inputs, targets_m.long())
number_of_pixels_per_class = \
torch.bincount(targets.flatten().type(self.dtype),
minlength=self.num_classes)
divisor_weighted_pixel_sum = \
torch.sum(number_of_pixels_per_class[1:] * self.weight) # without void
losses.append(torch.sum(loss_all) / divisor_weighted_pixel_sum)
# losses.append(torch.sum(loss_all) / torch.sum(mask.float()))
return losses
3:如何解决?
所以我们要给遍历的两个数据增加维度,或者说遍历[8, 40, 448, 448],我们希望的输出是[1,40,448,448],直接增加维度也是同理。然后我们就可以运行了。
inputs = inputs.unsqueeze(0)
targets = targets.unsqueeze(0)
targets_m = targets.clone()
总结:预测图和标签又要有batch这一维度,才能够匹配,才能够输入到损失函数中。正好就对应了bug,batch的不匹配。