torchvision.models.vgg
import torchvision.models as models
model = models.resnet34(pretrained=False) # ResNet 모델 불러오기(pretrained=False이므로 weight 제외하고 구조만 가져옴)
num_ftrs = model.fc.in_features # model의 Fully-Connected Layer를 구성하고 있는 부분에 접근해 input에 해당하는 노드 수 저장
model.fc = nn.Linear(num_ftrs, 10) # model에 새로운 레이어 추가
model = model.cuda() # model.to(device)와 동일
import torchvision.models as models
model = models.resnet34(pretrained=True)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 10)
model = model.cuda()
class VGG(nn.Module):
def __init__(self, features, num_classes=1000, init_weights=True):
super(VGG, self).__init__()
self.features = features # VGG Convolution Layers
self.avgpool = nn.AdaptiveAvgPool2d((7,7)) # output의 크기에 따라 알아서 average pooling
self.classifier = nn.Sequential([ # Fully-Connected Layer 3개
nn.Linear(512*7*7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes)
])
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x) # Convolution Layer
x = self.avgpool(x) # avgpool
x = x.view(x.size(0), -1)
x = self.classifier(x) # F/C Layer
return x
def _initialize_weights(self):
for m in self.modules(): # features에 남겨뒀던 값들을 하나씩 가져옴
if isinstance(m, nn.Conv2d): # convolution 일 경우
nn.init.kaiming_normal_(m.weight, model='fan_out', nonlinearity='relu') # He Initialization
if m.bias in not None: # VGG에서는 bias를 0으로 설정하기 때문에 0으로 설정
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
def make_layers(cfg, batch_norm=False):
layers = []
in_channels = 3
for v in cfg:
if v == 'M': # MaxPooling Layer
layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
else: # Conv Layer의 filter의 개수
conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
if batch_norm:
layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
else:
layers += [conv2d, nn.ReLU(inplace=True)]
in_channels = v # in_channels 갱신
return nn.Sequential(*layers)
위 모델 적용 코드
cfg = {
'A':[64, 'M', 128, 'M', 256, 256, 'M', 512, 512,'M', 512, 512, 'M']
}
model = VGG(make_layers(cfg['A']))
class BasicBlock(nn.Module):
def __init__(self, in_planes, planes, stride=1):
super(BasicBlock, self).__init__() # nn.Module 내에 있는 메소드 상속
self.conv1 = nn.Conv2d(in_planes, planes, # 데이터 채널 수(in_planes) 입력 받아 필터 개수(planes) 반환
kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes) # planes 크기의 데이터에 적용할 수 있는 BatchNorm2d
self.conv2 = nn.Conv2d(planes, planes,
kernel_size=3, stride=stride,
padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != planes: # for skip-connection
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, planes,
kernel_size=1, stride=stride,
bias=False),
nn.BatchNorm2d(planes)
)
def forward(self, x):
out = nn.functional.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += shortcut(x) # skip-connection
out = nn.functional.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, num_classes=10):
super(ResNet, self).__init__()
self.in_planes = 16 # 16 채널 수
self.conv1 = nn.Conv2d(3, 16, # Basic Block 클래스 내에서 이용하는 self.conv1과 다름 # input으로 이용하는 컬러 이미지에 적용
kernel_size=3, stride=1,
padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(16)
self.layer1 = self._make_layer(16, 2, stride=1)
self.layer2 = self._make_layer(32, 2, stride=2)
self.layer3 = self._make_layer(64, 2, strdie=2)
self.linear = nn.Linear(64, num_classes)
def _make_layer(self, planes, num_blocks, stride): # 여러 층의 레이어를 구성해 반환해주는 메소드
strides = [stride] + [1]*(num_blocks-1) # stride를 BasicBlock마다 설정
layers = []
for stride in strides:
layers.append(BasicBlock(self.in_planes, planes, stride)) # 처음에만 in_planes를 input으로
self.in_planes = planes # 두번째부터는 planes를 input으로
return nn.Sequential(*layers)
def forward(self, x):
out = nn.functional.relu(self.bn1(self.conv1(x)))
out = self.layer1(out) # 16채널을 input으로 받아 16채널을 output으로 하는 BasicBlock 2개 생성
out = self.layer2(out) # 16채널을 input으로 받아 32채널을 output으로 하는 BasicBlock 1개 생성 + 32->32 1개 생성
out = self.layer3(out) # 32->64 BasicBlock 1개 생성 + 64->64 1개 생성
out = nn.functional.avg_pool2d(out, 8) # feature map에 average pooling 적용
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
def conv3x3(in_planes, out_planes, stride=1):
# 3*3 conv with padding
return nn.Conv2d(in_planes, out_planes,
kernel_size=3, stride=stride,
padding=1, bias=False)
def conv1x1(in_planes, out_planes, stride=1):
# 1*1 conv
return nn.Conv2d(in_planes, out_planes,
kernel_size=1, stride=stride,
bias=False)
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
# out과 identity가 shape이 안맞을 경우 identity downsampling
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(Bottleneck, self).__init__()
self.conv1 = conv1x1(inplanes, planes)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = conv3x3(planes, planes, stride)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = conv1x1(planes, planes * self.expansion) # 4배로 뜀
self.bn3 = nn.BatchNorm2d(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):
super(ResNet, self).__init__()
self.inplanes = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=7,
stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, layers[0])
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1,1))
self.fc = nn.Linear(512 * block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
if zero_init_residual:
for m in self.modules():
if isinstance(m, Bottleneck):
nn.init.constant_(m.bn3.weight, 0)
elif isinstance(m, BasicBlock):
nn.init.constant_(m.bn2.weight, 0)
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplates != planes * block.expansion:
downsample = nn.Sequential([
conv1x1(self.inplanes, planes=block.expansion, stride),
nn.BatchNorm2d(planes=block.expansion)
])
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for _ in range(1, blocks):
layers.append(block(self.inplanes, planes))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
참고
파이썬 딥러닝 파이토치 (이경택, 방성수, 안상준)
모두를 위한 딥러닝 시즌 2 Lab 10-5, 10-6-1, 10-6-2