파이토치에서 Tensor는 가장 기본이 되는 데이터형태이다. 원래는 물리에서 정의되는 용어이지만, 딥러닝에서는 그냥 '다차원 배열'이라고 생각하면 된다. (N-dimensional array)
이와 상당히 유사한 데이터형태로 Numpy의 Ndarray가 있다.
파이토치에서 우리는 모델의 input (= Data), output, parameter 들에 대해서 모두 Tensor를 사용한다.
import torch
data = [[1,2],[3,4]]
x_data = torch.tensor(data)
Tensor 를 선언하는 방법은 다양한 방법이 존재한다.
위와 같이 파이썬의 리스트 (또는 넘파이의 ndarray)를 선언한 이후 torch.tensor()를 활용하면 Tensor로 바꿀 수 있다.
또는 torch.tensor([1,2])와 같이 직접 선언하여도 된다.
혹은 다음과 같이 각 성분을 random하게 만들거나 constant 값을 가지는 텐서를 선언할 수도 있다.
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
위 코드의 출력 결과로는 $2 \times 3$의 크기를 갖는 tensor (즉, 6개의 성분)가 생성되는데 첫 번째는 각 성분의 값이 random 하게 생성되고 두 번째는 모두 1, 세 번째는 모두 0인 텐서를 생성한다.
텐서의 속성을 확인하는 방법은 다음과 같다.
tensor = torch.rand(3, 4)
tensor.shape
tensor.dtype
tensor.device
.shape은 텐서의 크기를 확인한다. 위 텐서의 크기는 (3, 4)이다. 즉 $3 \times 4$ (12개의 성분)의 크기를 갖는 텐서이다.
두 번째는 텐서의 데이터타입이다. Default는 float32이다. 이를 int나 float64, uint 등으로 바꿀 수 있다.
이는 메모리량과 직결되는 요소이다.
다음은 위 텐서가 어느 장치에 저장되어져 있는가이다. (어느 메모리에 저장되어져 있는가)
결국 CPU, 또는 GPU에 저장될텐데 CPU에 저장된 경우에는 CPU, GPU에 저장되어져 있는 경우에는 CUDA가 나올것이다.
다음은 텐서를 활용한 연산이다.
앞서 이야기하였듯이 딥러닝에서 input (data), output, parameters 모두 텐서이기 때문에 이들 사이의 연산으로 모델 학습(train) 과 평가 (test)가 이루어진다.
즉, 텐서의 연산 문법은 매우 중요하면서 가장 기초적인 개념이다.
1. Indexing and slicing
tensor = torch.ones(4, 4)
print(tensor[0])
print(tensor[:,0])
print(tensor[..., -1])
tensor[:, -1] = 0
파이썬의 리스트나 넘파이를 다뤄봤다면 매우 익숙할 것이다.
우선 모든 성분이 1인 $4 \times 4$ tensor를 생성하였다.
tensor[0]은 텐서의 첫 번째 행을 출력한다. $4 \times 4$이므로 우리는 이를 4행 4열의 행렬로 볼 수 있다.
즉, tensor[0]은 (4, 4)에서 첫 번째 행의 모든 성분을 출력하라는 것이므로 $[1, 1, 1, 1]$이 출력될 것이다.
다음은 tensor[:, 0]은 모든 행의 첫 번째 열을 출력하라는 의미이다. 첫 번째 인덱싱 [:]은 행의 모든 성분을 출력하라는 의미이고, 두 번째 인덱싱 [0]은 모든 행의 첫 번째 열에 있는 성분들을 의미하는 것이므로, $[1, 1, 1, 1]$이 출력될 것이다.
마지막 첫 번째 인덱싱의 [...]은 역시나 행의 모든 성분을 출력하라는 의미로 [:]와 기능상 동일하다.
두 번째 인덱싱 [-1]은 모든 행의 마지막 열에 있는 성분들을 의미하는 것이므로 [1, 1, 1, 1]이 출력될 것이다.
2. Joining tensors (torch.cat, torch.stack)
torch.cat([tensor, tensor, tensor], dim=1)
이는 모든 텐서들을 서로 연결해서 이어붙이라는 (concatenate) 의미이다. 이때 무슨 축을 기준으로 이어붙이는 가를 'dim=1'과 같은 방식으로 표현하는데 이는 여기서 잠시 텐서의 차원과 축을 짚고 넘어가자.
앞서 텐서는 '다차원 배열'이라고 이야기하였다. 이는 결국 텐서는 '차원'을 갖고 있다는 이야기인데
우리는 1D-tensor는 벡터, 2D-tensor는 행렬, 3D-tensor부터는 그냥 텐서라고 표현한다.
앞서 선언한 부분 tensor = torch.ones(4,4)는 이야기하였듯이 2D-tensor이다.
만약 tensor = torch.tensor([2]) 또는 torch.ones(4) 이렇게 선언하였다면 이는 1D-tensor이다.
2D-TENSOR는 행과 열이라는 2개의 축으로 이뤄져 있는데 이를 우리는 .shape을 통해서 확인할 수 있다.
만약 $(3, 5)$ 크기의 행렬이라면 dim=0의 크기는 3, dim=1의 크기는 5가 될 것이다.
그리고 이는 또 다른 관점에서 (사실 이것은 중요한 관점이다.) 성분이 5개 있는 1D-tensor가 또 하나의 축을 기준으로 3개가 나열되어져 있다. 라고 볼 수 있다.
만약 $(2, 3, 4)$ 크기의 3D- tensor라면 dim=0의 크기는 2, dim=1의 크기는 3, dim=2 의 크기는 4가 된다.
그리고 이는 성분이 4개 있는 1D-tensor가 하나의 축을 기준으로 3개가 나열되어져 있는 (3, 4) 2D-tensor들이 또 다른 축을 기준으로 2개 나열되어져 있는 것이라고 볼 수 있다.
이 텐서의 성분의 총 개수는 당연히 24이다.
자, 그렇다면 우리는 $(4, 4)$ 크기의 tensor를 dim=1을 기준으로 이어붙인다면 (4, 4 + 4 +4)인 $(4, 12)$ Tensor가 만들어질 것이다.
3. Arithmetic operations
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)
z1 = tensor * tensor
z2 = tensor.mul(tensor)
@와 .matmul()은 가장 기본이 되는 내적 연산이다.
*와 .mul()은 element-wise operation이다. (이를 수학에서는 Hardamard product이라고도 한다.)
agg = tensor.sum()
tensor.add_(5)
두 번째는 덧셈이다. 여기서 중요한 것은 덧셈과 뺼셈은 기본적으로 element-wise operation이다. (벡터와 행렬의 덧셈, 뺼셈을 기억해보자)
위 코드에서 첫 번째 줄은 tensor 변수의 모든 성분을 더한 값을 성분으로 하는 0차원 텐서 (실수) egg를 생성한다.
두 번째 줄은 tensor라고 하는 현재 만들어진 변수의 모든 성분에 5가 더해진 텐서가 저장이 된다.
이를 우리는 in-place operation이라고 한다.
이와 같은 in-place operation의 예시로 x.copy_(y), x.t_() 등이 있다. _가 붙는다는 것을 알 수 있다.
이외에도 수많은 연산들이 정의되어져 있으나 그것을 하나 하나 다 살펴보는 것은 불가능하다.
그때그때 궁금한 게 생기면 구글링을 해서 찾아보도록 하자.
'Deep dive into Pytorch' 카테고리의 다른 글
Pytorch 5 : Save and Load (0) | 2023.07.17 |
---|---|
Pytorch 4 : Training (1) | 2023.07.16 |
Pytorch 3 : Neural network 구현 (0) | 2023.07.15 |
Pytorch 2 (0) | 2023.07.11 |
Pytorch 첫 번째 (1) | 2023.06.29 |
댓글