PyTorch - 01_텐서(Tensor)

pytorch
PyTorch의 기본 구조인 텐서에 대해서 알아본다.
저자

William

공개

2022년 2월 25일

Important: 본 내용은 파이토치 한국 사용자 모임의 튜토리얼의 내용이다.

텐서(Tensor)

텐서는 배열이나 행렬과 매우 유사한 자료구조. PyTorch에서는 텐서를 사용하여 모델의 입력과 출력뿐만 아니라 모델의 매개변수를 부호화(encode)한다.

GPU나 다른 연삭 가속을 위한 특수한 하드웨어에서 실핼 수 있다는 점을 제외하면, 텐서는 NumPy의 ndarray와 매우 유사

import torch
import numpy as np

텐서 초기화하기

텐서는 여러가지 방법으로 초기화할 수 있음. ### 데이터로부터 직접 생성하기 데이터로부터 직접 텐서를 생서할 수 있음. 데이터의 자료형은 파이썬답게 자동으로 유추

data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
x_data
tensor([[1, 2],
        [3, 4]])

NumPy 배열로부터 생성하기

텐서는 NumPy 배열로 생성할 수 있음(반대도 가능)

np_array = np.array(data)
print(f"np_array: \n {np_array} \n")
x_np = torch.from_numpy(np_array)
print(f"x_np: \n {x_np} \n")
np_array: 
 [[1 2]
 [3 4]] 

x_np: 
 tensor([[1, 2],
        [3, 4]]) 

다른 텐서로부터 생성하기:

명시적으로 재정의(override)하지 않는다면, 인자로 주어진 텐서의 속성(모양(shape), 자료형(datatype))을 유지함

x_ones = torch.ones_like(x_data) # x_data의 속성을 유지
print(f"Ones Tensor: \n {x_ones} \n")

x_rand = torch.rand_like(x_data, dtype=torch.float) # x_data의 속성을 덮어씁니다.
print(f"Random Tensor: \n {x_rand} \n")
Ones Tensor: 
 tensor([[1, 1],
        [1, 1]]) 

Random Tensor: 
 tensor([[0.8193, 0.6105],
        [0.0340, 0.1774]]) 

무작위(random) 또는 상수(constant)값을 사용하기:

shape은 텐서의 차원(dimension)을 나타내는 튜블(tuple)로, 아래 함수들에서는 출력 텐서의 차원을 결정함

shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)

print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
Random Tensor: 
 tensor([[0.2498, 0.3496, 0.6278],
        [0.3782, 0.6244, 0.9976]]) 

Ones Tensor: 
 tensor([[1., 1., 1.],
        [1., 1., 1.]]) 

Zeros Tensor: 
 tensor([[0., 0., 0.],
        [0., 0., 0.]])

텐서의 속성(Attribute)

텐서의 속성은 텐서의 모양(shape), 자료형(datatype) 및 어느 장치에 저장되는지를 타나냄

tensor = torch.rand(3, 4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu

텐서 연산(Operation)

전치(transposing), 인덱싱(indexing), 슬라이싱(slicing), 수학 계산, 선형 대수, 임의 샘플링(random sampling) 등, 100가지 이상의 텐서 연산들을 여기에서 확인할 수 있음

# GPU가 존재하면 텐서를 이동한다
if torch.cuda.is_available():
    tensor = tensor.to('cuda')
    print(f"Device tensor is stored on: {tensor.device}")
Device tensor is stored on: cuda:0

목록에서 몇몇 연산들을 시도해보자, NumPy API에 익숙하다면 Tensor API를 사용하는 것은 식은 죽 먹기라는 것을 알게 된다. ### NumPy식의 표준 인덱싱과 슬라이싱:

tensor = torch.ones(4, 4) # 1로 이루어진 4x4
tensor[:, 1] = 0 # 2번쨰 열의 값에 0을 삽입
print(tensor)
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

텐서 합치기

torch.cat을 사용하여 주어진 차원에 따라 일련의 텐서를 연결할 수 있음. torch.cat과 미묘하게 다른 또 다른 텐서 결함 연산인 torch.stack도 참고해보자

t1 = torch.cat([tensor, tensor, tensor], dim=1) # tensor 3개를 열로 연결
print(t1)
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
        [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])

텐서 곱하기

# 요소별 곱(element-wise product)을 계산
print(f"tensor.mul(tensor) \n {tensor.mul(tensor)} \n")
# 다른 문법:
print(f"tensor * tensor \n {tensor * tensor}")

#난 아래쪽이 직관적이고 편함
tensor.mul(tensor) 
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]]) 

tensor * tensor 
 tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]])

두 텐서간의 행렬 곱(matrix multiplication)을 계산

print(f"tensor.matmul(tensor.T) \n {tensor.matmul(tensor.T)} \n")
# 다른 문법:
print(f"tensor @ tensor.T \n {tensor @ tensor.T}")
tensor.matmul(tensor.T) 
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]]) 

tensor @ tensor.T 
 tensor([[3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.],
        [3., 3., 3., 3.]])

바꿔치기(in-place) 연산

_ 접미사를 갖는 연산들은 바꿔치기(in-place)연산임. 예를들어: x.copy_()x.t_()x를 변경함

print(tensor, "\n")
tensor.add_(5)
print(tensor)
tensor([[1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.],
        [1., 0., 1., 1.]]) 

tensor([[6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.],
        [6., 5., 6., 6.]])

Note: 바꿔치기 연산은 메모리를 절약하지만, 기록이 즉시 삭제되어 계산에 문제가 발생할 수 있음, 따라서 신중하게 사용해야함.

---

Numpy 변환(Bridge)

cpu 상의 텐서와 NumPy 배열은 메모리 공간을 공유하기 때문에, 하나를 변경하면 다른 하나도 변경됨. 이게 무슨말이냐면…. ## 텐서를 NumPy 배열로 변환하기

t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
t: tensor([1., 1., 1., 1., 1.])
n: [1. 1. 1. 1. 1.]

텐서의 변경 사항이 NumPy 배열에 반영됨

t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.])
n: [2. 2. 2. 2. 2.]

NumPy 배열을 텐서로 변환하기

n = np.ones(5)
t = torch.from_numpy(n) 

NumPy 배열의 변경 사항이 텐서에 반영됩니다.

np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
n: [2. 2. 2. 2. 2.]

송금하실 때 메세지에 어떤 글을 보았는지 남겨주시면 게시글에 남겨드립니다.
기부해주신 돈은 커피를 마시는데 사용됩니다.