
导入需要的模块
pytorch是比较常用的深度学习框架,集成类似numpy的功能,生成以及操作叫做张量的多维数组。张量(tensor):能放在GPU上跑的多维数组(大概)
import torch # 导入pytorch
import torch.nn as nn # 核心模块,网络搭建需要继承或调用nn(neural network)里面的一些类
import torch.nn.functional as F # 调用nn里的一些方法 搭建网络
网络基础框架
class net(nn.Module):
def __init__(self, *要传入的参数):
super(net,self).__init__()
...
def forward(self, x):
...
return x 全连接:图片数据需要转化成向量输入,如[1,2,3,4...m],size:1*m,之后一般经过一个非线性函数激活即数值归一化到0~1或-1~1之间
nn.Linear(输入向量维数,输出向量维数,是否添加偏置) 卷积:随机初始化一个卷积核(卷积核有提取特征的作用),铺在图片上做滑窗操作,对应的像素相乘再求和,之后一般经过一个非线性函数激活即数值归一化到0~1或-1~1之间(大概)
nn.Conv2d(输入通道数,输出通道数,卷积核大小,扩充多少像素,滑窗操作的步长,是否添加偏置) 池化:对n*n区域内像素进行操作,求最大值或求平均,得到一个数值代表该区域(大概)
nn.MaxPool2d(池化区域大小n)
F.max_pool2d(图片,池化区域大小n) #前者为类,需要实例化,后着为函数,直接调用 Unet

class conv_block(nn.Module): # 每层都有两次卷积,就直接封装在一起
def __init__(self, in_channels,out_channels):
super(conv_block,self).__init__()
self.conv1 = nn.Sequential(nn.Conv2d(in_channels = in_channels, # 输入通道
out_channels = out_channels, # 输出通道
kernel_size = (3,3), # 卷积核大小
padding=1, # 扩充像素
stride = (1,1), # 步长
bias = False), # 是否设置偏置
nn.BatchNorm2d(out_channels), # 规范化输出,增加网络性能稳定性(易于训练)
nn.ReLU())# 激活函数
#self.norm = nn.BatchNorm2d(in_channels)
self.conv2 = nn.Sequential(nn.Conv2d(in_channels = out_channels,
out_channels = out_channels,
kernel_size = (3,3),
padding=1,
stride = (1,1),
bias = False),
nn.BatchNorm2d(out_channels),
nn.ReLU())
def forward(self, x):
x1=self.conv1(x)
x2=self.conv2(x1)
return x2
class up(nn.Module): # 反卷积进行上采样
def __init__(self,in_channels,out_channels):
super(up,self).__init__()
self.upsample=nn.Sequential(nn.ConvTranspose2d(in_channels=in_channels,
out_channels=out_channels,
kernel_size=(2,2),
stride=(2,2)),
nn.BatchNorm2d(out_channels),
nn.ReLU())
def forward(self,x):
x = self.upsample(x)
return x
class Unet(nn.Module): # 开始搭Unet
def __init__(self):
super().__init__()
self.D1 = conv_block(3,64) # down_sample
self.D2 = conv_block(64,128)
self.D3 = conv_block(128,256)
self.D4 = conv_block(256,512)
self.D5 = conv_block(512,1024)
self.U1 = conv_block(1024,512) # up_sample
self.U2 = conv_block(512,256)
self.U3 = conv_block(256,128)
self.U4 = conv_block(128,64)
self.up1 = up(1024,512) # 反卷积实现上采样恢复图片大小和通道数
self.up2 = up(512,256)
self.up3 = up(256,128)
self.up4 = up(128,64)
self.out = nn.Conv2d(64,2,1)
def forward(self, x):# 图片x(即张量tensor)大小 [1,3,512,512] 对应->(batch,channels,width,height),
x1 = self.D1(x)
x1_pool = F.max_pool2d(x1,2) # 池化(压缩图片),[1,64,256,256]
x2 = self.D2(x1_pool)
x2_pool = F.max_pool2d(x2,2) # [1,128,128,128]
x3 = self.D3(x2_pool)
x3_pool = F.max_pool2d(x3,2) # [1,256,64,64]
x4 = self.D4(x3_pool)
x4_pool = F.max_pool2d(x4,2) # [1, 512, 32, 32]
x5 = self.D5(x4_pool)
# 图片通过反卷积放大->通道融合->两次卷积
xx1 = self.U1(torch.cat([self.up1(x5),x4],dim=1)) # [1, 1024, 32, 32] ->[1, 512+512, 64, 64]
xx2 = self.U2(torch.cat([self.up2(xx1),x3],dim=1))
xx3 = self.U3(torch.cat([self.up3(xx2),x2],dim=1))
xx4 = self.U4(torch.cat([self.up4(xx3),x1],dim=1))
out = self.out(xx4)
return out
查看是否能跑通
if __name__ == "__main__":
model = Unet()
loss_fn = nn.CrossEntropyLoss()
x = torch.ones(1,3,16,16)
y = torch.rand(1,16,16)
model.eval() # 验证模式,不启用BatchNorm和Dropout层
y_ = model(x)
print(y_.shape,loss_fn(y_, y.type(torch.long)))
print("Done!") 输出:

最后输出两个通道的图片,即两张特征图,对像素进行二分类,一张的”像素值“表示像素是1的概率,另一张表示不是1的概率(大概是这样)
不知道对不对,反正能跑通!