Gongsam 2023. 12. 13. 20:18

텐서(Tensor)란?

넘파이 라이브러리의 ndarray 클래스와 유사한 구조로 배열이나 행렬과 유사한 자료 구조(자료형)이다. 파이토치에서는 텐서를 사용하여 모델의 입출력뿐만 아니라 모델의 매개변수를 부호화(Encode)하고 GPU를 활용해 연산을 가속화 할 수 있다.

 

공통점

수학 계산, 선형 대수 연산을 비롯해 전치, 인덱싱, 슬라이싱, random 샘플링 등 다양한 텐서 연산을 진핼할 수 있다.

 

차이점

CPU에서 사용하는 텐서와 GPU에서 사용하는 텐서의 선언 방식에 있다. 파이토치는 CPU 텐서와 GPU 텐서로 나눠지고, 각각의 텐서를 상호 변환하거나 GPU 사용 여부를 설정한다.

 

텐서 생성

텐서 생성 방법

  • torch.tensor() 
    • 입력된 데이터를 복사해 텐서로 변환하는 함수
    • 데이터를 복사하기 때문에 값이 무조건 존재해야 하며 입력된 데이터의 형식에 가장 적합한 텐서 자료형을 변환
  • torch.Tensor()
    • 위와는 달리 대문자로 텐서의 기본형으로 텐서 인스턴스를 생성하는 클래스
    • 인스턴스를 생성하기 때문에 값을 입력하지 않는 경우 비어 있는 텐서를 생성
import torch

print(torch.tensor([1, 2, 3]))
print(torch.Tensor([[1, 2, 3], [4, 5, 6]]))
print(torch.LongTensor([1, 2, 3]))
print(torch.FloatTensor([1, 2, 3]))

출력 결과

tensor([1, 2, 3])
tensor([[1., 2., 3.],
        [4., 5., 6.]])
tensor([1, 2, 3])
tensor([1., 2., 3.])
  • torch.tensor()는 자동으로 자료형을 할당하므로 입력된 데이터 형식을 참조해 Int 형식으로 할당
  • torch.Tensor()는 입력된 데이터 형식이 Int형이지만 기본 유형이 Float이므로 정수형을 할당하더라도 소수점 형태로 변환됨
  • 나머지는 torch.Tensor 클래스를 상속받은 데이터 형식으로 데이터 형식이 미리 선언된 클래스

 

텐서 속성

tensor의 attribute는 크게 shape, dtype, device가 존재한다. device는 텐서의 GPU 가속 여부를 의미한다.

tensor = torch.rand(1, 2)
print(tensor)
print(tensor.shape)
print(tensor.dtype)
print(tensor.device)

출력 결과

tensor([[0.8762, 0.3570]])
torch.Size([1, 2])
torch.float32
cpu
  • torch.rand()는 [0, 1) 범위 무작위 수를 균등 분포로 생성하는 함수
  • 매개변수로 사용한 1, 2는 생성하려는 텐서의 shape
  • GPU 가속 여부를 설정하지 않았기 때문에 CPU 장치를 사용해 연산

 

차원 변환

tensor = torch.rand(1, 2)
print(tensor)
print(tensor.shape)

tensor = tensor.reshape(2, 1)
print(tensor)
print(tensor.shape)

출력 결과

tensor([[0.2289, 0.6758]])
torch.Size([1, 2])
tensor([[0.2289],
        [0.6758]])
torch.Size([2, 1])
  • 넘파이와 동일하게 reshape 메서드를 활용

 

자료형 설정

tensor = torch.rand((3, 3), dtype=torch.float)
print(tensor)

출력 결과

tensor([[0.7365, 0.6395, 0.6307],
        [0.4247, 0.4115, 0.1165],
        [0.6340, 0.0942, 0.4023]])
  • 텐서의 자료형 설정에 입력되는 인수는 torch.* 형태로 할당
  • torch.float이 아닌 float을 할당해도 오류 없이 작동하지만, 전자는 32비트 부동 소수점, 후자는 64비트 부동 소수점 형식을 갖는다.

 

device 설정

device = 'cuda' if torch.cuda.is_available() else 'cpu'
cpu = torch.FloatTensor([1, 2, 3])
gpu = torch.tensor([1, 2, 3], dtype=torch.float, device=device)
tensor = torch.rand((1, 1), device=device)
print(device)
print(cpu)
print(gpu)
print(tensor)

출력 결과

cuda
tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
tensor([[0.5365]], device='cuda:0')
  • device 매개변수에 장치 속성을 할당해 설정할 수 있다.
  • 추가적으로 책에서는 gpu를 torch.cuda.FloatTensor([1, 2, 3])과 같은 방식으로 사용했는데 warning이 발생했고 torch과 위와 같이 torch.cuda 보다는 device를 직접 할당하는 방식을 권장하였다.

 

device 변환

cpu = torch.FloatTensor([1, 2, 3])
gpu = cpu.cuda()
gpu2cpu = gpu.cpu()
cpu2gpu = cpu.to(device)
print(cpu)
print(gpu)
print(gpu2cpu)
print(cpu2gpu)

출력 결과

tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
  • cpu 텐서를 gpu로 변환하는 방법은 cuda 메서드를 사용하거나 to 메서드로 to('cuda')와 같은 방법으로 사용할 수 있다. 

 

넘파이 배열의 텐서 변환

import numpy as np

ndarray = np.array([1, 2, 3], dtype=np.uint8)
print(torch.tensor(ndarray))
print(torch.Tensor(ndarray))
print(torch.from_numpy(ndarray))

출력 결과

tensor([1, 2, 3], dtype=torch.uint8)
tensor([1., 2., 3.])
tensor([1, 2, 3], dtype=torch.uint8)

 

텐서의 넘파이 배열 변환

tensor = torch.tensor([1, 2, 3], dtype=torch.float, device=device)
ndarray = tensor.detach().cpu().numpy()
print(ndarray)
print(type(ndarray))

출력 결과

[1. 2. 3.]
<class 'numpy.ndarray'>
  • numpy 메서드를 사용
  • detach 메서드를 사용하는 이유는 텐서는 기존 데이터 형식과 다르게 학습을 위한 데이터 형식으로 모든 연산을 추적해 기록한다. 따라서 detach 메서드를 사용해 현재 연산 그래프에서 분리된 새로운 텐서를 반환한다.
  • GPU 텐서라면 CPU 텐서로 변환한 다음에 넘파이 배열로 변환해야 한다.