본문 바로가기
ML & DL/Deep Learning with PyTorch

[PyTorch] 텐서 구조체

by Glory_Choi 2023. 2. 25.
반응형

https://product.kyobobook.co.kr/detail/S000061353646

 

파이토치 딥러닝 마스터 | 엘리 스티븐스 - 교보문고

파이토치 딥러닝 마스터 | 파이토치 핵심 개발진이 직접 집필한 책으로, 최고 실무자가 바로 옆에 앉아서 딥러닝의 기초부터 실제 프로젝트 활용까지, 모델 성능 평가와 개선 방안을 차근차근

product.kyobobook.co.kr

이 글은 파이토치 딥러닝 마스터를 정리한 내용입니다.

 

텐서란?

  • 데이터 처리와 저장을 위해 파이토치에서 제공하는 기본 자료구조로 딥러닝에서의 텐서는 임의의 차원을 가진 벡터나 행렬의 일반화된 개념.
  • 다차원 배열(Multidimensional array)라고도 부르고 텐서의 차원 수는 텐서 안의 스칼라 값을 참조하기 위해 사용하는 인덱스 수와 동일.
  • 데이터 과학 분야의 표준이나 마찬가지인 넘파이(Numpy)가 현재까지는 제일 유명한 다차원 배열 라이브러리인데 파이토치는 넘파이와 깔끔하게 호환되도록 만들어져 있기 때문에 사이파이나 사이킷런, 판다스 같은 과학 라이브러리와 자연스럽게 통합됨.

파이썬 리스트에서 파이토치 텐서로

숫자 세 개를 가진 리스트를 파이썬으로 만든다.

파이썬 프로그램이 2차원에서 선의 좌표 같은 숫자 벡터를 다루기 위해 파이썬 리스트를 사용하는 일은 흔하다. 하지만 텐서 자료구조를 사용해 이미지와 시계열 데이터 혹은 문장들을 나타내는 것이 더 효율적이다.

때문에 텐서에 대해 연산을 정의해 두면 동일한 작업에 대해 파이썬 같은 고차원 언어보다 훨씬 효율적이고 알아보기 쉽게 데이터를 자르고 조작할 수 있다.

 

텐서의 핵심

숫자값으로 만든 파이썬 리스트나 튜플 객체는 메모리에 따로따로 할당된다. 반면 파이토치 텐서나 넘파이 배열은 파이
썬 객체가 아닌 언박싱된 C 언어의 숫자 타입을 포함한 연속적인 메모리가 할당되고 이에 대한 뷰를 제공한다. 여기서 각 요소는 32비트 float 타입이다. 100만 개의 float타입 숫자를 1차원 텐서에 보관한다면 400만 바이트의 연속적인 공간과 메타데이터 공간을 조금 더 차지한다. 

  • 1차원 텐서

  • 2차원 텐서

 

텐서 초기화를 위해 차원별 크기 정보를 튜플로 만들어 zeros나 ones로 넘겨줄 수도 있다.

 

텐서 인덱싱

파이토치는 범위 인덱싱과 더불어 고급 인덱싱이라는 더 강력한 방식도 지원한다. 이는 다음에 포스팅하겠다.

 

텐서의 요소 타입

  • 파이썬에서 숫자는 객체다. 통상 부동소수점 수는 컴퓨터에서 32비트 공간을 사용한다. 하지만 파이썬은 참조 카운터까지 만들어 부동소수점 수를 완전한 파이썬 객체로 변환한다. 박싱이라 부르는 이 연산은 수를 소량만 저장하는 경우라면 큰 문제가 없겠지만 백만 개가 넘어가면 상당히 비효율적이다.
  • 파이썬에서 리스트는 연속된 객체의 컬렉션이다. 파이썬은 두 벡터의 내적을 효율적으로 수행하는 연산이 없다. 벡터 합도 마찬가지다. 파이썬 리스트에 들어있는 데이터를 메모리에 최적화하여 배치할 방법은 딱히 없고, 리스트는 숫자 뿐만 아니라 임의의 파이썬 객체에 대한 임의 접근이 가능한 포인터의 모음이다. 기다가 파이썬 리스트는 단일 차원이며, 비록 시스트의 리스트를 만들 수도 있지만 이런 방식은 매우 비효율적이다.
  • 파이썬 인터프리터는 최적화를 거치는 컴파일된 코드보다 느리다. 다량의 숫자 데이터 모음에 대한 수학적 연산을 수행하는 일은 C 같은 저수준 컴파일을 통해 최적화한 바이너리 코드가 훨씬 빠르다.

이런 이유 때문에 데이터과학 라이브러리는 넘파이에 의존하거나 파이토치 텐서 같이 전용 데이터 구조를 만든 후 숫자 데이터 연산은 저수준 언어로 효율을 높이도록 구현하고 동시에 고차원 API로 이런 구현을 래핑하여 편리성을 더한다. 성능 최적화를 위해 텐서 내의 모든 객체는 같은 타입의 숫자여야 하고 파이토치는 실행 중에 이런 숫자 타입을 계속 추적하고 있어야 한다.

 

dtype으로 숫자 타입 지정하기

tensor나 zeros, ones 같은 텐서 생성자 실행 시 넘겨주는 dtype 인자로 텐서 내부에 들어갈 데이터 타입을 지정할 수 있다. 이를 통해 텐서가 가질 값이 정수 혹은 부동소수점 수 같은 타입을 지정하고 각 값이 차지하는 바이트 수도 명세한다. dtype 인자는 표준 넘파이 인자와 거의 동일하다. dtype 인자 타입을 열거하면 다음과 같다.

  • torch.float32 혹은 torch.float : 32비트 단정밀도 부동소수점
  • torch.float64 혹은 torch.double : 64비트 배정밀도 부동소수점
  • torch.float16 혹은 torch.half :16비트 반 정밀도 부동소수
  • torch.int8 : 부호 있는 8비트 정수
  • torch.uint8 : 부호 없는 8비트 정수
  • torch.int16 혹은 torch.short : 부호 있는 16비트 정수
  • torch.int32 혹은 torch.int : 부호 있는 32비트 정수
  • torch.int64 혹은 torch.long : 부호 있는 64비트 정수
  • torch.bool : 불리언

텐서의 기본 데이터 타입은 32비트 부동소수점이다.

 

모든 경우에 사용하는 dtype

신경망 연산은 대부분 32비트 부동소수점 연산이다. 배정밀도 64비트를 사용해봐야 모델의 정확도는 거의 개선되지 않고 더 많은 메모리와 시간만 낭비한다. 16비트 반정밀도 부동소수점은 표준 CPU 구현은 찾기 힘들지만 최신 GPU에는 내부에 구현되어 있다. 필요하다면 약간의 정확도를 희생해서 정밀도를 반으로 떨어뜨려 신경망이 차지하는 공간을 줄이는 방식도 가능하다.

텐서는 다른 텐서에 대한 인덱스로 사용할 수 있다. 이때 파이토치는 인덱싱용 텐서를 64비트 정수 데이터 타입으로 간주한다. 텐서를 만들 때 torch.tensor([2, 2])처럼 인자로 정수값을 주면 64비트 정수 텐서를 기본으로 만든다. 앞으로 대부분의 경우 float32나 float64 값을 다룬다.

마지막으로 points > 1.0 같은 술어는 텐서 내 각각이 이 조건을 만족하는지 알려주는 bool 텐서를 만들어낸다. 생성된 텐서 내 값은 숫자 타입이다.

 

텐서의 dtype 속성 관리

숫자 타입이 올바르게 지정된 텐서를 하나 할당할 때에는 생성자에 dtype 인자를 정확하게 전달해야 한다.

어떤 텐서가 가진 dtype을 알고 싶다면 다음과 같이 대응하는 속성을 읽어보면 된다.

텐서 함수가 반환하는 텐서의 타입을 대응하는 캐스팅 메소드를 사용해 올바른 타입으로 변환하는 것도 가능하다.

혹은 to 메소드를 사용하면 더 편하다.

to 메소드는 변환이 필요한 경우에만 진행한다. float 같은 dtype 이름을 사용한 캐스팅은 to를 사용한 것보다 짧게 쓸 수 있겠지만 to 메서도는 타입 외에도 추가적인 인자를 지정할 수 있다.

 

여러 타입을 가진 입력들이 연산을 거치며 서로 섞일 때 자동으로 제일 큰 타입으로 만들어진다.

따라서 만약 32비트 연산을 원한다면 입력 타입 중 가장 큰 것이 32비트인지 확인하기 바란다.

 

반응형