作为一个语义分割领域的小白,毕设打算用DeepLabv3+做遥感图像的语义分割,准确来说是水体提取。
相对于土地利用分类还是比较简单的,就设置了两类,即水体、非水体,使用的代码基础是这个:
大佬写的Pytorch版DeepLabv3+
现成的语义分割模型大多基于比较有名的数据集(比如VOC2012),因此参考这些数据集的格式自己弄了一个水体提取数据集。
然而,在训练的时候,发现MIoU这个指标很奇怪,与其他指标格格不入,大概如下所示:
当然,从准确率来看,上面的训练结果明显是过拟合的,这也是碰到的问题之一:训练过程中准确度经常一下子飙到特别高,然后就是过拟合状态,即使Loss并不理想(比如在0.05和0.15两个值来回跳。
。
。
)
这样的结果下,预测效果当然也是稀烂,这里就不放图了
问题定位这个问题真的挺让我抓狂的,卡住我毕设进度差不多半个月吧,在网上找了各种各样的方法,甚至换了网上的GID数据集测试代码,依然没能解决。
有段时间感觉整个人脑子里想的全是深度神经网络,甚至做梦都梦到过两次。
。
。
可能菜真的是原罪吧,小白一个人在语义分割领域摸爬滚打太难了(哭)
然后有一天,我突然意识到了,这个Acc、Acc_class、fwIoU这三个指标的值居然是相等的,而且正好是mIoU的二倍!当时我的脑子嗡的一下,就像抓住了跟救命稻草一样,开始疯狂的学习这四个指标的具体公式,并且草稿纸列了方程一顿计算。
。
。
然后我有了一个大胆的猜想:在水体、非水体两类中的其中一类的精度是0的时候,好像就会出现目前的状况!然后我把Acc_class这个指标给拆开了,也就是不让它做平均值,分别输出两类的精度,结果如下:
可以看到,第二类也就是水体的准确值居然是nan,空值。
这也是说的通的,因为计算平均值的时候会用到numpy.nanmean()这个方法,这个时候空值和0值效果是差不多的。
那么为什么会出现空值呢?我又从计算评价指标的方法开始追本溯源,最后基本定位了问题所在:读入的标签有问题。
在制作标签的时候,我把非水体的图像值设为0,水体设为255。
然而,神经网络输出的是一个1*2的概率矩阵,然后在计算精度评价指标的时候用了np.argmax()这个方法,将最大值的索引找出来,也就是只有[0, 1]两个值。
在计算混淆矩阵时,将非水体的0和0相比,显然没问题;但水体的255和1就出大问题了,导致了空值的出现。
这也解释了为何精度评价指标的值有错误,同时模型的训练效果也稀烂:重要的标签值对不上,根本没法训练呀。
到这里,解决方法就很简单了,直接在图像转换为Tensor的代码中加一行代码,将255全部转换为1,就可以正常运行喽
class ToTensor(object):
"""Convert ndarrays in sample to Tensors."""
def __call__(self, sample):
# swap color axis because
# numpy image: H x W x C
# torch image: C X H X W
img = sample['image']
mask = sample['label']
img = np.array(img).astype(np.float32).transpose((2, 0, 1))
mask = np.array(mask).astype(np.float32)
# 这一步很关键!!!
mask[mask == 255] = 1
img = torch.from_numpy(img).float()
mask = torch.from_numpy(mask).float()
return {'image': img,
'label': mask}
总结
不得不说改好了bug真是神清气爽啊!
在这段学习语义分割的时间里,最大的感受就是一定要确保数据格式正确,包括数据集的格式、图片的扩展名、单通道还是三通道、位深、波段数、读进程序之后array的shape,等等。
另外,最开始的时候一定不要拿到代码就着急实验,要争取先把代码看懂,否则就会像我这次一样,在漫长的改bug阶段逐渐读懂了代码,哈哈哈,也不失为一种思路。
就写到这里吧,不知不觉两千多字,现在预测效果还不理想,调参去了~~
最后说一句,深度学习还是很有趣的!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)