Spaces:
Sleeping
Sleeping
File size: 7,178 Bytes
907b7f3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
from collections import OrderedDict
from torch.nn import init
import math
import pdb # 파이썬 디버거
# Conv2D (3,3) + BatchNorm2D + ReLU
def conv_bn(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True)
)
# Conv2D (1,1) + BatchNorm2D + ReLU
def conv_1x1_bn(inp, oup):
return nn.Sequential(
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True)
)
# reshape -> flatten
def channel_shuffle(x, groups):
batchsize, num_channels, height, width = x.data.size() # data 정보
channels_per_group = num_channels // groups # 그룹당 채널 계산
# reshape
x = x.view(batchsize, groups, # reshape 적용된 모양의 tensor 반환 # 원본 data 공유
channels_per_group, height, width)
x = torch.transpose(x, 1, 2).contiguous() # transpose(): 2개의 차원 맞교환 # contiguous(): 원본과 다른 새로운 주소로 할당
# flatten => [batchsize, height * width]
x = x.view(batchsize, -1, height, width) # reshape 적용된 모양의 tensor 반환 # 원본 data 공유
return x
# Inverted Residual - 관련 모델: MobileNetV2
class InvertedResidual(nn.Module):
def __init__(self, inp, oup, stride, benchmodel):
super(InvertedResidual, self).__init__()
self.benchmodel = benchmodel
self.stride = stride
# stride 가 [1,2] 인지 확인, 아니면 AssertionError 메시지를 띄움
assert stride in [1, 2] # 원하는 조건의 변수값을 보증하기 위해 사용
oup_inc = oup//2
if self.benchmodel == 1:
#assert inp == oup_inc
self.banch2 = nn.Sequential(
# pw
nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False), # Conv2D (1,1)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
nn.ReLU(inplace=True), # ReLU
# dw
nn.Conv2d(oup_inc, oup_inc, 3, stride, 1, groups=oup_inc, bias=False), # Conv2D (3,3)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
# pw-linear
nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False), # Conv2D (1,1)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
nn.ReLU(inplace=True), # ReLU
)
else:
self.banch1 = nn.Sequential(
# dw
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), # Conv2D (3,3)
nn.BatchNorm2d(inp), # BatchNorm2D
# pw-linear
nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False), # Conv2D (1,1)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
nn.ReLU(inplace=True), # ReLU
)
self.banch2 = nn.Sequential(
# pw
nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False), # Conv2D (1,1)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
nn.ReLU(inplace=True), # ReLU
# dw
nn.Conv2d(oup_inc, oup_inc, 3, stride, 1, groups=oup_inc, bias=False), # Conv2D (3,3)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
# pw-linear
nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False), # Conv2D (1,1)
nn.BatchNorm2d(oup_inc), # BatchNorm2D
nn.ReLU(inplace=True), # ReLU
)
@staticmethod
def _concat(x, out):
# concatenate along channel axis
return torch.cat((x, out), 1) # Tensor list를 한번에 tensor로 만들기
# 모델이 학습데이터를 입력받아서 forward propagation 진행
def forward(self, x):
if 1==self.benchmodel:
x1 = x[:, :(x.shape[1]//2), :, :]
x2 = x[:, (x.shape[1]//2):, :, :]
out = self._concat(x1, self.banch2(x2))
elif 2==self.benchmodel:
out = self._concat(self.banch1(x), self.banch2(x))
return channel_shuffle(out, 2) # reshape -> flatten
# 셔플넷 V2
class ShuffleNetV2(nn.Module):
def __init__(self, n_class=1000, input_size=224, width_mult=2.):
super(ShuffleNetV2, self).__init__()
# 인풋사이즈 % 32 == 0 인지 확인, 아니면 AssertionError 메시지를 띄움
assert input_size % 32 == 0, "Input size needs to be divisible by 32" # 원하는 조건의 변수값을 보증하기 위해 사용
self.stage_repeats = [4, 8, 4]
# index 0 is invalid and should never be called.
# only used for indexing convenience.
if width_mult == 0.5:
self.stage_out_channels = [-1, 24, 48, 96, 192, 1024]
elif width_mult == 1.0:
self.stage_out_channels = [-1, 24, 116, 232, 464, 1024]
elif width_mult == 1.5:
self.stage_out_channels = [-1, 24, 176, 352, 704, 1024]
elif width_mult == 2.0:
self.stage_out_channels = [-1, 24, 244, 488, 976, 2048]
else:
raise ValueError( # 에러 발생시키기
"""Width multiplier should be in [0.5, 1.0, 1.5, 2.0]. Current value: {}""".format(width_mult))
# building first layer
input_channel = self.stage_out_channels[1]
self.conv1 = conv_bn(3, input_channel, 2) # Conv2D (3,3) + BatchNorm2D + ReLU
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # MaxPool2D
self.features = []
# building inverted residual blocks
for idxstage in range(len(self.stage_repeats)):
numrepeat = self.stage_repeats[idxstage]
output_channel = self.stage_out_channels[idxstage+2]
for i in range(numrepeat):
if i == 0:
#inp, oup, stride, benchmodel):
self.features.append(InvertedResidual(input_channel, output_channel, 2, 2))
else:
self.features.append(InvertedResidual(input_channel, output_channel, 1, 1))
input_channel = output_channel
# make it nn.Sequential
self.features = nn.Sequential(*self.features)
# building last several layers
self.conv_last = conv_1x1_bn(input_channel, self.stage_out_channels[-1]) # Conv2D (1,1) + BatchNorm2D + ReLU
self.globalpool = nn.Sequential(nn.AvgPool2d(int(input_size/32))) # AvgPool2D
# building classifier # 선형 회귀 모델
self.classifier = nn.Sequential(nn.Linear(self.stage_out_channels[-1], n_class))
# 모델이 학습데이터를 입력받아서 forward propagation 진행
def forward(self, x):
x = self.conv1(x)
x = self.maxpool(x)
x = self.features(x)
x = self.conv_last(x)
x = self.globalpool(x)
x = x.view(-1, self.stage_out_channels[-1])
x = self.classifier(x)
return x
|