머신러닝을 사용한다면 PyTorch를 모르기 어려울 것 같습니다. 전에는 TensorFlow를 주로 사용했던 것 같은데, 최근에는 Pytorch를 이용하는 경우가 더 많아지고 있는 것 같습니다. 저도 최근에서야 머신러닝을 다시 배우고 있는데, 배우는 과정에서 기초가 되는 내용들을 기록차원에서 적어보면서 공유도 해보려고 합니다.
배우면서 노트삼아 적는 만큼 한번에 모두 적는것이 아닌 배워가면서 하나씩 내용을 추가하는 방향으로 글을 지속적으로 업데이트 하고자 합니다.
Table of Contents
PyTorch와 Tensor
PyTorch는 TensorFlow와 비슷하게 tensor라는 데이터 형태를 사용하는 프레임워크 입니다. Tensor는 쉽게 표현하면 행렬 (Matrix)와 같은 형태라고 봐도 크게 무리는 아닙니다. 엄밀히 따지면 같은건 아니고 Tensor가 더 큰 개념인데, 실제 사용하다 보면 행렬 데이터로 보고 사용해도 될 정도로 거의 같습니다.
보통의 Matrix는 2차원 정도만 생각하지만, Tensor는 숫자 하나인 경우, 리스트와 같이 1차원 데이터인 경우도, 더 나아가 3차원 데이터인 경우도 많이 있습니다. 사용하는 application에 따라 다르다고 보면 되겠습니다.
Tensor 생성하기 (Initialize)
많은 경우 사진 등 데이터에서 값을 읽어 tensor를 구성하지만, 당연히 임의의 값을 가지고 tensor를 선언할 수 있습니다. 여러 방법 중 기본적으로 사용되는 몇가지를 적어봅니다.
작은 숫자부터 자동으로 채워넣는 방식
t = torch.arange(5) #from 0 to 4, size to be 5
>>> t
tensor([0, 1, 2, 3, 4])
0부터가 아닌 시작하는 숫자를 지정하여 생성
t = torch.arange(2,5) #from 2 to less than 5 (increment by 1 to 4)
>>> t
tensor([2, 3, 4])
list of list를 사용하여 를 생성
Tensor를 임의로 생성할 때 가장 직관적으로 이용할 수 있는 방법인 것 같습니다. 각 list가 하나의 차원이라고 보고, 이 list의 list를 가지고 2차원 데이터를, 더 나아가 3차원 데이터, n차원 데이터 까지도 생성하는 것이 가능합니다.
x = x = torch.tensor([[0,5],[4,10],[22,9]])
>>> x
tensor([[ 0, 5],
[ 4, 10],
[22, 9]])
Tensor 연산
Tensor 크기 (size) 가져오기
여러 차원이의 데이터를 담을 수 있는 데이터 구조인 만큼 데이터의 크기가 차원(dimension)별로 존재하고, 연산에 있어 이 크기를 맞추는 것이 필수적입니다.
2차원 Tensor라고 하면 Matrix의 row와 column 같이 첫번째 dimension의 크기 (row 수와 비슷) 그리고 두번째 dimension의 크기 (column의 수) 와 같이 크기를 나타낼 수 있습니다. 예를 들어, 2×3 tensor라면 2개의 row와 3개의 column을 가지는 tensor라고 볼 수 있겠습니다.
이미 만들어진 tensor의 크기를 확인하는 것 역시 가능합니다. 모든 dimension의 크기를 가져올 수도 있고, 한 dimension의 크기만 가져올 수도 있습니다.
>>> x.size()
torch.Size([3, 2])
>>> x.size(0)
3
Transpose
행렬과 마찬가지로 tensor 역시 transpose라는 개념이 동일하게 존재합니다. 아래과 같이 t()를 이용하면 transpose에 해당하는 tensor를 결과로 받아올 수 있습니다.
>>> x
tensor([[ 0, 5],
[ 4, 10],
[22, 9]])
>>> x.t()
tensor([[ 0, 4, 22],
[ 5, 10, 9]])
Reshape
종종 복잡하고 헷갈릴 수 있는 기능이지만, 동식 tensor에서 제공하는 가장 유용한 기능이기도 한 reshape 기능에 대해 적어봅니다. 행렬도 그렇고, tensor를 다루다 보면 안에 저장된 값은 같은데, 연산에 맞춰 형태만 바꿔야 할 때가 꽤 있습니다. 이를 때 reshape를 잘 이용하면 편하게 기능을 이용할 수 있습니다.
아래는 3차원 tensor에 있는 값을 그대로 2차원의 형태로 옮기는 예시 입니다.
# Create a 3D tensor
x = torch.tensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]],
[[9, 10],
[11, 12]]])
# Reshape to 2D
x_reshaped = torch.reshape(x, (3, -1)) # Shape will be (3, 2*2) = (3, 4)
Original tensor:
tensor([[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]],
[[ 9, 10],
[11, 12]]])
Shape of original tensor: torch.Size([3, 2, 2])
Reshaped tensor:
tensor([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
Shape of reshaped tensor: torch.Size([3, 4])
Masking
데이터를 처리하다 보면 tensor에 저장된 값 중 일부만 골라 사용하고 싶은 경우가 있습니다. 예를 들어 한 tensor에서 0보다 큰 값만 사용하고 싶은 경우가 있을 수 있습니다. 이런 경우 Masking이라는 방식을 이용해 볼 수 있습니다
아래 예시에서는 cache에 있는 값 중 0보다 큰 값이면 True, 아니면 False를 채운 동일한 크기의 boolean tensor를 반환합니다. 이를 이용해 dout에 저장된 값 중 True에 해당하는 값만 골라서 연산에 이용하는 것이 가능합니다.
dout = torch.tensor([2, -3, 4])
cache = torch.tensor([1, 0, -2])
dx = dout * (cache > 0) # Element-wise multiplication
print(dx)
>>>
tensor([2, 0, 0])