Machine Learning and Deep Learning

Pytorch - Introduction to Pytorch

Kkamang 2022. 3. 27. 20:08

Reference: Pytorch official video

 

Pytorch란? 

Research Prototyping에서 Production Deployment까지 일련의 과정을 도와주는 오픈 소스의 머신러닝 프레임워크.

 

1. Machine Learning Framework 

Deep learning primitives, NN Layer Types, activation/loss functions, Optimizers 를 포함한 Full Tool kits를 제공한다.

Libraries도 제공하며, 그 중 Torch vision은 사전 학습된 모델을 제공하기도 함.

 

2. Research Prototyping 

Python으로도 Computational Graph를 그릴 수 있으며, Autograd를 사용해서 간편하게 역전파를 사용할 수 있다.

 

3. Production Deployment

TorchScript, TorchServe, Quantization 등의 툴을 제공하여 기업에서 사용하기 용이하다.

 

4. Open Source 

 

 

Tensors

Pytorch에서 모델의 input과 output은 모두 Tensor로 이루어져 있으며 수학적으로는 Multi-dimensional array라고 이해하면 된다. 실제로 코드는 C++ 코드로 Compile된며, CPU와 GPU에 최적화 되어있다. 

 

 

Tutorials

1. Tensor

import torch
z = torch.zeros(5, 3)
print(z)
print(z.dtype)
i = torch.ones((5, 3), dtype=torch.int16)
print(i)
  • torch.init32: 소수 (default) 
  • torch.init16: 정수
# -1과 1 사이의 랜덤 숫자 
r = torch.rand(2, 2) - 0.5 * 2

# 절대값 취하기
print(torch.abs(r))

# 역함수 구하기
print(torch.asin(r))

# 선형대수학 determinant 값과 svd 값 구하기
print(torch.det(r))
print(torch.svd(r))

# 표준편차와 최대값
print(torch.std_mean(r))
print(torch.max(r))

 

 

이 복잡한 계산도 loss.backward() 한 줄의 코드로 계산할 수 있다: history tracking을 하기 때문에

2. A Simple PyTorch model

import torch                     # Pytorch 사용하기 위해
import torch.nn as nn            # 파이토치 모델들의 parent object가 되는 torch.nn
import torch.nn.functional as F  # 활성함수
class LeNet(nn.Module):

    def __init__(self):
    	# nn.Module의 변수들을 상속 받는다. 
        super(LeNet, self).__init__()
        
        # Conv2d(1,6, kernel_size=(3,3), stride=(1,1))
        self.conv1 = nn.Conv2d(1, 6, 3)
        # Conv2d(6,16, kernel_size=(3,3), stride=(1,1))
        self.conv2 = nn.Conv2d(6, 16, 3)
        
        # an affine operation: y = Wx + b
        # 6*6 from image dimension
        self.fc1 = nn.Linear(16 * 6 * 6, 120)  
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
        
	# 실제 계산이 이루어짐 
    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

[Pytorch nn.Module __init__ function]

nn.Module 의 변수들을 상속받아야 하기 때문에 

super(Child Class, self).__init__() 의 형태로 작성해 주어야 한다.

def __init__(self) -> None:
        """
        Initializes internal Module state, shared by both nn.Module and ScriptModule.
        """
        torch._C._log_api_usage_once("python.nn_module")

        self.training = True
        self._parameters: Dict[str, Optional[Parameter]] = OrderedDict()
        self._buffers: Dict[str, Optional[Tensor]] = OrderedDict()
        self._non_persistent_buffers_set: Set[str] = set()
        self._backward_hooks: Dict[int, Callable] = OrderedDict()
        self._is_full_backward_hook = None
        self._forward_hooks: Dict[int, Callable] = OrderedDict()
        self._forward_pre_hooks: Dict[int, Callable] = OrderedDict()
        self._state_dict_hooks: Dict[int, Callable] = OrderedDict()
        self._load_state_dict_pre_hooks: Dict[int, Callable] = OrderedDict()
        self._modules: Dict[str, Optional['Module']] = OrderedDict()

[Super function]

Parent Class의 methods에 접근할 수 있도록 하며, 사용될 때마다 Parent Class의 Temporary object를 반환한다. 

 

(1) Super Class를 사용하기 전: 반복되는 구문이 많다.

# Parent Class 
class Rectangle:
	pass

# child class1
class Square(Rectangle):
	def __init__(self, length, width):
    	self.length = length
        self.width = width

# child class2
class Cube(Rectanlge):
	def __init__(self, length, width, height):
    	self.length = length
        self.width = width
        self.height = height
        
 square = Square(3,3)
 cube = Cube(3,3,3)

(2) Super Function을 사용하여 Parent Class에 중복되는 코드들을 넣어준다.

# Parent Class 
class Rectangle:
	def __init__(self, length, width):
    	self.length = length
        self.width = width

# child class1
class Square(Rectangle):
	def __init__(self, length, width):
    	# Parent class의 __init__ method를 사용할 수 있음 
  		super().__init__(length, width)
        
    def area(self):
    	return self.length * self.width

# child class2
class Cube(Rectanlge):
	def __init__(self, length, width, height):
  		super().__init__(length, width)
        # Parent class에 height은 없으니까 써줘야 함 
        self.height = height
    def volume(self):
    	return self.length * self.width * self.height
        
 square = Square(3,3)
 cube = Cube(3,3,3)
 
 # 위에서 Class가 호출될 때 init 함수도 실행되었기 때문에 
 print(square.area)
 print(cube.volume)
net = LeNet()
print(net)                        

'''
LeNet(
  (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
  (fc1): Linear(in_features=576, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)
'''

input = torch.rand(1, 1, 32, 32)   # 32x32 사이즈의 흑백(1 color channel) 이미지 하나
print('\nImage batch shape:')
print(input.shape)

output = net(input)                # forward function을 직접 부르지 않는다. (모델에게 input전달)
print('\nRaw output:')
print(output)
print(output.shape)

 

Pytorch 모델들은 데이터의 batch 단위로 작업을 한다고 가정을 한다. 예를 들어서 batch size 16으로 이루어진 이미지 데이터는 (16, 1, 32, 32)의 모양을 가진다. 우리는 위에 코드에서는 하나의 이미지만 사용하기 때문에 batch size를 1로 하여 (1, 1, 32, 32)를 사용한다.

3. A Simple PyTorch Training Loop

%matplotlib inline

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import torchvision
import torchvision.transforms as transforms

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
import matplotlib.pyplot as plt
import numpy as np

# 이미지 원복하여 보여주기


def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))


# 랜덤한 training image 보여줌
dataiter = iter(trainloader)
images, labels = dataiter.next()

# 이미지 보여주기
imshow(torchvision.utils.make_grid(images))
# 라벨(정답값) 보여주기
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):  # 전체 데이터셋에 대해서 여러번의 epoch을 돌린다(여러 번 학습)

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # input과 정답값 가져오기
        inputs, labels = data

        # gradient 초기화
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 통계량 출력
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')
correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(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))