Skip to content

Latest commit

 

History

History
297 lines (200 loc) · 7.87 KB

File metadata and controls

297 lines (200 loc) · 7.87 KB

파이토치 텐서 기본 사용법

파이토치에서 사용하는 텐서의 기본적인 연산에 관한 내용입니다.


1. 텐서 생성

print(  torch.empty(5, 4) )
print(  torch.ones(3, 3)  )
print(  torch.zeros(2)  )
print(  torch.rand(3, 5)  )
코드 내용
torch.empty(5, 4) [5x4] 크기의 임의의 값으로 초기화 된 텐서 생성
torch.ones(3, 3) [3x3] 크기의 1.0으로 초기화 된 텐서 생성
torch.zeros(2) [2] 크기의 0.0으로 초기화 된 텐서 생성
torch.rand(3, 5) [0~1]사이의 임의의 값으로 초기화 된 [3x5]크기의 텐서 생성

2. 텐서 변환

l1 = [1, 2, 3] # list
l2 = np.array([4, 5, 6]) # numpy array
print(  torch.tensor(l1)  )
print(  torch.tensor(l2)  )
print(  torch.tensor(l2, dtype=torch.float32) )

l3 = [[1, 2], [3, 4], [5, 6]]
print(  torch.tensor(l3).size() )
print(  type(torch.tensor(l3))  )
코드 내용
torch.tensor(l1) 파이썬 리스트 혹은 numpy 배열로부터 텐서 생성
torch.tensor(l2, dtype=torch.float32) 타입을 지정한 텐서 생성
torch.tensor(l3).size() 텐서 크기 반환
type(torch.tensor(l3)) 텐서 타입 반환

코드를 직접 작성할 때, 형변환에 주의할것!!


3. 텐서 연산 (곱, 행렬곱, 합)

x = torch.rand(4, 4)
y = torch.rand(4, 4)

# 요소별 곱
print(  x*y )
print(  x.numpy()*y.numpy() )

# 행렬곱
print(  torch.matmul(x, y)  )
print(  np.matmul(x.numpy(), y.numpy()) )
코드 내용
x * y 요소별 곱 반환
x.numpy() * y.numpy() 요소별 곱 numpy에서 계산 후 반환
torch.matmul(x, y) 행렬곱 반환
np.matmul(x.numpy(), y.numpy()) 행렬곱 numpy에서 계산 후 반환

print(  x + y ) # x + y 반환
print(  torch.add(x, y) ) # x + y 반환
print(  y.add(x)  ) # x + y 반환
print(  y ) # y는 변하지 않음

print(  y.add_(x) ) # y = x + y 반환
print(  y ) # y가 변함
코드 내용
x+y x + y 반환
torch.add(x, y) x + y 반환
y.add(x) x + y 반환
y.add_(x) y = x + y 반환 (y가 변경됨)

4. 텐서 값 접근 및 변경

print(  y[0, 0] )
print(  y[0, 0].numpy() )
z = torch.Tensor([1])
print(  z.item()  )
# print(  y.item()  )
y[0, 0] = 4.44
print(y[0, 0]) # 4.44
코드 내용
y[0, 0] 텐서 y의 [0, 0]에 접근
y[0, 0].numpy() 텐서 y의 [0, 0]에 접근하여 값을 numpy로 반환
z.item() 텐서 z 스칼라 값 반환 단독 값으로 구성된 스칼라 텐서만 item() 사용 가능
y[0, 0]=4.44 텐서의 값 변경

파이토치 텐서 Reshape관련

5. 텐서의 형태 변환 : reshape(), .view()

x = torch.rand(4, 4)
y = torch.rand(4, 4)

print(  y.reshape(16) )
print(  y.view(16)  )
print(  y.reshape(-1) )
print(  y.view(-1)  ) 

print(  y.reshape(8, 2) )
print(  y.reshape(8, -1)  )

# print(  y.reshape(7, -1)  ) # 오류

print(  y.reshape(2, 2, 4)  )
print(  y.reshape(2, 2, -1) )

대다수의 경우에 .reshape().view는 동일하게 사용됩니다.

메서드의 매개변수로 이루어진 형태로 텐서를 변형하여 반환합니다. (복사가 아닌 참조)

또한 매개변수에 -1을 주면 토치에서 알아서 값을 추정해서 사용합니다. (물론 약수를 넣어야겠죠)


메서드 contiguous non-contiguous
reshape() 입력 텐서를 참조한 채로 변환하고 반환 입력 텐서의 복사본을 변환하여 반환
view() 입력 텐서를 참조한 채로 변환하고 반환 X

기본적으로 배열을 선언하면, 메모리상에 각 요소의 값이 순차적으로 저장됩니다.

이 때, 어떤 데이터(배열형태의)의 요소가 메모리상에 순서대로 저장되어 있다면 contiguous, 그렇지 않다면 non-contiguous라고 정의됩니다.


a = torch.Tensor([[1, 2], [3, 4]])
print(  torch.Tensor.is_contiguous(a) ) # True
print(  torch.Tensor.is_contiguous(a.t()) ) # False

a 라는 배열[[1, 2], [3, 4]]을 선언하면 메모리상에는 [1, 2, 3, 4]가 순서대로 저장되어 있겠죠. torch.Tensor.is_contiguous()a를 확인해보면 True 가 반환됩니다.

a.t()[[1, 3], [2, 4]]가 되는데, 파이썬은 결국에 모든 객체를 참조하는 것을 기본으로 설계되어 있기에, a.t()의 요소의 메모리상의 위치를 살펴보면, 뒤죽박죽이 되어 있겠죠. torch.Tensor.is_contiguous() 로 살펴보면 False가 반환됩니다.


b = a.t()
c = b.reshape(-1)
b[0][0] = 100
print(b) # [[100, 2], [3, 4]] # 변경됨
print(c) # [[1, 2], [3, 4]] # 그대로
# d = a.t().view(-1) # 에러

위 표에 적힌 것 처럼, non-contiguous한 텐서에 대해서는 reshape가 복사본을 반환합니다. 즉, b를 수정해도 c가 변하지 않죠. 또한, non-contiguous하므로 view()는 사용할 수 없습니다.


d = a.reshape(-1)
a[0][0] = 10
print(a) # [[10, 2], [3, 4]] # 변경됨
print(d) # [[10, 2], [3, 4]] # 변경됨

contiguous한 텐서에 대해서는 reshape가 입력 텐서를 참조하므로, 입력 텐서를 수정하면 반환 텐서도 같이 수정됩니다.


6. 텐서 합치기 (concatenation) : torch.cat())

  • Example 1
x = torch.Tensor([[[0, 1, 2], [3, 4, 5], [6, 7, 8]],
    [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
    [[18, 19, 20], [21, 22, 23], [24, 25, 26]]])
y = torch.Tensor([27, 28, 29])

print(  x.view(-1).shape  ) # [27]
print(  y.shape  ) # [3]

print(  torch.cat((x.view(-1), y), 0  )
print(  torch.cat((x.view(-1), y), 0).shape )

  • Example 2
x1 = torch.Tensor([[[1, 2, 3]]]) # [1 x 1 x 3]
x2 = torch.Tensor([[[7, 8, 9]]]) # [1 x 1 x 3]
a = torch.cat((x1, x2), 0)
b = torch.cat((x1, x2), 1)
c = torch.cat((x1, x2), 2)
print(  a.shape ) # [2 x 1 x 3]
print(  b.shape ) # [1 x 2 x 3]
print(  c.shape ) # [1 x 1 x 6]
메서드 설명
torch.cat([텐서1, 텐서2], dim = 차원) 텐서1과 텐서2를 '차원' 기준으로 합침

cat의 사용 예는 Example 1, 2를 통해 쉽게 살펴볼 수 있습니다. dim으로 지정된 차원 기준으로 텐서를 합쳐 반환합니다.

여전히 reshape, view와 마찬가지로 값을 참조하기 때문에 원본텐서 x1, x2의 값을 바꾸면 동일하게 변경됩니다.

Resnet등의 skip-connection이나 feature concatenation에 매우 많이 사용되는 메서드죠!!


7. 행렬 차원 바꾸기, 차원 순서 바꾸기 (Permute), 전치 (Transpose)

  • Example 3
x = torch.Tensor([[[1, 2, 3], [4, 5, 6]]])
print(  x )
print(  x.shape ) # [1 x 2 x 3]

y = x.permute(1, 0, 2)
print(  y )
print(  y.shape )  # [2 x 1 x 3]

  • Example 4
z = torch.transpose(x, 0, 2)
print(  z )
print(  z.shape ) # [3 x 2 x 1]
메서드 설명
텐서.permute(dims = 1, 2, 3, ...) 텐서를 dims 기준으로 차원 순서를 변경합니다
torch.transpose(입력 텐서, 변경 차원1, 변경 차원2) 입력 텐서의 변경 차원1과 2를 서로 변경합니다

Example 3을 보면 쉽게 이해가 가실거에요!

x[1 x 2 x 3] 형태의 텐서입니다. 0번째 차원부터 살펴보면, 0번째 차원 : 1, 1번째 차원 : 2, 2번째 차원 : 3의 크기를 갖고있습니다.

이를 .permute(1, 0, 2) 적용하면, 0번째 차원에 (기존의)1번째 차원, 1번째 차원에 (기존의) 0번째 차원, 2번째 차원에 (기존의) 2번째 차원을 넣겠다는 의미입니다.


Example 4는 더 쉽죠. torch.transpose에 지정된 텐서의 입력차원 2개를 서로 바꿔주겠다는 의미입니다.

PermuteTranspose는 보통, openCV등에서 입력받은 이미지 [h x w x c] 데이터를 파이토치가 사용할 수 있도록 [c x h x w]로 변경할 때 주로 사용됩니다.