萌新的卷积神经网络学习笔记--搭建Unet
YM学渣
编辑于 2023年02月09日 20:53

导入需要的模块


pytorch是比较常用的深度学习框架,集成类似numpy的功能,生成以及操作叫做张量的多维数组。张量(tensor):能放在GPU上跑的多维数组(大概)

代码块
Python
自动换行
复制代码
import torch # 导入pytorch
import torch.nn as nn # 核心模块,网络搭建需要继承或调用nn(neural network)里面的一些类
import torch.nn.functional as F # 调用nn里的一些方法
复制成功

搭建网络

网络基础框架

代码块
Python
自动换行
复制代码
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之间

代码块
Python
自动换行
复制代码
nn.Linear(输入向量维数,输出向量维数,是否添加偏置)
复制成功

卷积:随机初始化一个卷积核(卷积核有提取特征的作用),铺在图片上做滑窗操作,对应的像素相乘再求和,之后一般经过一个非线性函数激活即数值归一化到0~1或-1~1之间(大概)

代码块
Python
自动换行
复制代码
nn.Conv2d(输入通道数,输出通道数,卷积核大小,扩充多少像素,滑窗操作的步长,是否添加偏置)
复制成功

池化:对n*n区域内像素进行操作,求最大值或求平均,得到一个数值代表该区域(大概)

代码块
Python
自动换行
复制代码
nn.MaxPool2d(池化区域大小n)
F.max_pool2d(图片,池化区域大小n) #前者为类,需要实例化,后着为函数,直接调用
复制成功

Unet

代码块
Python
自动换行
复制代码
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
复制成功

查看是否能跑通

代码块
Python
自动换行
复制代码
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的概率(大概是这样)

不知道对不对,反正能跑通!