[LAB 11 · 从 RGB 到像素:读出、FCN 与边界](/course/notebooks/9e268706471944e3a59e6f568f9ce9a3.html) # 为什么同时训练 1×1 读出和 FCN · 实现代码 [独立阅读](/course/notebook-blocks/455f430e56104e59b4a5886436462ef5.html) 按问题继续 前景/背景/mIoU口径、RGB到mask训练、FCN与1×1读出及边界 展开完整实现代码 ``` %%writefile -a lab11_segmentation.py def binary_metrics(pred,target): ious=[] for cls in [0,1]: inter=((pred==cls)&(target==cls)).sum().item();union=((pred==cls)|(target==cls)).sum().item() ious.append(inter/union) return {'background_IoU':ious[0],'foreground_IoU':ious[1],'mIoU_two_classes':sum(ious)/2, 'pixel_accuracy':float((pred==target).float().mean()),'error_pixels':int((pred!=target).sum())} def sampling_warmup(out): mask=torch.zeros((8,8),dtype=torch.long);mask[2:6,3:7]=1 coarse=F.avg_pool2d(mask.float()[None,None],2) nearest=F.interpolate(coarse,scale_factor=2,mode='nearest')[0,0]>=.5 bilinear=F.interpolate(coarse,scale_factor=2,mode='bilinear',align_corners=False)[0,0]>=.5 nm=binary_metrics(nearest,mask);bm=binary_metrics(bilinear,mask) assert abs(nm['foreground_IoU']-2/3)<1e-12 assert abs(nm['background_IoU']-5/6)<1e-12 assert abs(nm['mIoU_two_classes']-.75)<1e-12 and nm['error_pixels']==8 # Bilinear interpolates probabilities/logits; categorical labels use nearest. fig,axes=plt.subplots(1,4,figsize=(10,3)) for ax,data,title in zip(axes,[mask,coarse[0,0],nearest,bilinear],['Known mask','2x2 average','Nearest >=0.5','Bilinear >=0.5']): ax.imshow(data,cmap='gray',vmin=0,vmax=1,interpolation='nearest');ax.set_title(title);ax.axis('off') fig.tight_layout();fig.savefig(out/'sampling-warmup.png',dpi=150);plt.close(fig) return {'nearest':nm,'bilinear_align_corners_false':bm,'identity':'known-mask resampling only; no image model'} class PixelReadout(nn.Module): """A learned softmax regression at every RGB pixel; no spatial receptive field.""" def __init__(self):super().__init__();self.readout=nn.Conv2d(3,3,1) def forward(self,x,mode='bilinear'):return self.readout(x) class SmallFCN(nn.Module): def __init__(self): super().__init__() self.fine=nn.Sequential(nn.Conv2d(3,12,3,padding=1),nn.ReLU(),nn.Conv2d(12,12,3,padding=1),nn.ReLU()) self.coarse=nn.Sequential(nn.Conv2d(12,24,3,stride=2,padding=1),nn.ReLU(),nn.Conv2d(24,32,3,stride=2,padding=1),nn.ReLU(),nn.Conv2d(32,32,3,padding=1),nn.ReLU()) self.head=nn.Conv2d(32,3,1);self.skip=nn.Conv2d(12,3,1) def forward(self,x,mode='bilinear'): fine=self.fine(x);coarse_logits=self.head(self.coarse(fine)) kw={'align_corners':False} if mode=='bilinear' else {} return F.interpolate(coarse_logits,size=x.shape[-2:],mode=mode,**kw)+self.skip(fine) def with_ignore(masks): masks=masks.clone();masks[:,0,:]=255;masks[:,-1,:]=255;masks[:,:,0]=255;masks[:,:,-1]=255 # A fixed small region represents missing annotation, not another semantic class. masks[:,20:23,20:23]=255 return masks ```