原帖每个像素只属于三个输出波段中的一个,但 masks 为 [N,3,H,W];旧交叉熵目标要求 [N,H,W]:
RuntimeError: only batches of spatial targets supported (3D tensors) but got targets of dimension: 4
先判断三通道掩码是否真是互斥 one-hot
原题称每像素只属于 Band5/6/7 中一个类别,但数据又做了归一化。转换前应检查 unique 和通道和,确认不是连续波段值、多标签或全零未标注。不能一律 argmax,把无标注像素默认为类别 0。
先证明三通道数据确实是互斥 one-hot,再转换。不要对多标签、连续波段值或未标注像素直接 argmax。
在 loss_new(outputs,masks) 前检查:
print("josh050608")
print("outputs:", outputs.shape, outputs.dtype)
print("masks:", masks.shape, masks.dtype)
print("values:", masks.unique()[:20])
print("channel sums:", masks.sum(dim=1).unique()[:20])
若每个像素恰有一个 1,其余为 0:
assert masks.ndim == 4 and masks.shape[1] == outputs.shape[1]
assert ((masks == 0) | (masks == 1)).all()
assert (masks.sum(dim=1) == 1).all()
target = masks.argmax(dim=1).long()
assert target.shape == (outputs.shape[0], *outputs.shape[2:])
loss = loss_new(outputs, target)
原帖把 y 送入 normalize_zero_to_one,这对类别编号通常不合适。标签应保留类别语义;如果只是对输入图像归一化,不要把相同操作无条件套在掩码上。
若存在全零的未标注像素,先定义 ignore_index,并在转换后把这些位置设为该值,再给 CrossEntropyLoss 配置相同 ignore_index。否则 argmax 会把全零像素误判为类别 0。若一个像素允许多个类,则应使用同形状浮点多标签目标和 BCE 类损失。
现代 CrossEntropyLoss 也支持合法软目标,但原帖旧报错路径不能据此自动视为支持。验证类别图范围、空间尺寸与可视化映射,再做一个 batch 的反向。
包含未标注像素的完整小例子
下面 CPU 合成分割目标先建立合法 one-hot,再将一个像素设为全零表示忽略。转换时显式使用 ignore_index,不把它当作背景类别:
import torch
import torch.nn.functional as F
from torch import nn
class_map = torch.tensor([[[0, 1], [2, 0]]], dtype=torch.long)
masks = F.one_hot(class_map, num_classes=3).permute(0, 3, 1, 2).float()
masks[:, :, 0, 0] = 0
channel_sum = masks.sum(dim=1)
assert ((masks == 0) | (masks == 1)).all()
assert ((channel_sum == 0) | (channel_sum == 1)).all()
target = masks.argmax(dim=1).long()
ignore_index = -100
target[channel_sum == 0] = ignore_index
head = nn.Conv2d(5, 3, kernel_size=1)
logits = head(torch.randn(1, 5, 2, 2))
loss = nn.CrossEntropyLoss(ignore_index=ignore_index)(logits, target)
assert target.shape == (1, 2, 2)
assert target[0, 0, 0].item() == ignore_index
assert torch.isfinite(loss)
loss.backward()
assert head.weight.grad is not None
print("target/loss:", target, loss.item())
这里“全零=忽略”是例子明确规定的标注语义,真实数据若把全零当背景,应按任务建立对应类别,而不是照搬。若整批所有像素都被忽略,还需处理有效像素数为零的情况,避免把空监督产生的非有限损失继续反向。
类别掩码通常不做输入图像那种逐值归一化。若原始通道值不是 0/1,先追查生成与归一化流程;多正类则按多标签损失设计,不能用互斥类图丢弃信息。
验收包含边界值、忽略区域、类别图可视化、空间 shape 和一次 backward。现代软目标接口有自己的约束,不能据它将原题旧版四维目标自动视为正确。