본문 바로가기

책/파이토치 트랜스포머를 활용한 자연어 처리와 컴퓨터비전 심층학습

03 파이토치 기초 (1) 텐서

텐서(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 텐서로 변환한 다음에 넘파이 배열로 변환해야 한다.