pytorch tensor 다루기 재활치료 2편 - view, squeeze, unsqueeze, type, cat, stack, ones_like, zeros_like, inplace -

1. view

 

원소의 수를 유지하면서 tensor의 크기를 변경하는 방법

 

numpy의 reshape와 같은 역할

 

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

 

1-1) 이제 ft tensor를 view를 사용해서 2차원 tensor로 변경

 

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은 두번째 차원의 길이는 3을 가지도록 하라는 의미다.

 

다시 말해 현재 3차원 tensor를 2차원 tensor로 변경하되, (?,3)의 크기로 변경하라는 의미다.

 

따라서, (2,2,3) -> (2*2, 3) -> (4,3)

 

view의 규칙은 다음과 같다

 

1) view는 기본적으로 변경 전과 변경 후의 tensor 안의 원소의 개수가 유지되어야 한다.

 

변경 전 (2*2*3) = 12개이고, 변경 후에도 (4*3) = 12로 일치한다.

 

2) pytorch의 view는 사이즈가 -1로 설정되면 다른 차원으로부터 해당 값을 유추한다

 

---------------------------------------------------------------------------------------------------------------------------------

 

1-2) 이번에는 3차원 tensor에서 3차원 tensor로 차원을 유지하되, 크기 shape만 바꾸는 작업을 해보자

 

view로 tensor의 크기를 변경하더라도 원소의 수는 유지되어야 한다.

 

그렇다면 (2*2*3) tensor를 (?*1*3) tensor로 변경하라고 한다면, ?는 몇 차원이어야 한가?

 

곱이 12여야 하므로 ?=4여야 한다.

 

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. squeeze

 

차원이 1인 경우 해당 차원을 제거한다

 

ft = torch.FloatTensor([[0],[1],[2]])

print(ft)

print(ft.shape)

tensor([[0.],
        [1.],
        [2.]])
torch.Size([3, 1])

 

해당 tensor는 (3*1)의 크기를 가진다.

 

두번째 차원이 1이므로 squeeze를 사용하면 (3,)의 크기를 가지는 tensor로 변경한다.

 

print(ft.squeeze())
print(ft.squeeze().shape)

tensor([0., 1., 2.])
torch.Size([3])

 

3. unsqueeze

 

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

 

squeeze와 정 반대로, 특정 위치에 1인 차원을 추가할 수 있다

 

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

torch.Size([3])

 

현재 차원이 1개인 1차원 벡터

 

첫번째 차원에 1인 차원을 추가하기 위해 unsqueeze()에 첫번째를 나타내는 인덱스 0을 넣으면

 

print(ft.unsqueeze(0)) #인덱스 0은 첫번째 차원을 의미
print(ft.unsqueeze(0).shape)

tensor([[0., 1., 2.]])
torch.Size([1, 3])

 

(3,)의 크기를 가진 1차원 벡터가 (1,3)의 2차원 텐서로 변경

 

view를 이용해서도 위와 같이 구현할 수 있다.

 

2차원으로 바꾸고 싶으면서, 첫번째 차원은 1이기를 원한다면... view([1,-1])을 주면 된다

 

#view를 이용해 unsqueeze를 구현할 수 있다

print(ft.view(1,-1))
print(ft.view(1,-1).shape)

tensor([[0., 1., 2.]])
torch.Size([1, 3])

 

이번에 unsqueeze의 인자로 1을 주면, 두번째 차원을 1로 만들어달라는 의미이다.

 

(3,)의 벡터에 unsqueeze(1)을 하면 (3,1)의 텐서가 될 것이다.

 

print(ft.unsqueeze(1))
print(ft.unsqueeze(1).shape)

tensor([[0.],
        [1.],
        [2.]])
torch.Size([3, 1])

 

unsqueeze(-1)을 하면, -1은 인덱스 상으로 마지막 위치를 나타내므로, 마지막 차원에 1을 추가하라는 의미가 된다.

 

#-1은 인덱스로 마지막 위치를 나타내므로, 마지막 차원에 1을 추가

print(ft.unsqueeze(-1))
print(ft.unsqueeze(-1).shape)

tensor([[0.],
        [1.],
        [2.]])
torch.Size([3, 1])

 

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

 

4. type casting

 

tensor의 각 원소는 자료형을 가진다.

 

 

32비트 부동소수점은 torch.FloatTensor

 

64비트 부호 있는 정수는 torch.LongTensor

 

GPU 연산을 위한 tensor는 torch.cuda.FloatTensor

 

lt = torch.LongTensor([1,2,3,4])
print(lt)
tensor([1, 2, 3, 4])

 

tensor에 .float()를 붙이면 바로 float형 tensor로 바뀐다

 

#.float()하면 float tensor로 변경

print(lt.float())
tensor([1., 2., 3., 4.])

 

다음은 byte tensor

 

bt = torch.ByteTensor([True, False, False, True])
print(bt)

 

여기에 .long()하면 long 타입의 tensor가 되고, .float()하면 float 타입의 tensor가 된다

 

#.long()하면 long 타입의 tensor가 되고
#.float()하면 float 타입의 tensor가 된다

print(bt.long())
print(bt.float())

tensor([1, 0, 0, 1])
tensor([1., 0., 0., 1.])

 

5. concatenate

 

두 tensor를 연결하는 방법

 

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

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

 

두 tensor를 연결하는 방법은 1가지만 있는 것은 아니다.

 

어느 차원을 기준으로 서로 이어붙일지 결정할 수 있다.

 

dim = 0은 첫번째 차원을 늘리라는 의미이다.

 

#dim = 0은 첫번째 차원을 기준으로 연결

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

 

dim = 0을 인자로 주니, 두개의 2*2 tensor가 4*2 tensor로 바뀌었다.

 

dim = 1을 인자로 주면 두번째 인자를 기준으로 붙여준다

 

#dim = 1은 두번째 차원을 기준으로 연결

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

 

dim = 1을 인자로 주니, 2*2 tensor 2개가 2*4 tensor로 바뀌었다

 

딥러닝에서 주로 모델의 입력, 또는 중간 연산에서 두개의 텐서를 연결하는 경우가 많다.

 

두 텐서를 연결해서 입력으로 사용하는 것은 두가지의 정보를 모두 사용한다는 의미이다.

 

6. stacking

 

여러 tensor를 연결하는 또 다른 방법으로 stacking이 있다.

 

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

print(x)
print(y)
print(z)

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

 

torch.stack()을 이용해 3개의 벡터를 stacking할 수 있다

 

#torch.stack()을 이용해 3개의 벡터를 stacking

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

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

 

(2,)의 x,y,z가 쌓여서 (3,2) tensor가 되었다

 

 

stacking은 cat과는 다르게 많은 연산을 한번에 축약하고 있다.

 

cat을 이용하면 다음과 같이 해야 stacking을 구현할 수 있다

 

#stacking은 cat에 unsqueeze를 동시에 써야 구현가능하다

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)로 바꿔준다.

 

그리고 dim = 0을 기준으로 concat을 수행해 (3,2) tensor가 되었다

 

stacking도 dim 연산이 가능하다.

 

dim = 1을 수행하면 다음과 같이 두번째 차원을 기준으로 stacking을 해준다

 

#dim = 1을 주면 두번째 차원을 기준으로 stacking해준다

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

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

 

 

 

7. ones_like와 zeros_like

 

ones_like는 모든 원소를 1로 채우고, zeros_like는 0으로 채운 tensor를 생성한다

 

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

print(x)

tensor([[0., 1., 2.],
        [2., 1., 0.]])

 

위 tensor에 ones_like를 하면 동일한 크기이면서, 1로만 채워진 tensor를 생성

 

#ones_like는 동일한 크기를 가지면서 모든 원소를 1로 채운다

print(torch.ones_like(x))

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

 

zeros_like를 하면 동일한 크기이면서 0으로만 채워진 tensor를 생성한다

 

#zeros_like를 하면 동일한 크기이면서 모든 원소를 0으로 채운다

print(torch.zeros_like(x))

tensor([[0., 0., 0.],
        [0., 0., 0.]])

 

 

8. in-place operation

 

덮어쓰기 연산

 

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

tensor([[1., 2.],
        [3., 4.]])

 

곱하기 연산을 한 값과 기존 tensor x를 모두 출력

 

#x의 각 원소에 2를 곱한 결과와 x를 출력

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

 

 

02-03 텐서 조작하기(Tensor Manipulation) 2 - PyTorch로 시작하는 딥 러닝 입문 (wikidocs.net)

 

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

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

wikidocs.net

 

 

 

TAGS.

Comments