AI Tech 7기/pytorch

pytorch 헷갈리는 함수 정리

sweetpotato7 2024. 8. 9. 19:27

목차

0. id() vs data_ptr() 

1. view() vs reshape() - shape 변경

2. flatten() vs squeeze() - dim 차원 축소

3. cat() vs stack() - tensor 연결

4. expand() vs repeat() - tensor 확장

 

 

 

0. id() vs data_ptr()

id(object) : 객체의 고유성을 반환

tensor.storage().data_ptr() -> int : tensor가 저장되어 있 메모리 주소를 반환

 

* 아직 잘 모르겠음.. 수정예정

 

1. view() vs reshape() - shape 변경

임의의 tensor가 주어질 때 tensor의 shape을 변경하는 가장 대표적인 메소드다. 

 

view() : 메모리가 연속적으로 할당된 경우

reshape() : 메모리의 연속성에 상관 없는 경우 

 

그렇다면 모든 경우에 reshape()을 사용하는게 낫지 않냐는 생각이 들지만, contiguous 하지 않을 경우에는 새로운 복사본을 할당하기 때문에 성능이 저하된다. 즉, 메모리 연속성이 확실한 경우에는 view 사용

 

* 메모리의 연속성은 is_contiguous() 로 확인할 수 있다. 

 

메모리가 연속적이다? tensor 안에 있는 값들의 메모리 주소가 연속적으로 할당되어 있을 때를 말한다. 

import torch

t = torch.tensor([1,2,3], dtype=torch.float32)
print(t[0].data_ptr()) # 97835977376640
print(t[1].data_ptr()) # 97835977376644
print(t[2].data_ptr()) # 97835977376648

 

torch.view 같은 경우  공식문서

https://pytorch.org/docs/stable/generated/torch.Tensor.view.html#torch.Tensor.view 

에 따르면 stride와 관련있다고 한다. 원래대로라면 is_contiguous()가 False이면 메모리가 연속적으로 할당되지 않았으므로 view() 를 사용할 수 없지만 메모리 간격(?)이 같은 간격을 가진다면 view()가 사용가능하다.

 

뭔가 설명 잘 되어 있는 듯한 stackoverflow..

python - What's the difference between reshape and view in pytorch? - Stack Overflow

 

2. flatten() vs squeeze() - dim 차원 축소

tensor.flatten(input, start_dim=0, end_dim=-1) -> tensor : tensor의 a차원부터 b차원까지 평탄화 (아무것도 입력하지 않으면 모든 차원 평탄화)

tensor.squeeze(input, dim=None) -> tensor : tensor에서 특정 dim = 1인 차원을 삭제 (dim이 주어지면 해당 번째 차원만 삭제 (shape 1인경우) )

 

* squeeze 의 경우 반환되는 tensor가 input tensor와 저장소를 공유하기 때문에 하나가 값이 바뀌면 나머지 하나도 값이 바뀐다. 

t = torch.randn(2,3,1)
t1 = torch.flatten(t) # or t1 = t.flatten()

t2 = torch.squeeze(t) # or t2 = t.squeeze()

 

 

3. cat() vs stack() - tensor 연결

tensor.cat(tensors, dim=0, *, out=None) -> tensor

: 기존의 차원을 유지하면서 tensor 연결 , 결합하고자 하는 dim 을 제외한 나머지 dim의 shape이 같아야함

tensor.stack(tensors, dim=0, *, out=None) -> tensor

: 새로운 차원 dim 을 생성하여 tensor들의 같은 위치의 값을 결합. 그렇기 때문에 모든 tensor가 같은 shape을 가져야함. 

 

둘 다 같은 인자를 입력받기에 굉장히 비슷하지만 결과는 다르다. 

공식 문서에 따르면,

torch.cat() concatenates the given sequence along an existing dimension. (존재하는 차원으로 tensor 연결)

torch.stack() concatenates a sequence of tensors along a new dimension. (새로운 차원으로 tensor 연결)

 

t1 = torch.tensor([1, 2, 3])
t2 = torch.tensor([4, 5, 6])
t3 = torch.tensor([7, 8, 9])

print(t1.shape) # torch.Size([3])

stacked = torch.stack([t1, t2, t3], dim=1)
print(stacked)
# tensor([[1, 4, 7],
#        [2, 5, 8],
#        [3, 6, 9]])
print(stacked.shape) # torch.Size([3,3])
        
stacked2 = torch.stack([t1, t2, t3], dim=0)
print(stacked2)
# tensor([[1, 2, 3],
#        [4, 5, 6],
#        [7, 8, 9]])
print(stacked2.shape) # torch.Size([3,3])

 

 

4. expand() vs repeat() - tensor 확장

tensor.expand(*sizes) -> tensor

: 텐서의 차원 중 크기가 1인 차원을 주어진  size로 확장, 새로운 메모리를 할당하지 않고 기존 텐서에 할당 -> 메모리 효율 높임,

  size를 -1로 입력하면 크기 변경하지 않고 그대로 유지

  그렇기 때문에 tensor의 차원 중 크기가 1인 차원이 존재해야함.  

 

tensor.repeat(*sizes) -> tensor

: 특정 차원을 따라서 tensor를 반복. 각 dimension을 다라서 이 텐서를 몇번 반복할지를 인자로 입력함. (1,2) 일 경우 dim0 을따라서 1번,          dim1을 따라서 2번 반복

  expand() 와 달리, tensor 데이터를 복제함. (새로운 메모리에 할당)

  그렇기 때문에 sizes에 입력되는 차원이 tensor의 차원보다 작을 수 없다. (tensor가 3차원이었다면, sizes는 인자 수가 3개 이상이도록 입력)

t = torch.randn(2,3,1)
t1 = t.expand(2,3,4)

print(t.shape) # torch.Size([2, 3, 1])
print(t1.shape) # torch.Size([2, 3, 4])

print(t.storage().data_ptr()) # 97835978417216
print(t1.storage().data_ptr()) # 97835978417216

 

t = torch.randn(2, 3)
t2 = t.repeat(1, 2)

print(t)				# tensor([[ 0.6665, -0.3331,  0.6336],
       					#	[ 0.5299,  0.4739, -1.5365]])
print(t.shape)				# torch.Size([2, 3])
print(t.storage().data_ptr()) 		# 97835952655808

print(t2) 				# tensor([[ 0.6665, -0.3331,  0.6336,  0.6665, -0.3331,  0.6336],
    					#	[ 0.5299,  0.4739, -1.5365,  0.5299,  0.4739, -1.5365]])
print(t2.shape)				# torch.Size([2, 6])
print(t2.storage().data_ptr())	 	# 97835978489344