关于pytorch语义分割⼆分类问题的两种做法
形式1:输出为单通道
分析
即⽹络的输出 output 为 [batch_size, 1, height, width] 形状。其中 batch_szie 为批量⼤⼩,1 表⽰输出⼀个通道,height 和 width 与输⼊图像的⾼和宽保持⼀致。
在训练时,输出通道数是 1,⽹络得到的 output 包含的数值是任意的数。给定的 target ,是⼀个单通道标签图,数值只有 0 和 1 这两种。为了让⽹络输出 output 不断逼近这个标签,⾸先会让 output 经过⼀个sigmoid 函数,使其数值归⼀化到[0, 1],得到 output1 ,然后让这个 output1 与 target 进⾏交叉熵计算,得到损失值,反向传播更新⽹路权重。最终,⽹络经过学习,会使得 output1 逼近target。
训练结束后,⽹络已经具备让输出的 output 经过转换从⽽逼近 target 的能⼒。⾸先将输出的 output 通过sigmoid 函数,然后取⼀个阈值(⼀般设置为0.5),⼤于阈值则取1反之则取0,从⽽得到预测图 predict。后续则是⼀些评估相关的计算。
代码实现
在这个过程中,训练的损失函数为⼆进制交叉熵损失函数,然后根据输出是否⽤到了sigmoid有两种可选的pytorch实现⽅式:
python
output = net(input)  # net的最后⼀层没有使⽤sigmoid
loss_func1 = BCEWithLogitsLoss()
loss = loss_func1(output, target)
当⽹络最后⼀层没有使⽤sigmoid时,需要使⽤ BCEWithLogitsLoss() ,顾名思义,在这个函数中,拿到output⾸先会做⼀个sigmoid操作,再进⾏⼆进制交叉熵计算。上⾯的操作等价于
python
output = net(input)  # net的最后⼀层没有使⽤sigmoid
output = F.sigmoid(output)
loss_func1 = BCEWithLoss()
loss = loss_func1(output, target)
当然,你也可以在⽹络最后⼀层加上sigmoid操作。从⽽省去第⼆⾏的代码(在预测时也可以省去)。
在预测试时,可⽤下⾯的代码实现预测图的⽣成
python
output = net(input)  # net的最后⼀层没有使⽤sigmoid
output = F.sigmoid(output)
predict = torch.where(output>0.s_like(output),s_like(output))
...
即⼤于0.5的记为1,⼩于0.5记为0。
形式2:输出为多通道
分析
即⽹络的输出 output 为 [batch_size, num_class, height, width] 形状。其中 batch_szie 为批量⼤⼩,num_class 表⽰输出的通道数与分类数量⼀致,height 和 width 与输⼊图像的⾼和宽保持⼀致。
在训练时,输出通道数是 num_class(这⾥取2),⽹络得到的 output 包含的数值是任意的数。给定的 target ,是⼀个单通道标签图,数值只有 0 和 1 这两种。为了让⽹络输出 output 不断逼近这个标签,⾸先会让 output 经过⼀个 softmax 函数,使其数值归⼀化到[0, 1],得到 output1 ,在各通道中,这个数值加起来会等于1。对于target 他是⼀个单通道图,⾸先使⽤onehot编码,转换成 num_class个通道的图像,每个通道中的取值是根据单通道中的取值计算出来的,例如单通道中的第⼀个像素取值为1(0<= 1 <=num_class-1,这⾥
num_class=2),那么onehot编码后,在第⼀个像素的位置上,两个通道的取值分别为0,1。也就是说像素的取值决定了对应序号的通道取1,其他的通道取0,这个⾮常关键。上⾯的操作执⾏完后得到target1,让这个 output1 与 target1 进⾏交叉熵计算,得到损失值,反向传播更新⽹路权重。最终,⽹络经过学习,会使得 output1 逼近target1(在各通道层⾯上)。
训练结束后,⽹络已经具备让输出的 output 经过转换从⽽逼近 target 的能⼒。计算 output 中各通道每⼀个像素位置上,取值最⼤的那个对应的通道序号,从⽽得到预测图 predict。后续则是⼀些评估相关的计算。
代码实现
在这个过程中,则可以使⽤交叉熵损失函数:
python
output的反义词output = net(input)  # net的最后⼀层没有使⽤sigmoid
loss_func = CrossEntropyLoss()
loss = loss_func(output, target)
根据前⾯的分析,我们知道,正常的output是 [batch_size, num_class, height, width]形状的,⽽target是[batch_size, height, width]形状的,需要按照上⾯的分析进⾏转换才可以计算交叉熵,⽽在pytorch中,我们不需要进⼀步做这个处理,直接使⽤就可以了。
在预测试时,使⽤下⾯的代码实现预测图的⽣成
python
output = net(input)  # net的最后⼀层没有使⽤sigmoid
predict = output.argmax(dim=1)
...
即得到输出后,在通道⽅向上出最⼤值所在的索引号。
⼩结
总的来说,我觉得第⼆种⽅式更值得推⼴,⼀⽅⾯不⽤考虑阈值的选取问题;另⼀⽅⾯,该⽅法同样适⽤于多类别的语义分割任务,通⽤性更强。
参考资料

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。