rtx 30xx번대 ampere 아키텍처 정식 지원 pytorch 1.7.1(stable) 버전이 나왔다.

 

기존 1.7.0 버전에서는 30xx번대에서 jit 오류를 뿜어댔으나

 

나를 포함한 다른 유저들의 열화와 같은 성원에

 

nightly 버전(1.8)부터 해결되더니 드디어 stable(1.7.1) 버전도 나왔다.

 

아마 기존에는 anaconda의 cuda 버전이 낮아서 안되지 않았나 싶다.

 

각설하고, 윈도우 환경에서 pytorch 1.7.1 + cuda 11.0 + cudnn 8.0.5로 기존 코드들 깔끔하게 동작한다.

 

30xx번대 그래픽 카드 사용자들은 pytorch 업데이트 해서 사용해보자.

'머신러닝 > Pytorch' 카테고리의 다른 글

define the optimizer and the learning rate schedule  (0) 2021.01.06
visualize the filters of the first CNN layer  (0) 2021.01.06
store best weights  (0) 2021.01.04
Storing and loading models  (0) 2020.12.31
model summary  (0) 2020.12.31

앞서 ubuntu에 pytorch 환경을 설정했지만 SSH 콘솔을 통해 코딩 및 디버깅을 하기엔 어려움이 따른다.

 

그래서 구글링을 통해 pycharm pro에서 SSH interpreter를 활용하는 방법을 찾았다.

 

그러니까 뭔말이냐면,

 

내 컴퓨터가 너무 구리거나

 

혹은 너무 좋아서 (RTX 3000번대.. 현재 pytorch에서 암페어 아키텍처를 정식 지원 안해서 못쓴다고 보면 된다, github보면 어거지로 컴파일해서 돌리는 친구들 있는데 속도 안나온다, 괜히 고생하지 말자)

 

딥러닝 개발환경이 안된다면,

 

server의 컴퓨팅 자원을 활용해서 학습도 하고 코딩도 하고 싶은데,

 

디버깅이 편하려면 pycharm을 써야되는데,

 

ssh 콘솔 접속을 통해서 pycharm을 어떻게 써야하나 고민중에,

 

이를 해결할 수 있는 방법이란 얘기이다.

 

왜 pycharm pro를 써야하냐면 pro만 SSH interpreter 기능을 제공한다...

 

참고로 학생일 경우 pro가 1년간 무료로 제공된다.

 

물론 졸업했더라도 학교 이메일만 가지고 있으면 된다.

 

학교 이메일을 통해 계정을 activate할 수 있다.

 

(참고로 본인은 학교 메일로 윈도우와 오피스365를 이용하면서 감사함을 느끼고 있다)

 

www.jetbrains.com/ko-kr/community/education/#students

 

Free Educational Licenses - Community Support

Learn or teach how to code with best-of-industry tools from JetBrains. Free Educational Licenses for JetBrains' tools.

www.jetbrains.com

pycham pro를 실행하고 settings -> project interpreter -> add python interpreter -> SSH interpreter

 

host 및 username을 입력하자.

 

warning 창이 뜨지만 yes를 클릭하고 넘어가자.

 

password를 입력하자.

 

interpreter 폴더를 지정해야하는데 본인은 가상환경을 생성해서 진행하기 때문에

 

anaconda3/envs/venv_name/bin/python3

 

위의 경로로 지정했다.

 

그리고 path mappings에서 local 폴더 및 server 폴더를 지정한다.

 

deployment를 통해 파일 동기화를 수행하는 경로를 지정하는 것이다.

 

설정을 잘 맞췄다면 local에 있는 폴더 및 파일이 서버에 업로드되는 것을 확인할 수 있다.

 

pycharm pro에서 terminal 이용도 가능하다.

 

tools -> start SSH session -> 위의 설정한 host 선택

 

이로써 원격으로 코딩하고 디버깅할 수 있는 환경이 마련됐다.

 

하지만 실사용을 해보니 단점이 명확했다.

 

local의 dataset이 server에도 있어야하니 업로드 시간이 필요했고

 

반대로 추론 결과 및 weight 파일이 server에 저장되니 local로 다운로드가 필요했다.

 

역시 뭐든 local에서 돌리는게 가장 좋은 환경인거 같다.

 

pytorch에서 암페어 아키텍처를 지원하면 서버는 안쓸거 같긴하다.

 

서버 성능이 그렇게 좋은것도 아니고 (1080TI)

계속 windows에서만 작업하다가 ubuntu에서 작업할 일이 생겨서

 

부랴부랴 구글의 도움을 받아 세팅을 완료했다.

 

이전에도 ubuntu에서 작업했지만 좀 오래되서 다 까먹음..

 

서버를 제공받은터라 putty를 통해 세팅을 진행했다.

 

또한 cuda 10.1과 cuDNN 7.6.5로 version을 정한 이유는

 

google colab pro 및 pytorch를 사용중인데

 

거기서 사용하는 version이기 때문이다. (구글 형님 믿고 갑니다)

 

ubuntu는 설치되어있다고 가정하에 cuda 설치를 진행한다.

 

cuda 설치시 driver가 설치되기 때문에 따로 graphic driver를 설치하지 않아도 된다.

 

- 기존 driver 및 cuda가 설치되어 있을 시 제거

$sudo apt-get purge nvidia* && sudo apt-get autoremove && sudo apt-get autoclean && sudo rm -rf /usr/local/cuda*

 

- cuda 10.1 update 2 다운로드 및 설치

 

developer.nvidia.com/cuda-10.1-download-archive-update2?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1804&target_type=runfilelocal

 

CUDA Toolkit 10.1 update2 Archive

Select Target Platform Click on the green buttons that describe your target platform. Only supported platforms will be shown. Operating System Architecture Distribution Version Installer Type Do you want to cross-compile? Yes No Select Host Platform Click

developer.nvidia.com

위의 링크로 가면 Installation Instructions에 따라 다운로드 받고 설치할 수 있다.

 

- 설치가 잘 됐는지 version check

 

nvidia-smi or nvcc --version

 

- cuDNN 7.6.5 설치

nvidia 계정을 생성 및 로그인을 통해 cuDNN 7.6.5를 다운받자.

 

linux용으로 확장자가 solitairetheme8 라는 이름으로 되어있다.

 

아래 명령어로 압축을 해제하자.

$tar -xzvf cudnn-10.1-linux-x64-v7.6.5.32.solitairetheme8

그리고 아래의 명령어로 cudnn을 등록?하자.

$sudo cp cuda/include/cudnn.h /usr/local/cuda/include
$sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
$sudo chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*

- anaconda 설치

 

www.anaconda.com/products/individual

 

Anaconda | Individual Edition

Anaconda's open-source Individual Edition is the easiest way to perform Python/R data science and machine learning on a single machine.

www.anaconda.com

위의 링크에서 linux용으로 다운로드 받고 bash쉘로 실행시켜서 설치하자.

$bash ./Anaconda3-2020.07-Linux-x86_64.sh

 

- pytorch 설치

 

cuda 10.1용 pytorch를 설치하자.

$conda install pytorch torchvision torchaudio cudatoolkit=10.1 -c pytorch

 

- python을 실행해서 torch에서 cuda 설치가 잘 됐는지 확인하자.

import torch
torch.cuda.is_available()

설치에 이상이 없으면 True 값이 반환될 것이다.

machine learning을 위해서는 dataset을 train/val/test set으로 분할할 필요가 있다.

 

이유는 cs231n 강의 초반에 자세하게 나오지만,

 

간략하게 정리하면

 

training의 평가를 위함이다.

 

generization이 잘되야 좋은 model이라고 볼 수 있다.

 

본인은 보통 train/val/test의 비율을 75/15/10으로 분할한다.

 

그리고 class imbalance를 피하기위해 각 class별 data 비율도 중요하고

 

또 하나는 random으로 sampling을 해야한다는 것이다.

 

기존에는 dataset을 하나의 폴더에 모아놓고

 

powershell 명령어를 통해 분할했었다.

Get-ChildItem SomeFolder | Get-Random -Count $x | Move-Item -Destination SomeOtherFolder

SomeFolder가 dataset 폴더가 되고 $x에 빼내려는 이미지 개수, SomeOtherFolder가 val or test 폴더를 지정해서 분할했었다.

 

단점은 전체 이미지의 개수를 이용해서 비율대로 개수를 계산해서 수행해야한다는 것이다.

 

예를 들어, 전체 dataset이 1,000 장이면, val를 15%로 설정할 시에는 150을 $x에 계산해서 넣어줘야한다.

 

처음에 이런 식으로 진행했다가 최근 검색을 통해 손쉬운 방법을 찾았다.

 

python library를 활용하는 것이다. (파이선 짱짱맨)

pip install split-folders

일단 설치한다.

import splitfolders
splitfolders.ratio('input folder', output="output folder", seed=1337, ratio=(0.75, 0.15, 0.1))

'Data' 에는 전체 이미지가 모아져있는 폴더를 지정한다.

 

'output' 에는 출력 폴더를 지정한다.

 

위의 코드를 실행시키면 출력 폴더에 train/val/test folder가 생성된다.


2020-11-30 추가

쓸일이 있어서 해보니까 안되서

 

왜 안되나 도큐먼트 찾아보니까

 

input 폴더에 class 폴더가 또 있어야 된다(...)

 

그래서 그냥 폴더 하나 만들어서 split 하고

 

파일 옮기는걸로 했다.

 

그래도 편하긴 하네!

Part 1

part 1에서는 활성 함수, 데이터 전처리, 가중치 초기화, batch normalization, 학습 과정, 하이퍼파라미터 최적화를 다룬다.

 

먼저 activation functions을 살펴보자.

 

Neural Networks

input으로 데이터 입력이 들어오고 가중치와 곱한다. (행렬 내적)

 

이는 FC 혹은 CNN이 될 수 있다.

 

이후 활성 함수(activation function), 즉 비선형 함수를 통과하게 된다.

 

Activation Functions

위의 그림은 다양한 활성 함수들의 예시를 보여준다.

 

먼저 sigmoid를 살펴보자.

 

The problem of Sigmoid Function

sigmoid는 입력을 받아서 0~1 사이의 값이 되도록 한다.

 

입력이 크면 1에 가깝고, 입력이 작으면 0에 가깝게 된다.

 

0 근처의 구간을 보면 선형 함수와 가까워 보인다.

 

이는 값이 적당할때 back-propagation이 잘될거라고 알 수 있다.

 

sigmoid는 역사적으로 유명하다.

 

왜냐하면 neuron을 firing rate를 saturation 시키는 것으로 해석할 수 있기 때문이다.

 

어떤 값이 0에서 1사이의 값을 가지고 있으면,

 

이를 firing rate로 생각할 수 있다.

 

생물학적으로는 0 보다 큰 값을 그대로 활성화 시키는 relu가 더 타당성이 크다는게 밝혀졌지만,

 

sigmoid 또한 firing rate를 saturation 시킨다는 관점에서 해석할 수 있다.

 

하지만 몇가지 문제점이 있다.

 

첫번째는 saturation 되는 것이 gradient를 없애는 것이다.

 

Sigmoid Function

입력 x에 -10을 대입하고 sigmoid function을 통과하면 backprop에서 gradient는 어떻게 될까?

 

우선 dL / dSigma와 dSigma/dx의 곱이 sigmoid function의 gradient가 된다. (chain rule)

 

이렇게 계산된 gradient는 계속 아래로 흐르며 backprop 될 것이다.

 

그럼 입력 x가 -10일 때 gradient는?

 

0에 한없이 가깝게 된다.

 

sigmoid에서 음의 큰 값은 sigmoid가 평평하게 되고 gradient는 0에 가깝게 된다.

 

이렇게되면 0에 가까운 값이 계속 아래로 흐르며 backprop 될 것이다.

 

입력 x가 0이 면 어떻게 될까?

 

이 구간은 backprop가 잘 동작될 것이다.

 

그럴싸한 gradient를 얻을 수 있다는 말이다.

 

그럼 입력 x가 10이면 어떻게 될까?

 

x값이 양의 큰 값일 경우에도 sigmoid가 평평하게 되고 gradient가 0에 가깝게 된다.

 

The problem of Sigmoid Function

두번째 문제는 sigmoid 출력이 zero centered 하지 않는 점이다.

 

Consider what happens when the input to a neuron (x) is always positive?

neuron의 입력이 항상 양수일때 어떻게 될까?

 

입력 양수 x는 가중치와 곱해지고 활성함수를 통과할 것이다.

 

이때 가중치 w에 대한 gradient를 생각해보자.

 

우선 dL/df(activation function)를 계산해서 loss가 아래로 흐를 것이다.

 

그리고 아래에서 (기본적으로 x라고 하자) local gradient가 있을텐데,

 

모든 x가 양수라면 해당 gradient는 '전부 양수' 혹은 '전부 음수'가 된다.

 

Always all positive or all negative

위에서 흘러온 dL/df(activation function)의 값이 양수 또는 음수가 될 것이다.

 

어떤 임의의 gradient가 흘러왔다고 가정하면,

 

우선 local gradient는 흘러온 값과 곱해질 것이고

 

df(activation function)/dw는 단지 x가 된다.

 

그렇게되면 gradient의 부호는 위에서 흘러온 gradient의 부호와 같아지게 된다.

 

이는 가중치 w가 모두 같은 방향으로만 움직일것이라는 것을 의미한다.

 

파라미터가 업데이트될 때 전부 증가하거나 전부 감소하게 된다.

 

이러한 gradient update는 굉장히 비효율적이다.

 

위의 그림 오른쪽은 w의 이차원 예제이다.

 

w에 대한 두개의 축이 있다.

 

전부 양수 혹은 음수로 update된다면 gradient가 이동할 수 있는 방향은

 

4분면 중 2개의 영역뿐이 되질 않는다.

 

2분면과 3분면의 방향으로만 업데이트 된다.

 

위의 오른쪽 그림에서 가장 최적의 w 업데이트가 파란색 화살표라고 가정하면,

 

초기 시작점(첫 빨간색 화살표)부터 업데이트한다고 했을 때,

 

파란색 방향으로 잘 내려갈 수 없게 된다.

 

때문에 여러번의 업데이트를 수행하게 된다. (빨간 화살표)

 

이것이 zero-mean data가 필요한 이유가 된다.

 

입력 x가 양수와 음수 모두 가지고 있다면,

 

전부 같은 방향으로 업데이트되는 일이 발생하지 않을 것이다.

 

Activation Functions

세 번째 문제는 exponential 함수로 인해 계산 비용이 크다는 점이다.

 

하지만 이 문제는 그리 큰 문제가 아니다.

 

비용 측면에서 보면 내적과 같은 연산의 비용이 더 크다.

 

굳이 문제를 뽑자면 exp() 비용을 꼽는다는 것이다.

 

tanh

두 번째 활성 함수는 tanh이다.

 

sigmoid와 유사하지만 범위가 -1~1 사이의 값을 가진다.

 

가장 큰 차이점이라면 zero-centered라는 것이다.

 

이를 통해 sigmoid의 두 번째 문제가 해결된다.

 

하지만 saturation되기 때문에 여전히 낮거나 높은 값에서의 gradient가 0에 가깝게 되버린다.

 

tanh는 sigmoid보다는 조금 낫지만 그래도 위처럼 여전히 문제점이 있다.

 

RuLU

이제 ReLU를 살펴보자.

 

이전에 봤던 CNN 모델들을 보면 conv layer 사이 사이에 ReLU가 있는 것을 볼 수 있었다.

 

ReLU 함수는 f(x) = max(0,x) 이다.

 

이 함수는 element-wise 연산을 수행하면서 

 

입력이 0 미만의 음수면 출력이 0이 된다.

 

그리고 입력값이 양수면 출력은 입력값 그대로를 출력한다.

 

기존 sigmoid와 tanh에게 있었던 문제점들을 ReLU에서 살펴보면,

 

우선 양수 값에서는 saturation되지 않는 것을 알 수 있다.

 

이는 적어도 입력 공간의 절반은 saturation되지 않을 것이며 ReLU의 가장 큰 장점이다.

 

그리고 계산 효율이 뛰어나다.

 

sigmoid는 계산 수식에 지수 항이 있었다.

 

반면 ReLU는 단순 max 연산이기 때문에 계산이 상대적으로 빠르다.

 

ReLU를 사용하면 실제로 sigmoid와 tanh 보다 수렴속도가 거의 6배 정도로 빠르다.

 

그리고 위에 언급했지만 생물학적 관점에서도 ReLU가 sigmoid보다 좀 더 알맞다.

 

실제 신경과학적 실험을 통해 neuron을 관찰해보면

 

sigmoid 보다는 ReLU스럽다는 것이다.

 

2012년 ImageNet에서 우승한 AlexNet에서 처음 ReLU를 사용하기 시작했다.

 

ReLU

하지만 zero-centered한 문제를 해결하지는 못했다.

 

또한 양수에서는 saturation되지 않지만 음수에서는 그렇지 못하다.

 

ReLU

입력 x가 -10일 경우 어떻게 될까?

 

gradient는 0이 된다.

 

입력 x가 10일 경우는 linear한 영역에 속하기 때문에 그럴싸한 gradient가 계산될 것이다.

 

x가 0일때의 gradient는 0이 된다.

 

기본적으로 ReLU는 gradient의 절반을 죽이는 것이다.

 

그래서 dead ReLU라는 현상을 겪을 수 있다.

 

Hyperplane on Training Data

위의 그림은 training data를 2차원 초평면 공간에 나타낸 그림이다.

 

여기서 ReLU는 평면의 절반만 activatite 하는 것을 알 수 있다.

 

ReLU가 training data에서 떨어져 있는 경우 dead ReLU가 발생할 수 있다.

 

dead ReLU에서는 activate가 일어나지 않으며 업데이트 되지 않는다.

 

반면에 activate ReLU에서는 일부 activate되고 일부 activate하지 않을 것이다.

 

몇 가지 이유로 이런 일이 발생할 수 있다.

 

첫 번째는 초기화가 잘못됐을 경우이다.

 

가중치 평면아 training data에서 멀리 떨어져 있는 경우이다.

 

이런 경우 어떠한 입력에서도 활성화되지 않을 것이고 backprop이 일어나지 않을 것이다.

 

업데이트도 활성화도 되지 않는다.

 

또 하나의 경우는 learning rate가 지나치게 높은 경우이다.

 

처음에는 적절한 초기화 인해 적절한 ReLU로 시작할 수 있다고 해도

 

업데이트를 지나치게 크게 해서 가중치가 널뛴다면

 

ReLU는 데이터의 manifold를 벗어나게 된다. (ReLU가 데이터의 차원을 벗어났다고 생각하면 될듯)

 

위의 일들은 학습 과정에서 충분하게 일어날 수 있다.

 

그래서 처음에는 학습이 잘 되다가 갑자기 안되는 경우가 생기는 것이다.

 

그리고 실제로 학습이 된 네트워크를 살펴보면

 

10~20% 가량은 dead ReLU가 되어 있다.

 

ReLU를 사용하면 대부분의 네트워크가 이 문제를 겪을 수 있다.

 

하지만 이 정도는 네트워크 학습에 크게 지장이 없다고 한다.

 

Initialize ReLU with bias

그래서 실제로 ReLU를 초기화할 때 positive biases(0.01정도)를 추가해 주는 경우가 있다.

 

가중치 업데이트 시 activate ReLU가 될 가능성을 조금이라도 더 높혀주기 위함이다.

 

하지만 이 positive biases가 도움이 된다는 의견도 있고 그렇지 않다는 의견도 있다.

 

보통은 zero-bias로 초기화한다.

 

Leaky ReLU

ReLU 이후에 살짝 수정된 Leaky ReLU가 나왔다.

 

ReLU와 유사하지만 negative 영역이 더 이상 0이 아니다.

 

negative에도 기울기를 살짝 주게되면 앞의 문제를 상당 부분 해결할 수 있다.

 

leaky ReLU의 경우 음수의 경우에도 saturation되지 않는다.

 

여전히 sigmoid와 tanh 보다 수렴을 빨리 할 수 있다.

 

또한 dead ReLU 현상도 사라진다.

 

또 다른 예시로 parametric rectifier, PReLU가 있다.

 

PReLU는 negative 영역에 기울기가 있다는 점에서 leaky ReLU와 유사하지만

 

기울기를 가중치 a(알파) 파라미터로 결정된다.

 

가중치 a는 정해진것이 아니며 backprop을 통해 학습이 된다.

 

activation function이 좀 더 유연해 질 수 있다.

 

ELU라는 것도 있다.

 

ELU

ELU는 ReLU의 이점을 그대로 가져온다.

 

하지만 ELU는 zero-mean에 가까운 출력값을 보인다.

 

zero-men에 가까운 출력은 leaky ReLU 및 PReLU가 가진 장점이였다.

 

하지만 leaky ReLU와 비교해보면 ELU는 negative영역에서 saturation된다.

 

ELU는 이런 saturation이 noise에 강인할 수 있다고 한다.

 

이런 deactivation이 좀 더 강인함을 줄 수 있다고 논문에서 주장한다.

 

ELU는 ReLU와 leaky ReLU의 중간정도라고 보면 된다.

 

ELU는 leaky ReLU처럼 zero-mean의 출력을 갖지만 

 

saturation 관점에서 ReLU의 특성 또한 지니고 있다. (negative 영역)

 

Maxout이라는 활성화 함수도 있다.

 

Maxout

무려 Generative Adversarial Network(GAN)의 창시자 이안굿펠로우가 2013년에 논문을 낸 내용이다.

 

dot product의 기본적인 form을 미리 정의하지 않는다.

 

대신 w1에 x를 내적한 값 + b1과 w2에 x를 내적한 값 + b2의 최대값을 사용한다.

 

maxout은 이 두 함수 중 최대값을 취한다.

 

maxout은 ReLU와 leaky ReLU의 좀 더 일반화된 형태이다.

 

왜냐하면 maxout은 위 두 개의 선형함수 중 최대값을 취하므로

 

선형이기 때문에 saturation되지 않으며 gradient를 잘 계산할 것이다.

 

하지만 문제점은 파라미터의 수가 두배가 된다는 점이다. (w1, w2)

 

가장 많이 쓰이는 활성 함수는 ReLU이다.

 

ReLU가 표준으로 많이 쓰이며 대부분 잘 동작한다.

 

하지만 위에도 언급했듯이 ReLU 사용 시 learning rate를 잘 설정해야 한다.

 

실제 네트워크를 학습하려면

 

일반적으로 입력 데이터에 대한 전처리가 필요하다.

 

Preprocess the data

가장 대표적인 전처리 과정은 zero-mean으로 만들고 normalize 해주는 것이다.

 

normalization은 보통 표준편차 이용해서 수행한다.

 

그럼 이러한 전처리가 왜 필요한것일까?

 

앞에서 입력이 전부 positive인 경우인 zero-centered에 대해 언급했다.

 

Always all positive or negative on w

그렇게되면 모든 neuron이 positive gradient를 얻게 되고

 

이는 최적화가 아닌 최적화가 되버린다.

 

입력 데이터가 전무 positive 뿐만 아니라 전부 0 이거나 전부 negative인 경우에도 동일하다.

 

normalization을 해주는 이유는 모든 차원이 동일한 범위를 갖게 함으로써

 

전부 동등한 contribute를 할 수 있게 한다.

 

Preprocess the data

입력 데이터가 이미지일 경우 전처리로 zero-centering 정도만 해준다.

 

normalization은 하지 않는다.

 

왜냐하면 이미지는 이미 각 차원 간에 스케일이 어느정도 맞춰져있기 때문이다.

 

따라서 스케일이 다양한 여러 machine learning 문제와는 달리

 

이미지에서는 normalization을 엄청 잘 해줄 필요가 없다.

 

Preprocess the data

ml에서는 PCA 혹은 whitening과 같은 복잡한 전처리 과정도 있지만

 

이미지에서는 단순히 zero-mean 정도만 사용하고

 

normalization을 비롯한 여러 복잡한 방법들은 잘 쓰이지 않는다.

 

일반적으로 이미지를 다룰 때는 굳이 입력을 더 낮은 차원으로 projection 시키지 않는다.

 

CNN에서는 원본 이미지 자체의 spatial한 정보를 활용해서 이미지의 spatial 구조를 얻을 수 있도록 한다.

 

Zero-mean

이미지에서의 zero-mean은

 

training data에서 평균 값을 계산하고 이 평균값을 네트워크에 입력되기 전에 빼준다.

 

inference할때도 training data 평균값을 빼주게 된다. (ex. AlexNet)

 

일부 네트워크는 이미지 컬러 채널 전체의 평균을 구하지 않고

 

채널마다 평균을 독립적으로 계산하는 경우도 있다. (ex. VGGNet)

 

이제 weight를 어떻게 초기화 시켜야 하는지에 대해 살펴보자.

 

what happens when W=0 init is used?

위의 그림 two layer neural network를 예시로 보자.

 

우리가 할 일은 가중치 업데이트이다.

 

어떤 초기 가중치들이 있고 gradient를 계산해서 가중치를 업데이트할 것이다.

 

이때 모든 가중치를 0으로 하면 어떻게 될까?

 

그렇게된다면 모든 가중치가 동일한 연산을 수행하게 된다.

 

출력도 모두 같게 되며, 결국 gradient도 서로 동일하게 된다.

 

이는 모든 가중치가 똑같은 값으로 업데이트 된다.

 

모든 neuron이 동일하게 생기게 된다.

 

이는 모든 가중치를 동일하게 초기화시키면 발생하는 일이다.

 

Initialize to small random numbers

이런 초기화 문제를 해결하는 첫번째 방법은

 

임의의 작은 값으로 초기화하는 것이다.

 

이 경우 초기 w를 standard gaussian(표준정규분포)에서 샘플링한다.

 

작은 네트워크라면 이런식의 초기화로 충분하지만

 

깊은 네트워크에서는 문제가 발생할 수 있다.

 

Example of deeper network

10개 layer로 이루어진 네트워크가 있다.

 

layer당 500개의 neuron이 있다.

 

활성 함수로는 tanh를 사용한다.

 

그리고 가중치는 임의의 작은 값으로 초기화시킨다.

 

데이터를 임의의 값으로 생성하고 forward pass를 시킨다.

 

그리고 각 레이어별 activation 수치를 통계화시키면 아래 그림과 같다.

 

위 수치는 각 layer 출력의 평균과 평균의 표준편차를 계산한 것이다.

 

중간의 왼쪽 그래프가 layer마다의 mean을 나타낸다.

 

평균은 항상 0 근처에 있다. (tanh 특성, zero-centered)

 

중간의 오른쪽 그래프가 layer마다의 std를 나타낸다.

 

이 std 보면 가파르게 줄어들면서 0에 수렴한다.

 

아래 그림은 layer별 std를 분포로 표현한 것이다.

 

첫번째 layer는 gaussian처럼 생긴 좋은 분포를 형성하고 있다.

 

문제는 w를 곱하면 곱할수록 값이 작아져서 출력 값이 급격히 줄어든다는 점이다.

 

backwards pass로 다시한번 살펴보자.

 

각 layer의 입력이 매우 작은 값이다. 이 값들은 점점 0에 수렴한다.

 

backprop를 생각해보면 upstream gradient가 점점 전파된다.

 

현재 가중치를 업데이트하려면 upstream gradient에 local gradient를 곱해주면 된다. (chain rule)

 

우선 wx를 w에 대해 미분하면 local gadient는 입력 x가 된다. (w의 gradient는 x)

 

x가 매우 작은 값이기 때문에 gradient도 작을 것이고 결국 업데이트가 잘 되지 않을 것이다.

 

gradient를 backprop하는 과정은

 

upstream gradient에 w의 gradient인 x를 곱한다.

 

그리고 입력 x는 어떤 내적의 결과이다.

 

backward pass의 과정에서 upstream gradient를 구하는 것은

 

현재 upstream에 가중치를 곱하는 것이다.

 

w를 계속해서 곱하기 때문에 backward pass에서도 forward 처럼 점점 gradient 값이 작아지게 된다.

 

따라서 upstream gradients는 0으로 수렴하게 된다.

 

여기서 upstream은 gradient가 흘러가는것이다.

 

loss에서부터 시작해서 최초 입력까지 흘러간다.

 

현재 노드를 계산하고 또 밑으로 흘러내려간다.

 

backprop으로 노드에 흘러 들어온 것이 upstream이다.

 

Initialize 1.0 instead of 0.01 on w

그렇다면 가중치를 좀 더 큰 값(0.01 -> 1.0)으로 초기화하면 어떨까?

 

이렇게 큰 가중치를 통과한 출력 wx를 구하고 이를 tanh를 거친다면 어떻게 될까?

 

앞서 언급했던데로 tanh에서 큰 값이 입력으로 들어오면 saturation 될 것이다.

 

그렇게되면 항상 -1 혹은 +1 일 것이다.

 

값이 saturation이 되면 gradient는 0이 되며, 가중치 업데이트가 되지 않기에 적절한 가중치를 얻기 어려워진다.

 

너무 작으면 사라져버리고, 너무 크면 saturation되어 버린다.

 

Xavier Initialization

그래서 사람들이 어떻게 하면 가중치 초기화를 잘 할 수 있을까 고민했다.

 

널리 알려진 좋은 방법 중 하나는 Xavier initialization이다.

 

Glorot가 2010에 발표한 논문이다.

 

그림 위의 w 공식을 살펴보면

 

std gaussian으로 뽑은 값을 입력의 수로 scaling 해준다.

 

기본적으로 xavier initialization가 하는 일은 입력/출력의 분산을 맞춰주는 것이다.

 

입력의 수가 작으면 더 작은 값으로 나누기 때문에 좀 더 큰 값을 얻는다.

 

우리는 더 큰 가중치가 필요하다.

 

왜냐하면 작은 입력의 수가 가중치와 곱해지기 때문에

 

가중치가 더 커야만 출력의 분산 만큼 큰 값을 얻을 수 있다.

 

반대로 입력의 수가 많은 경우에는 더 작은 가중치가 필요하다.

 

when using the ReLU nonlinearity it breaks

하지만 문제가 하나 있다.

 

ReLU를 쓰면 잘 동작하지 않는다는 것이다.

 

ReLU는 출력의 절반을 0으로 만든다.

 

이는 결국 출력의 분산을 반토막 낸다는 것이다.

 

ReLU에서는 값이 너무 작아지기 때문에 잘 작동하지 않게 된다.

 

위의 그림은 이러한 분포가 줄어드는 현상을 나타낸다.

 

점점 더 많은 값들이 0이 되고 결국은 비활성(deactivated)된다.

 

He Initialization

이 문제를 해결하기 위한 논문이 있다.

 

여기서는 추가적으로 2를 더 나눠준다.

 

neuron 중 절반이 없어진다는 사실을 고려하기 위함이다.

 

실제 입력은 반밖에 안들어가므로 반으로 나워주는 텀을

 

추가적으로 더해주는 것이고 실제로 잘 동작한다.

 

실제로 2를 더 나눠주는 이 작은 변화는 학습에 있어서 큰 차이를 보인다.

 

일부 논문에서는 이러한 작은 차이가 학습이 정말 잘 되거나 아예 안되거나하는 결정하는 결과를 보이기도 한다.

 

gaussian의 범위로 activation을 유지시키는 것에 관련된 다른 아이디어를 살펴보자.

 

Batch Normalization

우리는 layer의 출력이 unit gaussian이길 원한다.

 

batch normalization은 unit gaussian 형태로 강제로 만들어보자는 아이디어이다.

 

어떤 layer로부터 나온 batch 단위 만큼의 activations가 있다고 했을 때,

 

우린 이 값들이 unit gaussian이기를 원한다.

 

현재 batch에서 계산한 mean과 variance를 이용해서 normalization 할 수 있다.

 

가중치를 잘 초기화 시키는 것 대신에

 

학습할 때 마다 각 layer에 이러한 normalization을 통해

 

모든 layer가 unit gaussian이 되도록 한다.

 

결국 학습하는 동안 모든 layer의 입력이 unit gaussian이 됐으면 좋겠다는 것이다.

 

그렇게하기 위해 네트워크의 forward pass에서

 

unit gaussian이 되도록 명시적으로 만들어준다.

 

각 neuron을 mean과 variance로 normalization 해주므로

 

이런 일을 함수로 구현하는 것이다.

 

batch 단위로 한 layer에 입력으로 들어오는 모든 값들을 이용해서

 

mean과 variance를 계산해서 normalization 해준다.

 

이렇게 만든 함수를 보면 미분이 가능하다.

 

mean과 variance를 상수로 가지고 있으면 미분이 가능해서 backprop가 가능하게 된다.

 

Batch Normalization

위 그림을 보면 batch당 N개의 학습 데이터가 있고 각 데이터가 D차원이라고 가정하자.

 

각 차원별로 mean을 각각 계산한다.

 

하나의 batch 내에 이걸 전부 계산해서 normalize한다.

 

Batch Normalization

그리고 이 BN은 FC 혹은 conv layer 다음에 넣어준다.

 

BN은 입력의 scale만 살짝 조정해주는 역할이기 때문에

 

FC와 conv 어디에든 적용할 수 있다.

 

conv layer에서 차이점이 있다면 normalization을 차원마다 독립적으로 수행하는 것이 아니라

 

같은 activation map의 같은 채널에 있는 요소들은 같이 normalize 해준다.

 

왜냐하면 conv 특성상 같은 방식으로 normalize 시켜야하기 때문이다.

 

conv layer의 경우 activation map(channel, depth) 마다 mean과 variance를 하나만 계산한다.

 

그리고 현재 batch에 있는 모든 데이터로 normalize 해준다.

 

문제는 FC를 거칠 때 마다 매번 normalization을 해주는 것이다.

 

위의 그림에서 tanh의 입력이 unit gaussian 형태가 맞는것인가?

 

Batch Normalization

normalization의 역할은 입력이 tanh의 linear한 영역에만 존재하도록 강제하는 것이다.

 

그렇게 되면 saturation이 전혀 일어나지 않게 된다.

 

하지만 saturation이 전혀 일어나지 않는것보다

 

얼마나 saturation이 일어날지를 조절하는것이 더 좋다.

 

BN은 normalization 이후 scaling 연산을 추가한다.

 

이를 통해 unit gaussian으로 normalize 된 값들을

 

감마는 scaling 효과를, 베타는 shifting의 효과를 준다.

 

이렇게 하면 normalized 된 값들을 다시 원상복구할 수 있다.

 

감마 = variance, 베타 = mean으로 하면 BN을 하기 전 값으로 돌아갈 수 있다. (오른쪽 수식)

 

네트워크에서 데이터를 tanh에 얼마나 saturation 시킬지를 학습하기 때문에 유연성을 얻을 수 있다.

 

Batch Normalization

batch normalization을 요약하면,

 

모든 mini-batch 각각에서의 mean과  variance를 계산한다.

 

그리고 mean과 variance로 normalize 한 이후에

 

다시 추가적으로 scaling, shifting factor를 사용한다.

 

BN은 gradient의 흐름을 보다 원할하게 해주며

 

결국 학습이 더 잘되게 해준다.

 

BN을 사용하면 learning rate를 좀 더 높일 수 있고

 

다양한 초기화 방법들을 사용해볼 수 있다.

 

그래서 사람들이 BN을 쓰면 학습이 더 쉬워진다고 평가한다.

 

또 한가지는 BN이 regularization의 역할도 한다는 것이다.

 

각 layer의 출력은 해당 데이터 하나 뿐만 아니라

 

batch 안에 존재하는 모든 데이터들에 영향을 받는다(mean, variance).

 

왜냐하면 각 layer의 입력은 해당 배치의 평균으로 normalize되기 때문이다.

 

그렇기 때문에 이 layer의 출력은 이제 오직 하나의 샘플에 대한

 

deterministic한 값이 아니게 되고

 

batch 내의 모든 데이터가 입력으로 한대 묶인다고 볼 수 있다.

 

그러므로 더 이상 레이어의 출력은 deterministic하지 않고

 

조금씩 바뀌게 되고 이는 regularization effect를 주게된다.

 

Batch Normalization

BN에서 mean과 variance는 학습 데이터에서 계산한다.

 

inference time에 추가적인 계산을 하지 않는다.

 

training time에서 running average 같은 방법으로

 

mean, variance를 계산하고 inference time에 사용한다.

 

이제 학습 과정을 어떻게 모니터링하고 하이퍼파라미터를 조절할 것인지 살펴보자.

 

Preprocess the data

첫 단계는 데이터 전처리이다.

 

앞서 설명한대로 zero-mean을 사용한다.

 

Choose the architecture

그리고 아키텍처를 선택한다.

 

위의 그림은 하나의 hidden layer와 50개의 neuron을 가진 모델이다.

 

Initialize

이후 네트워크를 초기화해야한다.

 

Forward pass를 하고 난 후에 계산된 loss가 그럴듯해야 한다.

 

만약 softmax를 사용하고자 한다면 지난 강의들을 토대로

 

가중치가 작은 값일 때 loss가 대강 어떻게 분포해야 하는지 이미 알고 있다.

 

softmax classifier의 loss는 negative log likelihood가 되어야 한다.

 

10개의 클래스라면 loss는 -log(1/10)이 될 것이다.

 

위의 그림에서 loss가 약 2.3으로 잘 동작한다는 것을 알 수 있다.

 

이러한 방법은 좋은 sanity check 방법이다.

 

Initialize

loss가 정상인것을 확인했다면 regularization term을 추가해보자.

 

이전은 regularization term을 0으로 했었는데

 

1000으로 regularization term을 추가하면 loss가 증가한다.

 

이것 또한 유용한 sanity check 방법이다.

 

Let's try to train...

처음 학습을 시작할 때 좋은 방법은 데이터의 일부만 우선 학습시켜 보는 것이다.

 

데이터가 적으면 당연히 over fitting이 생길 것이고 loss가 많이 줄어들 것이다.

 

이때는 regulaization을 사용하지 않고 loss가 내려가는지 확인한다.

 

Let's try to train...

위 그림처럼 epoch 마다 loss가 잘 내려가는지 확인한다.

 

loss가 0을 향해 꾸준히 잘 내려가는지를 확인하자.

 

loss가 감소함과 동시에 train accuracy는 점점 증가한다.

 

데이터가 작은 경우라면 모델이 완벽하게 데이터를 over fitting 할 수 있어야 한다.

 

Let's try to train...

이제는 전체 데이터셋을 사용하고 regularization을 약간만 주면서

 

적절한 learning rate를 찾아야한다.

 

learning rate는 가장 중요한 하이퍼파라미터 중 하나이다.

 

가장 먼저 정해야만 하는 하이퍼파라미터이다.

 

우선 몇 가지 정해서 실험을 해보자.

 

위의 예제에서는 1e-6으로 정했다.

 

그 결과로 loss가 크게 변하지 않는 것을 볼 수 있다.

 

loss가 잘 감소하지 않는 가장 큰 요인은 learning rate가 지나치게 작은 경우이다.

 

learning rate가 지나치게 작으면 gradient 업데이트가 충분히 일어나지 않고

 

loss가 변하지 않는다.

 

여기서 유심히 살펴봐야할 점은 loss가 잘 변하지 않음에도

 

training 및 validation accuracy가 20%가지 상승하였다.

 

위의 epoch 마다 확률 값들이 멀리 퍼져있기 때문에 loss가 비슷비슷한 결과를 보인다.

 

하지만 학습을 통해 확률이 조금씩 옮은 방향으로 바뀌기 때문에 

 

가중치는 조금씩 바뀌지만 accuracy는 갑자기 증가할 수 있는 것이다.

 

Let's try to train...

learning rate를 좀 크게 바꿔보자.

 

1e-6에서 1e6으로 바꿔서 학습한 결과를 살펴보자.

 

Let's try to train...

결과를 보면 loss가 NaNs임을 볼 수 있다.

 

NaNs는 loss가 발산(exploded)한 것이다.

 

주된 이유는 learning rate가 지나치게 높기 때문이다.

 

Let's try to train...

이 경우에는 learning rate를 낮춰야만 한다.

 

그래서 3e-3으로 바꿔지만 여전히 발산한다.

 

보통 learning rate는 1e-3에서 1e-5 사이의 값을 사용한다.

 

이 범위의 값을 이용해서 cross-validation을 수행한다.

 

이 사이의 값들을 이용해서 learning rate가 지나치게 작은지 큰지를 결정할 수 있다.

 

그럼 하이퍼파라미터는 어떻게 정해줘야 할까?

 

하이퍼파라미터를 최적화시키고 그 중 가장 좋은 것을 선택하려면 어떻게 해야 할까?

 

Cross-validation strategy

한 가지 전략은 cross-validation이다.

 

cross-validation은 training set으로 학습시키고 validation set으로 평가하는 방법이다.

 

우선 coarse stage에서는 넓은 범위에서 값을 골라낸다.

 

epoch 몇 번 만으로도 현재 값이 잘 동작하는지 알 수 있다.

 

epoch가 많지 않아도 어떤 하이퍼파라미터가 좋은지 나쁜지를 알 수 있다.

 

NaN이 뜨거나 혹은 loss가 감소하지 않거나 하는 것을 확인하면서

 

적절히 잘 조절할 수 있을 것이다.

 

coarse stage가 끝나면 어느 범위에서 잘 동작하겠다는것을 대충 알 수 있다.

 

두 번째 fine stage에서는 좀 더 좁은 범위를 설정하고

 

학습을 좀 더 길게 시켜보면서 최적의 값을 찾는다.

 

NaNs로 발산하는 징조를 미리 감지할 수도 있다.

 

학습하는 동안 loss가 어떻게 변하는지를 살펴보는 것이다.

 

이전의 loss 보다 더 커진다면 (많이)

 

잘못되고 있다는 것이다.

 

이럴때는 다른 하이퍼파라미터를 선택해야한다.

 

coarse search

위의 예시는 5 epoch을 돌며 coarse search를 하는 과정이다.

 

여기서 확인해야하는 것은 validation accuracy이다.

 

높은 val acc는 빨간색으로 표시되어있다.

 

이 빨간색 구간이 바로 fine-stage를 시작할만한 범위가 된다.

 

한 가지 주목할 점은 하이퍼파라미터 최적화시에는

 

regularization term과 learning rate를 log scale로 값을 주는 것이 좋다.

 

파라미터 값을 샘플링할때 10^-3 ~ 10^-6을 샘플링하지 말고

 

10의 차수 값만 샘플링하는 것이 좋다. (-3 ~ -6)

 

왜냐하면 learning rate는 gradient와 곱해지기 때문에

 

learning rate 선택 범위를 log scale을 사용하는 편이 좋다.

 

따라서 차수(orders of magnitute)를 이용하는 것이 좋다.

 

fine search

범위를 다시 한번 조절해보자. (오른쪽 그림)

 

reg는 범위를 10^-4에서 10^0 정도로 좁히면 좋을 것 같다.

 

val_acc 중간 범위에서 53%의 val_acc를 보이는 것을 볼 수 있다.

 

학습이 가장 잘 되는 구간일것이다.

 

그러나 문제가 있다.

 

아래에 있는 빨간색 박스는 가장 좋은 acc를 보인다. (0.531)

 

해당되는 learning rate들을 보면 전부 10e-4 사이에 존재하고 있다.

 

learning rate의 최적 값들이 우리가 다시 범위를 좁혀 설정한 범위의

 

경계부분에 집중되어 있다는 것을 알 수 있다.

 

이렇게 되면 최적의 learning rate를 효율적으로 탐색할 수 없을수도 있다.

 

실제 최적의 값이 1e-5 혹은 1e-6 근처에 존재할수도 있다.

 

탐색 범위를 조금만 이동시키면 더 좋은 범위를 찾을 수 있을지도 모른다.

 

여기서 우리는 최적의 값이 내가 정한 범위의 중앙 쯤에 위치하도록

 

잘 설정해주는것이 중요하다.

 

Random search vs. Grid search

하이퍼파라미터를 찾는 또 다른 방법은 grid search를 이용하는 것이다.

 

하이퍼 파라미터를 고정된 값과 간격으로 샘플링하는 것이다.

 

하지만 실제로 grid search 보다는 random search를 하는 것이 더 좋다.

 

random search를 하는 경우는 오른쪽 그림과 같다.

 

random이 더 좋은 이유는

 

내 모델이 어떤 특정 파라미터의 변화에 더 민감하게 반응을 하고 있다고 생각해보면 (노랑 < 초록)

 

이 함수가 더 비효율적인 dimentionality를 보인다고 할 수 있으며 (노랑에는 별 영향을 받지 않음)

 

random search는 중요한 파라미터(초록)에게도 더 많은 샘플링이 가능하므로

 

위에 그려놓은 초록색 함수를 더 잘 찾을 수 있다.

 

grid layour에서는 오직 세 번의 샘플링 밖에 할 수 없으므로

 

good region이 어디인지 제대로 찾을 수 없다.

 

random search를 사용하면 important variable에서 더 다양한 값을 샘플링할 수 있다.

 

cross-validation

실제 우리는 하이퍼파라미터 최적화와 cross-validation을 많이 해야한다.

 

많이 해서 많은 하이퍼파라미터를 직접 돌려보고 모니터링해서 어떤 값이 좋고 나쁜지를 확인해야 한다.

 

loss curve를 보면서 좋은 것을 찾아서 시도해보는 일을 계속 반복해야 한다.

 

Monitor and visualize the loss curve

loss curve를 모니터링 하는데 있어서 learning rate가 정말 중요하다.

 

loss curve를 보고 어떤 learning rate가 좋고 나쁜지를 알아볼 수 있다.

 

loss가 발산하면 learning rate가 높은 것이고

 

너무 평평(linear)하면 낮은 것이다.

 

가파르게 내려가다가 어느 순간 정체기가 생기면

 

이 또한 여전히 너무 높다는 의미이다.

 

learning step이 너무 크게 점프해서 적절한 local optimum에 도달하지 못하는 경우이다.

 

최적의 learning rate에 대한 loss curve는 왼쪽 그림과 같다.

 

비교적 가파르게 내려가면서도 지속적으로 잘 내려간다면 현재 learning rate를 유지해도 좋다.

 

Loss curve

loss가 평평하다가 갑자기 가파르게 내려간다면

 

이는 초기화 문제일수있다.

 

gradient의 backprop이 초기에는 잘 동작하지 못하다가 학습이 진행되면서 회복이 되는 경우이다.

 

Monitor and visualize the accuracy

위의 그림은 accuracy를 모니터링하면 볼 수 있는 현상이다.

 

train acc와 val acc가 큰 차이를 보인다면 over fitting일수도 있다.

 

따라서 regularization 강도를 높여야 될 수 있다.

 

큰 차이가 없다면 아직 overfitting 하지 않은 것이고 capacity를 높일 수 있는 여유가 있는 것을 의미한다.

 

Track the ratio of weight updates / weight magnitudes

가중치의 크기 대비 가중치 업데이트의 비율을 확인할 필요도 있다.

 

먼저 파라미터의 norm을 계산해서 가중치의 규모를 계산한다.

 

그리고 norm을 통해 업데이트 사이즈를 계산할 수 있고

 

얼마나 크게 업데이트 되는지를 알 수 있다.

 

우리는 이 비율이 대략 0.001 정도 되길 원한다.

 

이 값은 변동성이 커서 정확하지 않을 수 있다.

 

하지만 업데이트가 지나치게 크거나 작은지에 대한 감을 어느정도 가질 수 있다.

 

업데이트가 너무 지나치거나 아무런 업데이트도 없으면 안된다.

Mark 1 Perceptron

1957년에 Frank Rosenblatt가 Mark I Perceptron Machine을 개발했다.

 

최초의 perceptron을 구현한 기계이다.

 

perceptron은 wx+b와 유사한 함수를 사용한다.

 

왜 똑같은게 아니고 유사하냐면 출력값이 1 또는 0이기 때문이다.

 

가중치 w는 update rule이 존재한다.

 

하지만 이것은 w를 조절하면서 맞추는 식이였다.

 

Adaline and Madaline

1960년대에는 Widrow와 Hoff가 Adaline과 Madaline을 개발했다.

 

이것은 최초의 multilayer perceptron network를 구현한 기계였다.

 

하지만 backpropagation과 같은 알고리즘은 없었다.

 

First Time Back-Propagation became popular

최초의 backpropagation은 1986년에 Rumelhart가 제안했다.

 

chain rule과 update rule을 확인할 수 있다.

 

이때가 최초로 network를 학습시키는 개념이 정립되기 시작했다.

 

RBM

2006년에 RBM을 통해 DNN의 학습 가능성을 보여줬다.

 

하지만 이때까지도 모던한 NN은 아니였다.

 

backpropagation을 수행하려면 세심한 초기화가 필요했다.

 

초기화를 위해 전처리 과정이 필요했고

 

각 히든레이어 가중치를 학습해야했다.

 

이렇게 초기화된 히든 레이어를 이용해서

 

전체 신경망을 backpropagation 하거나 fine tune을 수행했다.

 

First strong results

실제로 NN이 유명세를 타기 시작한 때는 2012년부터이다.

 

NN이 음성 인식에서 아주 좋은 성능을 나타냈기 때문이다.

 

또한 2012년에 영상 인식 관련해서도 유명한 AlexNet이 나온다.

 

이 논문은 ImageNet classification에서 최초로 NN을 사용했고 결과는 굉장했다.

 

AlextNet은 ImageNet benchmark의 error를 그때 당시 극적으로 감소시켰다.

 

이후 ConvNet은 널리 쓰이고 있다.

 

Hubel & Wiesel

다시 1950년대로 돌아가 Hubel & Wiesel이 일차 시각피질의 뉴런에 관한 연구를 수행한 것을 떠올려보자.

 

고양이 뇌에 전극을 꼽아서 다양한 자극을 주고 실험을 진행했다.

 

여기서 뉴런이 oriented edge와 shape와 같은 것들에게 반응한다는 것을 알게 되었다.

 

Topographical mapping in the cortex

고양이 실험에서 내린 중요한 결론 중 하나는

 

피질(cortex) 내부에 topograhical mapping이 존재한다는 것이다.

 

피질 내 서로 인접해 있는 세포들은 visual field에서 지역성을 나타낸다.

 

위의 오른쪽 그림을 보면 해당되는 spatial mapping을 볼 수 있다.

 

쉽게 말하면 눈으로 본거를 피질에서 공간적으로 표현된다고 볼 수 있다.

 

또 다른 중요한 점은 뉴런들이 계층 구조를 지니고 있다는 점이다.

 

Hierarchical Organization

다양한 종류의 시각 자극에 대해 시각 신호가 가장 먼저 도달하는 위치가

 

망막으로부터 시각적인 정보를 뇌로 전달하는 망막절 세포(retinal ganglion cell)이라는 것을 발견했다.

 

가장 상위에는 simple cell들이 있는데

 

다양한 edge의 방향과 빛의 방향에 대해 반응을 나타냈다.

 

그리고 simple cell이 complex cell과 연결되어 있다는 것을 발견했다.

 

complex cell은 빛의 방향 뿐만 아니라 움직임에 대해서도 반응을 나타냈다.

 

복잡도가 증가한 hypercomplex cell들은

 

끝 점(end point)에 반응하는 것을 나타냈다.

 

이러한 발견들로부터 corner나 blob에 대한 아이디어를 얻기 시작했다.

 

Neocognition

1980년에 Neocognitron은 Hubel & Wiesel의

 

simple/complex cell 아이디어를 사용한 최초의 NN이다.

 

simple/complex cell을 교차시켰다.

 

simple cell은 학습 가능한 파라미터를 가지고 있었고,

 

complex cell은 pooling과 같은 역할로 구현했으며 작은 변화에 simple cell보다 좀 더 강인하다.

 

지금가지 1980년대까지의 업적을 살펴보았다.

 

LeNet-5

1998년 Yann LeCun이 최초로 NN을 학습시키기 위해 backpropagation과

 

gradient-based learning을 적용했다.

 

이 방법은 우체국에서 문자 인식과 숫자 인식에 쓰였으며 잘 동작했다.

 

하지만 네트워크를 더 크게 만들지 못했고 숫자 데이터는 단순했다.

 

AlexNet

 

2012년 AlexNet이 CNN의 현대화를 불러 이르켰다.

 

이는 Yann LeCun의 CNN과 크기 다르지 않고 단지 더 크고 깊어졌다.

 

또한 ImageNet과 같은 대량의 dataset을 활용할 수 있었으며 GPU를 활용할 수 있었다.

 

ConvNets are everywhere

오늘날 ConvNets 어디에서나 쓰인다.

 

AlexNet의 ImageNet classification 결과를 보면 이미지 검색(retrieval)에 좋은 성능을 보인다.

 

위의 그림에서 오른쪽 그림을 보면 유사한 특징을 매칭시키는데 탁월하다는 것을 알 수 있다.

 

Detection & Segmentation

Detection에서도 ConvNet을 사용한다.

 

이미지에서 객체를 찾고 bounding box를 그린다.

 

segmentation도 수행할 수 있다. 이는 bbox만 치는게 아니라

 

객체를 구별하는데 픽셀 하나하나를 classification한다.

 

Self-Driving Cars

ConvNets은 자율주행 자동차에도 활용될 수 있다.

 

대부분 작업을 GPU가 수행하며, 병렬처리를 통해 ConvNet을 효과적으로 학습하고 추론할 수 있다.

 

임베디드 시스템에서도 동작하며, 최신 GPU에서도 가능하다.

 

Application of ConvNets

위 그림은 ConvNet을 활용한 다양한 어플리케이션의 예를 보여준다.

 

ConvNet을 단일 이미지와 시간적 정보를 이용하여 비디오에도 활용할 수 있다.

 

Application of ConvNets

pose estimation도 가능하다.

 

어깨나 팔꿈치와 같은 다양한 관절 포인트를 인식할 수 있다.

 

사람의 비정형적인 포즈를 잘 검출하는 것을 볼 수 있다.

 

강화학습을 통해 게임을 하거나 바둑을 두는것도 가능하다.

 

이러한 여러 작업들에서 ConvNet은 아주 중요한 역할을 한다.

 

Application of ConvNet

또 다른 예로는 의료 영상을 가지고 해석하거나 진단을 하는데 활용되는 것이다.

 

우주 은하를 분류하거나 표지판을 인식할수도 있다.

 

Application of ConvNet

Kaggle Chanllange에서 고래를 분류하는 작업도 있었다.

 

항공 지도를 활용하여 길과 건물 인식을 수행할 수 있다.

 

Image Captioning

classification과 detection에서 좀 더 나아가 image captioning을 수행할 수 있다.

 

image captioning은 해당 이미지의 설명을 문장으로 생성한다.

 

Application of ConvNet

또한 Neural Network를 이용해서 멋진 예술작품도 만들어 낼 수 있다.

 

위의 그림 왼쪽은 Deep Dream 알고리즘의 결과를 나타낸다.

 

오른쪽 그림은 style transfer라는 방법은 원본 이미지를 가지고 특정 화풍으로 다시 그려주는 알고리즘이다.

 

오른쪽 하단 그림은 원본 주택 이미지를 반 고흐의 별이 빛나는 밤의 화풍으로 바꾼것이다.

 

Fully Connected Layer

CNN에 대해 어떻게 동작하는지 알아보자.

 

우선 FC를 살펴보자.

 

위의 그림에서 32x32x3의 이미지를 입력으로 활용했다.

 

이 이미지를 길게 펴서 3072-dim의 벡터로 만들었다.

 

그리고 10x3072 크기의 가중치 W와 곱하면(Wx) activation을 얻을 수 있다.

 

activation은 10개의 행으로 되어 있으며 3072-dim 입력과 가중치 W를 내적한 결과를 나타낸다.

 

그러면 어떤 하나의 숫자를 얻을 수 있는데 이는 neuron 중 하나의 값이라고 할 수 있다.

 

위 예시는 10개의 출력이 있는 경우이다.

 

preserve spatial structure

conv layer와 FC layer의 주요 차이점은

 

conv layer는 기존의 spatial 구조를 보존한다는 것이다.

 

기존의 FC layer가 입력 이미지를 길게 폈다면

 

conv layer는 이미지 구조를 그대로 유지한다.

 

Convolution Filter

위의 그림에서 파란색 작은 필터가 가중치 W가 된다.

 

5x5x3 필터가 이미지를 슬라이딩하면서 공간적으로 내적(dot product)을 수행한다.

 

필터는 항상 입력의 깊이(depth)만큼 확장된다. (입력 깊이 3, 필터 깊이 3)

 

위의 예제는 32x32 이미지에 필터 크기가 5x5이지만

 

depth를 보면 입력의 전체 depth를 취한다. 여기서는 5x5x3이 된다.

 

Convolution Layer

이 필터 w를 가지고 전체 이미지에 내적을 수행한다.

 

필터 w와 이에 해당하는 이미지의 픽셀을 곱한다.

 

필터의 크기가 5x5x3이 의미하는 것은 그 만큼 곱셈연산을 수행한다는 의미이다. (5x5x3 = 75)

 

추가로 bias term이 하나 있을 수 있다.

 

w에 transpose를 한 이유는 행벡터(1x75)를 만들어 내적을 하기 위한 것이다.

 

Convolution Layer

convolution은 이미지의 좌측 상단부터 필터가 슬라이딩하면서 수행된다.

 

필터의 모든 요소를 가지고 내적을 수행하게 되면 하나의 값을 얻을 수 있다.

 

그리고 슬라이딩을 해서 반복한다.

 

이렇게 나온 값들을 output activation map의 해당 위치에 저장한다.

 

위의 그림에서 입력 이미지와 출력 activation map의 차원(dim)이 다른것을 볼 수 있다.

 

입력은 32x32이고 출력은 28x28이다.

 

출력 크기는 슬라이딩을 어떻게 하느냐에 따라 달라질 수 있지만 보통 1씩 건너서 슬라이딩한다.

 

Convolution Layer

또 다른 하나의 필터를 이용하여 전체 이미지에 convolution 연산을 수행한다.

 

또 다른 같은 크기의 activation map을 얻게된다.

 

보통 이처럼 여러개의 필터를 이용한다.

 

왜냐하면 필터마다 다른 특징을 추출하기 위함이다.

 

Convolution Layer

하나의 Layer에서 원하는 만큼의 필터를 사용할 수 있다.

 

위 그림에서는 5x5x3크기의 필터 6개를 사용했다

 

Convolution Layer

CNN은 보통 conv layer의 연속된 형태를 이룬다.

 

각각 conv layer를 쌓아 올리면 linear layer로 된 NN이 된다.

 

그 사이에 ReLU같은 activation function을 추가하여 비선형성을 만든다.

 

이렇게 되면 conv-relu가 반복되고 중간중간에 pooling layer도 들어간다.

 

그리고 각 layer 출력은 다음 layer의 입력이 된다.

 

각 layer는 여러개의 필터를 가지고 있고 각 필터마다 각각의 activation map을 만든다.

 

이런식으로 여러개의 layer를 쌓으면 각 필터들을 계층적으로 학습할 수 있다.

 

VGG Layer

앞쪽에 있는 필터들은 edge와 같은 low level feature를 학습한다.

 

mid level feature를 보면 corner나 blob과 같은 좀 더 복잡한 특징을 가지게 된다.

 

high level feature를 보면 객체와 비슷한 것들을 특징으로 가지는 것을 볼 수 있다.

 

이는 layer의 계층에 따라 단순하거나 복잡한 특징을 뽑는다는것을 보여준다.

 

Hubel & Wiesel

이러한 현상은 Hubel & Wiesel의 이론과도 잘 맞는다.

 

네트워크 앞단에는 단순한 작업을 처리하고 뒤로 갈수록 점점 더 복잡해지는것이다.

 

이러한 현상은 강제로 학습시킨것이 아니라

 

계층적 구조를 설계하고 역전파로 학습을 시켜서 나온 결과이다.

 

여러개의 각 필터는 이런식으로 학습이 된다.

 

Visualization of Activation Map

위의 그림은 5x5 필터의 출력인 activation map의 visualization한것이다.

 

상단의 작은 그림은 5x5 필터가 어떻게 생겼는지 보여준다.

 

입력 이미지는 자동차의 헤드라이트 부분이다.

 

빨간 네모박스를 보면 edge를 찾고 있다.

 

이 필터를 슬라이딩 시키면 이 필터와 비슷한 값들은 값이 커지게 된다. (하단 빨간 네모박스)

 

이미지 중 어느 위치에서 각 필터가 크게 반응하는지 알 수 있다.

 

CNN

CNN은 입력 이미지를 여러 layer에 통과시킨다.

 

첫 번째 conv layer후에 ReLU non-linear layer를 통과한다.

 

conv-relu-conv-relu를 수행한 뒤 pooling layer를 통과한다.

 

pooling은 activation map 사이즈를 줄이는 역할을 한다.

 

CNN 끝단에는 FC layer가 있다.

 

conv layer 마지막 출력과 연결되어 최종 스코어를 계산한다.

 

Spatial Dimension

32x32x3 이미지가 있고 5x5x3 필터를 가지고 연산을 수행한다고 생각해보자.

 

왜 출력이 28x28이 나오게 될까?

 

Spatial Dimension

간단한 예시로 7x7 입력에 3x3 필터가 있다고 가정해보자.

 

이미지 좌측상단부터 필터를 슬라이딩하면서 내적을 수행한다.

 

내적의 출력 값들은 activation map의 좌측 상단부터 저장된다.

 

다음 단계를 필터를 오른쪽으로 한칸 슬라이딩한다.

 

그럼 또 하나의 내적 값을 얻을 수 있다.

 

이런식으로 반복하다보면 결국 5x5의 activation map을 얻을 수 있다.

 

이 필터는 좌우 방향과 상하방향으로 5번씩만 슬라이딩할 수 있다.

 

Spatial Dimension

슬라이딩을 두 칸씩 움직일수도 있다.

 

여기서 움직이는 칸을 stride라고 한다. stride를 2로 설정하면 슬라이딩을 2칸씩 건너뛰면서 하는 것이다.

 

그럼 결국 3x3 activation map을 얻게될것이다.

 

Spatial Dimension

그렇다면 stride가 3일때 출력 사이즈는 몇이 될까?

 

이때는 필터가 이미지를 슬라이딩해도 모든 이미지를 커버할 수 없게된다.

 

이렇게되면 데이터 손실도 있고 불균형한 결과를 볼 수 있다.

 

Calculation of Output Size

위의 그림은 상황에 따라 출력 사이즈가 어떻게 되는지 계산할 수 있는 수식을 나타낸다.

 

입력의 차원이 N이고 필터 사이즈가 F이고 stride가 주어지면 출력 크기는 (N-F) / stride + 1이 된다.

 

이를 이용해서 어떤 필터 크기를 사용해야 하는지 알 수 있다.

 

또한 어떤 stride를 사용했을때 이미지에 맞는지, 몇 개의 출력값을 낼 수 있는지도 알 수 있다.

 

Zero Padding

출력 사이즈를 입력 사이즈와 동일하게 만들기 위한 방법 중

 

흔히 쓰이는 방법이 zero padding이다.

 

이미지의 경계를 포함할 수 있는 방법이기도 하다.

 

zero pad는 이미지의 가장자리에 0을 채워 넣는다.

 

이렇게되면 왼쪽 상단 끝부터 conv 필터 연산을 수행할 수 있다.

 

7x7입력에 3x3필터 연산시 zero padding을 넣는다면 출력은 7x7이 된다.

 

일반적으로 필터 크기가 3일때 zero pad는 1로,

 

5일때 2로, 7일때 3을 사용한다.

 

Stride

여러 layer를 계층적으로 쌓을때 zero padding을 하지 않는 다면

 

출력 사이즈가 계속 줄어들것이다.

 

깊은 네트워크가 있다면 activation map은 계속 줄어들고 엄청 작아진다.

 

그렇게되면 일부 정보를 잃게 될것이고 원본 이미지를 표현하기에는 너무 작은 값을 사용하게 된다.

 

출력 크기가 줄어드는 이유는 입력의 가장자리를 계산하지 못하기 때문이다.

 

Example of Output Size

입력 이미지 크기가 32x32x3이고 5x5x3 필터 10개 있다.

 

여기서 stride를 1로 하고 padding을 2로 했을때 출력사이즈는 몇이 될까?

 

입력 사이즈 F가 32가 되고 padding으로 2씩 증가시키면 32+4 = 36이된다.

 

여기서 필터 사이즈 5를 빼고 stride 1로 나누고 1을 더하면 각 필터는 32x32가 된다.

 

전체 필터 개수는 10개로 10개의 activation map이 만들어진다.

 

그럼 전체 출력 크기는 32x32x10이 된다.

 

Example of Parameter Size

그럼 파라미터는 총 몇개일까?

 

하나의 필터는 5x5x3개의 파라미터가 있고 하나의 bias를 가질 수 있다. 75 + 1 = 76

 

이러한 필터가 10개기 때문에 총 파라미터의 개수는 76 * 10 = 760개이다.

 

Summary of Conv Layer

위의 그림은 conv layer의 요약이다.

 

일반적인 필터 사이즈로 3x3, 5x5를 사용한다.

 

stride는 1이나 2를 많이 쓴다.

 

padding은 설정에 따라 조금씩 다르다.

 

보통 필터 개수는 32, 64, 128, 512처럼 2의 제곱수로 설정한다.

 

1x1 convolution layer

1x1 convolution은 depth를 조절할때 활용된다.

 

1x1은 3x3이나 5x5 등 처럼 공간적인 정보를 이용하지는 않는다.

 

하지만 depth 만큼은 연산을 수행한다.

 

그렇기 때문에 1x1 conv는 입력 전체의 depth에 대한 내적을 수행하는것과 같다.

 

위의 그림은 입력이 56x56x64이다. 여기서 32개의 1x1x64 convolution을 수행하면

 

56x56x32 크기의 activation map이 생성된다.

 

The Brain/Neuron View of Conv Layer

conv layer를 뇌의 neuron관점에 생각해보자.

 

오른쪽 그림은 내적과 같은 아이디어를 나타낸다.

 

입력 x가 들어오면 가중치 w와 곱한다. w는 필터값이라고 할 수 있다.

 

이는 하나의 값을 출력한다.

 

그렇지만 가장 큰 차이점은 neuron이 local connectivity를 가지고 있다는 점이다.

 

conv layer 처럼 슬라이딩을 하는것이 아니라 특정 부분에만 연결되어 있다.

 

하나의 neuron이 한 부분만 처리하고, 이러한 neuron들이 모여서 전체 이미지를 처리한다.

 

이런식을 spatial 구조를 유지한채로 layer의 출력인 activation map을 만든다.

 

The Brain/Neuron View of Conv Layer

5x5 필터는 각 neuron의 receptive field가 5x5라고 할 수 있다.

 

receptive field는 하나의 neuron이 한 번에 수용할 수 있는 영역을 의미한다.

 

여기서 필터가 5개라고 가정한다. (5x5x5)

 

The Brain/Neuron View of Conv Layer

출력값은 위 그림에서 파란색 volume처럼 생긴다.

 

출력의 크기는 28x28x5인 3D grid가 된다.

 

여기서 어떤 한 점을 찍어서 depth 방향으로 바라보면 

 

이 5개의 점은 정확하게 같은 지역에서 추출된 서로 다른 특징이라고 볼 수 있다.

 

각 필터는 서로 다른 특징을 추출한다.

 

그렇기 때문에 각 필터는 이미지에서 같은 지역을 슬라이딩하더라도

 

서로 다른 특징을 뽑는다고 할 수 있다.

 

Pooling Layer

pooling layer는 representation을 더 작게 하고 더 관리하기 쉽게 해준다.

 

즉, 파라미터 수를 줄이는 것이다.

 

공간적 불변성(invariance)를 얻을 수 있다.

 

pooling layer의 역할은 심플하다. 바로 downsampling하는 것이다.

 

예를 들어 224x224x64의 입력이 있다면 pooling을 통해 112x112x64로 '공간적으로' 줄여준다.

 

depth에는 아무런 영향이 없다.

 

최대값을 뽑는 max pooling이 일반적으로 쓰인다.

 

Max Pooling

pooling에도 필터의 크기를 정할 수 있다.

 

필터의 크기는 얼만큼의 영역을 한번에 묶을지 정하는것이다.

 

위의 그림에서 왼쪽은 4x4 입력이고 여기에 stride 2의 2x2 max pooling을 수행하면

 

오른쪽 그림과 같이 계산된다.

 

conv layer처럼 슬라이딩하면서 연산한다.

 

max pooling이기 때문에 conv처럼 내적이 아니라 필터 안에서 가장 큰 값 하나를 뽑는다.

 

왼쪽 빨간색 영역을 보면 6이 가장 크고 녹생 영역은 8이 가장 크다.

 

노란색 영역은 3, 파란색 영역은 4가 가장 크다.

 

pooling을 수행할때는 겹치지 않게 슬라이딩하는것이 일반적이다.

 

최근은 pooling보다 stride를 이용하여 downsampling을 수행한다.

 

좀 더 좋은 성능을 나타낸다고 한다.

 

Pooling Layer

pooling layer는 입력이 W(width), H(height), D(Depth)라고 할때

 

필터 size를 정해줄 수 있다.

 

여기에 stride까지 정해주면 앞에서 언급한

 

conv layer에서 사용했던 수식을 그대로 이용할 수 있다.

 

(W - Filter Size) / Stride + 1 이 된다.

 

pooling layer는 padding을 수행하지 않는다.

 

왜냐하면 pooling의 목적은 downsampling이고 

 

conv layer처럼 가장자리 값을 계산 못하는 경우가 없기 때문이다.

 

일반적으로 pooling 필터 사이즈는 2x2, 3x3이며, 

 

stride는 보통 2로 한다.

 

CNN

conv layer가 있고 비선형성인 activation function ReLU layer를 통과한다.

 

downsampling을 할 때는 pooling layer를 섞어준다.

 

마지막에는 FC layer가 있다.

 

FC의 입력으로 사용되는 마지막 conv layer의 출력은 3차원 volume으로 이루어진다.

 

이 값들을 전부 1차원 벡터로 만들어서 FC layer의 입력으로 사용한다.

 

이렇게되면 ConvNet의 모든 출력을 서로 연결하게 되는것이다.

 

마지막 FC layer부터는 공간적 구조(spatial structure)를 신경쓰지 않게된다.

 

전부 하나로 통합시켜서 최종적인 추론을 수행한다.

 

그렇게 되면 score가 출력으로 나오게 된다.

 

최근은 pooling이나 FC layer를 점점 없애는 추세이다.

 

conv layer만 깊게 쌓는 것이다.

Computational Graphs

analytic gradient를 어떻게 계산할까?

 

computational graph를 이용하면 어떠한 함수도 표현이 가능하다.

 

위 그림에서 각 노드는 연산 단계를 나타내며,

 

input이 x, w인 선형 classifier를 보여준다.

 

곱셉 노드는 행렬 곱셈을 나타내며, 데이터 x와 가중치 w의 곱을 통해 score vector를 출력한다.

 

그 다음 hinge loss라는 계산 노드가 있다.

 

loss를 계산하는데 하단에 regularization 노드가 더해져서 최종 loss가 된다.

 

이렇게 computational graph를 이용하여 함수를 표현하면,

 

gradient를 계산하기 위해 computational graph 내부의 모든 변수에 대해

 

chain rule을 재귀적으로 사용하는 backpropagation을 사용할 수 있다.

 

AlexNet

computational graph는 복잡한 함수를 이용하여 작업할때 유용하다.

 

위의 AlexNet(CNN)은 top에 input image가 들어가고

 

bottom에 loss가 계산된다.

 

top에서 bottom까지 많은 layer를 거치게 된다.

 

복잡한 구조를 computational graph로 표현했다고 상상해보자.

 

graph가 매우 복잡해보일 수 있겠지만

 

결국은 풀리게 된다.

 

Example of Backpropagation

위의 그림은 간단한 backpropagation의 예시이다.

 

일단 함수가 있다. f(x, y, z) = (x+y)z

 

그리고 f에 대한 어떤 변수의 gradient를 알고 싶은 것이다.

 

위 함수 f를 computational graph로 표현하면 오른쪽 그림과 같다.

 

x, y에 대해 덧셈 노드가 있고 z와의 곱셈 노드가 있다.

 

여기에 예시로 값을 전달한다. x = -2, y = 5, z = -4

 

x + y 덧셈 노드를 q로 정의하면 f = q x z가 된다.

 

값을 기준으로 각 노드를 계산하면 q = 3, f = -12가 된다.

 

x와 y 각각에 대한 q의 gradient는 덧셈 노드이기 때문에 1이 된다. (수학 카테고리 참조)

 

q와 z 각각에 대한 f의 gradient는 곱셈 노드이기 때문에 각각 z와 q가 된다. (수학 카테고리 참조)

 

우리는 x, y, z 각각에 대한 f의 gradient를 알고 싶다.

 

앞서 말했듯이 backpropagation은 chain rule의 재귀적 응용이다.

 

chain rule에 의해 뒤에서부터 계산이 된다.

 

Example of Backpropagation

위의 그림에서 볼 수 있듯이 f에 대한 gradient는 1이 된다.

 

다음은 z에 대한 gradient를 계산해보자.

 

z에 대한 f의 미분은 q이다. (파란색 박스 오른쪽)

 

q의 값은 3이기 때문에 z에 대한 f의 미분값은 3이 된다.

 

미분값은 빨간색으로 초록색 값 하단에 표시됨.

 

다음은 q에 대한 f의 미분으로 정답은 z이다.

 

z의 값은 -4이기 때문에 q에 대한 f의 미분값은 -4가 된다.

 

Example of Backpopagation

이제 y에 대한 f의 미분값을 알아보자.

 

그런데 y는 f에 바로 연결되어 있지 않다.

 

f는 z와 연결되어 있다. chain rule을 이용하여 계산할 수 있다.

 

Example of Backpropagation

chain rule에 의해 y에 대한 f의 미분은

 

q에 대한 f의 미분과 y에 대한 q의 미분의 곱으로 나타낼 수 있다.

 

q에 대한 f의 미분은 z이고, y에 대한 q의 미분은 1이기 때문에

 

-4 * 1로 y에 대한 f의 미분값은 -4인것을 알 수 있다.

 

같은 방법으로 x에 대한 f의 미분은

 

q에 대한 f의 미분과 x에 대한 q의 미분의 곱으로 나타낼 수 있다.

 

q에 대한 f의 미분은 마찬가지로 -4가 되고, x에 대한 q의 미분 또한 1이 되기 때문에

 

-4 * 1로 x에 대한 f의 미분값 역시 -4인 것을 확인할 수 있다.

 

Example of Backpropagation
Local Gradient

computational graph는 모든 노드를 표현한다.

 

하지만 각 노드는 각 노드와 각 노드에 연결된 local 입력만 알고 있다.

 

위의 예시에서의 local 입력은 x, y이고 출력은 z가 된다.

 

위의 노드에서 우리는 local gradient를 계산할 수 있다. (빨간색 박스)

 

x에 대한 z의 gradient, y에 대한 z의 gradient를 계산할 수 있다.

 

Local Gradient

 

최종 loss L이 이미 정해져 있고

 

여기에서 z에 대한 L의 gradient를 나타낼 수 있다.

 

이제 x와 y의 값에 대한 gradient를 계산해보자.

 

chain rule을 이용하면 z에 대한 L의 미분값과 x에 대한 z의 미분값의 곱으로 계산할 수 있다.

 

여기서 x에 대한 z의 미분값은 x에 대한 z의 local gradient이므로

 

z에 대한 gradient와 local gradient의 곱으로 표현할 수 있다.

 

Local Gradient

마찬가지로 y의 gradient는

 

z에 대한 L의 미분값과 y에 대한 z의 미분값의 곱으로 계산할 수 있다.

 

여기서 y에 대한 z의 미분값은 y에 대한 z의 local gradient이므로

 

z에 대한 gradient와 local gradient의 곱으로 표현 할 수 있다.

 

이것을 통해 상위 노드 방향으로 출력 gradient와 local gradient의 곱으로

 

입력에 대한 gradient를 계산할 수 있다는 것을 알 수 있다.

 

이처럼 각 노드가 local gradient를 갖고 있으며,

 

backpropagation을 통해 상위 노드 방향으로 계속 전달되고

 

이것을 전달받아 local gradient와 곱하기만 하면 되는 것이다.

 

하지만 실제 입력값들은 scalar가 아닌 vector로 구성되어 있다.

 

그래서 backpropagation을 vectorized operation을 통해 수행해야한다.

 

mini-batch 크기의 jacobian matrix를 계산하게 되는데

 

이 부분은 생략하기로 한다. (...)

 

이제 신경망(neural network)에 대해 살펴보자.

 

Neural Networks

 

위의 2-layer NN을 보면

 

처음에는 선형 layer인 w와 x의 행렬 곱이고

 

이를 비선형성 함수인 max(0, w)를 이용하여 max를 얻는다.

 

그리고 여기에 선형 layer인 행렬 곱을 통해

 

최종적으로 score 함수를 얻을 수 있다.

 

여기서 비선형 변환이 중요하다.

 

이 비선형 변환이 없이 선형 함수만 추가된다면

 

f(f(f(x))) 이런식이 되어버린다.

 

이 결과는 다시 입력의 선형 함수가 되어버린다.

 

이렇게 되면 layer를 쌓을 수 없게 된다. 선형 함수를 쌓아봤자 선형적인 함수가 되버린다.

 

비선형의 복잡한 함수를 만들기 위해 간단한 함수들을 계층적으로 여러개 쌓아올린다.

 

Neural Networks

기존에 선형 layer 행렬곱에 의한 score 함수를 보자. f=Wx

 

가중치 행렬 w의 각 행이 클래스 각각의 템플릿과 비슷한지 비교하였다.

 

W1을 하단 이미지로 표현한거라고 생각하자.

 

여기에서 문제는 각 클래스 마다 오직 하나의 템플릿만 가지고 있다는 것이다.

 

예를 들어, 자동차는 색상이 다양할 수 있지만 위의 자동차 템플릿은 빨간색을 주로 찾는거로 보인다.

 

또한 자동차가 트럭일수도 있고 여러 종류가 될 수 있지만 위의 템플릿은 스포츠가 느낌이 난다.

 

우리는 다양한 형태의 자동차를 분류하기를 원한다.

 

multi layer network는 이러한 것을 가능하게 한다. (W2 추가)

 

Neural Networks

다른 비선형 행렬 w3로 곱하면 3-layer NN이 된다.

 

물론 중간에 비선형 함수 max를 거친다.

 

이런식으로 layer를 쌓으면서 deep neural network라는 용어가 생겼다.

 

Diagram of Neuron

왼쪽 위의 그림은 뉴런의 다이어그램을 나타낸다.

 

각 뉴런은 수상돌기(dendrite)를 지니고 있다.

 

수상돌기는 뉴런에서 들어오는 신호를 받는다.

 

세포체(cell body)는 들어오는 신호를 종합한다.

 

합쳐진 모든 신호는 하류 뉴런과 연결된 다른 세포체로 축삭(axon)을 통해 이동된다.

 

이는 위에서 살펴봤던 computational node가 실제 뉴런과 비슷하게 동작한다는것을 알 수 있다.

 

오른쪽 하단 그림을 보면

 

computational graph에서 노드가 서로 연결되어 있고

 

입력되는 신호는 x이다.

 

모든 x(x0, x1, x2)는 가중치 w와 결합되어 합쳐진다.

 

그리고 활성 함수를 적용하여 출력값을 얻으며 아래로 연결된 뉴런에게 전달된다.

 

왼쪽 하단의 그림은 활성화 함수 sigmoid이다.

 

Neural Networks: Architectures

위의 그림은 FC(fully-connected) layer로 구성된 신경망이다.

 

각 layer는 행렬곱을 했고, 2-layer NN은 선형 layer를 2개 가지고 있는 것을 의미한다.

 

이것을 하나의 hidden layer network라고 나타낼 수 있다.

 

행렬 곱의 수를 세는 대신 히든 레이어의 수를 센다.

 

그래서 2 layer NN을 1 hidden layer NN이라고 부를 수 있다.

 

마찬가지로 3 layer NN을 2 hidden layer NN이라고 부를 수 있다.

 

Example feed-forward computation of a neural network

위의 그림에 f는 sigmoid 함수이다.

 

데이터를 x로 받고, 가중치 w1과 행렬곱을 한다.

 

그리고 sigmoid 함수를 통해 비선형성을 적용하고 h1을 얻는다.

 

그 다음 두 번째 hidden layer인 h2를 얻기 위해

 

h1과 w2를 행렬곱을 한다.

 

그리고 비선형성을 적용시키고 w3와 행렬곱을 함으로써 최종 출력을 얻는다.

R-CNN 이전의 object detection에 사용되던 방법들은

 

hand-crafted feature에 기반하는 SIFT와 HOG와 같은 알고리즘이였다.

 

AlexNet 이후에 버클리 연구팀이 CNN을 이용하여

 

detection 분야에 R-CNN이라는 알고리즘을 만들었다.

 

R-CNN: Regions with CNN features

먼저 입력 이미지로부터 약 2,000개의 후보 영역을 설정한다.

 

후보 영역 설정은 selective search 방법을 사용한다.

 

selective search는 계층적 그룹핑 방법이며, region의 유사도를 따져서 반복적으로 통합하는 방법이다.

 

후보 영역을 설정하면 해당 영역을 모두 동일한 224x224 크기로 warping을 수행한다.

 

AlexNet 구조를 활용했기때문에 입력 크기를 224x224 크기로 통일했다. (위 그림 3번)

 

마지막 layer인 FC를 통해 feature vector를 얻으면

 

linear SVM loss를 이용하여 해당 후보 영역에 대한 클래스를 분류한다.

 

selective search로 추출한 bounding box는 부정확하기 때문에 선형 회귀(linear regression) 방법을 사용한다.

 

아래 수식에서 x, y, w, h는 x, y 좌표, width, height를 뜻한다.

 

P는 selective search의 bounding box 이고 G는 ground truth의 bounding box이다.

 

P를 G로 회귀하도록 학습하는것이 목표이다.

 

Bounding Box Regression

x, y, w, h를 각각 이동하는 함수들을 표현하면 아래와 같다.

 

x, y는 좌표이기 때문에 이미지 크기에 관계없이 위치만 이동시키면 된다.

 

width와 height는 이미지 크기에 비례하여 조정해야 한다.

 

이러한 특성을 적용하여 P를 G로 이동시키는 함수는 식 (1), (2), (3), (4)와 같다.

 

여기서 학습을 통해 얻으려는 함수는 d 함수이다.

 

R-CNN에서는 d 함수를 구하기 위해 pool5 layer에서 얻은 feature vector를 사용했다. 식 (5) 아래.

 

loss 함수인 식 (5)에서 t는

 

P를 G로 이동시키기위해 필요한 이동량을 의미하며 식 (6), (7), (8), (9)와 같다.

 

결국 pool5 layer에서 얻은 feature vector와 x, y, w, h를 조정하는 함수의 w를 곱해서

 

bounding box를 조정해주는 regression을 학습한다.

 

R-CNN은 여러 문제점이 있다.

 

입력 이미지의 크기를 224x224로 강제 warping 했기 때문에 이로 인한 성능 저하가 있을 수 있다.

 

약 2,000개의 후보 영역(region proposal)에 대해 순차적으로 CNN을 수행해야해서 학습시간이나 추론(inference)시간이 오래 걸린다.

 

특히 후보 영역 검출 방법이 병렬적인 처리가 빠른 GPU 사용에 적합하지 않았다.

 

selective search는 CPU를 이용하는 알고리즘이다.

 

SPPNet은 R-CNN의 문제를 해결하려고 했다.

 

AlexNet에서 convolutional layer는 sliding window 방식이기 때문에 입력 크기에 영향을 받지 않으며,

 

뒤에 오는 FC layer만 입력 이미지 크기에 영향을 받았다.

 

Distortion due to Warping

위 그림은 crop이나 warping을 하면 왜곡이 발생하는 것을 보여준다.

 

이 문제는 crop이나 warp 대신 conv layer를 거치고 spatial pyramid pooling을 수행함으로써 해결했다.

 

spatial pyramid pooling은 pyramid 연산을 통해 입력 이미지의 크기에 따라 대응할 수 있게 한다.

 

SPPNet은 BoW(Bag of Word) 개념처럼 여러 단계의 pyramid 레벨에서 얻을 수 있는 feature들을 FC layer의 입력으로 사용하고,

 

pyramid 출력을 영상의 크기에 관계없이 사전에 미리 정하면,

 

이미지의 크기에 제한을 받지 않게 된다.

 

BoW 개념이란, 특정 객체를 분류하는데 굵고 강한 특징에 의존적인게 아닌

 

여러개 작은 특징을 사용하여 객체를 잘 구별할 수 있다는 것이다.

 

spatial pyramid pooling은 직전 conv layer 결과를 여러 단계의 pyramid로 나누고,

 

여러 단계에서 얻어진 결과를 concat하여 FC layer의 입력으로 사용한다.

 

여러 단계의 예시를 들면,

 

1단계는 1x1 pooling, 2단계는 2x2 pooling, 3단계는 4x4 pooling 이런식으로 spatial bin이라고 불리는 총 M개의 영역으로 나눈다고 볼 수 있다.

 

아래 그림을 보면 좀 더 쉽게 이해할 수 있다.

SPPNet

z =f(x,y) = xy 라는 곱셈 노드 함수가 있다.

 

이 곱셉 노드의 로컬 그래디언트는 다음과 같다.

 

편미분을 r이라고 표현하면,

 

rz / rx = r(xy) / rx = y

 

rz / ry = r(xy) / ry = x

 

현재 입력값에 대한 loss의 변화량은 로컬 그래디언트에 흘러들어온

 

그래디언트를 각각 곱해주면 된다.

 

곱셈 노드의 역전파는

 

순전파 때의 입력 신호들을 서로 바꾼 값을 곱해서 흘려보낸다.

 

 

'수학' 카테고리의 다른 글

덧셈 노드의 역전파  (0) 2020.09.15
정비례는 비례의 강조  (0) 2017.03.17
좌표평면  (0) 2017.03.17
대응  (0) 2017.03.16
함수의 정의  (0) 2017.03.16

z = x + y 라는 함수에서,

 

입력값은 x, y이다.

 

편미분을 r로 표현해서 rz / rx 값을 구해보자.

 

y가 픽스된 상태에서 x값의 변화에 대해 z에게 어떤 영향을 주는지 보자.

 

x + 1 = z 이런 함수로 가정했을때,

 

x값이 1 증가하면 z도 1 증가하고,

 

x값이 2 증가하면 z도 2 증가한다.

 

이는 rz / rx의 기울기(gradient)가 1이라는 것이다.

 

반대로 x가 픽스된 상태에서 y값의 변화에 대해 z에게 어떤 영향을 주는지 보자.

 

y + 1 = z 이런 함수로 가정했을때,

 

y값이 1 증가하면, z도 1 증가하고,

 

y값이 2 증가하면, z도 2 증가한다.

 

이는 rz / ry의 기울기(gradient)가 마찬가지로 1이라는 것이다.

 

이렇기 때문에 덧셈 노드에 대한 역전파시 rz / rx * rL / rz = 1 * rL / rz가 된다.

'수학' 카테고리의 다른 글

곱셈 노드의 역전파  (0) 2020.09.15
정비례는 비례의 강조  (0) 2017.03.17
좌표평면  (0) 2017.03.17
대응  (0) 2017.03.16
함수의 정의  (0) 2017.03.16

+ Recent posts