Tensor 형태 변경하기

x = torch.arange(12)
print('view generation:', x.is_contiguous())
print('x=:', x)
print('shape of x:', x.shape)

view generation: True
x=: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
shape of x: torch.Size([12])

y = x.view(3, -1)
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
shape of y: torch.Size([3, 4])

  1. Tensor.view()는 메모리의 복사없이 텐서의 모양을 변경한 View를 생성한다.
  2. tensor.is_contiguous()는 데이터가 메모리에 연속적으로 저장되어 있는 지를 확인한다.
  3. y는 텐서 x의 (3, 4) 뷰를 생성하여, 2차원 텐서인 y를 생성한다.

Tensor 생성하기

z = x.view(2, 2, -1)
print('z=:', z)
print('shape of z:', z.shape)

z=: tensor([[[ 0, 1, 2],
[ 3, 4, 5]],

    [[ 6,  7,  8],
     [ 9, 10, 11]]])

shape of z: torch.Size([2, 2, 3])

x[0] = 10
print('x[0]:', x[0])
print('y[0,0]:', y[0,0])
print('z[0,0,0]:', z[0,0,0])
print(x.storage().data_ptr()==y.storage().data_ptr())

x[0]: tensor(10)
y[0,0]: tensor(10)
z[0,0,0]: tensor(10)
True

  1. z는 텐서 x의 (2, 2, 3) 뷰를 생성하여, 3차원 텐서인 z를 생성한다.
  2. x[0] = 10 은 x, y, z가 메모리를 공유하기 때문에, x[0], y[0, 0], z[0, 0, 0]의 값은 모두 10이 된다.

Tips

  1. tensor.storage() : 텐서의 형태에 관계없이 1차원 배열로 저장된 실제 데이터 블록을 의미. 서로 다른 View라도 같은 storage를 공유할 수 있음
  2. tensor.data_ptr() : 텐서의 첫번째 위치에 저장된 메모리 주소를 반환함.
    즉, data_ptr()이 같다면, 같은 메모리 위치를 참조한다는 것을 의미.

View()

  1. 기존 텐서의 데이터(storage)를 공유하면서, 텐서의 차원(shape)만 변환시켜 보여주는 메서드이다.
  2. 데이터를 물리적으로 복사하지 않아 연산이 빠르고, 효율적이다.
  3. 원본데이터나 view()의 데이터를 바꾸면 둘 다 바뀐다.
  4. 메모리상에 데이터가 연속적(contiguous)으로 배치되어 있어야 활용 가능

Tensor 펼치기

x = torch.arange(6)
y = x.view(1, 2, -1)
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([[[0, 1, 2],
[3, 4, 5]]])
shape of y: torch.Size([1, 2, 3])

y2 = y.flatten()
print('y2=:', y2)
print('shape of y2:', y2)

y2=: tensor([0, 1, 2, 3, 4, 5])
shape of y2: tensor([0, 1, 2, 3, 4, 5])

  1. y는 텐서 x의 (1, 2, 3) 뷰를 생성하여 , 3차원 텐서인 y를 생성한다.
  2. torch.flatten()은 Input tensor를 start_dim에서 end_dim까지 평평하게 1차원 텐서로 변경한다. 즉, y2는 y를 1차원 텐서로 변경하여 y2를 생성한 것이다.
y3 = y.flatten(1)
print('y3=:', y3)
print('shape of y3:', y3.shape)

y3=: tensor([[0, 1, 2, 3, 4, 5]])
shape of y3: torch.Size([1, 6])

y4 = y.flatten(0, 1)
print('y4=:', y4)
print('shape of y4:', y4.shape)

y4=: tensor([[0, 1, 2],
[3, 4, 5]])
shape of y4: torch.Size([2, 3])

  1. y3는 start_dim을 1부터 시작하여, 1차원 텐서로 변경하여, y3을 생성한 것이다. 즉, y3의 차원은 [1, 6]이 된다.
  2. y4는 start_dim은 0이고, end_dim은 1로 하여, y를 1차원 텐서로 변경하였기 때문에, y4의 차원은 [2, 3]이 된다.

주의 : end_dim은 미포함

x[0] = 10
print('y=:', y)
print('y2=:', y2)
print('y3=:', y3)
print('y4=:', y4)

y=: tensor([[[10, 1, 2],
[ 3, 4, 5]]])
y2=: tensor([10, 1, 2, 3, 4, 5])
y3=: tensor([[10, 1, 2, 3, 4, 5]])
y4=: tensor([[10, 1, 2],
[ 3, 4, 5]])

Tensor의 변환 및 전치

x = torch.arange(6)
y = x.reshape(shape=(-1, 3))
print('x=:', x)
print('y=:', y)
print('contiguous:', y.is_contiguous())

x=: tensor([0, 1, 2, 3, 4, 5])
y=: tensor([[0, 1, 2],
[3, 4, 5]])
contiguous: True

z = y.transpose(dim0 = 0, dim1 = 1)
print('z=:', z)
print('contiguous:', z.is_contiguous())

z=: tensor([[0, 3],
[1, 4],
[2, 5]])
contiguous: False

  1. torch.reshape(input, shape)은 input tensor를 shape의 모양으로 변환한다. 이때, 변경된 tensor는 input tensor와 메모리를 공유한다.
  2. torch.transpose(input, dim0, dim1)는 input tensor의 dim0과 dim1을 전치한다. 즉, 0과 1을 전치하여, 2차원 텐서인 z를 만든다. 즉, x, y, z는 모두 메모리를 공유하고 있다.
z = z.contiguous()
print(z.is_contiguous())
print('z.view(-1, 3)=:', z.view(-1, 3))

True
z.view(-1, 3)=: tensor([[0, 3, 1],
[4, 2, 5]])

  1. 새로운 tensor z를 새로운 메모리에 연속적으로 변경한 것이다. 이제 데이터가 메모리에 연속적으로 배치되어 있으므로, view()를 생성할 수 있다.

Tensor 차원의 축소와 확장

x = torch.arange(6).view(1, -1, 1)
print('x=:', x)
print('shape of x:', x.shape)

x=: tensor([[[0],
[1],
[2],
[3],
[4],
[5]]])
shape of x: torch.Size([1, 6, 1])

y = torch.squeeze(x)
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([0, 1, 2, 3, 4, 5])
shape of y: torch.Size([6])

z = torch.squeeze(x, dim=0)
print('z=:', z)
print('shape of z:', z.shape)

z=: tensor([[0],
[1],
[2],
[3],
[4],
[5]])
shape of z: torch.Size([6, 1])

w = torch.unsqueeze(z, dim=0)
print('w=:', w)
print('shape of w:', w.shape)

w=: tensor([[[0],
[1],
[2],
[3],
[4],
[5]]])
shape of w: torch.Size([1, 6, 1])

  1. torch.squeeze(input, dim=None)는 input 텐서에서 dim이 1인 차원을 없앤다.
  2. torch.unsqueeze(input, dim)은 input 텐서에서 dim의 차원을 확장한다.
  3. y는 x에서 1인 차원이 모두 삭제 되므로, y의 차원은 [6]이 된다.
  4. z는 dim=0의 차원만 삭제하게 되므로, x의 차원 [1, 6, 1]에서 z는 [6, 1]로 축소된다.
  5. w는 torch.unsqueeze()에 의해서, dim=0의 차원이 확장되므로, w의 차원은 [1, 6, 1]로 확장된다.

Tensor 연결하기

x = torch.arange(4).view(-1, 2)
y = torch.tensor([4, 5])
print('x=:', x)
print('y=:', y)
print('shape of y:', y.shape)

x=: tensor([[0, 1],
[2, 3]])
y=: tensor([4, 5])
shape of y: torch.Size([2])

z = torch.cat((x, y.reshape(1, 2)))
print('z=:', z)
print('shape of z:', z.shape)

z=: tensor([[0, 1],
[2, 3],
[4, 5]])
shape of z: torch.Size([3, 2])

z2 = torch.concat((x, y.reshape(1, 2)))
print('z2=:', z2)
print('shape of z2:', z2.shape)

z2=: tensor([[0, 1],
[2, 3],
[4, 5]])
shape of z2: torch.Size([3, 2])

w = torch.cat((x, y.reshape(2, 1)), dim = 1)
print('w=:', w)
print('w.shape=:', w.shape)

w=: tensor([[0, 1, 4],
[2, 3, 5]])
w.shape=: torch.Size([2, 3])

  1. torch.cat()은 텐서를 dim의 차원으로 연결한다. 연결되는 텐서는 차원이 연결 가능해야 한다.
  2. torch.concat()도 텐서를 dim의 차원으로 연결하여 준다. torch.cat()과 완전히 동일한 결과를 반환한다.
  3. z는 x와 모양이 [1, 2]로 변경된 y를 서로 연결하여 텐서를 반환한 것이다.
  4. torch.concat()을 사용해도, torch.cat()과 동일한 결과를 반환한다.
  5. w는 x와 모양이 [2, 1]로 변경된 y를 dim 1의 방향으로 서로 연결하여 텐서를 반환한 것이다.
x = torch.tensor([[0, 1], [2, 3]])
print('shape of x:', x.shape)

shape of x: torch.Size([2, 2])

y = torch.stack((x, x, x))
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([[[0, 1],
[2, 3]],

    [[0, 1],
     [2, 3]],

    [[0, 1],
     [2, 3]]])

shape of y: torch.Size([3, 2, 2])

z = torch.stack((x, x, x), dim=1)
print('z=:', z)
print('shape of z:', z.shape)

z=: tensor([[[0, 1],
[0, 1],
[0, 1]],

    [[2, 3],
     [2, 3],
     [2, 3]]])

shape of z: torch.Size([2, 3, 2])

  1. torch.stack(tensors, dim=0)은 새로운 차원 dim으로 확장하여, 텐서를 쌓아서, 반환한다.
  2. y는 x를 dim = 0의 방향으로 3번 쌓아서, y는 [3, 2, 2] 모양의 텐서가 생성된 것이다.
  3. z는 x를 dim = 1의 방향으로 3번 쌓아서, y는 [2, 3, 2] 모양의 텐서가 생성된 것이다.

Tensor 확장하기

x = torch.tensor([0, 1, 2])
print('shape of x:', x.shape)

shape of x: torch.Size([3])

y = x.expand(2, 3)
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([[0, 1, 2],
[0, 1, 2]])
shape of y: torch.Size([2, 3])

x = x.view(3, 1)
z = x.expand(3, 2)
print('x=:', x)
print('z=:', z)
print('shape of z:', z.shape)

x=: tensor([[0],
[1],
[2]])
z=: tensor([[0, 0],
[1, 1],
[2, 2]])
shape of z: torch.Size([3, 2])

x[0] = 10
print('y=:', y)
print('z=:', z)

y=: tensor([[10, 1, 2],
[10, 1, 2]])
z=: tensor([[10, 10],
[ 1, 1],
[ 2, 2]])

  1. tensor.expand(sizes) 는 텐서에서 dim = 1인 텐서를 메모리를 함께 공유하면서, (size)의 크기로 확장한다.
  2. 즉, y는 dim = 0 방향으로 x를 2행 확장하여서, [2, 3]의 크기가 된다.
  3. view() 를 통해, x의 크기를 [3, 1]로 변경한 후, z는 dim = 1 방향으로 x를 2열 확장하여서, 크기가 [3, 2]가 된것이다.
  4. tensor.expand()는 메모리에 새로 할당하는 것이 아니라, 텐서의 메모리를 공유하기 때문에, x[0]을 10으로 바꾸면, y와 z 역시 10으로 변경되었음을 알 수 있다.

Tensor의 반복

x = torch.tensor([0, 1, 2])
print('shape of x:', x.shape)

shape of x: torch.Size([3])

y = x.repeat(2, 3)
print('y=:', y)
print('shape of y:', y.shape)

y=: tensor([[0, 1, 2, 0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2, 0, 1, 2]])
shape of y: torch.Size([2, 9])

x = x.view(3, 1)
z = x.repeat(2, 3)
print('z=:', z)
print('shape of z:', z.shape)

z=: tensor([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[0, 0, 0],
[1, 1, 1],
[2, 2, 2]])
shape of z: torch.Size([6, 3])

  1. tensor.repeat(sizes)는 텐서를 (size)의 크기로 확장하고, 메모리를 새로 할당한다.
  2. y는 x를 dim = 0으로 2번 반복하고, dim = 1로 3번 반목하여, size를 [2, 9]의 크기로 만들게 된다.
  3. 먼저, view()로 x의 차원을 [3, 1]로 변경한 후, dim = 0으로 2번, dim = 1로 3번 반복하므로, z의 크기는 [6, 3]이 된다.

0개의 댓글