(Pytorch) 논문을 보고 Unet 네트워크를 직접 구현합니다.
https://arxiv.org/pdf/1505.04597.pdf
Reference
hanyoseob님의 "머신러닝/딥러닝 강의 - 004 UNet 네트워크 구현하기" 강의를 참고했습니다.
Datasets
- "ISBI 2012 EM segmentation Challenge"에서 사용된 데이터셋
실습 환경
- colab
후기
네트워크 구조만 놓고 본다면 encoder-decoder 모델의 기초인 것 같아서 따라 하고 이해하는 데는 큰 어려움이 없었다. 하지만 파이 토치를 처음 사용해서 익숙하지 않고 서브 클래싱으로 모델 구현하는 건 많은 연습을 해야 할 거 같다.
Network
Code
#Unet 구현
class UNet(nn.Module):
def __init__(self):
super(UNet, self).__init__()
# 논문의 파란색 화살표
def CBR2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=True):
layers = []
# Conv2d layer 정의
layers += [nn.Conv2d(in_channels=in_channels, out_channels=out_channels,
kernel_size=kernel_size, stride=stride, padding=padding,
bias=bias)]
layers += [nn.BatchNorm2d(num_features=out_channels)]
layers += [nn.ReLU()]
cbr = nn.Sequential(*layers)
return cbr
# Contracting path
# 좌측 레이어 enc (인코더)
# 1층 좌측 첫번째 레이어 두개
self.enc1_1 = CBR2d(in_channels=1, out_channels =64, kernel_size =3, stride=1, padding =1, bias =True)
self.enc1_2 = CBR2d(in_channels=64, out_channels =64, kernel_size =3, stride=1, padding =1, bias =True)
# 다음 빨간색 화살표 max_pool 2*2
self.pool1 = nn.MaxPool2d(kernel_size=2)
# 2층 파란색 화살표
self.enc2_1 = CBR2d(in_channels=64, out_channels =128, kernel_size =3, stride=1, padding =1, bias =True)
self.enc2_2 = CBR2d(in_channels=128, out_channels =128, kernel_size =3, stride=1, padding =1, bias =True)
# 다음 빨간색 화살표 max_pool 2*2
self.pool2 = nn.MaxPool2d(kernel_size =2)
# 3층 파란색 화살표
self.enc3_1 = CBR2d(in_channels=128, out_channels =256, kernel_size =3, stride=1, padding =1, bias =True)
self.enc3_2 = CBR2d(in_channels=256, out_channels =256, kernel_size =3, stride=1, padding =1, bias =True)
# 다음 빨간색 화살표 max_pool 2*2
self.pool3 = nn.MaxPool2d(kernel_size =2)
# 4층 파란색 화살표
self.enc4_1 = CBR2d(in_channels=256, out_channels =512, kernel_size =3, stride=1, padding =1, bias =True)
self.enc4_2 = CBR2d(in_channels=512, out_channels =512, kernel_size =3, stride=1, padding =1, bias =True)
# 다음 빨간색 화살표 max_pool 2*2
self.pool4 = nn.MaxPool2d(kernel_size =2)
# 5층 파란색 화살표
self.enc5_1 = CBR2d(in_channels=512, out_channels =1024, kernel_size =3, stride=1, padding =1, bias =True)
# Expansive path
# 5층 파란색 2번쨰 화살표인데 디코더로
self.dec5_1 = CBR2d(in_channels=1024, out_channels =512, kernel_size =3, stride=1, padding =1, bias =True)
# 초록색 화살표
self.unpool4 = nn.ConvTranspose2d(in_channels =512, out_channels = 512, kernel_size = 2, stride = 2, padding = 0, bias = True)
# enc4_2와 대칭이되는 점을 보면 dec4_2 input값은 512가 맞는데, unet 아키텍쳐를 보니
# enc4_2 에서 회색 화살표로 dec4_2로 와서 copy and crop이 일어남
# 따라서 dec4_2 in_channels = 1024로 설정
self.dec4_2 = CBR2d(in_channels=2 * 512, out_channels =512, kernel_size =3, stride=1, padding =1, bias =True)
self.dec4_1 = CBR2d(in_channels=512, out_channels =256, kernel_size =3, stride=1, padding =1, bias =True)
# 초록색 화살표
self.unpool3 = nn.ConvTranspose2d(in_channels=256, out_channels=256,
kernel_size=2, stride=2, padding=0, bias=True)
# 3층 파란색 화살표
self.dec3_2 = CBR2d(in_channels=2 * 256, out_channels=256)
self.dec3_1 = CBR2d(in_channels=256, out_channels=128)
# 초록색 화살표
self.unpool2 = nn.ConvTranspose2d(in_channels=128, out_channels=128,
kernel_size=2, stride=2, padding=0, bias=True)
# 2층 파란색 화살표
self.dec2_2 = CBR2d(in_channels=2 * 128, out_channels=128)
self.dec2_1 = CBR2d(in_channels=128, out_channels=64)
# 초록색 화살표
self.unpool1 = nn.ConvTranspose2d(in_channels=64, out_channels=64,
kernel_size=2, stride=2, padding=0, bias=True)
# 1층 파란색 화살표
self.dec1_2 = CBR2d(in_channels=2 * 64, out_channels=64)
self.dec1_1 = CBR2d(in_channels=64, out_channels=64)
# segmentation에 필요한 n개의 클래스에 대한 output 정의
self.fc = nn.Conv2d(in_channels=64, out_channels=1, kernel_size=1, stride=1, padding=0, bias=True)
def forward(self, x):
# 좌측 1층 레이어 2개 연결 및 빨간색 화살표
enc1_1 = self.enc1_1(x)
enc1_2 = self.enc1_2(enc1_1)
pool1 = self.pool1(enc1_2)
# 좌측 2층 레이어 2개 연결 및 빨간색 화살표
enc2_1 = self.enc2_1(pool1)
enc2_2 = self.enc1_2(enc2_1)
pool2 = self.pool2(enc2_2)
# 좌측 3층 레이어 2개 연결 및 빨간색 화살표
enc3_1 = self.enc3_1(pool2)
enc3_2 = self.enc3_2(enc3_1)
pool3 = self.pool3(enc3_2)
# 좌측 4층 레이어 2개 연결 및 빨간색 화살표
enc4_1 = self.enc4_1(pool3)
enc4_2 = self.enc4_2(enc4_1)
pool4 = self.pool4(enc4_2)
# 좌측 5층 레이어
enc5_1 = self.enc5_1(pool4)
# 우측 5층 레이어 및 초록색 화살표
dec5_1 = self.dec5_1(enc5_1)
unpool4 = self.unpool4(dec5_1)
# 하얀색 부분 연결하기
cat4 = torch.cat((unpool4, enc4_2),dim =1)
# 파란색 화살표 실행
# cat에서 512 + 512 로 1024의 레이어 만들고 파란색 화살표 수행후 아웃풋값을 512로 만듬
dec4_2 = self.dec4_2(cat4)
# 여기까지 하면 우측 4층 레이어까지 생성
dec4_1 = self.dec4_1(dec4_2)
# 반복 3층
unpool3 = self.unpool3(dec4_1)
cat3 = torch.cat((unpool3, enc3_2),dim =1)
dec3_2 = self.dec3_2(cat3)
dec3_1 = self.dec3_1(dec3_2)
# 반복 2층
unpool2 = self.unpool2(dec3_1)
cat2 = torch.cat((unpool2, enc2_2),dim =1)
dec2_2 = self.dec2_2(cat2)
dec2_1 = self.dec2_1(dec2_2)
# 반복 1층
unpool1 = self.unpool1(dec2_1)
cat1 = torch.cat((unpool1, enc1_2),dim =1)
dec1_2 = self.dec1_2(cat1)
dec1_1 = self.dec1_1(dec1_2)
x = self.fc(dec1_1)
return x
전체 코드(Data formating, Modeling, Train)
'논문' 카테고리의 다른 글
[논문 공부] Transformer : Attention IS All YOU NEED (0) | 2022.07.25 |
---|---|
[논문 공부] LSTM(Long Short-Term Memory)(feat. RNN) (0) | 2022.07.16 |
[논문 공부] Sequence to Sequence Learning with Neural Networks (0) | 2022.07.11 |
[모델공부]U-Net : Image segmentations (0) | 2022.01.10 |
[논문 공부]SSD : Singl1e Shot Multibox Detector (0) | 2021.12.25 |