0. Pytorch(파이토치)란
파이토치는 2017년 초에 공개된 딥러닝 프레임워크로 개발자들과 연구자들이 쉽게 GPU를 활용하여 인공 신경망 모델을 만들고 학습시킬 수 있게 도와준다. 파이토치의 전신이라고 할 수 있는 토치(torch)는 루아 프로그래밍 언어로 되어 있었지만, 파이토치는 파이썬으로 작성되어 파이썬의 언어 특징을 많이 가지고 있다.
파이토치는 페이스북의 인공지능 연구팀 멤버들이 주로 관리하며, 독자적으로 운영되는 파이토치 포럼은 사람들이 질문을 올리면 프레임워크 개발자를 비롯한 많은 사람이 답을 해주는 등 활발히 교류가 일어나고 있다.
참고 : https://jfun.tistory.com/238
Pytorch란?
1. 파이토치란 무엇일까? 출처 : 파이토치 첫걸음 - 최건호 파이토치는 2017년 초에 공개된 딥러닝 프레임워크로 개발자들과 연구자들이 쉽게 GPU를 활용하여 인공 신경망 모델을 만들고 학습시킬
jfun.tistory.com
1. 파이토치 패키지의 기본 구성
- torch : 메인 네임스페이스입니다. 텐서 등의 다양한 수학 함수가 포함되어져 있으며 Numpy와 유사한 구조를 가집니다.
- torch.autograd :자동 미분을 위한 함수들이 포함되어져 있습니다. 자동 미분의 on/off를 제어하는 콘텍스트 매니저(enable_grad/no_grad)나 자체 미분 가능 함수를 정의할 때 사용하는 기반 클래스인 'Function' 등이 포함되어져 있습니다.
- torch.nn : 신경망을 구축하기 위한 다양한 데이터 구조나 레이어 등이 정의되어져 있습니다. 예를 들어 RNN, LSTM과 같은 레이어, ReLU와 같은 활성화 함수, MSELoss와 같은 손실 함수들이 있습니다.
- torch.optim : 확률적 경사 하강법(Stochastic Gradient Descent, SGD)를 중심으로 한 파라미터 최적화 알고리즘이 구현되어져 있습니다.
- torch.utils.data : SGD의 반복 연산을 실행할 때 사용하는 미니 배치용 유틸리티 함수가 포함되어져 있습니다.
- torch.onnx : ONNX(Open Neural Network Exchange)의 포맷으로 모델을 익스포트(export)할 때 사용합니다. ONNX는 서로 다른 딥 러닝 프레임워크 간에 모델을 공유할 때 사용하는 포맷입니다.
2. 텐서 조작하기(Tensor Manipulation)
벡터, 행렬 그리고 텐서
기본적인 개념인 vector(벡터), matrix(행렬), tensor(텐서)에 관해 알아보자.
사실 본인 전공은 수학과(수리과학부)라 벡터와 행렬은 정말 익숙하다. 또한 차원 개념에 대해서도 편안하게 받아들여진다. 다만 딥러닝에서는 이를 '차원' 개념에서의 n-th dimension의 입장이 아닌 nd-tensor로 표기하며 이해한다. 사실 이는 다차원 행렬 혹은 다차원 배열과 동일하다.
(관련 도움이 될 포스트: https://coding-kindergarten.tistory.com/147)

Tip
- [1,2,3]과 같은 1차원 tensor의 size는 3이다.
- [[1,2,3],[4,5,6]]과 같은 2차원 tensor의 사이즈는 (2,3)인지 (3,2)인지 헷갈리기 시작한다.
=> 항상 size를 확인할때는 바깥쪽의 괄호부터 카운팅한다. 결국 (2,3) 이다.
Tensor Example


읽다보니 batch_size라는 것이 있다. 이 batch가 data 작업서 사용하는 batch처리와 일관된 맥락인 것 같아 이해가 편했다.
*NLP 분야의 3D tensor 이해
[[나는 사과를 좋아해], [나는 바나나를 좋아해], [나는 사과를 싫어해], [나는 바나나를 싫어해]]
다음과 같은 4개의 문장으로 구성된 훈련 데이터가 있다.
컴퓨터 입력을 위해 각 문장을 단어 단위로 쪼갠다.
[['나는', '사과를', '좋아해'], ['나는', '바나나를', '좋아해'], ['나는', '사과를', '싫어해'], ['나는', '바나나를', '싫어해']]
이제 위 데이터는 4X3의 크기를 갖는 2D tensor이다. 컴퓨터에 입력을 위해 각 단어를 벡터로 변환한다.
예시로 각 단어를 다음과 같은 벡터로 변환한다하자.
'나는' = [0.1, 0.2, 0.9]
'사과를' = [0.3, 0.5, 0.1]
'바나나를' = [0.3, 0.5, 0.2]
'좋아해' = [0.7, 0.6, 0.5]
'싫어해' = [0.5, 0.6, 0.7]
이 내용을 바탕으로 맨 위의 훈련 데이터를 재구성 하면 다음과 같다.
[[[0.1, 0.2, 0.9], [0.3, 0.5, 0.1], [0.7, 0.6, 0.5]],
[[0.1, 0.2, 0.9], [0.3, 0.5, 0.2], [0.7, 0.6, 0.5]],
[[0.1, 0.2, 0.9], [0.3, 0.5, 0.1], [0.5, 0.6, 0.7]],
[[0.1, 0.2, 0.9], [0.3, 0.5, 0.2], [0.5, 0.6, 0.7]]]
이 훈련 데이터는 4X3X3의 size를 갖는 3d tensor이다.
만일 Batch size를 2로 한다면
첫번째 배치 #1
[[[0.1, 0.2, 0.9], [0.3, 0.5, 0.1], [0.7, 0.6, 0.5]],
[[0.1, 0.2, 0.9], [0.3, 0.5, 0.2], [0.7, 0.6, 0.5]]]
두번째 배치 #2
[[[0.1, 0.2, 0.9], [0.3, 0.5, 0.1], [0.5, 0.6, 0.7]],
[[0.1, 0.2, 0.9], [0.3, 0.5, 0.2], [0.5, 0.6, 0.7]]]
다음과 같이 두 번의 연산으로 수행할 것이다.
각 배치의 훈련 데이터 tensor의 크기는 2X3X3이다.
3. Numpy로 텐서 만들기
이미 어느 정도 아는 내용이라 따라만 쳐보고 넘어간다.

- ndim은 몇차원인지를 출력
- shape은 크기를 출력. (7,)은 1X7의 size를 의미.

인덱스는 0부터 시작이며 (python이니까..), list와 동일한 문법.

당연히 slicing도 마찬가지.
2차원의 경우도 동일하다.

2차원부턴 shape이 (4,3)인지 (3,4)인지 헷갈리지 말도록 하자.
4. 파이토치 텐서 선언하기

위에서 선언한 t는 1차원이며 print 했을때 list나 array와는 다르게 tensor라는 표기가 같이 나온다.

2차원의 경우 Size가 [4,3]으로 표기된다. 이 맥락에서 1차원에 대해 (7,)과 같이 표기되었던 np.array에 비해 좀 더 직관적인 표현이 아닌가 싶다. (1차원 한정이긴하지만..)

:를 통해 첫번째 차원에 대해 전체를 선택하며 두번째 차원에 대해 1번 인덱스 (즉, 두번째 값)을 가져온다.
브로드캐스팅
같은 크기의 행렬들에 대해 덧셈이나 뺄셈을 할 때는 우리가 생각하는 대로 계산하면 된다.

다만, 현실세계는 녹록치 않다. 불가피하게 크기가 다른 두 행렬에 대해서도 계산을 해야하는 상황이 온다.
파이토치에서는 알아서(자동으로) 크기를 맞춰 연산을 수행하게 하는 브로드캐스팅이라는 기능을 제공한다.

다음과 같이 크기가 다른 두 행렬에 대해 연산이 원래는 되면 안되지만 m2가 자연스레 [10,10]으로 바뀌며 연산이 되었다.

다음과 같이 2 by 1 matrix와 1 by 2 matrix에 대한 연산도 하기의 수정본과 같이 브로드캐스팅 된 이후 이루어진다.
=> 이는 굉장히 편리하지만 주의해서 이용해야한다. (python의 eval()과 비슷한 향기..)
자주 사용되는 기능들
행렬 곱셈 vs 곱셈

matmul : 이는 선형대수학에서 아는 평범한 행렬 곱셈을 의미한다.

mul : element-wise한 곱셈이다.
이는 m2가 브로드캐스팅 된 이후 각 동일한 위치의 element끼리 곱해졌다.
(관련하여 numpy의 dot과 matmul에 대해서도 공부하면 좋을듯
관련 링크 : https://jimmy-ai.tistory.com/104)

mean : 평균 계산
다만 1차원 tensor에서는 각 element에 대한 mean value가 나오지만
2차원에서 dimension에 대한 인자를 설정함에 따라 값이 소폭 다르게 나온다.
설명 : dimension을 인자로 주면 해당 차원을 제거한다는 의미라고 한다.

sum : mean과 동일하게 작동하지만 평균이 아닌 합을 구하는 것 뿐. 이하 동일.

max : 원소의 최댓값을 리턴
argmax : 최댓값을 가진 인덱스를 리턴
dimension을 설정하지 않은 상태에서 max를 쓰면 전체 element 중 maximum이 나온다.
하지만 dim=0을 설정하면 위에서 말했듯이 1번째 차원(행)을 삭제하며 (1,2)행렬이 되고 이에 대한 값의 계산은 [3,4]가 된다.
뒤에 딸려나오는 indices(index의 복수형)은 함께 리턴되는 argmax 값이다.

사실 다음과 같이 type을 확인해보니 t.max(dim~)에 대한 값이 tuple이나 다른걸로 나올줄 알았는데 torch관련 class로 정의된 것이 조금 신기했다.
뷰(View) - 원소의 수를 유지하면서 텐서의 크기 변경.
pytorch에서의 view는 numpy에서의 reshape과 같은 역할. -> 텐선의 크기(shape)을 변경해주는 역할.

위 텐서의 크기는 (2,2,3)
3차원 텐서 -> 2차원 텐서로 변경

-1의 경우 pytorch에게 맡기는 의미이며 두번째 차운의 길이는 3이 되도록 한다는 것이다.
reshape처럼 일렬로 배열 후 다시 합친다 생각하면 좋을 것 같다.
만일 [-1,7]의 값을 넣는 경우
RuntimeError: shape '[-1, 7]' is invalid for input of size 12
와 같은 에러가 발생한다. 12의 약수를 넣어야 변환되기 때문이다.


사이즈를 3차원 -> 3차원도 된다.
-1로 두 자리 이상을 pytorch에게 맡기는 것은 안된다.
스퀴즈(Squeeze) & 언스퀴즈(Unsqueeze)

[3,1] 사이즈의 텐서 ft를 정의한 후 squeeze를 사용하면 [3]의 크기를 갖는 텐서로 변경된다.
궁금한 점 : 꼭 맨뒤의 차원을 내리는 것인지.




결론 (예시에 매몰되지말며 글을 잘 읽자)
- 맨 뒤의 차원은 아님. 1인 차원을 없애는 것임.
- 그리고 1인 차원을 제거하는 것임 (글을 잘 읽기.)
- 1인 차원이 없다면 이전과 동일한 텐서 출력
- 1인 차원이 여러개 있다면 다 제거함.

unsqueeze : 특정 위치에 1인 차원 추가
이는 view를 통해 만든 것과 동일한 결과를 만들 수 있음
=> view(), squeeze(), unsqueeze()는 텐서의 원소 수를 그대로 유지하면서 모양과 차원을 조절.
타입 캐스팅 (Type casting)

텐서에는 자료형 존재. CPU 연산이 아닌 GPU 연산을 위한 자료형 또한 존재.
이런 자료형 변환을 하는 것 => 타입 캐스팅

long type => 64bit 정수형
float type => 32bit 실수형 (각 element에 . 붙어있음)
연결(Concatenate)

pandas dataframe concat과 큰 차이가 없는듯.
스택킹(stacking)

처음에 생각했을때는 concatenate과 무슨 차이가 있지 ? 했지만
print된 shape을 보았을때 놀랐다.
이는 각 tensor끼리 쌓으며 하나의 dimension을 추가로 구성한 것이고
concat의 경우 list의 append마냥 그저 붙이기에 불과한 것이었다.
이 또한 dim=0 or 1을 통해 stacking할 방향을 정해줄 수 있다.
ones_like와 zeros_like

이는 numpy array에 zeros나 ones와 동일한 기능을 하는 것 같다.
In-place Operation (덮어쓰기 연산)

[2,2] tensor 하나 만들고 mul(2.)를 통해 element-wise하게 곱한 결과 및 기존 값 출력

다만, mul이 아닌 mul_ 사용시 원래의 값에 덮어쓰기 하며 출력
(이는 dataframe에 대한 연산을 할 때 inplace=True 와 같은 기능인듯하다.)
파이썬 클래스(class)
pytorch의 구현체들은 대부분 class 개념을 애용하고 있어 설명한다고 한다.
다만 본인도 class를 건너건너 배웠기에 이번 기회에 다시 쌓아나가보자.
함수로 덧셈기 구현

(result를 전역 변수로 선언. 다만 실제 사용한적은 정말 오랜만인듯)
함수 외부에서 정의된 result가 함수 내에서도 전역변수로서 정의가 되었고 이 객체에 그대로 더해지는 모습.

만약 두 개의 객체에 대해 계속하여 덧샘기를 구현하려면 다음과 같이 별도의 함수를 계속 독립적으로 만들어주어야함.
클래스(class)로 덧셈기 구현

클래스 생성 후 cal1, cal2라는 각각의 객체 생성.
동일한 method로도 각 객체에 대해 개별적으로 계산되는 모습.
'ML & AI > Pytorch' 카테고리의 다른 글
| [PyTorch] MNIST 손글씨 분류 실습 (3) | 2024.10.26 |
|---|---|
| 순환 신경망(Recurrent Neural Network) (2) | 2024.09.24 |
| [Pytorch로 시작하는 딥 러닝 입문] 3. 머신 러닝 입문하기(Machine Learning Basics) (0) | 2024.09.21 |
| [Pytorch로 시작하는 딥 러닝 입문] 1. 시작 & 딥 러닝을 시작하기 전에 (1) | 2024.09.18 |






















