본문 바로가기

그 땐 AI했지/그 땐 DeepLearning했지

[TAVE/study] ch02 파이토치 기초 | 03 텐서 조작하기②

728x90

참고자료: https://wikidocs.net/book/2788

자주 사용하는 기능

5️⃣뷰(View)⭐️

👉🏻원소의 수를 유지하면서 텐서의 크기를 변경한다.

👉🏻넘파이의 리쉐이프 역할을 하는데 리쉐이프란 텐서의 크기(shape)를 변경해주는 것이다.

t = np.array([[[0, 1, 2],
              [3, 4, 5]],
             [[6, 7, 8],
              [9, 10, 11]]])
ft = torch.FloatTensor(t)
print(ft.shape) #torch.Size([2, 2, 3])

👉🏻3차원 텐서인 ft를 만들었고 shape은 다음과 같다.

 

✅3차원 텐서에서 2차원 텐서로 변경

print(ft.view([-1, 3]))
print(ft.view([-1, 3]).shape)
#tensor([[ 0.,  1.,  2.],
#        [ 3.,  4.,  5.],
#        [ 6.,  7.,  8.],
#        [ 9., 10., 11.]])
#torch.Size([4, 3])

👉🏻ft라는 텐서를 (?, 3)의 크기로 변경하는 것이다.

✍🏻view([[-1, 3])의 의미: 3차원 텐서를 2차원 텐서로 변경하되 (?, 3)의 크기로 변형하라는 의미이다. 결과적으로 (4, 3)의 크기를 갖는 텐서를 얻었다.

  • -1: 첫번째 차원은 사용자가 잘 모르겠으니 파이토치에게 맡기겠다는 의미
  • 3: 두번째 차원의 길이는 3을 가지도록 하라는 의미

👉🏻(2, 2, 3)  → (2 *2, 3) → (4, 3)

👉🏻규칙

  • view는 기본적으로 변경 전과 변경 후의 텐서 안의 원소의 개수가 유지되어야 한다.
  • 파이토치의 view는 사이즈가 -1로 설정이 되면 다른 차원으로부터 해당 값을 유추한다.

 

✅3차원 텐서의 크기 변경

👉🏻차원은 유지하되 크기만 바꾼다.

print(ft.view([-1, 1, 3]))
print(ft.view([-1, 1, 3]).shape)

#tensor([[[ 0.,  1.,  2.]],
#
#        [[ 3.,  4.,  5.]],
#
#        [[ 6.,  7.,  8.]],
#
#        [[ 9., 10., 11.]]])
#torch.Size([4, 1, 3])

✍🏻원소의 수가 유지되어야 함을 잊지말자! (2 * 2 * 3) 텐서를 (? * 1 * 3)텐서로 바꾸기 위해 ?는 4가 되어야 한다.

 

6️⃣스퀴즈(Squeeze)

👉🏻1인 차원을 제거한다.

ft = torch.FloatTensor([[0], [1], [2]])
print(ft)
print(ft.shape)
#tensor([[0.],
#        [1.],
#        [2.]])
#torch.Size([3, 1])

👉🏻(3 x 1)인 2차원 텐서가 있다. 두번째 차원이 2이므로 squeeze를 사용하면 (3,)의 크기를 가지는 텐서로 변경된다.

print(ft.squeeze())
print(ft.squeeze().shape)
#tensor([0., 1., 2.])
#torch.Size([3])

👉🏻1차원 벡터가 되었다!

 

 

7️⃣언스퀴즈(Unsqueeze)

👉🏻특정 위치에 1인 차원을 추가한다.

ft = torch.Tensor([0, 1, 2])
print(ft.shape)
#torch.Size([3])

👉🏻(3,)의 크기를 가지는 1인 차원 텐서(1차원 벡터)를 만들었다.

print(ft.unsqueeze(0))
print(ft.unsqueeze(0).shape)
#tensor([[0., 1., 2.]])
#torch.Size([1, 3])

👉🏻첫번째 차원의 인덱스를 의미하는 0을 인자로 넣으면 첫번째 차원에 1인 차원이 추가된다. 결과적으로 (1, 3)의 2차원 텐서로 변경되었다. 이는 view로도 구현이 가능하다.

print(ft.view([1, -1]))
print(ft.view([1, -1]).shape)
#tensor([[0., 1., 2.]])
#torch.Size([1, 3])

👉🏻view에서는 (1, -1)을 인자로 사용하면 1인 차원을 추가할 수 있다.

print(ft.unsqueeze(1))
print(ft.unsqueeze(1).shape)
#tensor([[0.],
#        [1.],
#        [2.]])
#torch.Size([3, 1])

👉🏻두번째 차원을 의미하는 1을 인자로 넣으면 두번째 차원에 1을 추가해서 (3, 1)의 크기를 가지게 된다.

print(ft.unsqueeze(-1))
print(ft.unsqueeze(-1).shape)
#tensor([[0.],
#        [1.],
#        [2.]])
#torch.Size([3, 1])

👉🏻마지막 차원을 의미하는 -1을 인자로 넣으면 (3, 1)의 크기를 가지게 된다.

 

📌view(), squeeze(), unsqueeze()는 텐서의 원소 수를 그대로 유지하면서 모양과 차원을 조절한다.

 

8️⃣타입 캐스팅(Type Casting)

👉🏻텐서에 자료형이 있는데 각 데이터형별로 정의되어 있다. 아래는 예시이고 이 자료형을 변환하는 것을 타입 캐스팅이라고 한다.

  • torch.FloatTensor - 32비트의 부동 소수점
  • torch.LongTensor - 64비트의 부호 있는 정수
  • torch.cuda.FloatTensor - GPU연산을 위한 자료형
lt = torch.LongTensor([1, 2, 3, 4])
print(lt)
print(lt.float())
#tensor([1, 2, 3, 4])
#tensor([1., 2., 3., 4.])

👉🏻.float()를 붙이면 float형으로 타입이 변경된다.

bt = torch.ByteTensor([True, False, False, True])
print(bt)
#tensor([1, 0, 0, 1], dtype=torch.uint8)

print(bt.long())
print(bt.float())
#tensor([1, 0, 0, 1])
#tensor([1., 0., 0., 1.])

👉🏻Byte타입의 bt텐서를 long과 float 타입으로 변경해보았다.

 

9️⃣연결하기(concatenate)

x = torch.FloatTensor([[1, 2], [3, 4]])
y = torch.FloatTensor([[5, 6], [7, 8]])
print(torch.cat([x, y], dim=0))
#tensor([[1., 2.],
#        [3., 4.],
#        [5., 6.],
#        [7., 8.]])

👉🏻torch.cat([])을 통해 연결해줄 수 있다. 게다나 어느 차원을 늘릴 것인지 인자로 줄 수 있다. 위와 같이 dim=0은 첫번째 차원을 늘리라는 의미를 담고 있다.

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

👉🏻인자를 dim=1로 주었다. (2 x 2)텐서가 (2 x 4)가 되었다.

✍🏻딥러닝에서 두 텐서를 연결해 입력한다는 것은 두 가지의 정보를 모두 사용한다는 의미를 가진다.

 

🔟스택킹(Stacking)

👉🏻연결을 하는 것 외 스택킹이이라는 방법도 있다. 스택킹은 많은 연산을 포함하고 있다.

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

👉🏻(3 x 2)텐서가 되었다.

print(torch.cat([x.unsqueeze(0), y.unsqueeze(0), z.unsqueeze(0)], dim=0))
#tensor([[1., 4.],
#        [2., 5.],
#        [3., 6.]])

👉🏻위의 스택킹 작업은 이렇게 많은 연산과 동일한 작업이다.

✍🏻x, y, z는 기존에 (2,) 크기를 가졌지만 .unsqueeze(0)를 통해 모두 (1, 2)크기의 2차원 텐서가 되었다. 여기에 cat을 통해 연결하며 (3 x 2)텐서가 되었다.

print(torch.stack([x, y, z], dim=1))
#tensor([[1., 2., 3.],
#        [4., 5., 6.]])

👉🏻스택킹에도 dim 인자를 줄 수 있다. dim=1을 인자로 주면서 두 번째 차원이 증가하도록 쌓았다.

 

🔟1️⃣ones_like와 zeros_like

x = torch.FloatTensor([[0, 1, 2], [2, 1, 0]])
print(x)
#tensor([[0., 1., 2.],
#        [2., 1., 0.]])

👉🏻(2 x 3)텐서를 만들었다.

print(torch.ones_like(x))
print(torch.zeros_like(x))
#tensor([[1., 1., 1.],
#        [1., 1., 1.]])
#tensor([[0., 0., 0.],
#        [0., 0., 0.]])

👉🏻ones_like를 하면 동일한 크기지만 1로만 값이 채워진 텐서를 생성한다. zeros_like를 하면동일한 크기지만 0으로만 값이 채워진 텐서를 생성한다.

 

🔟2️⃣덮어쓰기 연산(In-place Operation)

x = torch.FloatTensor([[1, 2], [3, 4]])
print(x.mul(2.))
print(x)
#tensor([[2., 4.],
#        [6., 8.]])
#tensor([[1., 2.],
#        [3., 4.]])

👉🏻(2 x 2)텐서를 만들고 곱하기 연산을 한 x와 기존 x를 출력했다. 곱하기 연산을 해도 기존 x가 변하지 않은 것을 알 수 있다.

print(x.mul_(2.)) 
print(x)
#tensor([[2., 4.],
#        [6., 8.]])
#tensor([[2., 4.],
#        [6., 8.]])

👉🏻_를 붙이면 x의 값이 덮어쓰기된다.

728x90