Unet++单类别训练报错:'tuple' object has no attribute 'size'
问题描述
开展Unet++单类别分割模型训练任务时,编写的训练代码如下:
model.train() for epoch in range(3): running_loss = 0.0 for i,data in enumerate(train_loader,0): inputs,labels,_ = data inputs = inputs[:,None,:] #labels = labels[:,None,:] print(inputs.shape) inputs = inputs.to(device=device) labels = labels.to(device=device) optimizer.zero_grad() #print("Input: ", inputs.shape) #print("Labels: ",labels.shape) outputs = model(inputs) #thresh_output = threshold(outputs,0.5) loss = criterion(outputs,labels) #dice_score = dice_loss(thresh_output,labels).item() loss.backward() optimizer.step() #wandb.log({"Train_Loss":loss}) if(i%500 == 0): scheduler.step() running_loss += loss.item() # if(i == 1000): # break print("Epoch : {}, iteration : {} and Loss : {}".format(epoch+1,i+1,loss.item()))
运行代码触发异常,控制台警告与完整报错信息如下:
torch.Size([1, 1, 256, 256]) /usr/local/lib/python3.7/dist-packages/torch/nn/functional.py:1944: UserWarning: nn.functional.sigmoid is deprecated. Use torch.sigmoid instead. warnings.warn("nn.functional.sigmoid is deprecated. Use torch.sigmoid instead.") AttributeError Traceback (most recent call last) in () 16 outputs = model(inputs) 17 #thresh_output = threshold(outputs,0.5) ---> 18 loss = criterion(outputs,labels) 19 #dice_score = dice_loss(thresh_output,labels).item() 20 loss.backward() 2 frames /usr/local/lib/python3.7/dist-packages/torch/nn/functional.py in binary_cross_entropy(input, target, weight, size_average, reduce, reduction) 3053 else: 3054 reduction_enum = _Reduction.get_enum(reduction) -> 3055 if target.size() != input.size(): 3056 raise ValueError( 3057 "Using a target size ({}) that is different to the input size ({}) is deprecated. " AttributeError: 'tuple' object has no attribute 'size'
报错触发于损失计算语句loss = criterion(outputs,labels),最终抛出AttributeError异常,提示'tuple' object has no attribute 'size'。
根因定位
- 核心报错诱因:主流开源Unet++实现默认开启**深监督(deep supervision)**机制,模型前向传播的返回值不是单个预测张量,而是不同解码层分支输出的预测结果组成的元组。二元交叉熵损失函数预期输入为张量,会调用
.size()方法做形状校验,传入元组时就会触发属性不存在的错误。 - 隐性问题:代码中调整标签维度的
labels = labels[:,None,:]语句被注释,单通道分割场景下标签缺少通道维度,就算解决元组问题,后续也会触发输入、输出形状不匹配的报错。 - 警告诱因:模型定义代码中使用了已弃用的
nn.functional.sigmoid接口,PyTorch新版本要求替换为torch.sigmoid。
修复方案
- 处理模型输出格式
- 方案一:初始化Unet++模型时,将深监督开关设为关闭,例如初始化时传入参数
deep_supervision=False,模型前向传播就会直接返回单个最终预测张量。 - 方案二:如果需要保留深监督提升训练效果,训练阶段可以对所有分支的输出计算平均损失,推理阶段取最后一个分支的最终输出即可。如果不需要用到多分支输出,也可以在得到模型返回值后直接提取最终分支结果:
outputs = model(inputs) # 适配深监督返回的元组格式 if isinstance(outputs, tuple): outputs = outputs[-1] - 方案一:初始化Unet++模型时,将深监督开关设为关闭,例如初始化时传入参数
- 取消标签维度调整语句的注释,保证输入、预测输出、标签三者形状完全对齐,均为
[batch_size, 1, H, W]格式。 - (可选)修复弃用警告:找到模型定义文件中调用
nn.functional.sigmoid的位置,替换为torch.sigmoid即可。
修复后的核心训练循环代码如下:
model.train() for epoch in range(3): running_loss = 0.0 for i,data in enumerate(train_loader,0): inputs,labels,_ = data inputs = inputs[:,None,:] # 取消注释,给标签增加通道维度 labels = labels[:,None,:] print(inputs.shape) inputs = inputs.to(device=device) labels = labels.to(device=device) optimizer.zero_grad() outputs = model(inputs) # 处理深监督返回的元组 if isinstance(outputs, tuple): outputs = outputs[-1] loss = criterion(outputs,labels) loss.backward() optimizer.step() if(i%500 == 0): scheduler.step() running_loss += loss.item() print("Epoch : {}, iteration : {} and Loss : {}".format(epoch+1,i+1,loss.item()))
内容的提问来源于stack exchange,提问作者Alvi Rahman
相关产品推荐
相关产品推荐

