# 1. 라이브러리 로드
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transform as transform
# 2. Visdom 이용
import visdom
vis = visdom.Visdom()
vis.close(env="main")
# 3. Value(Accuracy, Loss) tracker
def value_tracker(value_plot, value, num):
vis.line(X=num,
Y=value,
win=value_plot,
update='append'
)
# 4. GPU 설정
device = 'cuda' if torch.cuda.is_available() else 'cpu'
torch.manual_seed(777)
if device == 'cuda':
torch.cuda.manual_seed_all(777)
# 팁! Normalize 값 계산하는 방법 1 : 데이터 로드
transform = transforms.Compose([
transforms.ToTensor()
])
trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform)
# 팁! Normalize 값 계산하는 방법 2 : 계산
'''
이전의 VGG 예시에서는 transform을 이용 할 때 Normalize 값에 0.5와 같은 값을 무작정 넣었는데,
이번에는 Normalize를 직접 계산해서 transform 할 때 계산 값을 넣어 보자!
'''
train_data_mean = trainset.train_data.mean( axis = (0, 1, 2) ) # 데이터 셋의 각 축에 대한 평균 계산
train_data_std = trainset.train_data.std( axis = (0, 1, 2) ) # 데이터 셋의 가 축에 대한 std 계산
print(train_data_mean, train_data_std)
train_data_mean = train_data_mean / 255 # 앞에서 구한 값을 255로 나눠준다.
train_data_std = train_data_std / 255 # 앞에서 구한 값을 255로 나눠준다.
print(train_data_mean, train_data_std)
# 5. 데이터 로드
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4), # padding을 4로 두른 후, 32만큼 랜덤하게 crop한다. 데이터 셋의 형태를 변형시키는 데 좋은 방법!
transforms.ToTensor(), # 텐서화
transforms.Normalize(train_data_mean, train_data_std) # 앞에서 직접 구한 Normalize 값으로 Normalize 해 준다.
])
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(train_data_mean, train_Data_std)
])
trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=256, shuffle=True, num_workers=0)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False, download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, barch_size=256, shuffle=False, num_worksers=0)
classes = ('plane', 'car', 'bird', cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 6. ResNet 네트워크 구축
# 라이브러리 로드
import resnet
import torchvision.models.resnet # resnet.py를 저장해서 이용하는 것이 아닌, 이렇게 라이브러리를 불러와서 이용해도 된다.
# resnet 기본 설정
conv1x1 = resnet.conv1x1
Bottleneck = resnet.Bottleneck
BasicBlock = resnet.BasicBlock
# ResNet 네트워크 구축 해 놓기
# 변형!은 기존의 ResNet 예제에서 변형 한 것을 뜻한다.
class ResNet(nn.Module):
def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):
super(ResNet, self).__init__()
self.inplanes = 16 # 변형! 이번에는 inplanes을 16으로 설정했다.
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False) # input 3채널, output 16채널
self.bn1 = nn.BatchNorm2d(16)
self.relu = nn.ReLU(inplace=True)
self.layer1 = self._make_layer(block, 16, layers[0], stride=1) # 변형! layer의 사이즈를 64-> 16과 같이 간소화. stride도 바꿈
self.layer2 = self._make_layer(block, 32, layers[1], stride=1)
self.layer3 = self._make_layer(block, 64, layers[2], stride=2)
self.layer4 = self._make_layer(block, 128, layers[3], stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(128 * block.expansion, num_classes) # input : 128 * block.expansion = 512, output : 10
# 변형 x
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)
# 변형 x
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)
# 변형 x
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplanes != 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) # [1, 16, 32, 32]
x = self.bn1(x)
x = self.relu(x)
x = self.layer1(x) # [1, 128, 32, 32]
x = self.layer2(x) # [1, 256, 32, 32]
x = self.layer3(x) # [1, 512, 16, 16]
x = self.layer4(x) # [1, 1024, 8, 8]
x = self.avgpool(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
# 7. ResNet 모델 이용하기!
resnet50 = ResNet(resnet.Bottleneck, [3, 4, 6, 3], 10, True).to(device) # Bottleneck과 [3, 4, 6, 3]을 설정하면 resnet 50을 만들 수 있다.
resnet50
# 8. ResNet 모델에 오류 없나 테스트 해보기
a = torch.Tensor(1, 3, 32, 32).to(device)
out = resnet50(a)
print(out)
# 9. Loss, Optimizer, LR
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(resnet50.parameters(), lr = 0.1, momentum = 0.9, weight_decay = 5e-4)
lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
# 10. Loss와 Acc로 Plot으로 만들기
loss_plt = vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='loss_tracker', legend=['loss'], showlegend=True))
acc_plt = vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='Accuracy', legend=['Acc'], showlegend=True))
# 11. Accuracy 체크하는 함수
def acc_check(net, test_set, epoch, save=1):
correct = 0
total = 0
with torch.no_grad():
for data in test_set:
images, labels = data
images = images.to(device)
labels = labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
acc = (100 * correct / total)
print('Accuracy of the network on the 10000 test images: %d %%' %acc)
if save: # save를 True로 설정 할 경우, 지정 된 폴더에 epoch 별 정확도를 저장할 수 있다.
torch.save(net.state_dict(), "./model/model_epoch_{}_acc_{}.pth".format(epoch, int(acc)))
return acc
# 12. ResNet으로 학습 시작!
print(len(trainloader))
epochs = 150
for epoch in range(epochs):
running_loss = 0.0
lr_sche.step()
for i, data in enumerate(trainloader, 0):
inputs, labels = data
inputs = inputs.to(device)
labels = labels.to(device)
outputs = resnet50(inputs)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 30 == 29:
value_tracker(loss_plt, torch.Tensor([running_loss/30]), torch.Tensor([i + epoch * len(trainloader)])) # value tracker
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 30))
running_loss = 0.0
# epoch이 한 번 돌면, 정확도를 체크하고 정확도를 value_tracker에 저장
acc = acc_check(resnet50, testloader, epoch, save=1)
value_tracker(acc_plt, torch.Tensor([acc]), torch.Tensor([epoch]))
print('Finished Training')
correct = 0
total = 0
# 13. 모델 평가
with torch.no_grad():
for data in testloader:
images, labels = data
images = images.to(device)
labels = labels.to(device)
outputs = resnet50(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' %(100 * correct / total))
# Accuracy : 87%