본문 바로가기
Python/PyTorch

[PyTorch] 텐서의 연산과 조작

by _sweep 2022. 2. 22.

이수안컴퓨터연구소파이토치(PyTorch) 기초 영상을 보고 정리한 내용입니다.

 

 

operations

텐서의 연산은 덧셈, 뺄셈, 곱셈, 나눗셈의 사칙연산과 내적(dot product)이 존재한다.

사칙연산은 torch에서 제공하는 메서드나 기본 연산자인 +, -, *, / 을 사용한다.

 

import torch

x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[5, 6], [7, 8]])

 

✔️ 덧셈

print(x + y)

# output
# tensor([[ 6,  8],
#         [10, 12]])

print(torch.add(x, y))

# output
# tensor([[ 6,  8],
#         [10, 12]])

 

덧셈+ 연산자를 사용하거나 torch.add()를 사용한다.

+ 연산자를 사용하든 torch.add()를 사용하든 둘의 결과는 동일하다.

 

result = torch.empty(2, 2)
torch.add(x, y, out=result)
print(result)

# output
# tensor([[ 6.,  8.],
#         [10., 12.]])

 

torch.add()를 사용할 때 out 옵션을 사용하면 결과 텐서를 인자로 제공할 수 있다.

따라서 x와 y를 더한 값이 result에 들어가게 된다.

 

y.add_(x)
print(y)

# output
# tensor([[ 6,  8],
#         [10, 12]])

 

위와 같이 in-place 방식을 사용할 수도 있다.

다른 코드들과는 달리 위 방식에서는 x와 y를 더한 결과를 출력하거나 다른 텐서에 저장하는 것이 아니라 피연산자가 될 텐서에 다른 텐서를 직접 더한다.

따라서 위 코드에서는 y에 x를 더하고 있기 때문에 y를 출력했을 때 x + y의 값이 들어있는 것을 확인할 수 있다.

 

참고로 in-place 방식으로 텐서의 값을 변경하는 경우 연산의 뒤에 _가 붙는다.

 

✔️ 뺄셈

print(x-y)
print(torch.sub(x, y))
print(x.sub(y))

# output
# tensor([[-5, -6],
#         [-7, -8]])
# tensor([[-5, -6],
#         [-7, -8]])
# tensor([[-5, -6],
#         [-7, -8]])

 

뺄셈- 연산자를 사용하거나 torch.sub()를 사용한다.

 

✔️ 곱셈

print(x * y)
print(torch.mul(x, y))
print(x.mul(y))

# output
# tensor([[ 6, 16],
#         [30, 48]])
# tensor([[ 6, 16],
#         [30, 48]])
# tensor([[ 6, 16],
#         [30, 48]])

 

곱셈* 연산자를 사용하거나 torch.mul()을 사용한다.

 

✔️ 나눗셈

print(x / y)
print(torch.div(x, y))
print(x.div(y))

# output
# tensor([[0.1667, 0.2500],
#         [0.3000, 0.3333]])
# tensor([[0.1667, 0.2500],
#         [0.3000, 0.3333]])
# tensor([[0.1667, 0.2500],
#         [0.3000, 0.3333]])

 

나눗셈/ 연산자를 사용하거나 torch.div()를 사용한다.

 

✔️ 내적

print(torch.mm(x, y))

# output
# tensor([[26, 32],
#         [58, 72]])

 

내적은 따로 연산자가 존재하지 않는다.

대신 torch.mm()을 사용하여 내적 값을 구할 수 있다.

 

 

✅ manipulations

✔️ 인덱싱

텐서는 NumPy와 같이 인덱싱으로 값에 접근할 수 있다.

 

import torch

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

print(x)
print(x[0])
print(x[:, 1])

# output
# tensor([[1, 2],
#         [3, 4]])
# tensor([1, 2])
# tensor([2, 4])

 

✔️ view

Tensor.view()는 텐서의 크기나 모양을 변경한다.

 

Tensor.view(*shape)

 

인자로는 바뀔 shape이 주어지며 Tensor의 값은 동일하지만 shape이 바뀐 새로운 Tensor가 리턴된다.

 

x = torch.randn(3, 5)
a = x.view(15)
b = x.view(5, -1)

print(x.size())
print(a.size())
print(b.size())

# output
# torch.Size([3, 5])
# torch.Size([15])
# torch.Size([5, 3])

 

✔️ item

Tensor.item()은 텐서에 스칼라 값 하나가 존재할 경우 그 값을 얻어올 수 있다.

해당 텐서에 2개 이상의 값이 있을 경우에는 .item()을 적용할 수 없다.

이 경우 ValueError: only one element tensors can be converted to Python scalars 라는 에러를 확인할 수 있다.

 

x = torch.randn(1)
print(x)
print(x.item())

# output
# tensor([-0.4725])
# -0.47247570753097534

 

✔️ squeeze

Tensor.squeeze()는 텐서의 차원을 축소(제거)한 새로운 텐서를 반환한다.

 

tensor = torch.rand(1, 3, 3)
print(tensor)

t = tensor.squeeze()
print(t)
print(t.shape)

# output
# tensor([[[0.2378, 0.1449, 0.1843],
#          [0.4728, 0.0580, 0.9944],
#          [0.2301, 0.1764, 0.2485]]])
# tensor([[0.2378, 0.1449, 0.1843],
#         [0.4728, 0.0580, 0.9944],
#         [0.2301, 0.1764, 0.2485]])
# torch.Size([3, 3])

 

1x3x3의 텐서에 대해 .squeeze()를 적용했을 때 3x3으로 차원이 축소된 것을 확인할 수 있다.

 

✔️ unsqueeze

Tensor.unsqueeze()는 .squeeze()와는 반대로 텐서의 차원을 증가(생성)시킨 새로운 텐서를 반환한다.

이 경우 증가시킬 차원의 방향을 dim 옵션으로 지정해주어야 한다.

 

tensor = torch.rand(1, 3, 3)
print(tensor)

t = tensor.unsqueeze(dim=0)
print(t)
print(t.shape)

# output
# tensor([[[0.3780, 0.5946, 0.6233],
#          [0.0215, 0.3017, 0.6218],
#          [0.0466, 0.6926, 0.7092]]])
# tensor([[[[0.3780, 0.5946, 0.6233],
#           [0.0215, 0.3017, 0.6218],
#           [0.0466, 0.6926, 0.7092]]]])
# torch.Size([1, 1, 3, 3])

 

✔️ stack

torch.stack()은 텐서들을 결합한다.

 

torch.stack(tensors, dim=0, *, out=None)

 

이때 dim의 기본값은 0이다.

따라서 dim 옵션을 따로 지정하지 않았을 경우 아래 방향으로 텐서들이 결합한 형태이다.

 

x = torch.FloatTensor([1, 4])
y = torch.FloatTensor([2, 5])
z = torch.FloatTensor([3, 6])

print(torch.stack([x, y, z]))

# output
# tensor([[1., 4.],
#         [2., 5.],
#         [3., 6.]])

 

✔️ cat

torch.cat()은 torch.stack()과 동일하게 텐서를 결합하는 메서드이다.

해당 차원을 늘려준 후 결합하기 때문에 쌓을 dim이 존재해야 하고 dim을 지정해 주어야 한다.

 

x = torch.randn(3, 3)
y = torch.randn(3, 3)
z = torch.cat((x, y), dim=0)

print(z)
print(z.size())

# output
# tensor([[ 1.1492, -1.5953,  0.8305],
#         [-0.3136,  0.0373, -1.1060],
#         [-2.9980,  0.2925,  1.7472],
#         [-0.5904,  2.1679, -0.1241],
#         [-0.0538, -1.1872, -0.3323],
#         [-0.5231,  0.0733, -0.4277]])
# torch.Size([6, 3])

 

✔️ chunk

torch.chunk()는 텐서를 여러 개로 나눌 때 사용한다.

 

torch.chunk(input, chunks, dim=0)

 

나눌 대상이 될 텐서와 나눌 개수, 나눌 차원의 방향을 인자로 받는다.

즉, torch.chunk()는 나눌 텐서의 개수가 정해져 있을 때 사용한다.

 

tensor = torch.rand(3, 6)
t1, t2, t3 = torch.chunk(tensor, 3, dim=1)
# 3은 chunk의 개수, dim은 기준이될 dim

print(tensor)
print(t1)
print(t2)
print(t3)

# output
# tensor([[0.9393, 0.9592, 0.5965, 0.0793, 0.7652, 0.9374],
#         [0.8259, 0.7901, 0.4846, 0.0010, 0.3290, 0.4188],
#         [0.6288, 0.6366, 0.2115, 0.5906, 0.0591, 0.0450]])
# tensor([[0.9393, 0.9592],
#         [0.8259, 0.7901],
#         [0.6288, 0.6366]])
# tensor([[0.5965, 0.0793],
#         [0.4846, 0.0010],
#         [0.2115, 0.5906]])
# tensor([[0.7652, 0.9374],
#         [0.3290, 0.4188],
#         [0.0591, 0.0450]])

 

✔️ split

torch.split()도 텐서를 여러 개로 나눌 때 사용한다.

torch.chunk()가 나눌 텐서의 개수를 지정한다면 torch.split()은 나누어진 텐서 하나 당 크기를 지정한다.

 

tensor = torch.rand(3, 6)
t1, t2 = torch.split(tensor, 3, dim=1)

print(tensor)
print(t1)
print(t2)

# output
# tensor([[0.3084, 0.0062, 0.5784, 0.1557, 0.7011, 0.2024],
#         [0.9844, 0.7181, 0.0286, 0.1599, 0.7431, 0.8248],
#         [0.2361, 0.0135, 0.7826, 0.9212, 0.2481, 0.9945]])
# tensor([[0.3084, 0.0062, 0.5784],
#         [0.9844, 0.7181, 0.0286],
#         [0.2361, 0.0135, 0.7826]])
# tensor([[0.1557, 0.7011, 0.2024],
#         [0.1599, 0.7431, 0.8248],
#         [0.9212, 0.2481, 0.9945]])

 

따라서 torch.chunk()와 torch.split()에 동일한 인자를 주었을 때 같은 인자 3에 대해 결과값이 다르게 나타난다.

torch.chunk()는 텐서를 3개로 나누는 반면 torch.split()은 나누어진 텐서 하나당 크기를 3으로 지정한다.

따라서 torch.chunk()는 t1, t2, t3의 3개의 텐서를 얻을 수 있고 torch.split()은 t1, t2의 2개의 텐서를 얻는다.



torch ↔ numpy

torch는 NumPy와 호환이 가능하다.
Torch의 Tensor를 NumPy의 array로 변환할 수 있고 그 반대의 경우도 가능하다.

  • numpy() : torch -> numpy
  • from_numpy() : numpy -> torch

텐서가 CPU 상에 존재한다면 NumPy 배열은 메모리공간을 공유하기 때문에 텐서가 변하면 배열의 값도 변한다. (반대의 경우도 동일하다.)

 

a = torch.ones(7)
b = a.numpy()
print(a)
print(b)

# output
# tensor([1., 1., 1., 1., 1., 1., 1.])
# [1. 1. 1. 1. 1. 1. 1.]

a.add_(1)
print(a)
print(b)

# output
# tensor([2., 2., 2., 2., 2., 2., 2.])
# [2. 2. 2. 2. 2. 2. 2.]

 

.numpy()를 이용해 a라는 텐서를 NumPy의 array로 변환했다.

이 경우 a의 값을 변경하면 array인 b의 값도 변경된 것을 확인할 수 있다.

 

import numpy as np

a = np.ones(7)
b = torch.from_numpy(a)
np.add(a, 1, out=a)

print(a)
print(b)

# output
# [2. 2. 2. 2. 2. 2. 2.]
# tensor([2., 2., 2., 2., 2., 2., 2.], dtype=torch.float64)

 

.from_numpy()를 이용해 배열을 텐서로 변경했다.

이 경우에도 하나의 값이 변하면 다른 하나의 값이 변경된다.

 

 

🔍 참조

torch.Tensor.view https://pytorch.org/docs/stable/generated/torch.Tensor.view.html
torch.stack https://pytorch.org/docs/stable/generated/torch.stack.html?highlight=stack#torch.stack 

 

 

 

 

 

'Python > PyTorch' 카테고리의 다른 글

[PyTorch] Torchvision과 utils.data  (0) 2022.02.23
[PyTorch] nn과 nn.functional  (0) 2022.02.23
[PyTorch] CUDA와 자동미분  (0) 2022.02.23
[PyTorch] 파이토치와 텐서  (0) 2022.02.22

댓글