👀 코드 보기 , 🤷♀️
이 두개의 아이콘을 누르시면 코드, 개념 부가 설명을 보실 수 있습니다:)
CS231N: Lecture 3 강의를 빠짐 없이 정리하였고, 어려운 개념에 대한 보충 설명까지 알기 쉽게 추가해 두었습니다.
[23] CS231N: Lecture 3 Regularization and Optimization Regularization (1/2) 보러가기
Optimization
자 손실함수 이제 다음 질문은, 실제로 어떻게 해야 하는 것일까?
어떻게 실제 Loss를 줄이는 최적의 W를 찾을 수 있는 걸까?
➡ Optimization(최적화)를 통해 가능하다.
최적화에 대해서 자세히 설명해 둔 포스트를 먼저 읽어보는 것을 추천한다.
[방법]
- “iterative한 방법”사용
- 어떤 명시적인 솔루션을 만들어내서 최적의 솔루션(minima)를 직접 얻어내는 것은 불가능 하기 때문에 이 방법을 사용한다.
임의 탐색: random search
임의로 샘플링한 W들을 엄청 많이 모아놓고 Loss를 계산해서 어떤 W가 좋은지를 살펴보는 것이다.
이는 너무 비효율적 이므로 좋지 않은 방법이다.
[코드 구현] W를 많이 받아서, 임의로 샘플링하고, 그걸 손실함수에 넣는다.
[결과 평가]
이것에는 10개의 클래스가 있고 랜덤 확률은 10%다.
그리고 몇 번의 랜덤 시도를 하면,
우리는 순전히 운으로 W의 어떤 세팅이 15%정도의 정확도가 나온다는 것을 파악할 수 있다.
완전 랜덤 보단 나은 결과이긴 하다.
하지만 최첨단 수준은 95% 이다(아직 좁혀야할 갭이 크다)
➡ 실제에 사용하기엔 턱없이 부족하다.
그담엔 얼마나 잘되는지 봅니다. 스포일러지만, 이건 정말 나쁜 알고리즘이죠. 이걸 쓰면 안됩니다. 적어도 시도할 수 있겠다고 생각은 해볼 수 있죠. CIFAR 10에 대해 랜덤 탐색으로 선형 분류기를 훈련시키는 것 같은 것을요.
경사 따라가기
더 나은 전략은 주변의 기하학을 이용하는 것이다.
더 나은 이해를 위해서 비유를 해보자.
여러분이 아래 풍경에서 계곡바닥으로 내려가는 길을 찾는 상황이라고 가정해보자
이때,
- 여러분: 최솟값을 찾고자 하는 사람
- 풍경의 모든 지점: W 파라미터의 어떤 세팅
- 이 점들의 높이: 손실, W에 의해 발생
- 계곡바닥: 찾고자 하는 W 값
여러분이 이 풍경을 돌아다니는 이 작은 사람이라면,
아마도 여러분은 계곡 바닥으로 내려 가는 길을 바로 알 수는 없다.
그러나 여러분이 할 수 있는 것은 발로 느끼면 그 근처의 기하학적 구조 어떻게 생겼는지 알아내는 것이다.
➡ 여러분이 여기 서 있다면, 어떤 방향이 나를 좀 더 아래쪽으로 데려다 줄 지를 발로 느껴보는 것이다.
➡ 땅의 경사가 어디 있는지, 나를 이 방향으로 내려가게 해 줄 지를 말이다.
➡ 그 방향으로 한발 움직여서 조금 내려가고, 어느쪽이 아래인지를 발로 다시 느끼는 걸 계속 반복해서, 결국 계곡의 바닥에 도착하길 바라는 방법이다.
[경사(gradient)의 의미]
적어도 1차원에서는, 경사는 이 함수의 미분이다.
만약 1차원 함수 f가 있다면, 그건 스칼라 x를 받아서, 어떤 커브의 높이를 돌려준다.
➡ 우리는 이 덕분에 어떤 지점에서든지 경사를 혹은 미분값을 계산 가능하다.
➡ 어느 방향으로든지 작은 걸음 h를 받아서 그 걸음에 대한 함수값의 차이를 비교하는 것이다.
➡ 그리고 걸음 크기를 0으로 하면, 그 지점에서 그 함수의 경사가 나오는 것이다.
[경사 계산법]
이제 어떤 점(\(x\))에서의 경사, 즉 도함수(derivative)를 계산하는 법을 알아보겠다.
- \(f(x+h) - f(x)\): 작은 스텝 , \(h\)가 있고 이 스텝 간의 함수 값의 차이(\(f(x+h) - f(x)\))를 비교해 볼 것이다.
- \(h\)->0: 그 스텝사이즈를 0으로 만들면, 그것이 바로 어떤 점에서의 이 함수의 경사가 된다.
[경사(gradient) 일반화]
실제에서, x(스칼라)가 전체 벡터로 바뀜
➡ x가 벡터이기 때문에 위의 개념을 다변수로 확장시켜야 한다.
- 의미
- 다변수인 상황에서의 미분으로 일반화시켜보면 gradient이고,
- gradient는 벡터 x의 각 요소를 편도함수들의 집합이다.
- 형(shape)
- gradient의 모양(shape)은 x와 같다. (입력의 shape이 (3,)이면 gradient의 shape도 (3,))
➡ 각 x값마다의 기울기를 구한 것이기 때문이다.
- gradient의 모양(shape)은 x와 같다. (입력의 shape이 (3,)이면 gradient의 shape도 (3,))
- 방향의 의미
- 함수에서 “가장 많이 올라가는 방향” 이 된다.(우리의 목표와 반대)
- 그러므로 우리의 목표인 “가장 적게 올라가는 방향”을 얻기 위해선 gradient의 반대방향으로 가면 된다.
수치 미분: numerical gradient
수치미분의 종류인 유한차분에 대해서 이야기해보겠다.
유한 차분 = finite difference methods
그럼 위의 결사를 가장 나이브 (naive)한 방인 유한 차분을 이용해서 계산해보자.
컴퓨터로 위의 gradient를 써먹을 수 있는 가장 단순한 방법 중 하나이다
[유한차분이란?]
- 함숫값의 차를 이용해 미분계수를 근사하는 방법이다.
- 이를 통해 편미분방정식을 푸는 것이다.
- 다. 유한차분계수를 구하는 방법은 다양하며 편미분방정식을 푸는 유한차분법의 결과도 달라진다.
📜 수치 미분(numerical gradient) 더
위의 공식을 바탕으로 하는 미분인 수치 미분은,
(기억이 안날까봐 다시 가져오겠다.)
- 수치적 미분은 해석적 미분(뒤에 설명할 ) 방식으로는 풀 수 없는 문제가 있을 때 수치적 접근을 통해 근사 값을 찾는 방식이다.
- 수치 미분은 현실세계에 미분을 적용하기 위한 실용적인 학문으로 수치해석과목에서 자세하게 배울 수 있다.
- 수치 미분은 아주 작은 차분으로 미분함으로써, 미분 공식과 근사적으로 가까운 결과를 낼 수 있게 해준다.
[특징]
- 느림
- 어림값임
- 작성하기 쉬움
[적용]
- current W: 파라미터 벡터
- 이 W를 사용하면 1.25정도의 Loss를 가짐
- gradient “dW”: 이를 구하는 것이 우리의 목표
- W와 모양은 같음
- gradient의 각 요소: 우리가 그쪽 방향으로 아주 조금(h) 이동했을 때 Loss의 변화.
- W의 첫번쨰 요소 계산
- [W + h (first dim)]
- W의 첫번째 요소(0.34)에 아주 작은 값 h를 더해 보고, loss를 다시 계산해 본다.
- 여기에서 알 수 있는 것은, 우리가 첫 번째 요소를 조금 움직이면 Loss가 1.2534에서 1.25322로 감소한다는 것이다.
- [gradient “dW”]
- 극한 식을 이용해서 FDM으로 근사시킨 gradient를 구합니다.
- 극한 식을 이용해서 FDM으로 근사시킨 gradient를 구합니다.
- [W + h (first dim)]
- W의 나머지 요소 계산
- 첫번째 요소와 똑같이 계산하면 된다.
- 첫 번째 요소의 값은 원래대로 돌려놓고 두 번째 요소를 아주 조금 증가시킵니다.
- [Second dim & gradient]
- [Third dim & gradient]
- [반복….]
- [Second dim & gradient]
[평가: 시간 문제]
- 시간이 너무 오래걸여서, 이건 실제로는 정말 끔질한 방법이다.
- 이 함수 f가 CNN같이 엄청 큰 함수였다면 엄청 오래 걸렸을 것이다.
- 파라미터 W가 여기서는 10개뿐이 없지만, 엄청 크고 깊은 신경망이라면 파라미터가 수천 수억개 일 수도 있다.
- gradient 하나를 구하려고 수천개의 함수값을 일일이 다 계산해야 함
- 이건 엄청 느리고 좋지 않음.
➡ 실제로는 이런식으로 gradient를 계산하지 않는다.
해석적 미분: Analytic gradient
위의 시간문제를 해결하기 위해 해석적 미분을 사용한다.
우리는 단지 손실함수를 적어놓고 미분을 사용하면 gradient가 계산되어 나온다.
해석적으로 푸는 것이 수치적으로 푸는 것 보다 더 효율적이다.
➡ 일단 더 정확하기도 할 뿐더러, 식 하나로 계산할 수 있기 때문에 계산도 훨씬 더 빠르기 때문
📜 해석적 미분(Analytic gradient) 이란?
위의 공식을 바탕으로 하는 미분인 해석적 미분은,
(기억이 안날까봐 다시 가져오겠다.)
- 종이와 펜을 이용해 논리적인 전개로 풀 수 있는 문제를 말함.
- 수학시간에 배운 미분: 해석적 미분
- 오차를 포함하지 않는 ‘진정한 미분’ 값
[특징]
- 빠름
- 정확함
- 실수하기 쉬움
[W를 통해 gradient dW를 구하는 방법]
- W 의 모든 원소를 순회하는 것이 아니라 기울기에 대한 해석적 표현이 뭔지 먼저 알아낼 것이다.
- 그리고 그것을 기록하고 W에서 직접 이동하여 한 단계에서 dw 혹은 기울기를 계산한다.
- 즉, gradient를 나타내는 식이 뭔지만 먼저 찾아내고,
- 그걸 수식으로 내타내서 한번에 gradient dW를 계산해 버리는 것이다.
➡ 훨씬 더 효과적임
[수치미분과 해석적 미분]
- 수치적인 gradient는 간단하고 그럴듯 하긴 하지만 실제로는 사용하지 않는다
- 실제 gradient 계산을 구현할때는 해석적인 방법을 이용한다.
- ✨ gradient check
- 수치적 gradient는 디버깅 툴로는 유용할 수 있음
- 작성한 미분 코드가 잘 작동하는지를 수치적 미분으로 확인가능
- 하지만 디버깅에 이용하고자 할 땐 시간문제를 고려하여 파라미터의 스케일을 줄이는 편이 좋음.
코드
이 세 줄짜리 간단한 알고리즘은 엄청 크고 복잡하고 싶은 신경망 알고리즘을 어떻게 학습시킬지에 대한 핵심 아이디어를 담고 있다.
while True:
weight_grad = evaluate_gradient(loss_fun, data, weights)
weight += -step_size * weight_grad # parameter update
[코드 해석]
1) Gradient Descent 에서 먼저 W를 임의의 값으로 초기화
2) 그리고 Loss와 gradient를 계산
3) 가중치를 gradient의 반대 방향으로 업데이트
➡ 그러니 -gradient 방향으로 아주 조금씩 이동할 것이고 이걸 영원히 반복하다 보면 결국에는 수렴할 것입니다.
[용어 해석]
step_size
: Learning rate라고도 함- 그 -gradient 방향으로 얼마나 나아가야 하는지를 알려줌.
- 실제 학습을 할때 여러분이 정해줘야 하는 가장 중요한 하이퍼파라미터 중 하나
-gradient
: 내려가는 방향- gradient가 함수에서 증가하는 방향이기 때문에 -gradient를 해야 내려가는 방향이 됨
경사 하강법
= gradient descent method
강의에선 명확하게 정리가 되어있지 않아 더 자세히보기를 클릭해 먼저 경사하강법을 익히고 아래 강의 정리를 보는 것을 추천한다.
여기 그림을 보면 2차원 공간의 예시를 보자.
- 그릇 모양: 손실함수
-
Loss의 높낮음: 빨간 부분< 초록 부분 < 파란 부분 < 초록 부분
- 이제 여기서 임의의 점에 W를 설정해 두겠다.
- 이제 우리는 -gradient를 계산할 것이고 이를 통해 우리는 결국 loss가 가장 낮은 지점에 도달할 것입니다.
- 아래 그림은 그 과정을 담은 그림으로 그리고 파라미터가 최저점으로 휘어져 들어가는 것을 볼 수 있다.
하지만 지금까지 본 예제는 아주 단순한 방법이다.
매 스텝마다 -gradient의 방향으로만 이동했을 뿐이다.
우리는 Update Rule을 갖은 여러 optimizer를 통해 조금 더 효율적으로 loss의 최저점을 찾을 것이다.
새롭게 업데이트된 CS231n의 ppt에는 옵티마이저에 대해 세세하게 설명이 되어있는데 아쉽게도 강의(2015년 기준)가 올라와있지 않다.
그래도 내가 정말 세세하게 정리해 뒀으니 아래 링크를 정독하면 슬라이드만 봐도 98%는 이해할 수 있을 것이다.
[Optimizers] 아래 방법들의 핵심은 Gradient Descent라는 기본적인 알고리즘을 사용하고 있고,
다음 스텝을 결정하기 위해서 이전의 모든 스텝의 gradient정보를 이용하는 것이다.
SGD
[기존 손실함수의 한계]
- 시간문제
- 실제로는 N(계산해야하는 데이터의 개수)이 엄청 커질수도 있다.
- ➡ 따라서 Loss를 계산하는건 정말 오래 걸리는 작업이 된다.
- 업데이트 비효율성
- 사실 gradient 는 선형 연산자 이기 때문에, 실제 gradient를 계산하는 과정을 살펴보면, Loss는 그저 각 데이터 Loss의 Gradient의 합이라는 것을 알 수 있다.
- 그러니 gradient를 한번 더 계산하려면 , N개의 전체 트레이닝 셋 을 한번 더 돌면서 계산해야 하는 것이(비효율적이다)
➡ 해결: SGD(stochastic gradient descent)
[SGD]
- 전체 데이터 셋의 gradient과 loss를 계산하기 보다는 Minibatch라는 작은 트레이닝 샘플 집합으로 나눠서 학습하는 것이다.
- Minibatch: 보통 2의 승수로 정하며 32, 64, 128 많이 사용함
- 따라서 이 작은 minibatch를 이용해서 Loss의 전체 합의 “추정치”와 실제 gradient의 “추정치”를 계산하는 것임.
- 거의 모든 DNN알고리즘에 사용되는 기본적인 학습 알고리즘
[코드]
- 임의의 minibatch를 만들어내고,
- minibatch에서 Loss와 Gradient를 계산
- 그리고 W를 업데이트함.
➡ Loss의 “추정치” 와 Gradient의 “추정치” 를 사용하는 것입
while True:
data_batch = sample_training_data(data,256) # sample 256 example
weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
weights += - step_size * weights_grad # perform parameter update
[웹데모]
Linear classifier를 이용해서 SGD로 어떻게 이것을 학습시키는 지에 대한 웹 데모를 준비했다.
링크 열기
- 환경 설명
- 초록, 파랑, 빨간 배경: 세개의 카테고리 클래스
- 여기 초록, 파랑, 빨간 점: 트레이닝 샘플
- 방향: 갹 세개의 클래스 스코어가 증가하는 방향
- 변화 설명
- W를 조절: 결정 경계가 회전
- Bias를 조정: 하면 결정경계가 회전하지 않고 위아래로만 움직입니다.
- 스텝사이즈를 변경: GD를 돌랄 떄 크게 움직임
Image Feature
- 이미지의 특징을 추출하는 것이다.
[기존 방법의 한계]
기존 Linear classifier의 방식은 실제 Raw 이미지 픽셀을 입력으로 받는 방식이었다.
➡ 이는 Multi-Modality과 같은 문제를 일으켜 좋은 방법이 아니다.
➡ 실제로는 영상 자체를 입력으로 사용하는 것은 성능이 좋지 않다.
[해결]
그래서 DNN이 유행하기 전에 주로 쓰는 방법은 두가지 스테이지를 거치는 방법이다.
- 첫번째: 먼저, 이미지를 받아들임.
- 두번째: 이미지의 다양한 피쳐 표현을 계산.
- 아마도 이미지에 나타난 모양과 관련한 다른 여러 종류의 정량적인 것들을 계산.
➡ 그럼 이미지의 피쳐 표현 (feature representation) 이 나타남. - 픽셀 자체를 넣는 대신 이 이미지의 피쳐 표현이 선형 분류기로 들어갑니다.
- 아마도 이미지에 나타난 모양과 관련한 다른 여러 종류의 정량적인 것들을 계산.
[이미지 피처의 동기]
- 여기에서의 모티브는 , 여기 그림과 같은 트레이닝 셋이 있다고 해 보면
- 이런 데이터 셋의 경우에는 Linear한 결정 경제를 그릴 방법이 없다.
- 만약 우리가 적절하게 특징 변환을 해본다면(이경우에는 극좌표계로 바꾼 것),
그 복잡하던 데이터가, 변환 후에 선형으로 분리가 가능하게 바뀜
- 만약 우리가 적절하게 특징 변환을 해본다면(이경우에는 극좌표계로 바꾼 것),
- 여기서의 목표: 해결해야 할 문제에 대해 뭐가 맞는 정량을 계산하는 피처 트랜스폼인지 알아내는 것
- 이미지에 대해서는, 픽셀을 극좌표계로 바꾸는 것은 말이 안되지만 이미지의 피쳐 표현을 작성해 볼 수 있는 있다.
color histogram
특징 변환의 한 예
이미지 전체적으로 어떤 색이 있는지를 나타냄
[방법]
이미지에서 Hue 값만 뽑아서 모든 픽셀을 각 양동이에 넣음
1) 각 픽셀에 해당하는 색의 양동이(하늘색 histogram)에 넣음
2) 각 양동이에 들어있는 픽셀의 개수를 셈
[효과]
이는 이미지가 적체적으로 어떤 색인지를 알려줌
➡ 이 개구리의 예를 보면 초록색이 많은걸 알 수 있음
➡ 자주색이나 붉은색은 별로 없음
Histogram of oriented gradients (HOG)
NN이 뜨기 전에 인기있었던 또 다른 특징 벡터중 하나
Local orientation edges(부분마다의 모서리)를 측정
HOG는 영상 인식에서 정말 많이 활용한 특징벡터임
엄청 오래전에 사용했던 것이 아님.
[방법]
1) 이미지를 8x8로 픽셀을 나움.
2) 8x8 픽셀 지역 내에서, 가장 지배적인 edge의 방향을 계산
3) edge directions을 양자화해서 양동이에 넣음
4) 그리고 다양한 edge oreirentations에 대한 히스토그램을 계산
➡ 그러면 전체 특징 벡터는, 각각의 모든 8x8 지역들이 가진 “edge orientation에 대한 히스토그램” 이 됨.
[효과]
전반적으로 어떤 종류의 edge정보가 있는지를 나타냄
이미지를 여러 부분으로 지역화해서, 지역적으로 어떤 edge가 존재하는지도 알 수 있음
➡ 그래서 이 개구리의 경우를 보면, 이파리 위에 앉아있는 것을 파악 가능
➡ 그리고 이파리들은 주로 대각선 edge를 가지고 있음
bag of words
자연어처리(NLP)에서 영감을 받음
[방법]
NLP에서의 bag of words: 어떤 문장이 있고, BOW에서 이 문장을 표현하는 방법은 바로 문장의 여러 단어의 발생 빈도를 세서 특징 벡터로 사용하는 것임.
➡ 이 직관을 그대로 이미지에 적용한 것
이미지에 적용을 위해 그래서 “시각 단어(visual words)” 정의 필요
- 1) 세분화
- 엄청 많은 이미지를 가지고, 그 이미지들은 임의대로 조각냄
- 2) 군집화
- 그리고 그 조각들을 K-means와 같은 알고리즘으로 군집화
- 이미지내의 다양한 것들을 표현할 수 있는 다양한 군집들을 만들어 냄
- 3) 시각 단어(visual words) 집합인 Codebook 만들기
- 군집화 단계를 거치고나면, 시각 단어(visual words)는 빨간색, 파랑색, 노랑색과 같은 다양한 색을 포착해냄
- 다양한 종류, 다양한 방향의 oriented edges 또한 포착 가능.
- 이런 edges들을 데이터 중심적인 방법을 통해 얻어낸 것임
- 4) 이미지 인코딩
- 그러면 어떤 이미지가 있으면, 이 이미지에서의 시각 단어들의 발생 빈도를 통해서 이미지를 인코딩 가능.
[효과]
이 이미지가 어떻게 생겼는지에 대한 다양한 정보를 제공
Image feature vs ConvNet
[Image classification의 pipleline]
5~10년 전까지의 이미지 처리법
- 1) 이미지를 입력받음
- 2) 계산한 특징들을 한데 모아 연결
- 3) BOW나 HOG와 같은 다양한 특징 표현을 계산
- 4) 출된 그 특징들을 Linear classifier의 입력으로 사용
➡ 제가 어느정도 단순화시키긴 했지만, 대부분은 일반적으로 이런 직관임
[특징]
특징이 한번 추출되면 feature extractor는 classier를 트레이닝하는 동안 변하지 않음
트레이닝 중에는 오직 Linear classifier만 훈련이 됨
[CNN이나 DNN에서의 사용 차이점]
실제로 크게 다르지 않음
유일하게 다른점이 있다면 이미 만들어 놓은 특징들을 쓰기 보다는 데이터로부터 특징들을 직접 학습하려 한다는 것
➡ raw 픽셀이 CNN에 그대로 들어가고 여러 레이어를 거쳐서 데이터를 통한 특징 표현을 직접 만들어냄
➡ Linear classifier만 훈련하는게 아니라 가중치 전체를 한꺼번에 학습하는 것
더 알아보기