Super Kawaii Cute Cat Kaoani [PyTorch] Tensor 조작법 기본) indexing, view, squeeze, unsqueeze

연구/PyTorch

[PyTorch] Tensor 조작법 기본) indexing, view, squeeze, unsqueeze

치킨고양이짱아 2023. 9. 17. 17:21
728x90
728x90

tensor 쓰는법이 안익숙해서 numpy array로 버티면서 코딩하다가..ㅋㅋㅎㅎ더이상 안될것같아 코드 전체를 tensor 기반으로 바꾸는 작업을 진행하게 되었다.

쓰다보면 당연히 체화되어 익숙해질테지만 그래도 어느정도는 공부하는게 좋지 않을까 싶어 tensor 조작법을 정리해보았다!

1) torch.Tensor()와 torch.tensor()의 차이

* torch.Tensor는 Tensor 자료구조의 클래스로, 이 클래스를 사용하면 클래스 인스턴스를 생성할 수 있다. T = torch.Tensor() 라고 입력하면 T는 Tensor 클래스의 인스턴스가 된다. T의 경우 data를 입력해주지 않았으니 빈 tensor가 된다.

* torch.tensor는 어떤 data를 tensor로 copy해주는 클래스이다. torch.tensor에 data를 넣어주었을때 data가 tensor형이 아니면 torch.Tensor 클래스를 적용하여 복사한다. 따라서 t = torch.tensor([1, 2, 3])처럼 꼭 data를 넣어주어야한다. data를 넣어주지 않으면 copy할 데이터가 없으므로 에러가 발생하게 된다.

 

2) tensor 인덱싱

Tensor 조작에서 slicing은 매우 중요하다! tensor의 차원이 낮을때는 헷갈리지 않지만 높아질수록 점점 헷갈리기 때문에 개념을 확실하게 익힐 필요가 있다.

x = torch.tensor([[0, 1, 2, 3, 4],
		  [5, 6, 7, 8, 9],
                  [10, 11, 12, 13, 14]])

 

* x[1]

tensor([5, 6, 7, 8, 9])

* x[:, -1] -> x[0], x[1]. x[2]...의 -1번 인덱스

tensor([4, 9, 14])

주의) x[:][-1]과는 다름. x[:]를 하면 x와 똑같아지고 거기서 -1 인덱스의 원소를 들고오는것이기 때문에 tensor([10, 11, 12, 13, 14]) 를 들고오게 된다.

* x[:, 1] -> x[0], x[1], x[2],...의 1번 인덱스

tensor([1, 6, 11])

주의) x[:][1]과는 다름. x[:]를 하면 x와 똑같아지고 거기서 1번째 인덱스의 원소를 들고오는거기 때문에 tensor([5, 6, 7, 8, 9])를 들고오게 된다.

* x[1, 2] -> x[1] (tensor([5, 6, 7, 8, 9]) 의 2번 인덱스

tensor(7)

* x.shape

torch.Size([3, 5])

* x.size(0)

3

* x.size(1)

5

* x[1] [[True, False, False, False, True]] -> 1번 행 (tensor([5, 6, 7, 8, 9]) 에서 True인 열만 인덱싱해오기

tensor([5, 9])

 

3) 뷰(View)

파이텐서의 뷰(View)는 numpy에서의 Reshape와 같은 역할을 한다. 즉, 원소의 수를 유지하면서 텐서의 shape를 변경해주는 역할을 하는 것으로, 매우 중요하다.

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

현재 ft의 shape는 다음과 같다.

print(ft.shape)
torch.Size([2, 2, 3])

 

* 3차원 텐서에서 2차원 텐서로 변경하기

이제 ft 텐서를 사용하여 2차원 텐서로 변경해보자.

print(ft.view([-1, 3])) # ft 텐서를 (?, 3)의 크기로 변경
print(ft.view([-1, 3]).shape))
tensor([[ 0.,  1.,  2.],
        [ 3.,  4.,  5.],
        [ 6.,  7.,  8.],
        [ 9., 10., 11.]])
torch.Size([4, 3])

view([-1, 3])이 가지는 의미는 -> -1은 첫번째 차원은 잘 모르겠으나 두번째 차원의 길이가 3이 되도록 만들어달라는 의미이다. 원소의 총 개수가 12개이기 때문에 두번째 차원의 길이가 3이 되기 위해서는 첫번째 차원이 4가 되어야한다. 결과적으로 ft.view([-1, 3])를 실행했을 때 (4, 3)의 크기를 가지는 텐서를 얻을 수 있다.

즉 view는 다음과 같은 규칙을 가진다.

  • view는 기본적으로 변경 전과 후의 텐서 안의 원소의 개수가 같도록 유지하며
  • 사이즈가 -1로 세팅되면 다른 차원으로부터 해당 값을 유추한다.

 

* 3차원 텐서의 shape 변경

ft의 원래 shape는 (2x2x3)였다. 이를 (? x 1 x3)으로 변경해보자.

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])

 

4) 스퀴즈(Squeeze)

스퀴즈는 차원이 1인 경우에 해당 차원을 제거한다.

예를 들어 (3x1)의 shape를 가지는 2차원 텐서에 squeeze를 적용하면 (3,)의 크기를 가지는 텐서로 변경된다.

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

ft는 (3x1)의 shape를 가진다. 두번째 차원이 1이므로 squeeze를 사용하게 되면 (3,)의 shape를 가지는 tensor로 변경된다.

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

 

5) 언스퀴즈(Unsqueeze)

언스퀴즈는 스퀴즈와 정반대로, 특정 위치에 1인 차원을 추가할 수 있다.

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

ft는 현재 차원이 1개인 1차원 벡터이다. ft의 첫번째 차원에 1인 차원을 추가하여 [1, 3]의 차원을 가지는 tensor를 만들어보자.

print(ft.unsqueeze(0)) # 인덱스가 0부터 시작하므로 0은 첫번째 차원을 의미한다.
print(ft.unsqueeze(0).shape)
tensor([[0., 1., 2.]])
torch.Size([1, 3])

이렇게 ft의 0번째 인덱스에 unsqueeze를 적용하면 [1, 3]의 shape를 가지는 텐서를 만들 수 있다.

만약 두번째 차원에 1을 추가하고 싶으면 unsqueeze의 인자에 1을 넣어주면 된다. 현재 차원이 (3,)이였으므로 두번째 차원에 1을 추가하게 되면 shape가 (3, 1)인 tensor가 만들어진다.

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

이번엔 squeeze의 인자로 -1을 넣어보자. -1은 인덱스 상으로 마지막 차원을 의미하므로 마지막 차원에 1을 추가하면 (3, 1)의 크기를 가지는 tensor가 만들어진다.

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

 

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

 

참고)

https://wikidocs.net/52846

 

02-03 텐서 조작하기(Tensor Manipulation) 2

이어서 텐서를 조작하는 방법을 알아보겠습니다. #### 4) 뷰(View) - 원소의 수를 유지하면서 텐서의 크기 변경. 매우 중요! 파이토치 텐서의 뷰(View)는 넘파이에서…

wikidocs.net

https://rfriend.tistory.com/780

 

[PyTorch] 텐서의 인덱싱과 슬라이싱 (indexing & slicing of PyTorch tensor)

이번 포스팅에서는 PyTorch 에서 인덱싱(indexing)과 슬라이싱(slicing)을 이용해서 텐서 내 원하는 위치의 원소 부분집합을 가져오는 방법을 소개하겠습니다. PyTorch 로 딥러닝을 할 때 인풋으로 사용

rfriend.tistory.com

 

728x90
728x90