본문 바로가기

Deep Learning(강의 및 책)/CS231

Lecture 8. Deep Learning Software

728x90

이번에는 Deep Learning software들에 대한 내용입니다. Caffe, Caffe2, TensorFlow, Pytorch 등 여러 software에 대해서 설명을 합니다만 저는 Pytorch에 대해서만 작성하겠습니다.

먼저 GPU와 CPU에 대한 설명을 하시는데 결국 GPU는 수천개의 코어를 이용해 병렬 처리를 합니다. 그래서 행렬 곱, convolution 연산 등 GPU에 특화되어 있다는 말을 하십니다.

 

GPU를 사용하기 위해서 NVIDIA는 GPU에 고도로 최적화시킨 기본 연산 라이브러리를 배포해줍니다. cuDNN의 경우 convolution, forward pass, backward pass, batch norm, rnn 등 딥러닝에 필요한 거의 모든 기본적인 연산들을 제공합니다. CUDA는 NVIDIA에만 작동합니다. OpenCL이 좀 더 범용적입니다. NVIDIA GPU에서만 동작하는 것이 아니라 AMD에서도 그리고 CPU에서도 동작합니다. 하지만 OpenCL은 딥러닝에 극도로 최적화된 연산이나 라이브러리가 개발되지는 않았기 때문에 CUDA보단 성능이 떨어집니다.

 

위 그림은 GPU와 HDD에 위치를 알려줍니다. train time에 disk에서 데이터를 읽어드리는 작업을 세심하게 신경 쓰지 않으면 병목현상(bottle neck)이 발생할 수 있습니다. GPU는 forward/backward가 아주 빠른 것은 사실이지만 디스크에서 데이터를 읽어드리는 과정에서 bottle neck이 일어날 수 있습니다. 이를 해결하기 위해 데이터의 크기가 크지 않다면 데이터를 RAM에 올리는 방법도 있으며 데이터를 읽는 속도가 빠른 SSD를 쓰는 방법도 있습니다. 또 다른 방법은 CPU의 다중 스레드를 이용해 데이터를 RAM에 미리 올려놓고 buffer에서 GPU로 데이터를 전송시키게 되면 성능 향상을 기대할 수 있게 됩니다.

 

우리가 직접 코드를 손으로 작성하지 않고 딥러닝 프레임워크(Pytorch, tensorflow ... )를 이용하는 세 가지 이유가 있습니다. 첫째, 딥러닝 프레임워크를 이용하게 되면 엄청 복잡한 그래프를 우리가 직접 만들지 않습니다. 두 번째, 딥러닝에서는 항상 gradient를 계산합니다. 그리고 loss를 계산하고 loss에 부합하는 weight의 gradient를 계산합니다. gradient가 자동으로 생성되면 훨씬 편하고 좋습니다. 딥러닝 프레임워크를 이용해 forward pass만 잘 구현한다면 back propagation은 알아서 구성됩니다. 마지막 이유로 딥러닝 프레임워크를 이용하면 GPU를 효율적으로 사용할 수 있습니다. 그다음 내용으로 Numpy는 CPU에서만 돌아가기 때문에 tensorflow, pytorch로 구현하는 모습을 보여줍니다.

 

이제 Pytorch에 대해서 설명합니다. pytorch에서 사용하는 tensor, variable, module에 대해서 말해줍니다. tensor는 numpy array처럼 명령형 배열입니다. 이는 GPU에서 사용가능합니다. variable은 그래프의 노드라고 할 수 있습니다. 그래프를 구성하고 gradient 등을 계산할 수 있습니다. module을 이용해서 neural network를 구성할 수 있습니다. module은 다양한 higher level 프레임워크와 유사합니다. 

 

이 부분에서 backward pass가 진행됩니다. numpy코드와 비슷하게 pytorch tensor를 이용해 작성할 수 있습니다. numpy는 GPU에서 안돌아가지만 pytorch는 돌아갑니다.

 

이 코드를 추가해주면 GPU로 연산을 진행됩니다. tensor가 cuda data type으로 생성됩니다.

 

variable은 computational graph를 만들고 gradient를 자동으로 계산하는 등의 목적으로 이용합니다. 여기서 x는 variable이고 x.data는 tensor입니다. x.grad도 variable인데 loss에 대한 gradient를 담고 있습니다. 그래서 x.grad.data에 실제 gradient tensor가 담겨있습니다. pytorch의 tensor와 variable은 같은 API를 공유합니다. 그래서 pytorch tensor로 동작하는 모든 코드는 variable로도 만들 수 있습니다. imperative 한 연산자들이 수행되는 것이 아니라 computational graph를 만듭니다.

 

forward pass의 경우 tensor를 사용했을 때와 완전히 같은 코드를 보이는데 둘이 같은 API를 공유하기 때문입니다. 예측값(y_pred)와 손실(loss)을 계산할 때 이런 식으로 imperative 한 방법을 사용할 수 있습니다. 그 아래 loss.backward를 호출하게 되면 loss에 대한 gradient가 자동으로 계산됩니다.

 

tensorflow는 그래프를 명시적으로 구성한 다음에 그래프를 돌립니다. pytorch의 경우 forward pass 할 때마다 매번 그래프를 다시 구성합니다. pytorch에서는 자동으로 gradient를 계산하는 함수를 정의할 수 있습니다. 위 forward와 backward처럼 따로 정의해주면 됩니다.

 

이렇게 직접 autograd부분을 구현해도 됩니다. 하지만 대부분의 경우 필요한 연산들은 이미 구현이 되어있기 때문에 pytorch에서는 nn.package가 high level wrapper를 제공합니다.

 

이 예시를 보면 nn.package를 이용하는 것을 볼 수 있습니다. 파란색 박스에서 loss function을 정의하는 모습을 볼 수 있습니다.

 

이번에는 optimzer에 대한 내용입니다. optim을 이용해 Adam과 같은 알고리즘을 더 쉽게 쓸 수 있습니다. 이런 식으로 optimizer 객체를 구성해 놓는 것은 모델에게 parameter를 optimize 하고 싶다고 말해준다고 볼 수 있습니다. 마지막 코드인 optimzer.step()을 호출하게 되면 모델 parameter가 updatae 됩니다.

 

전체 network model이 정의되어 있는 class를 nn.module class로 작성해야 합니다. module은 일종의 network layer라고 보면 됩니다. 다른 module이 포함될 수도 있고 학습 가능한 가중치도 포함될 수 있습니다.

 

이 부분을 보면 optimizer를 구성하고 반복문을 돌면서 데이터를 넣어주고 backward로 gradient를 구하고 step으로 update 합니다.

 

pytorch에는 dataloader가 아주 유용합니다. dataloader는 minibatch를 관리합니다. 학습 도중 disk에서 minibatch를 가져오는 일련의 작업들을 multi threading을 통해 알아서 관리해줍니다.

 

pytorch는 pretrained model을 제공합니다. torchvision.models.alexnet(pretrained = True) 이런 식으로 만 작성해주면 pretrained model이 다운로드됩니다.

 

tensorboard와 유사하게 loss에 대한 통계 같은 것들을 시각화해주는 패키지입니다. 하지만 computational graph의 시각화는 제공해주지 않습니다.

이러한 Pytorch는 기존에 존재하는 Torch의 upgrade버전입니다. 

 

Torch에는 autograd가 존재하지 않지만 오래된 만큼 버그도 적고 안정적입니다. Pytorch는 python으로 작성할  수 있고 autograd도 지원해줍니다. 그렇기 때문에 복잡한 모델도 더 쉽게 다룰 수 있습니다. 이러한 pytorch는 dynamic computational graph방식(실행 때마다 graph를 새로 작성하는 방식)입니다. 그로 인해 tensorflow보다 코드도 더 간단하게(기존 python처럼) 사용 가능합니다. 그리고 더 다양하고 복잡한 dynamism을 가진 문제를 해결할 때 매우 효율적입니다.