U − N e t − M o d e l ( p y t o r c h 版 本 ) U-Net-Model(pytorch版本) U−Net−Model(pytorch版本)
训练、验证代码逻辑
All.ipynb
原 版 原版 原版
原版U-Net,是用于医学细胞分割,在边缘做了镜像,所以输入图大小和输出图大小不一致,输入大小:
568*568
;输出为338*338
其实可以想想为什么细胞分割可以做边缘镜像,我认为其实这和细胞图片特征有关,细胞就是对称的。
import torch
from torch import nn
def contracting_block(in_channels, out_channels):
block = torch.nn.Sequential(
nn.Conv2d(kernel_size=(3,3), in_channels=in_channels, out_channels=out_channels),
nn.ReLU(),
nn.BatchNorm2d(out_channels),
nn.Conv2d(kernel_size=(3,3), in_channels=out_channels, out_channels=out_channels),
nn.ReLU(),
nn.BatchNorm2d(out_channels)
)
return block
class expansive_block(nn.Module):
def __init__(self, in_channels, mid_channels, out_channels):
super(expansive_block, self).__init__()
self.up = nn.ConvTranspose2d(in_channels, in_channels//2, kernel_size=(3, 3), stride=2, padding=1,
output_padding=1, dilation=1)
self.block = nn.Sequential(
nn.Conv2d(kernel_size=(3,3), in_channels=in_channels, out_channels=mid_channels),
nn.ReLU(),
nn.BatchNorm2d(mid_channels),
nn.Conv2d(kernel_size=(3,3), in_channels=mid_channels, out_channels=out_channels),
nn.ReLU(),
nn.BatchNorm2d(out_channels)
)
def forward(self, e, d):
d = self.up(d)
#concat
diffY = e.size()[2] - d.size()[2]
diffX = e.size()[3] - d.size()[3]
e = e[:,:, diffY//2:e.size()[2]-diffY//2, diffX//2:e.size()[3]-diffX//2]
cat = torch.cat([e, d], dim=1)
out = self.block(cat)
return out
def final_block(in_channels, out_channels):
block = nn.Sequential(
nn.Conv2d(kernel_size=(1,1), in_channels=in_channels, out_channels=out_channels),
nn.ReLU(),
nn.BatchNorm2d(out_channels),
)
return block
class UNet(nn.Module):
def __init__(self, in_channel, out_channel):
super(UNet, self).__init__()
#Encode
self.conv_encode1 = contracting_block(in_channels=in_channel, out_channels=64)
self.conv_pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv_encode2 = contracting_block(in_channels=64, out_channels=128)
self.conv_pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv_encode3 = contracting_block(in_channels=128, out_channels=256)
self.conv_pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv_encode4 = contracting_block(in_channels=256, out_channels=512)
self.conv_pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
# Bottleneck
self.bottleneck = torch.nn.Sequential(
nn.Conv2d(kernel_size=3, in_channels=512, out_channels=1024),
nn.ReLU(),
nn.BatchNorm2d(1024),
nn.Conv2d(kernel_size=3, in_channels=1024, out_channels=1024),
nn.ReLU(),
nn.BatchNorm2d(1024)
)
# Decode
self.conv_decode4 = expansive_block(1024, 512, 512)
self.conv_decode3 = expansive_block(512, 256, 256)
self.conv_decode2 = expansive_block(256, 128, 128)
self.conv_decode1 = expansive_block(128, 64, 64)
self.final_layer = final_block(64, out_channel)
def forward(self, x):
#set_trace()
# Encode
encode_block1 = self.conv_encode1(x)
print('encode_block1:', encode_block1.size())
encode_pool1 = self.conv_pool1(encode_block1)
print('encode_pool1:', encode_pool1.size())
encode_block2 = self.conv_encode2(encode_pool1)
print('encode_block2:', encode_block2.size())
encode_pool2 = self.conv_pool2(encode_block2)
print('encode_pool2:', encode_pool2.size())
encode_block3 = self.conv_encode3(encode_pool2)
print('encode_block3:', encode_block3.size())
encode_pool3 = self.conv_pool3(encode_block3)
print('encode_pool3:', encode_pool3.size())
encode_block4 = self.conv_encode4(encode_pool3)
print('encode_block4:', encode_block4.size())
encode_pool4 = self.conv_pool4(encode_block4)
print('encode_pool4:', encode_pool4.size())
# Bottleneck
bottleneck = self.bottleneck(encode_pool4);print('bottleneck:', bottleneck.size())
# Decode
decode_block4 = self.conv_decode4(encode_block4, bottleneck)
print('decode_block4:', decode_block4.size())
decode_block3 = self.conv_decode3(encode_block3, decode_block4)
print('decode_block3:', decode_block3.size())
decode_block2 = self.conv_decode2(encode_block2, decode_block3)
print('decode_block2:', decode_block2.size())
decode_block1 = self.conv_decode1(encode_block1, decode_block2)
print('decode_block1:', decode_block1.size())
final_layer = self.final_layer(decode_block1)
return final_layer
# 随机生成输入数据
rgb = torch.randn(1, 3, 572, 572)
# 定义网络
net = UNet(3, 12)
# 前向传播
out = net(rgb)
# 打印输出大小
print('-----'*5)
print(out.shape)
print('-----'*5)
UNet(3, 12)
常 用 版 常用版 常用版
常用版的输入和输出大小一致
from collections import OrderedDict
import torch
import torch.nn as nn
class UNet(nn.Module):
def __init__(self, in_channels=3, num_classes=1, init_features=32):
super(UNet, self).__init__()
features = init_features
self.encoder1 = UNet._block(in_channels, features, name="enc1")
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.encoder2 = UNet._block(features, features * 2, name="enc2")
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.encoder3 = UNet._block(features * 2, features * 4, name="enc3")
self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
self.encoder4 = UNet._block(features * 4, features * 8, name="enc4")
self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
self.bottleneck = UNet._block(features * 8, features * 16, name="bottleneck")
self.upconv4 = nn.ConvTranspose2d(
features * 16, features * 8, kernel_size=2, stride=2
)
self.decoder4 = UNet._block((features * 8) * 2, features * 8, name="dec4")
self.upconv3 = nn.ConvTranspose2d(
features * 8, features * 4, kernel_size=2, stride=2
)
self.decoder3 = UNet._block((features * 4) * 2, features * 4, name="dec3")
self.upconv2 = nn.ConvTranspose2d(
features * 4, features * 2, kernel_size=2, stride=2
)
self.decoder2 = UNet._block((features * 2) * 2, features * 2, name="dec2")
self.upconv1 = nn.ConvTranspose2d(
features * 2, features, kernel_size=2, stride=2
)
self.decoder1 = UNet._block(features * 2, features, name="dec1")
self.conv = nn.Conv2d(
in_channels=features, out_channels=num_classes, kernel_size=1
)
def forward(self, x):
# 编码器
enc1 = self.encoder1(x);print('enc1:', enc1.size())
enc2 = self.encoder2(self.pool1(enc1));print('enc2:', enc2.size())
enc3 = self.encoder3(self.pool2(enc2));print('enc3:', enc3.size())
enc4 = self.encoder4(self.pool3(enc3));print('enc4:', enc4.size())
# bottleneck
bottleneck = self.bottleneck(self.pool4(enc4));print('bottleneck:', bottleneck.size())
# 解码器
dec4 = self.upconv4(bottleneck);print('dec4:', dec4.size())
dec4 = torch.cat((dec4, enc4), dim=1);print('dec4:', dec4.size()) # 那根线
dec4 = self.decoder4(dec4);print('dec4:', dec4.size())
dec3 = self.upconv3(dec4);print('dec3:', dec3.size())
dec3 = torch.cat((dec3, enc3), dim=1);print('dec3:', dec3.size())
dec3 = self.decoder3(dec3);print('dec3:', dec3.size())
dec2 = self.upconv2(dec3);print('dec2:', dec2.size())
dec2 = torch.cat((dec2, enc2), dim=1);print('dec2:', dec2.size())
dec2 = self.decoder2(dec2);print('dec2:', dec2.size())
dec1 = self.upconv1(dec2);print('dec1:', dec1.size())
dec1 = torch.cat((dec1, enc1), dim=1);print('dec1:', dec1.size())
dec1 = self.decoder1(dec1);print('dec1:', dec1.size())
return torch.sigmoid(self.conv(dec1))
@staticmethod
def _block(in_channels, features, name):
return nn.Sequential(
OrderedDict(
[
(
name + "conv1",
nn.Conv2d(
in_channels=in_channels, # 确定卷积核的深度
out_channels=features, # 确实输出的特征图深度,即卷积核组的多少
kernel_size=3,
padding=1,
bias=False,
),
),
(name + "norm1", nn.BatchNorm2d(num_features=features)),
(name + "relu1", nn.ReLU(inplace=True)),
(
name + "conv2",
nn.Conv2d(
in_channels=features,
out_channels=features,
kernel_size=3,
padding=1,
bias=False,
),
),
(name + "norm2", nn.BatchNorm2d(num_features=features)),
(name + "relu2", nn.ReLU(inplace=True)),
]
)
)
# 随机生成输入数据
rgb = torch.randn(1, 3, 512, 512)
# 定义网络
net = UNet(in_channels=3,num_classes=8)
# 前向传播
out = net(rgb)
# 打印输出大小
print('-----'*5)
print(out.shape)
print('-----'*5)
UNet()