[Lecture Review] 한줄정리
1. Introduction to PyTorch
- PyTorch compared to Numpy
Pytorch | Numpy | |
---|---|---|
모듈 | import torch | import numpy as np |
기본 | torch.tensor([[2,3,5],[1,2,9]]) | np.array([[2,3,5],[1,2,9]]) |
난수 | torch.rand(2,2) | np.random.rand(2,2) |
크기 | a.shape | a.shape |
행렬곱 | torch.matmul(a,b) | np.dot(a,b) |
원소곱 | a*b | np.multiply(a,b) |
0행렬 | torch.zeros(2,2) | np.zeros((2,2)) |
1행렬 | torch.ones(2,2) | np.ones((2,2)) |
단위행렬 | torch.eye(2) | np.identity(2) |
변환 | torch.from_numpy(a) | a.numpy() |
- Backpropagation
import torch
x = torch.tensor(-3., requires_grad=True)
y = torch.tensor(5., requires_grad=True)
z = torch.tensor(-2., requires_grad=True)
q = x + y
f = q * z
f.backward()
z.grad # tensor(2.)
y.grad # tensor(-2.)
x.grad # tensor(-2.)
2. Artificial Neural Networks
- Activation Functions
행렬곱(신경망)을 여러개 쌓아도 하나의 행렬곱으로 단순화시킬 수 있기 때문에 활성화 함수를 사용하여 비선형적인 관계를 처리하고 복잡한 모델을 구축한다. 즉, 활성화 함수는 신경망의 각 계층에 삽입하는 비선형 함수이다. 다양한 활성함 함수는 이 페이지에서 확인할 수 있다.
import torch.nn as nn
relu = nn.ReLU()
tensor = torch.tensor([2., -4.])
relu(tensor) # tensor([2., 0.])
- Loss Functions
손실함수는 모델이 얼마나 잘못 수행하고 있는지에 대한 척도이다. 대표적으로 회귀에서는 최소 제곱 오차를 계산하고, 분류에서는 softmax cross-entropy를 계산한다. 일반적으로 네트워크가 정확할수록 손실값은 더 작다.
logits = torch.tensor([[3.2, 5.1, -1.7]])
ground_truth = torch.tensor([0])
criterion = nn.CrossEntropyLoss()
loss = criterion(logits, ground_truth) # tensor(2.0404)
softmax cross-entropy는 각 클래스별 logit을 자연상수(e)에 제곱하고 normalize하여 확률값을 계산한 후, 정답 클래스의 확률값에 음의 자연로그를 취한 값이다.
import math
-math.log(math.exp(3.2) / (math.exp(3.2)+math.exp(5.1)+math.exp(-1.7))) # 2.0404
- Datasetes and Dataloaders
import torch
import torchvision
import torch.utils.data
import torchvision.transforms as transforms
transform = transforms.Compose( [transforms.ToTensor().
transforms.Normalize((0.4914, 0.48216, 0.44653),
(0.24703, 0.24349, 0.26159))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=4)
testloader = torch.utils.data.DataLoader(testset, batch_size=32, shuffle=False, num_workers=4)
print(testloader.dataset.test_data.shape) # (10000, 32, 32, 3)
print(testloader.batch_size) # 32
- Build a neural network
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(32*32*3, 500)
self.fc2 = nn.Linear(500, 10)
def forward(self, x):
x = F.relu(self,fc1(x))
return self.fc2(x)
- Train neural networks
net = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=3e-4)
for epoch in range(10):
for i, data in enumerate(trainloader, 0):
inputs, labels = data
inputs= inputs.view(-1, 32*3*3)
optimizer.zero_grad() # gradients를 축적하지 않기 위해
outputs = net(inputs)
loss = creterion(outputs, labels)
loss.backward()
optimizer.step()
- Get predictions
correct, total = 0, 0
predictions = []
net.eval()
for i, data in enumerate(testloader, 0):
inputs, labels = data
inputs = inputs.view(-1, 32*32*3)
outputs = net(inputs)
_, predicted = torch.max(outputs.data, 1)
predictions.append(outputs)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100*correct/total
3. Convolutional Neural Networks (CNNs)
- Convolution operator
fully-connected 신경망은 모든 features 사이의 관계를 고려하기 때문에 연산이 너무 커져 비효율적이고 파라미터가 많아 과적합될 수도 있다. 이를 해결하기 위한 Convolution은 일부의 features만 연결하고 가중치를 공유하도록 한다.
Convolution filter는 입력과 depth의 차원이 일치해야 하며, 이미지와 필터 사이의 원소곱으로 계산된다. 관련 용어들은 이 페이지에서 확인할 수 있다.
PyTorch의 두 가지 모듈이 Convolution 연산을 제공하고 있다.
- torch.nn : Conv2d 연산 내에서 필터를 설정
import torch
import torch.nn
image = torch.rand(16, 3, 32, 32)
conv_filter = torch.nn.Conv2d(in_channels=3, out_channels=1, kernel_size=5, stride=1, padding=0)
output_feature = conv_filter(image)
- torch.nn.functional : 필터를 따로 선언하고 conv2d 연산 수행
import torch
import torch.nn.functional as F
image = torch.rand(16, 3, 32, 32)
filters = torch.rand(1, 3, 5, 5)
out_feat_F = F.conv2d(image, filters, stride=1, padding=0)
Convolution 연산 후의 이미지 사이즈는 다음과 같이 계산한다.
- Pooling operator
Pooling은 단순히 크기를 줄이는 것으로 연산을 더 효율적이게 하고 이미지의 이동에 대해 모델을 더 robust하게 한다.
대표적으로 Max-Pooling과 Average-Pooling이 있다.
- Max-Pooling
max_pooling = torch.nn.MaxPool2d(2)
output_feature = max_pooling(image)
out_feat_F = F.max_pool2d(image, 2)
- Average-Pooling
avg_pooling = torch.nn.AvgPool2d(2)
output_feature = avg_pooling(image)
out_feat_F = F.avg_pool2d(image, 2)
- Build a Convolutional Neural Network
class Net(nn.Module):
def __init__(self, num_classes):
super(Net, self).__init__()
self.relu = nn.ReLU()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=5, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(in_channels=5, out_channels=10, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(kernel_size=2, stride=2) # 크기의 절반이 되도록
self.fc = nn.Linear(10*7*7, 10) # pool을 두번 거쳤기 때문에
def forward(self, x):
x = self.relu(self.conv1(x)) # (1, 28, 28) -> (5, 28, 28)
x = self.pool(x) # (5, 28, 28) -> (5, 14, 14)
x = self.relu(self.conv2(x)) # (5, 14, 14) -> (10, 14, 14)
x = self.pool(x) # (10, 14, 14) -> (10, 7, 7)
x = x.view(-1, 10*7*7)
return self.fc(x)
4. Using Convolutional Neural Networks
- The Sequential Module
위처럼 모든 layer를 개별적으로 선언하고 순차적으로 적용할 수 있다. 하지만 좀 더 효율적이고 간단하게 하려면 순차 모듈로 캡슐화하여 여러 layer를 한 번에 쌓는 방법이 있다.
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.features = nn.Sequential(nn.Conv2d(in_channels=1, out_channels=5, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(in_channels=5, out_channels=10, kernel_size=3, padding=1),
nn.MaxPool2d(2, 2), nn.ReLU(inplace=True))
self.classifier = nn.Sequential(nn.Linear(14 * 14 * 10, 1024), nn.ReLU(inplace=True),
nn.Linear(1024, 2048), nn.ReLU(inplace=True),
nn.Linear(2048, 10))
def forward(self, x):
x = self.features(x)
x = x.view(-1, 14 * 14 * 10)
x = self.classifier(x)
return x
- The problem of overfitting
모델은 훈련 세트 내에서 학습하지만 실제로는 테스트 세트의 성능이 얼마나 좋은지, 즉 일반화 성능이 더 중요하다. 일반화 성능이 떨어지는 것을 과적합이라고 부르며, 알고리즘의 정확도를 플로팅하여 훈련 세트의 정확도와 테스트 세트의 정확도의 차이가 크면 과적합이라고 판단한다.
테스트 세트의 과적합을 줄이는 방법 중 하나는 검증 세트를 만드는 것이다. 테스트 세트가 여러 번 사용되면 이는 이미 오염되어 신뢰할 수 없는 결과를 가져온다. 따라서 모델을 선정해주는 검증 세트를 따로 두고 가장 좋은 모델을 골라 테스트 세트를 확인한다. 이 때 검증 세트와 테스트 세트는 서로 겹치면 안된다.
indices = np.arange(60000)
np.random.shuffle(indices)
train_loader = torch.utils.data.DataLoader(datasets.MNIST('mnist', download=True, train=True, transform=transform),
batch_size=64, shuffle=False, sampler=torch.utils.data.SubsetRandomSampler(indices[:55000]))
val_loader = torch.utils.data.DataLoader(datasets.MNIST('mnist', download=True, train=True, transform=transform),
batch_size=64, shuffle=False, sampler=torch.utils.data.SubsetRandomSampler(indices[55000:]))
- Regularization techniques
- L2 regularization : 큰 가중치에 대한 패널티를 손실 함수에 추가하는 방법 (weight_decay 추가)
optim.Adam(net.parameters(), lr=3e-4, weight_decay=0.0001)
- Dropout : 몇 개의 노드를 제거하여 연산에서 제외시키는 방법, 제거되는 노드는 학습이 반복될 때마다 바뀜, 일반적으로 fc layer에서 사용
nn.Dropout(p=0.5)
- Batch normalization : 각 배치별 평균과 분산을 구해 표준화시키는 방법
nn.BatchNorm2d(num_features=64, eps=1e-05, momentum=0.9) # num_features는 채널수
- Early stopping : 검증 세트의 정확성을 확인하고 성능이 좋아지지 않으면 학습을 종료시키는 방법
- Transfer Learning
ImageNet과 같은 큰 데이터 세트로 학습된 모델을 가져와 다시 학습시키는 방법이다. 시간도 절약하고 작은 데이터 세트에서도 신경망을 사용할 수 있다는 장점이 있다.
model = Net()
model.load_state_dict(torch.laod('cifar10_ent.pth'))
for param in model.parameters(): # 모든 가중치를 고정시키려면
param.requires_grad = False
model.fc = nn.Linear(4*4*1024, 100)
model.train()
torchvision 라이브러리를 사용하면 사전훈련된 모델을 한 번에 가져올 수 있다.
import torchvision
model = torchvision.models.resnet18(pretrained=True)
model.fc = nn.Linear(512, num_classes)
이 내용은 가짜연구소에서 DSF프로그램으로 수강한 DataCamp의 Introduction to Deep Learning with PyTorch 강의를 기반으로 공부한 자료이다.