图像分割整理之FCN

本文深入探讨了FCN(全卷积网络)的结构改造,从VGG为例,讲解了特征提取、网络输出如何与数据标注对应,并剖析了多分类交叉熵在图像分割中的应用。特别关注了网络输出的one-hot编码和损失函数的设计策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


FCN是第一篇将深度学习算法引入图像分割中的论文,这篇论文将那些经典的网络结构(如VGG、AlexNet)改造成端到端的图像分割网络。下面以VGG为例,说一说FCN的思想。
解读顺序为网络结构、网络输出/数据标注与损失函数。
这里面有些图片是之前在别的地方看到,觉得不错就截图保存下来,但具体忘了是在哪里看到的了,在此感谢一下原作者的辛苦付出

网络结构

举个栗子来说明:
提取特征:假设输入为224x224x3,经过VGG16Net最后一个池化层之后输出就转化为7x7x512,去掉VGG16Net原先的全连接层,换成卷积层1x1x512x4096 (其中每个卷积核有512个通道,共有4096个卷积核) 得到7x7x4096,再接上1x1x4096x类别数,最后得到输出为7x7x类别数。
重建回原尺寸:由于每次经过池化层图像缩小为1/2,所以在第五个池化层 (网络输出)时,图像尺寸为原来的1/32。作者使用了上采样逐步恢复。继续上面的栗子,网络最终输出尺寸为7x7,使用第一次反卷积扩大两倍为14x14,然后与pool4的输出(尺寸为14x14)经1x1卷积调整通道数之后进行相加,所得到的特征图继续上采样扩大两倍为28x28,然后与pool3的输出(尺寸为28x28)经1x1卷积调整通道数之后相加得到新的28x28的特征图,最后将该特征图直接8倍上采样得到与原图像一样大小,以此作为网络最终输出。
这里有个疑问,为什么作者后面不每一步都逐渐上采样回去呢?而是直接就8倍上采样。
根据作者前面做的32倍上采样、16倍上采样与8倍上采样结果来看,其实后面再继续4倍上采样也是有效果的,但我觉得这可能是作者想均衡好计算量与效果,而且也是为了与其他网络对比,因为AlexNet中只有3个pool层。

图像分割标注由于是像素级的分类与定位,标注起来会很麻烦,所以是不是也可以跟YOLOV9000一样,搞一个联合训练呢?
在这里插入图片描述

网络输出与数据标注

网络输出
分割网络的输出通常是原图像尺寸x分割类别数 (也有例外,比如UNet的输出尺寸就与原图像不同)。其形式为one-hot编码,现在重点说一下通道维度,如下图所示:**每个通道代表一个类别,其特征图的值 在(0,1)之间,代表该位置属于该类别的概率。**还有就是,在训练网络时,不需要事先指明网络输出的哪个通道就指哪种类别,数据标注会指定给我们,后面通过学习自然就可以学到了该特征。
在这里插入图片描述
数据标注
图像标注并不需要像网络输出那样分成多个通道,图像标注只需要单通道,其数值表示该位置所属类别在网络输出的第几个通道上:如下图所示:下图为上图的数据标注,例如图中的person标注为1,意思就是说在网络输出的第一个通道是判断是否为person的通道。
在这里插入图片描述
接下来就有一个疑问:网络输出与数据标注不一样啊,这怎么做损失嘞???且听我细细道来:
你没看错,确实是不一样。但别忘了,网络输出与数据标注还要经过损失函数函数处理才能得到损失吧?所以玄机就在下面的损失函数中。

损失函数

图像分割的损失函数有多种损失函数,在这里以Pytorch为背景,拿最普通的多分类交叉熵来说:
在pytorch中的交叉熵函数为nn.CrossEntropy。该函数直接接受第二部分的网络输出与数据标注这两个为输入,然后直接返回结果—loss。
首先我们看多分类交叉熵的公式:x是网络经softmax函数之后的输出 (也就是刚才上面网络输出那个形式还要经过softmax层才能计算损失函数),y是该位置对应与x输出的标签值。
在这里插入图片描述
Pytorch中的nn.CrossEntropy可拆分为先计算nn.LogSoftmax,在计算nn.NLLLoss
其中,nn.LogSoftmax就是上面公式中的logx,先对网络全部输出 (如:7x7x分割类别数 ) 计算softmax然后取log。
nn.NLLLoss则负责上面红方框内。具体做法就是取出网络输出中对应数据标注位置的值并取负号。例如上面的person在第一个通道,那么就是在数据标注为1的位置拿出网络输出第一个通道对应位置的数值并取负号。其实这个过程就相当于只取出标注为1的数值来训练,标注为0的则不取,就相当于完成了蓝色方框中的事情,然后取负号就对应公式中的负号。最后就是将取出来的数值相加或取平均
总结来说,数据标注的是索引,nn.LogSoftmax先不区分地将网络全部输出统一softmax+log,然后nn.NLLLoss就根据数据标注的索引去拿出部分数值并取负号相加或平均,从而得到loss
其实最后就可以发现对于每个通道,nn.CrossEntropy只训练正样本,让正样本取得更大值,负样本通通丢弃,这是为什么?
我们看softmax的公式:
在这里插入图片描述
显然softmax计算网络一个点的输出是根据其他点来计算的,说得直接一些,Softmax就是一种归一化,当使正样本取得更大值时,负样本自然就变得更小了,也就间接被训练到了。

### 解决 Python 中因权限不足导致加载数据集时发生 `RuntimeError` 的方法 当尝试加载数据集时遇到 `Permission denied` 错误,通常是因为程序试图访问受保护的资源而未获得适当权限。以下是可能的原因及其解决方案: #### 1. 文件或目录缺少执行/读取权限 如果目标文件或脚本本身缺乏必要的权限,则会触发此错误。可以通过修改文件权限来解决问题。 ```bash chmod 777 datax.py ``` 上述命令赋予了 `datax.py` 完全权限(读、写、执行),适用于调试阶段[^1]。然而,在生产环境中应谨慎设置权限,仅授予所需的最低权限级别以保障安全性。 #### 2. Hadoop 集群中的权限问题 在分布式计算场景下,尤其是涉及 Apache Flink 或 HDFS 的应用中,可能会因为用户角色的不同而导致权限冲突。例如,Flink 用户无法向 `/user` 路径写入数据时会出现以下异常: ```plaintext Caused by: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.AccessControlException): Permission denied: user=flink, access=WRITE, inode="/user":hdfs:supergroup:drwxr-xr-x ``` 这表明当前运行作业的用户 (`flink`) 缺少对该路径的写入权限[^2]。可以采取以下措施之一解决该问题: - **调整 HDFS 权限**: 使用管理员账户更改指定目录的权限。 ```bash hdfs dfs -chmod 777 /path/to/directory ``` - **切换至具有权限的用户**: 如果存在其他具备所需权限的角色 (如 `hdfs`),则考虑改用这些身份提交任务。 #### 3. PyTorch 加载模型权重失败引发的权限错误 对于基于深度学习框架的应用而言,比如 YOLOv5 训练过程中也可能遭遇类似的权限拒绝状况。具体表现为尝试恢复先前保存的状态字典(`state_dict`)却找不到对应的权重组件: ```python d = torch.load(last, map_location='cpu')['opt'] ``` 这种情况下报错的根本原因是前次中断训练后未能妥善关闭相关连接或者清理临时文件所致[^3]。因此建议按照下面方式排查并修正潜在隐患: - 确认所有输入参数均有效无误; - 删除残留缓存项后再重新启动流程; 另外值得注意的是某些操作系统会对特定位置实施额外的安全策略(例如 Windows 上 C:\Program Files\ 下的内容默认受到严格管控), 故最好把工作区迁移到允许自由操作的空间里去. --- ### 提供一段示范代码用于验证修复效果 假设我们正在处理本地存储的数据集,并希望规避任何不必要的许可障碍,那么可以从头构建一个简单的例子来进行测试: ```python import os from torchvision import datasets, transforms def load_dataset(data_dir): transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) try: train_set = datasets.MNIST(root=data_dir, train=True, download=True, transform=transform) test_set = datasets.MNIST(root=data_dir, train=False, download=True, transform=transform) print(f"Dataset loaded successfully from {data_dir}.") except Exception as e: print(f"Failed to load dataset due to error: {e}") if __name__ == "__main__": current_directory = "./datasets" if not os.path.exists(current_directory): os.makedirs(current_directory) os.chmod(current_directory, 0o777) # Ensure directory has proper permissions load_dataset(current_directory) ``` 通过预先创建好存放资料的位置并且设定恰当属性值之后再调用函数即可顺利完成整个过程而不至于再次碰到类似难题。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值