CS231n - lecture 8: Deeplearning software

Nunucoder·2021년 9월 27일
0

GPU vs CPU

CPU

  • Central Processing Unit
  • 조그마한 칩
    GPU
  • Graphics Processing Unit
  • graphics card
  • 쿨러 소유
  • 파워 소모 심함
  • 크기도 큼

딥러닝의 경우 : NVIDIA
AMD : 딥러닝에 사용하는데 문제 많다.


1. 코어의 개수
CPU 코어수 :코어가 4개에서 6개 조금 많으면 10개 정도

  • 8 ~ 20개의 스레드를 동시에 실행시킬 수 있습니다.
  • CPU의 멀티스레드는 아주 강력합니다.
  • 독립적으로 수행합니다.

GPU의 코어수: 수천개의 코어

  • NVIDIA Titan XP와 같은 최상위 모델의 경우 3840개의 코어
  • 동일 가격의 CPU를 보면 코어가 10개 뿐이 없죠
  • GPU의 단점을 보자면 각각의 코어가 더 느린 clock speed에서 동작한다는 점입니다.
  • 코어들이 그렇게 많은 일을 할 수 없다는 것입니다.
  • CPU와 GPU코어는 1:1로 비교할 수 없습니다.
  • GPU코어들은 독립적으로 동작하지 않습니다.
  • 많은 코어들이 하나의 테스크를 병렬적으로 수행하는 것이죠
  1. 메모리

CPU의 메모리

  • 캐시가 있지만 비교적 작다.
  • CPU는 대부분의 메모리를 RAM에서 끌어다 쓴다

GPU의 메모리

  • GPU는 칩 안에 RAM이 내장되어 있다.
  • 실제 RAM와 GPU간의 통신은 상당한 보틀넥을 초래한다.그래서 GPU는 보통 칩에 RAM이 내장되어 있다.
  • Titan XP의 경우 내장 메모리가 12GB정도 됩니다.
  • GPU는 12GB의 메모리와 GPU 코어 사이의 캐싱을 하기 위한일종의 다계층 캐싱 시스템을 가지고 있다.
  1. 동작방식
    CPU
  • 각 원소를 하나씩만 계산한다.
  • CPU는 여러개의 코어가 있고 Vectorized instructions이 존재한다.

GPU
-알고리즘은 행렬곱(Matrix multiplication) 연산이다.

  • GPU는 결과 행렬의 각 요소들을 병렬로 계산한다.
  • 엄청나게 빠르다.

딥러닝에서 CPU가 GPU보다 많이 느리다는 것을 보여준다.

Train time에 디스크(SSD,HDD)에서 데이터를 읽어드리는 작업을 세심하게 신경쓰지 않으면 보틀넥이 발생할수 있다!!!

GPU는 forward/backward 가 아주 빠른 것은 사실이지만, 디스크에서 데이터를 읽어드리는 것에서 보틀넥 가능성이 있다.

해결방안
1. 데이터셋이 작은 경우에는 전체를 RAM에 올려 놓기

데이터셋이 작지 않더라도, 서버에 RAM 용량이 크다면 가능할 수도 있다.

  1. HDD대신에 SSD를 사용하기

데이터를 읽는 속도를 개선시킬 수 있다.

  1. CPU의 다중스레드를 이용해서 데이터를 RAM에 미리 올려 놓기(pre-fetching)
    buffer에서 GPU로 데이터를 전송시키게 되면 성능향상을 기대할 수 있을 것입니다.

Deep learning frameworks

Tensorflow: 유명해져서 많은 사람들이 선택하는 메인 프레임워크가 되었다.

대부분 PyTorch와 TensorFlow에 대해서 말씀드릴 것입니다.

우리가 굳이 코드를 손으로 작성하지 않고 딥러닝 프레임워크를 이용하는 세 가지 이유

  1. 딥러닝 프레임워크를 이용하게 되면 이처럼 엄청 복잡한 그래프를 우리가 직접 만들지 않아도 된다

  2. 딥러닝에서는 항상 그래디언트를 계산해야 한다 Loss를 계산하고 Loss에 부합하는 가중치의 그래디언트를 계산해야 한다. 프레임워크에선 그래디언트가 자동으로 계산해준다.

  3. GPU을 효율적으로 사용을 위해 써야한다. cuBLAS, cuDNN, CUDA 그리고 memory등을 세심하게 다루지 않아도 된다.

numpy로 구현한 computational graph

  1. 입력으로 X,Y,Z가 있다.
  2. X와 Y를 곱하면 a가 된다.
  3. a와 Z를 더하면 b가 된다.
  4. b를 sum out 연산을 이용해 다 더하면 scaler 값 하나가 나옵니다. = C

Numpy를 이용해서 random data를 입력으로 해서 적절하게 곱하고 더하고 하면 아주 쉽게 구현할 수 있다
BUT X,Y,Z에 대한 그래디언트를 구하는 경우라면 어떨까요?

Numpy로 작성한 경우 문제점

  • backward도 스스로 작성해야 한다.

  • GPU에서 돌아가지 않는다는 것입니다.

Tensorflow의 예

  • Numpy 와 tensor의 computational graph는 동일하다.
  • forward pass에서 Tensorflow 의 코드를 보면 Numpy의 forward pass 코드와 유사하다.

    Tensorflow에는 여러분을 위해 그래디언트를 계산해 주는 마법의 코드 : 직접 backward pass를 작성 할 필요가 없다.


Tensorflow가 좋은 점 중 하나는 명령어 한 줄이면 CPU/GPU에서 동작하도록 전환 시킬 수 있다


PyTorch의 예

여기에 번수를 선언하고 forward pass를 진행합니다.
Numpy에서의 코드와 유사하다는 것을 알 수 있다.


PyTorch에서도 한줄이면 그레디언트를 계산할 수 있습니다.


PyTorch에서 GPU를 사용하려면 CUDA data type으로 선언하기만
하면 된다. GPU에서 연산이 수행된다.

지금까지 세가지 예제 Numpy, Tensorflow, PyTorch

  • TensorFlow와 PyTorch 모두 Numpy 스럽게 생겼습니다.
  • Numpy가 사용하기 쉽기 때문에 Numpy처럼 생긴 것은 좋은 특성이다.
  • Numpy스럽게 생겼을 뿐만 아니라 그레디언트도 알아서 계산해주고 GPU에서도 자동으로 돌아간다.

1. Tensorflow

Tensorflow

  • 두개의 fc layer + ReLU를 학습시키는 네트워크
  • 손실함수로는 L2 Euclidean 사용
  • numpy와 tensorflow를 이미 import

computational graph를 정의하는 코드를 작성할 것입니다.

그래프를 정의하고 나면 그래프를 실행시켜야겠죠 우선 연산을 수행하려면 데이터를 넣어줘야 합니다.
Tensorflow를 사용할 때 아주 일반적인 순서가 되겠습니다.

X,Y,w1,w2를 정의합니다. 이들은 모두 tf.placeholder 객체입니다.

  • 그래프의 입력노드가 됩니다.
  • computational graph의 입구 역할을 하는 것이죠. 가령 데이터를 넣어주고자 하면 이쪽으로 넣어주면 됩니다.
  • 이 부분에서 실제적인 메모리할당이 일어나진 않습니다. 그래프만 구성하는 것입니다.

  • X와 w1의 행렬곱 연산을 수행합니다. 그리고 tf.maximum을 이용해 ReLU를 구현할 수 있습니다.
  • 행렬곱 연산을 한번 더 수행해서 그래프의 최종 출력값을 계산할 수 있습니다.
  • 예측값과 정답값 Y의 유클리디안 거리를 계산하기 위해서도 Tensor operation 을 사용합니다.

한가지 주목할 점은 이 코드는 현재 아무런 연산도 수행하지 않는다는 점입니다. 아직까지 계산할 데이터가 존재하지 않습니다.

그래프 구조만 만들어 놓아서 실제 데이터가 들어왔을때 어떻게 연산을 진행해야 하는지만 구성합니다.
그래프를 구성하는 것 외에 아무 일도일어나지 않습니다.


로스를 계산하고 w1과 w2의 그레디언트를 계산해준다.

Tensorflow session으로 들어가 봅시다. 이를 통해 실제 그래프를 실행시키고 데이터를 넣어줄 수 있습니다


session에 진입하려면 그래프에 들어갈 실제 값을 만들어 줘야 합니다.
대부분의 경우 TensorFlow는 Numpy arrays 이용할 수 있습니다.
이 경우에도 Numpy를 이용해서 x,w1,w2,y의 값을 할당해 줬습니다.


실제로 그래프를 실행시키는 부분입니다.
session.run 을 이용해서 그래프의 일부를 실행시킬 수 있습니다.
첫 번째 인자가 loss라는 것은, 우리가 그래프의 출력으로 어떤 부분을 원하는 지를 말해주는 부분입니다.
예제에서는 우리가 현재 Loss와 grad1과 grad2를 계산하기를 원한다고 말해주고 있는 것입니다.
그리고 feed_dict를 통해 실제 값을 전달해 주는 것이죠

이 한 줄만 실행시키면 그래프가 실행되고 loss와 grad1과 grad2가 계산됩니다. 그리고 출력 값들도 Numpy array입니다.
output이란 변수를 나눠보게 되면 loss와 gradient가 Numpy array의 형태로 반환됨을 알 수 있습니다.


이 경우에는 그래프에서 단 한 번의 forward/backward pass를 수행한 경우 입니다.

네트워크를 학습시키기 위해서는 단 몇 줄이면 충분합니다.
그래프를 여러번 실행시키기 위해서는 for loop를 사용하면 됩니다.
반복적으로 session.run을 호출해 loss와 grad를 계산합니다.
이 예제에서는 가중치를 업데이트하기 위해 그레디언트를 계산해서 "수동으로(manual)" Gradient diescent를 하고 있습니다. 이 코드의 loss를 계산해 보면 loss가 아주 잘 내려가며 이는 네트워크의 학습이 이루어지는 것입니다.

TensorFlow에서 네트워크가 어떤 식으로 학습이 되는지를 아주 명확하게 알려줍니다.

문제: CPU와 GPU의 데이터 교환문제

 forward pass에서 그래프가 실행될 때마다 가중치를 넣어줘야 합니다.
현재 Numpy로 된 가중치를 가지고 있고 이 값을 그래프에 넣어줘야 하는 것이죠
그래프가 한번 실행되고 나면 그레디언트를 반환해 줬습니다. 그레디언트는 가중치와 동일한 크기의 행렬이었죠
이것이 의미하는 바는 여러분이 그래프를 실행할 때 마다 우선 Numpy array로 된 가중치행렬을 Tensorflow로 복사합니다.
그리고 gradient를 계산해서는 gradient행렬을 Tensorflow에서 Numpy array로 반환해 주는 것이죠
만약 코드가 CPU에서 실행이 되고 있다면 큰 문제가 되지는 않을 것입니다.

CPU/GPU 메모리 데이터교환을 상당히 비용이 크다.
네트워크가 엄청 크고 가중치가 엄청 많다면 이런 GPU/CPU간의 데이터 교환은 이는 엄청 느리고 비용이 클 것이다..

해결책 : CPU와 GPU의 데이터 교환문제

In TensorFlow

placeholder를 써서 가중치를 넣어 줄 필요는 없습니다. 대신에 단지 variables로 선언하는 것이죠
variable은 computational graph안에 서식하는 변수입니다. 그래프가 실행될 때 마다 지속적으로 그래프 안에 상주합니다.따라서 w1와 w2를 placeholder 대신에 variables로 선언해 줍니다.
Tensorflow에게 어떻게 초기화시킬 것인지를 알려줘야 합니다.
왜냐하면 기존에 그래프 밖에 있을때는 그래프에 넣어주기 전에 Numpy를 이용해서 초기화 시켜주면 그만이었습니다.

그 변수들을 초기화시킬 권한은 TensorFlow에 있는 것입니다. -> tr.randomnormal 이 필요하다

명령어 자체가 변수들을 초기화시켜 주는 것은 아니고 Tensorflow에게 어떻게 이 값들이 초기화되야 하는지는 알려준다.

가중치를 그래프 안에서 업데이트 하기 위해서는 그 연산 자체가 그래프에 포함되어야 합니다.

이를 위해 assign 함수를 이용합니다. 변수가그래프 내에서 업데이트가 일어나도록 하는 것입니다.

이 업데이트된 값들은 항상 그래프 내부에 존재하게 됩니다.

이제 실제 학습을 진행하기에 앞서 그래프 내부의 변수들을 초기화시켜 주는 명령어(tf.global_variables_initializer() )가 필요합니다.
초기화가 끝나면 이제 그래프를 돌릴 차례입니다.
이 코드를 보시면 이제는 데이터와 레이블만 넣어주면 되고 가중치는 그래프 내부에 항상 상주하게 됩니다.
Tensorflow에게 loss를 계산해 달라고 하는 것이죠
여러분은 이 코드로 네트워크를 학습시킬 수 있다고 생각할 수 있습니다.

이 코드에는 버그가 있다.이 코드를 돌려서 Loss를 그려보면 전혀 학습이 안됩니다. -> why?


학습이 안된이유?

Tensorflow에게 w1과 w2를 업데이트하라고 명시적으로 말해 줘야 한다
그래서 우리는이 큰 계산 그래프 데이터를 구축했습니다.
구조를 메모리에 저장하고 실행을 호출 할 때, 우리는 TensorFlow에게 우리가 손실을 계산하기를 원한다고 말했습니다.

그래프 내부의 dependence를 고려 해 보면 Loss를 계산하기 위해서 굳이 업데이트를 할 필요는
없다는 것을 알게 될 것입니다. Tensorflow는 아주 스마트하기 때문에 output 에 필요한 연산만 수행합니다.
이는 Tensorflow의 장점이기도 합니다. 필요한 부분만 실행하기 때문이지요
하지만 간혹 이 특성이 우리를 헷갈리게 하고 예상하지 못했던 결과를 초래할 수도 있습니다.
이 경우에는 우리가 Tensorflow에게 업데이트를 수행하라고 명시적으로 말해줘야 합니다.


학습이 안됐을시 해결법
1. new_w1과 new_w2 를 출력으로 추가

  • new_w1과 new_w2의 사이즈가 큰 tensor라면은 상황이 안좋아집니다.
  • Tensorflow에게 출력을 요청하는 것은 매 반복마다 CPU/GPU간의 데이터 전송이 요구되기 때문입니다.
  1. 더미 노드(dummy node)하나를 그래프에 추가
  • fake data dependencies를 이용하면 new_w1과 new_w2를 업데이트 할 수 있습니다.
    그리고 그래프를 실행시키면 loss와 더미노드를 계산하게 됩니다.
  • 더미노드는 아무 것도 반환하지 않지만 dependence를 만들었기 때문에 이를 통해 가중치가 업데이트 될 수 있는 것입니다.


optimizer

여기 보시면 tf.train.GradientDescentOptimizer 라는 것을 사용하고 있습니다.
learning rate도 정해줍니다.
여기에 Adam이나 RMSprop같은 다양한 oprimization 알고리즘이 있는 것입니다.
자 이제 optimizer.minimize(loss) 를 호출합니다.

이는 w1과 w2가 학습 가능하다는 것을 인식합니다. optimizer.minimize의 내부를 살펴보면
그래프에 w1와 w2의 그레디언트를 계산하는 노드도 추가하고 그리고 w1와 w2의 update operation도 추가합니다.
assigns을 위한 grouping operation도 있죠 이 같은 많은 일들이 내부적으로 수행됩니다.

이 함수를 들여다보면 tf.group도 들어가 있는 등 이전에 봤던 예제와 유사한 일을 하는 것입니다.

updates
반복문을 살펴보면 매 반복마다loss와 update를 계산해 달라고 하고 있죠
매번 그래프에게 update를 계산하라고 할 때 마다 update가 일어납니다.

tf.losses.mean_squared_error를 사용할 수 있습니다.

이 tensor operations를 사용하면 L2 loss를 직접 구현하지 않아도 됩니다.
그리고 귀찮은 것이 하나 더 있습니다. 입력과 가중치를 정의하고 행렬 곱연산으로 둘을 묶는 그런 일들입니다.
이 예제에서는 bias도 넣지 않았습니다. bias를 넣을라 치면 bias를 또 초기화시켜 줘야 하고
적절한 shape으로 맞춰도 줘야 하고 또 출력 값에 맞춰서 broadcasting이 필요할 수도 있을 것입니다.
bias 하나를 추가하는데도 엄청나게 많은 코드가 필요한 것이죠
이를 일일이 다 작성하는 것은 불편합니다.

여러분이 딥러닝에 들어가는 기본적인 레이어들 가령 convolutions이나 batch norm을 구현하려 치면
입/출력도 선언해 줘야하고 이를 모두 묶어서 computational graph를 만들어줘야 하는데
이의 가중치를 초기화시켜주고 shape를 잘 맞춰주는 일은 정말로 성가신 일입니다.

TensorFlow에서 제공해주는 것중 하나는 tf.layers입니다.

이 예제를 보시면 X와 Y만 placeholders로 선언해 줍니다. 그리고 밑에 줄을 보시면 h = tf.layers라고 선언합니다.
그리고 inputs = x, units = H 를 넣어줍니다. 내부적으로 w1과 b2을 variables로 만들어주고 그래프 내부에
적절한 shape으로 선언해 줍니다. 다만 우리에게는 보이지 않습니다.

그리고 xavier initialize 객체를 사용하여 어떻게 초기화시킬 것인지를 말해줍니다.
기존에는 tf.randomnormal을 사용해서 일일이 초기화시켜 줬었죠
하지만 지금은 이 모든 것들을 알아서 해줍니다. 그리고 h가 출력으로 나옵니다.

다음 레이어를 보면 이전 레이어 출력인 h를 받아서 똑같은 일을 해줍니다.

그리고 여기 activation=tf.nn.relu 를 볼 수 있는데 이를 통해 레이어에 relu를 추가해 줄 수 있습니다.
이처럼 이들은 모두 우리를 대신에서 대부분의 아키텍쳐와 관련된 세부사항들을 다뤄줍니다.

2. Keras


Keras은 아주 훌륭한 API로 TensorFlow를 backend로해서 computational graph를 알아서 만들어줍니다.
Keras는 Theano backend도 지원합니다.


레이어의 시퀀스로 모델을 구성하는 것을 보실 수 있습니다.



optimizer 객체를 만들고 model.compile을 하면 그래프가 알아서 만들어진다.


model.fit을 하게되면 전체 학습과정이 알아서 진행됩니다.

3.Theano


Theano로 작성된 코드가 TensorFlow와 아주 유사하다는 것을 아실 수 있을 것입니다.


변수를 정의



forward pass를 수행


그레디언트를 계산

함수 몇개를 컴파일하고 네트워크를 계속 돌리는 것입니다.


TensorFlow와 아주 유사합니다.

4. Pytorch


Facebook에서 나온 PyTorch는 TensorFlow와는 다릅니다.
세 가지 추상화 레벨

  • tensor : Numpy array와 유사 , 명령형(imperative) 배열,GPU에서 수행

  • variable: 그래프의 노드, 그래프를 구성 , 그레디언트 계산

  • module : Neural network를 구성

PyTorch와 TensorFlow를 간단히 비교해 보자면
PyTorch의 Tensor = Tensorflow의 Numpy array
PyTorch의 variable = Tensorflow의 tensor,variable 또는 placeholder
PyTorch의 module = tf.slim이나 tf.layers 혹은 그 밖에 다양한 higher level 프레임워크

PyTorch에 대해 한가지 명심해야 할 점은 PyTorch가 고수준의 추상화를 이미 내장하고 있습니다.
바로 modules 객체가 그것인데, 때문에 Tensorflow처럼 어떤 모듈을 선택할 지 고민할 필요가 없습니다.

PyTorch tensor로 구성한 2-layer 네트워크를 보실 수 있습니다.
실제 Numpy array를 사용하지는 않고 PyTorch tensor를 이용합니다.

Random data를 선언

forward pass 를 계산하는 연산


backward pass도 직접 구현


그레디언트를 계산하고 Learning rate를 이용해서 가중치를 직접(manual) 계산


PyTorch tensor와 Numpy의 가장 큰 차이점

PyTorch tensor는 GPU에서도 돌아간다는 것

PyTorch tensor = Numpy + GPU



variable에 대해 알아보겠습니다.

variables은 computational graphs를 만들고 이를 통해 그레디언트를 자동으로 계산하는 등의 목적으로 이용합니다.

X는 variable이고 x.data는 tensor 입니다. x.grad도 variable인데 Loss에 대한 그레디언트를 담고 있습니다.
따라서 x.grad.data가 실제 tensor이고 이 안에 그레디언트가 담겨 있습니다.
그리고 PyTorch의 tensors 와 variables는 같은 API를 공유합니다.
PyTorch tensors로 동작하는 모든 코드는 variables로도 만들 수 있습니다.
그렇게 되면 imperative한 연산자들이 수행되는 것이 아니라 Computational graph를 만드는 것이 됩니다.


여기 보시면 variables를 선언할 때 해당 variables에 대한 그레디언트를 계산할 것인지를 지정해 줄 수 있습니다.

forward pass의 경우를 보면 tensor를 사용했을 때와 완전히 같은 코드입니다. 왜냐하면 이 둘은 같은 API이기 떄문이죠
예측값(y_pred)와 손실(loss)를 계산할 때 이런 식으로 imperative한 방법을 사용할 수 있습니다.

loss.backwards 를 호출하게 되면 그레디언트가 알아서 반환이 됩니다.


w1.grad.data의 값을 이용해서 가중치 업데이트를 할 수 있습니다.

그레디언트가 자동으로 계산된다는 것 이외에는 Numpy와 아주 유사합니다.

PyTorch에서는 여러분만의 새로운 "자동으로 그레디언트를 계산하는(autograd)" 함수를 정의할 수 있습니다.

tensors의 형태로 forward와 backward를 정의해주면 됩니다. 이는 여러분이 과제2에서 수행했던 모듈 레이어와
비슷하게 생겼습니다.여러분이 tensor operations을 이용해서 forward/backward 만 구현하면 그래프에 넣을 수 있습니다.여기 예제에서 ReLU를 직접 한번 구현해 봤습니다.
이런 식으로 직접 구현하게 되면 이 ReLU를 computational graph에 적용할 수 있습니다.
하지만 대부분의 경우에 이렇게 직접 autograd를 구현할 필요는 없을 것입니다.
대부분의 경우에 여러분이 필요한 연산들은 이미 구현이 되어있기 때문입니다


Tensorflow의 경우에는 Keras나 TF.Learn과 같은 라이브러리가 higher level API 를 제공해 주었습니다.
PyTorch에서는 그 역할을 "nn package"가 담당합니다. high level wrappers를 제공해 줍니다.
다만 TensorFlow에는 종류가 다양했죠 하지만 PyTorch에서는 단 하나만 있는데 아주 잘 동작하고 쓸만합니다.


Linear/ReLU layer를 model sequence에 추가하는 부분은 Keras와 유사하게 생겼습니다.

그리고 바로 밑에는 nn package에서 제공하는 손실함수를 정의해 줍니다.
여기에서는 mean squared error loss 입니다.

  • 매 반복마다 forward pass를 수행하여 prediction 결과를 얻습니다.(첫 번째 라인)

  • 손실함수를 실행하여 Loss도 구해줍니다.(두 번째 라인)

loss.backward를 호출하면 매 반복시 마다 그래디언트가 저절로 계산이 됩니다.

그리고 모델 업데이트를 위해 gradient descent step을 명시적으로 수행시켜 줍니다.

PyTorch는 forward pass 할 때 마다 매번 새로운 computational graph를 만들어 주는 것입니다.

PyTorch도 optimizer operations를 제공합니다.

가중치 업데이트 부분을 추상화시켜서 Adam 과 같은 알고리즘을 더 쉽게 쓸 수 있습니다.
이런 식으로 optimizer 객체를 구성해 놓는 것은 모델에게 파라미터를 optimize하고 싶다고 말해 주는 것입니다.
learning rate같은 하이퍼 파라미터도 정해줘야 합니다.


그레디언트를 계산하고 난 후에 optimizer.step를 호출하게 되면 모델 파라미터가 업데이트 됩니다.


PyTorch를 사용할 때 여러분들이 아주 흔하게 접하게 될 것은 바로 여러분만의 nn modules을 정의하는 것입니다.

여러분의 전체 네트워크 모델이 정의되어 있는 class를 nn module class로 작성해야만 합니다.
module은 일종의 네트워크 레이어라고 보시면 됩니다. 다른 module이 포함될 수도 있고 학습가능한 가중치도 포함이 될 수 있죠

2-Layer Network 예제를 다시한번 보겠습니다. nn module class로 작성이 되어있는 예제입니다.

이 클래스의 생성자를 보면 linear1과 linear2가 선언이 되어 있습니다.
두 개의 module objects를 여러분의 클래스 안에 저장하고 있는 것입니다.

forward pass에서는 네트워크 출력을 계산하기 위해 앞서 정의한 모듈도 사용할 수 있고 다양한 autograd도 사용할 수 있습니다.

  • forward함수 내부를 한번 들여다보면 입력 x는 variable입니다.
  • 입력 x가 첫 번째 레이어인 self.linear 1을 통과합니다.
  • autograd op을 사용해서 relu를 계산합니다.
  • 그 출력 값이 linear2를 통과해서 다시 값을 출력합니다.

optimizer를 구성하고 반복문을 돌면서 데이터를 넣어주고
backwards로 그래디언트를 구하고 step으로 업데이트하죠
이 예제가 PyTorch로 학습을 하는 경우 가장 일반적인 패턴입니다.

PyTorch에는 dataloader가 아주 유용합니다.
dataloader는 여러분을 위해 minibatches를 관리하죠

학습 도중 Disk에서 minibatches를 가져오는 일련의 작업들을 multi-threading를 통해 알아서 관리해 줍니다.
dataloader는 dataset를 wrapping하는 일종의 추상화 객체를 제공해 줍니다.
실제로 여러분의 데이터를 이용하고자 할 때 데이터를 어떤 방식으로 읽을 것이지를 명시하는 dataset class만 작성해 준다면이 class를 dataloader로 wrapping 시켜서 학습을 시킬 수 있을 것입니다.

여기 보시면 dataloader 객체를 순회하면서 매 반복시 마다 데이터의 minibatch를 적절하게 반환시켜 줍니다.

그리고 내부적으로 data shuffling이나 multithreaded ,dataloading와 같은 것들을 알아서 관리해 주게 됩니다.
지금 보시는 예제가 여러분이 PyTorch 를 사용할 때 가장 많이 보게 될 코드패턴이라고 보시면 되겠습니다.

PyTorch는 pretrained models를 제공합니다.

torchvision.models.alexnet(pretained=true) 라고만 쓰면 끝입니다.

PyTorch는 또한 Visdom라는 패키지를 제공합니다.

Tensorboard와 유사하게 Loss에 대한 통계같은 것들을 시각화해주는 패키지입니다.

Torch와 PyTorch의 주된 차이점

  • Torch는 Python이 아닌 Lua로 작성
  • Torch는 autograd도 제공하지 않습니다.
  • Torch가 더 오래되었기 때문에 안정성이 조금 더 보장되고버그도 적습니다.
  • Torch의 예제코드가 더 많죠
  • 이 둘의 연산 속도도 거의 비슷합니다.
    PyTorch
  • PyTorch는 Python으로 작성할 수 있다
  • autograd도 지원합니다
  • 복잡한 모델을 더 쉽게 다룰 수 있겠지요
  • PyTorch를 사용하는 것이 좀 더 모험적인 일

static graphs VS dynamic graphs (PyTorch와 TensorFlow의 주된 차이점 )

 TF는 두 단계로 나뉩니다. 첫째는 그래프를 구성하는 단계입니다.
그리고 두번째는 이 그래프를 반복적으로 돌리는 단계입니다.

이를 static computational graph라고 합니다.
그래프가 단 하나만 고정적으로 존재하기 때문이죠

PyTorch는 완전히 다른 방식입니다. 매번 forward pass할 때
마다 새로운 그래프를 구성합니다.
이를 dynamic computational graph라고 합니다.
이런 단순한 feed forward neural network의 경우에는 별로 큰 차이점이 없어 보일 수도 있습니다.

static과 dynamic간의 서로 다른 특징과 trade-off에대해서 조금 더 말씀드리고자 합니다.

static graphs의 관점

  • 그래프를 한번 구성해 놓으면 학습시에 똑같은 그래프를 아주 많이 재사용하게 됩니다.
  • static graphs에서는 그 그래프를 최적화시킬 기회가 주어질 수 있는 것입니다.
  • 일부 연산들을 합쳐버리고 재배열시키는 등으로 가장 효율적으로 연산을 하도록 최적화 시킬 수 있는 것이죠.
  • 처음의 최적화작업 자체가 조금 오래 걸릴순 있어도 최적화된 그래프를 여러번 사용한다는 것을 고려해보면
    최적화에 소요되는 시간은 아무것도 아닐 수 있습니다.

EX) 조금 더 구체적인 예를 들어보자면 여기 Conv와 ReLU가 반복되는 모델이 있다고 해봅시다.
이 그래프를 최적화 한다고 했을때 Conv와 ReLU를 합쳐버리는 것을 생각해 볼 수 있을 것입니다.
어짜피 합쳐도 같은 연산을 하는 것이고 대신 코드는 더 효율적으로 실행될 수 있을 것입니다.
확실한 것은 static graphs에서는 그래프를 최적화시킬 수 있는 여지가 주어진다는 것입니다


dynamic graph의 경우에는 "그래프 구성" 과 "그래프 실행" 하는 과정이 얽혀 있기 때문에(interleaving)
모델을 재사용하기 위해서는 항상 원본 코드가 필요합니다.

하지만 dynamic graphs의 장점은 대다수의 경우에 코드가 훨씬 더 깔끔하고 작성하기 더 쉽습니다.
가령 조건부 연산(if 문)을 해야하는 상황을 가정해 봅시다. 변수 z의 값에 따라서 y값이 달라지는 경우입니다.
Z가 양수면 w1x 연산을, 음수면 w2x 연산을 수행해야 하죠
조건부 연산을 사용해서 이 두 가지 연산을 오가야 하는 상황입니다.

PyTorch에서는 dynamic graphs를 사용하므로 이 문제를 다루기 아주 수월합니다.
Numpy와 아주 유사합니다. 이 문제를 다루데는 일반적인 Python if문을 사용하면 그만이죠
dynamic graphs의 경우에는 매번 새로운 그래프를 구성하기 때문에 매번 이 두 가지 선택지 중에 현재 forward pass에 적절한 하나를 선택해서 새로운 그래프를 만들어주면 그만입니다.
새롭게 구성된 그래프로 backporb에도 아무 문제가 없겠죠

TensorFlow에서는 조건부 연산을 넣기 조금 더 복잡합니다. 왜냐하면 우선 그래프를 하나 만들어 놔야 합니다.
그래프 내에 조건부 연산을 명시적으로 정의하는 Control flow operator를 추가해야만 합니다.
Tensorflow의 예제에서는 tf.cond라는 것을 보실 수 있습니다. Tensorflow버전의 if문 이라고 보시면 됩니다.
Python의 if문을 쓰는 것 대신에 이런 식으로 그래프 내에 control flow자체를 넣어줘야 하는 것입니다.
Tensorflow에서는 그래프가 단 한번만 만들어지기 때문에 가능한 모든 control flow를 미리 고려해서
그래프 내에 한번에 넣어줘야만 합니다. 그래프를 실제로 실행시키기 전에 말이죠
Control flow를 위해서는 간단한 python 문법으론 불가능하고 반드시 특수한 Tensor Flow 연산자가 필요하다는 것입니다.

지금 예제의 경우에는 tf.cond이 되겠습니다.

조건부 연산과 유사한 상황이 반복연산(loop) 에서도 발생합니다.

가령 어떤 재귀적인 연산을 한다고 해봅시다. Y_t = (Y_t-1 + X) * W 같은 경우죠
이런 연산을 수행할 때 우리의 데이터 sequence는 다양한 사이즈일 수 있습니다.
데이터의 sequence 길이가 얼마인지 신경쓰지 않고 재귀연산을 할 수 있다면 좋겠죠
PyTorch의 경우에 엄청나게 간단합니다. Python에서 제공하는 기본 for loop를 이용하면 끝입니다.
그리고 이는 backprob에도 지장이 없습니다.

TensorFlow에서는 이 과정이 아주 지저분합니다.

Tensorflow에서는 그래프를 앞에서 미리 만들어줘야 하기 때문에 그래프에 명시적으로 loop를 넣어줘야만 합니다.
여러분이 함수형 프로그래밍을 잊지 않고있길 바랍니다. 왜냐하면 TensorFlow에서 looping을 구현하려면 반드시 필요하기 때문이죠 이 경우에 특정 재귀적인 관계를 정의하기 위해서는 tf.foldl연산을 사용해서 구현해볼 수 있습니다.
TensorFlow에 대해 기본적으로 말씀드리고 싶었던 것은 TensorFlow를 사용하게 되면 여러분들은 반드시
computational graphs의 "전체 흐름" 을 여러분들이 전부 다 구성해 놔야 한다는 것입니다.
그래프에 필요한 모든 control flow연산 그리고 모든 데이터 구조 등을 전부다 구현해 놔야 합니다.
따라서 Tensorflow에서는 Python의 명령어들을 같이 활용할 여지가 별로 없습니다.
TF에서 필요한 control flow 연산들을 전부 다시 배워야만 합니다. TensorFlow에서 그것을 사용해야 한다면 말이죠.

TensorFlow Fold라는 TF 라이브러리가 있습니다.TensorFlow에서 dynamic graphs를 작성하게 해줍니다.

TensorFlow Fold가 dynamic graph를 만들어주는 것처럼 보이지만 사실 static graph로 만든 트릭입니다.


dynamic graph의 사용을 고려해 볼만 한 것일까요?

recurrent networks :image captioning의 경우, 이는 다양한 길이의 sequences를 다루기 위해 RNN을 이용합니다


dynamic graph의 사용을 고려해 볼만 한 것일까요?

recursive networks : 가령 자연어 처리 분야에서 문장을 파싱하는 문제에서 트리를
파싱하기 위해 recursive한 네트워크가 필요

dynamic graph의 사용을 고려해 볼만 한 것일까요?

Neuromodule

"이미지"와 "질문"을 던지면 적절한 답을 하는 것이죠. 이 이미지에는 고양이와 강아지가 있군요
"고양이의 색은?" 과 같은 질문을 던지면 이 문제를 풀기에 적합한 네트워크 를 구성합니다. 여기에선 "색" 과 "고양이" 를 찾게 되겠죠

질문이 주어지면 그 질문에 답하기 위한 적절한 네트워크 (custom)를 구성하는 것입니다.

"고양이가 강아지보다 많은지?" 와 같은 질문을 한다면 어떻까요?
이 경우에도 이전과 같이 "고양이"와 "강아지" 를 찾는 네트워크가 들어갈 테지만 조금은 구성이 달라질 것입니다.
여기에서 dynamism이 적용될 것입니다. 문제가 달라지면 computation graph도 다르게 구성되겠죠

5. Caffe


Caffe에 대해서 간단하게 말씀드려보겠습니다.Berkeley에서 처음 만든 프레임워크입니다.

Caffe는 다른 딥러닝프레임워크랑은 다소 다릅니다. 많은 경우에 코드를 작성하지 않아도 네트워크를 학습시킬 수 있죠
기존에 빌드된 바이너리를 호출하고 Configuration 파일만 조금 손보면 굳이 코드를 작성하지 않아도 데이터를 학습시킬 수 있습니다.


caffe traininng 순서

우선 데이터를 HDF5 또는 LMDB포맷으로 변환시켜 줍니다.
Caffe에서 지원하는 스크립트를 이용하면 됩니다. 폴더안에 저장된 이미지를 적절한 포맷으로 변환시켜줍니다.

Caffe에서는 그래프를 구성하려면 코드를 작성하는 대신에 prototxt라는 텍스트파일을 만들어야 합니다.


여기 구조를 보시면 입력을 HDF5 파일로 받는 부분도 있고 내적을 하는 부분도 있습니다. 이런 형식으로 그래프를 구성합니다


이런 방식은 네트워크의 규모가 커지면 상당히 보기 안좋다는 단점이 있습니다.

가령 152레이어의 ResNet 모델을 Caffe로 훈련시키려 하면 prototxt 파일은 거의 7000줄에 이르게 됩니다


Caffe는 optimizer나 solver를 정의하기 위한 또 다른 prototxt 파일을 작성해야 합니다.
learning rate와 optimiation algorithm 같은 것들을 정의해 줍니다.

이런 식의 작업을 모두 마치고 Caffe의 바이너리만 실행시키면 학습이 자동으로 수행됩니다.

Caffe에도 아주 다양한 pretrained model을 제공하는 model zoo가있습니다.


Caffe는 Python 인터페이스를 지원하긴 하지만 문서화가 잘 되어있는 편은 아닙니다.

python 인터페이스와 관련된 소스코드도 있긴 하지만 상당히 불편합니다.

Caffe의 경우에는 feed forward 모델에 적합한 편입니다.
그리고 production의 측면에 적합하다고 볼 수 있습니다.

왜냐하면 Python에 의존적이지 않기 때문이죠

하지만 오늘날 Caffe는 research의 목적으로는잘 사용하지 않습니다.

-->Caffe는 industry에서 production 개발을 위해서 여전히 많이 사용합니다.

6. Caffe2


Caffe 2를 Caffe의 다음 버전입니다. Facebook에서 만들었죠

Caffe2도 TensorFlow처럼 Static graph를 사용합니다.
Caffe처럼 코어는 C++로 작성되어있고 Python 인터페이스도 제공합니다.
Caffe1과 다른점이 있다면 더이상 prototxt 파일을 만들기 위해 Python 스크립트를 작성할 필요가 없다는 것입니다.
Tensorflow 스럽게 Python으로 그래프를 작성할 수 있습니다.
그리고 이 그래프를 prototxt 파일로 변환시켜 줄 수 있습니다.

모델을 한번 훈련시켜 놓게 되면 앞서 Static 그래프의 이점에 대해서 말씀드렸듯이 기존의 소스코드 없이도 학습시킨 모델을 쓸 수 있는 것입니다.

Google 과 facebook framework model difference

Facebook의 경우

  • PyTorch와 Caffe2
  • PyTorch는 연구(research)에 특화되어 있습니다.

연구 사이클을 상당히 단축하도록 만들어졌습니다.

  • 연구에는 PyTorch가 쉽지만 제품(production)개발을 위한 지원이 그닥 많지 않습니다.

대신 Caffe2가 그 역할을 담당하는 것입니다.

Google의 경우

  • 딥러닝이 필요한 모든 곳에서 동작하는 프레임워크를 만들고싶어 합니다.

통합된 프레임워크 하나를 만들고 올인하는 것이죠
TF하나로 distributed systems, production, deployment, mobile, research를 다 커버하도록 말이죠

주관적인 의견을 말씀드리자면

  • Tensorflow는 어떤 환경에서든 잘 동작하는 만능 프레임워크이다.

하지만 여러 higher level wrapper를 섞어 써야 하거나 Dynamic graph가 필요한 경우 곤란해 질 수도 있습니다.
Tensorflow의 Dynamic 그래프 코드가 조금 지저분하기 떄문이죠

  • PyTorch가 research에는 아주 유용하다고 생각합니다.

하지만 나온지 얼마 안되서 커뮤니티 규모가 작고
찾을만한 코드도 적습니다. 모험을 해야할 수도 있습니다.
이미 사람들이 잘 다져놓은 길을 가고 싶다면Tensorflow가 더 좋은 선택일 수 있습니다.

-여러분이 제품을 배포하려는 목적이라면 Caffe, Caffe2 또는 Tensorflow를 선택해야 합니다.

특히 모바일 디바이스라면 Tensorflow와 Caffe2가 지원하는 것으로 알고 있습니다.

불행하게도 무조건 이 프레임워크가 최고다 라는 것은 없습니다.
선택은 여러분의 문제의 성격에 달려있는 것입니다.

profile
집에가고싶다

0개의 댓글