Pytorch에 대해서 알아보자.araboza
딥러닝 플랫폼인 Pytorch에 대하여 2020-03-23

주의

필자인 저 또한 배운 것들을 정리 하면서 쓰는 글이기 때문에, 틀린 부분이 있을 수도 있습니다. 또한, 이 글은 Numpy에 대한 기본 적인 배경 지식을 필요로 합니다. 오타, 지적사항 발생 시, 댓글 혹은 이메일로 남겨 주시면 감사하겠습니다!

Pytorch

Pytorch는 두 가지 목표를 달성하기 위해 만들어진 오픈 소스 라이브러리이자, Python Package입니다. 두 가지 목표는 다음과 같습니다.

  • Numpy가 기존에 하던 연산들을 GPU로 대체하기 위함.
  • 높은 유연성과 속도를 제공하는 Deep Learning 연구 플랫폼

Facebook 인공지능 연구 팀이 개발 하였으며, 러닝 커브가 낮고, 코드 가독성 또한 좋은 편입니다. 실시간 결과 값을 시각화도 가능할 뿐더러, 기계 학습 과정을 보며, 수학적으로 이해하는 데 상당히 유용하죠. 자, 한번 Pytorch의 기본 문법을 알아 볼까요?

Pytorch 로고


Installation

Python3, pip이 설치된 상태에서 터미널에 해당 명령어를 입력 해 주세요.

pip install torch===1.4.0 torchvision===0.5.0 -f https://download.pytorch.org/whl/torch_stable.html

Tensor

Tensor 객체는 Numpyndarray와 상당히 유사한 구조를 가집니다. 애초에 PytorchNumpy를 대체하기 위해서 나왔기 때문이죠, 일단 torch 모듈을 import 해 볼까요?

import torch

Make some Tensor Objects

그 다음, Tensor 객체를 만드는 여러 가지 방법들을 알려 드리겠습니다.

  • torch.empty(x, y): x * y 사이즈의 요소들의 값이 초기화 되지 않은 행렬 반환.
  • torch.rand(x, y): x * y 사이즈의 요소들이 0 ~ 1 사이의 랜덤한 값으로 초기화 된 행렬 반환.
  • torch.randn(x, y): x * y 사이즈의 요소들이 정규분포 그래프 상의 랜덤한 값으로 초기화 된 행렬 반환.
  • torch.zeros(x, y, dtype=type): x * y 사이즈의 요소들이 0으로 초기화 된 행렬 반환, 요소들은 type에 맞게 초기화 된다.
  • torch.ones(x, y, dtype=type): x * y 사이즈의 요소들이 1으로 초기화 된 행렬 반환, 요소들은 type에 맞게 초기화 된다.
  • torch.tensor(iterable): iterable한 객체를 Tensor 객체로 변환한다.
  • torch.zeros_like(tensor, dtype=type): 파라미터로 들어 간 Tensor 객체의 사이즈과 똑같은 행렬을 반환하며, 요소들은 0으로 초기화 되어 있다.
  • torch.ones_like(tensor, dtype=type): 파라미터로 들어 간 Tensor 객체의 사이즈과 똑같은 행렬을 반환하며, 요소들은 1으로 초기화 되어 있다.
  • torch.randn_like(tensor, dtype=type): 파라미터로 들어 간 Tensor 객체의 사이즈과 똑같은 행렬을 반환하며, 요소들은 정규분포 그래프 상의 랜덤한 값으로 초기화 되어 있다.

Code implementation

  • 입력
empty_tensor = torch.empty(3, 3)											# 3 * 3의 빈 행렬 생성
rand_tensor = torch.rand(3, 3)												# 3 * 3의 요소들이 0 ~ 1의 랜덤 값으로 초기화된 행렬 생성
randn_tensor = torch.randn(3, 3, dtype=torch.double)						# 3 * 3의 요소들이 정규분포 그래프 값으로 초기화된 행렬 생성
zero_tensor = torch.zeros(3, 3, dtype=torch.long)							# 3 * 3의 요소들이 0으로 초기화된 행렬 생성
one_tensor = torch.ones(3, 3, dtype=torch.double)							# 3 * 3의 요소들이 1으로 초기화된 행렬 생성
iterable_tensor = torch.tensor([1, 2, 3])									# list 객체를 Tensor 객체로 변환
zeros_like_tensor = torch.zeros_like(iterable_tensor, dtype=torch.double)	# iterable_tensor와 사이즈가 같은, 요소들이 0으로 초기화된 행렬 생성
ones_like_tensor = torch.ones_like(iterable_tensor, dtype=torch.double)		# iterable_tensor와 사이즈가 같은, 요소들이 1으로 초기화된 행렬 생성
randn_like_tensor = torch.randn_like(iterable_tensor, dtype=torch.double)	# iterable_tensor와 사이즈가 같은, 요소들이 정규분포 그래프 값으로 초기화된 행렬 생성

print(empty_tensor)
print(rand_tensor)
print(randn_tensor)
print(zero_tensor)
print(one_tensor)
print(iterable_tensor)
print(zeros_like_tensor)
print(ones_like_tensor)
print(randn_like_tensor)

  • 출력
tensor([[1.2045e-35, 0.0000e+00, 4.4842e-44],
        [0.0000e+00,        nan, 6.1657e-44],
        [4.3722e-05, 5.2475e-08, 1.7662e-04]])
tensor([[0.8250, 0.8530, 0.6120],
        [0.4726, 0.9426, 0.7616],
        [0.5276, 0.1977, 0.1966]])
tensor([[-0.1279, -0.9227,  0.0434],
        [-0.2085,  0.1541, -1.8450],
        [-0.7687,  0.0956, -0.6723]], dtype=torch.float64)
tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
tensor([1, 2, 3])
tensor([0., 0., 0.], dtype=torch.float64)
tensor([1., 1., 1.], dtype=torch.float64)
tensor([ 0.5920, -0.2960, -0.2184], dtype=torch.float64)

Operations

당연히, Tensor 객체의 연산을 위한 연산자, 인덱싱 또한 준비 되어 있습니다.

Code Implementation

일단 기본적인 연산자는 다음과 같이 사용 할 수 있습니다. 또한, Numpy에서 사용 했던, 차원별 인덱싱브로드캐스팅이 가능합니다.

  • 입력
x_tensor = torch.tensor([[1, 2], [3, 4]]) # 2 * 2 행렬 생성
y_tensor = torch.tensor([[5, 6], [7, 8]])
z_tensor = torch.tensor([[[1, 2], [3, 4]], [[1, 2], [3, 4]]])

print(x_tensor)
print(y_tensor)

print(x_tensor + y_tensor)  # Index가 일치하는 요소 끼리 덧셈
print(x_tensor - y_tensor)  # Index가 일치하는 요소 끼리 뺄셈
print(x_tensor * y_tensor)  # Index가 일치하는 요소 끼리 곱셈
print(x_tensor @ y_tensor)  # 행렬 곱
print(x_tensor * 3)         # x_tensor 각 요소에 3을 곱해줌

print(x_tensor + z_tensor)  # 일치하는 요소에 브로드캐스팅

a_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
b_tensor = torch.tensor([[1, 2], [3, 4], [5, 6]])
print(a_tensor)
print(b_tensor)
print(a_tensor @ b_tensor)  # 2 * 3 행렬과 3 * 2 행렬 곱셈
print(a_tensor[:, 1])       # 각 행의 1번째 열 추출
print(b_tensor[1, :])       # 1번째 행의 모든 열 추출

  • 출력
tensor([[1, 2],
        [3, 4]])
tensor([[5, 6],
        [7, 8]])
tensor([[ 6,  8],
        [10, 12]])
tensor([[-4, -4],
        [-4, -4]])
tensor([[ 5, 12],
        [21, 32]])
tensor([[19, 22],
        [43, 50]])
tensor([[ 3,  6],
        [ 9, 12]])
tensor([[[2, 4],
         [6, 8]],

        [[2, 4],
         [6, 8]]])
tensor([[1, 2, 3],
        [4, 5, 6]])
tensor([[1, 2],
        [3, 4],
        [5, 6]])
tensor([[22, 28],
        [49, 64]])
tensor([2, 5])
tensor([3, 4])



Tensor Resize

딥러닝 모델을 설계하다 보면 행렬 사이즈를 재조정 해야 하는 경우가 상당히 많습니다. 이를 위한 몇 가지 함수를 소개 시켜 드리겠습니다.

  • Tensor.size(): Tensor 객체의 사이즈를 반환 한다.
  • Tensor.view(size): 파라미터로 들어간 사이즈로 Tensor 객체의 사이즈를 변환 시켜 주며, 파라미터로 -1이 들어갈 시, 행렬의 차원 수를 낮춰 리사이징 하며, (-1, n)이 들어가면 가장 하위 차원에서 n개씩 끊어 넣는 방식으로 리사이징 한다. numpyresize와 방법이 유사하다.

Code Implementation

  • 입력
a_tensor = torch.tensor([[1, 2, 3, 4],      # 4 * 4 행렬
                         [5, 6, 7, 8],
                         [9, 10, 11, 12],
                         [13, 14, 15, 16]])
print(a_tensor)
print(a_tensor.size())

b_tensor = a_tensor.view(16)                # 사이즈가 16 array
print(b_tensor)
print(b_tensor.size())

c_tensor = a_tensor.view(-1, 8)             # 4 * 4 => 2 * 8
print(c_tensor)                             # 6 * 4 일시 3 * 8 으로 리사이징 된다.
print(c_tensor.size())

d_tensor = a_tensor.view(-1)                # 4 * 4 => 16
print(d_tensor)                             # 6 * 4 일시 24로 리사이징 된다.
print(d_tensor.size())

e_tensor = a_tensor.view(8, 2)              # 8 * 2 행렬로 사이즈 변환
print(e_tensor)
print(e_tensor.size())

  • 출력
tensor([[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12],
        [13, 14, 15, 16]])
torch.Size([4, 4])
tensor([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16])
torch.Size([16])
tensor([[ 1,  2,  3,  4,  5,  6,  7,  8],
        [ 9, 10, 11, 12, 13, 14, 15, 16]])
torch.Size([2, 8])
tensor([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16])
torch.Size([16])
tensor([[ 1,  2],
        [ 3,  4],
        [ 5,  6],
        [ 7,  8],
        [ 9, 10],
        [11, 12],
        [13, 14],
        [15, 16]])
torch.Size([8, 2])



Numpy Bridges

PytorchNumpy연산을 GPU를 이용하여, 더욱 빠르게 하기 위해 탄생했습니다. 그렇기 때문에 NumpyPytorch를 연동할 수 있도록, Pytorch에서는 API를 제공합니다.

  • Tensor.numpy(): Tensor 객체를 numpy.ndarray 객체로 변환 하여 반환합니다.
  • torch.from_numpy(ndarray): numpy.ndarrayTensor 객체로 변환 하여 반환합니다.

Code Implementation

  • 입력
import numpy as np
import torch

a_matrix = np.array([[1, 2], [3, 4]])   # np.ndarray 객체 할당
b_matrix = torch.from_numpy(a_matrix)   # np.ndarray 객체를 이용하여 Tensor 객체 할당
c_matrix = b_matrix.numpy()             # Tensor 객체를 이용하여 np.ndarray 객체 할당

print(a_matrix)                         # np.ndarray
print(b_matrix)                         # Tensor
print(c_matrix)                         # np.ndarray

  • 출력
[[1 2]
 [3 4]]
tensor([[1, 2],
        [3, 4]])
[[1 2]
 [3 4]]



마치며

PytorchNumpyAPI 형태가 상당히 유사합니다. 하지만, Numpy와 다른 점이 있다면, Backpropagation을 쉽게 구현할 수 있고, loss function, optimizer 등 많은 고수준 API를 제공합니다. 다음 시간에는 Pytorch로 미분하기, autograd에 대해서 알아 보겠습니다.

Reference