본문 바로가기
ML & DL/CS231n (Stanford Univ.)

[CS231n] Lecture 3 | Loss Functions and Optimization

by 남르미누 2020. 10. 24.

저번 강의에서 배운 것을 정리해보자.

 

인식에서의 어려운 점을 살펴보았고, 데이터 중심의 방식에 초점을 맞춰보았다.

그리고 이미지 분류에 대해 배웠고 왜 이미지 분류가 어려운지,

컴퓨터가 보는 것과 사람이 보는 것의 차이가 있다는 것도 배웠다.

분류를 어렵게하는 조명의 변화, 변형에 대해 다뤘고 왜 어려운지를 배웠다.

 

데이터 중심 접근 방법 중에서 가장 단순한 방법인 KNN 분류기에 대해서도 배웠다.

어떻게 KNN을 이용해서 학습데이터를 가지고

각 카테고리 클래스를 분류하는 결정경계선을 학습시킬 수 있는지도 배웠다.

 

cross validation에 대해서 배웠고,

train, validation, test 셋으로 나눠서 하이퍼파라미터를 찾는 법도 배웠다.

마지막으로 Linear classification에 대한 간략한 개요에 대해서만 배웠다.


지금부터 실제로 행렬 W를 어떻게 만드는지를 배우도록 하겠다.

즉, 최적의 W 구하기 !!

 

최적의 알고리즘과 최적의 W를 결정하기 위해서는

만든 W가 좋은지 나쁜지를 정량화 할 방법이 필요하다.

 

 

W를 입력받아서 각 score를 확인하고

이 W가 지금 얼마나 나쁜지를 정량적으로 말해주는 것이

바로 " 손실함수 (loss function) " 이다.

 

이번 강의를 통해 image classification에서 쓸만한 몇 가지 손실함수에 대해 알아보도록 하자.


손실함수를 정의하자면,

예측모델 f와 정답 값 Y를 입력으로 받아서

이 train 샘플을 얼마나 나쁘게 예측하는지를 정량화시켜준다.

그리고 최종 Loss인 "L"은 우리의 데이터 셋에서 각 N개의 샘플들의 Loss의 평균이 된다.

 

즉, 어떤 알고리즘이든 가장 일반적으로 진행되는 과정은

어떤 X와 Y가 존재하고 여러분이 만들 파라미터 W에

W가 얼마나 좋은지를 정량화하는 손실함수를 만드는 것이다.

 

결국 W의 공간을 탐색하면서 train 데이터의 Loss를 최소화하는 W를 찾는 것이다.


< Multi Class SVM Loss >

 

이전에 봤던 SVM은 두 개의 클래스만을 다룬다.

하지만 위의 SVM은 여러 클래스를 다루기 위한 이진분류의 SVM의 일반화된 형태이다.

 

 

먼저, 정답인 카테고리와 나머지 카테고리를 구별한다.

정답 카테고리의 score와 나머지 카테고리의 score를 각각 비교한다.

 

만약 올바른 카테고리의 score가 나머지 카테고리의 score보다 더 높으면,

그리고 그 격차가 safety margin 이상이라면,

Loss는 0이 된다.

(여기서 safety margin은 1로 두었다.)

 

이런 식으로 나머지 카테고리의 score값과 비교한 모든 값들을 합치면

그것이 바로 한 이미지의 최종 Loss가 되는 것이다.

그리고 전체 train 데이터 셋에서 그 Loss들의 평균을 구한다. (=Average Loss)

 

max(0,s_j - s_yj + 1)

 

s_j는 나머지 카테고리의 score이고, s_yj는 정답 카테고리의 score이다.

1은 safety margin이다.

 

"Hinge Loss"

 

 

이런 류의 손실함수를 "hinge loss" 라고 부른다.

x축은 S_Yi이고 실제 정답 클래스의 score이다.

y축은 Loss 값이다.

 

정답 카테고리의 점수가 올라갈수록 Loss가 선형적으로 줄어든다.

이 Loss는 0이 된 이후에도 safety margin을 넘어설 때까지 줄어든다.

Loss가 0이라는 것은 클래스를 잘 분류했다는 뜻이다.

 

이 Loss가 말하고자 하는 것은

정답 score가 나머지 다른 score보다 높으면 좋다는 것이다.

정답 score는 safety margin을 두고 다른 스코어들 보다 훨씬 더 높아야 한다.

충분히 높지 않다면 Loss가 높아지게 된다.

 

 

여기에서 2.9라는 숫자가

"얼마나 분류기가 이 이미지를 나쁘게 분류하는지"에 대한 척도이다.

 

 

평균 Loss를 구하면 대략 5.3이 나오는데

이는 우리의 분류기가 5.3점 만큼 이 train 셋을 나쁘게 분류하고 있다는

"정량적 지표"가 되는 것이다.

 

이 때,

safety margin을 몇으로 지정해주어야 하냐에 대해서는 중요치않다.

우리가 궁금한 것은 여러 스코어 간의 상대적인 차이이므로

오로지 정답 스코어가 다른 스코어에 비해

얼마나 더 큰 스코어를 갖고 있는지에 대해 집중해야한다.

 

Loss의 최솟값은 0이고

Loss의 최댓값은 무한대이다.

 

Q. 만약, 스코어들이 거의 0에 가깝고 값이 서로 비슷하다면 Loss 값은 어떻게 되는가?

A. Loss는 (Class의 수 - 1)의 값을 얻을 것이다.

정답이 아닌 클래스들만을 가지고 이용하기 때문에

정답인 클래스의 스코어가 바뀐다고 해서 SVM Loss가 변하진 않는다.

 

 

Q. Loss 함수를 제곱항으로 바꾼다면?

A. 결과는 달라질 것이다.

좋은 것과 나쁜 것 사이의 trade-off를 비선형적인 방식으로 바꿔주는 것이다.

그렇게 된다면 손실함수의 계산 자체가 바뀌게 되어 결과는 달라질 것이다.

하지만 결과가 좋지 않은 것들은 더 좋지 않게 나와서 구분하기가 좋다.

하지만 기존 힌지 함수는 조금 잘못된 것과 많이 잘못된 것 간의 차이가 별로 나타나지 않을 것이다.


 

위 코드는 Multiclass SVM Loss를 코드화 시킨 것이다.

 

margins[y] = 0

 

이 부분은 정답 클래스에 대한 손실을 0으로 하여 굳이 전체를 돌 필요가 없게 해주는 것이다.

전체 합을 구할 때 제외하고 싶은 부분만 0으로 만들어 준다.

 

 

 

Q. Loss가 0이 되게 하는 W는 unique한 것인가?

A. 아니다!

2W와 같이 W의 스케일이 변해도 Loss는 똑같기 때문이다.

 

 

Loss를 0으로 만드는 W를 구하는 것에 초점을 맞추는 것은

오직 train 데이터에만 집중적으로 맞추려는 것과 같다.

train 데이터와 별개로 우리는 결국엔 test 데이터에 적용할 것이기에

train 데이터에 집중적으로 맞추려다보면 Overfitting되는 결과를 초래하게 될 것이다.


이러한 문제점을 해결해주기 위한 방법이 있다.

바로 "Regularization"을 추가적으로 해주는 것이다.

 

 

Loss함수에 Regularization 항을 추가한다.

이는 모델이 더 단순한 W를 선택하도록 도와준다.

더 단순한 것 즉, 더 일반적인 것이 예측 성능이 더 좋기 때문이다.

 

일반적으로 Regularization은 L2를 사용한다.

Weight decay라고도 한다.

L2 Regularization은 가중치 행렬 W에 대한 Euclidean Norm이다.

 

 

위 슬라이드에서 나온 w1와 w2의 경우를 비교해보자.

 

L2는 분류기의 복잡도를 상대적으로 w1, w2 중 어떤 값이 더 매끄러운지 측정한다.

결과적으로 w2를 선호한다.

 

Linear classification에서 w가 의미하는 것은

얼마나 x가 output class와 닮았는지이다.

결국, x의 모든 요소가 영향을 주었으면 하는 것이다.

x의 특정 요소에 의존하기보다는 x의 모든 요소가 골고루 영향을 미치길 원한다면,

L2 Regularization을 통해 더욱 단단해질 수 있다.

 

L1의 경우는 정반대이다.

w1을 선호한다.

L1 Regularization은 가중치 w의 0의 갯수에 따라 모델의 복잡도를 다룬다.

 

따라서,

L1이 복잡하다고 느끼고 측정하는 것은 "0이 아닌 요소들의 갯수"이고

L2는 W의 요소가 전체적으로 퍼져있을 때 덜 복잡하다고 생각하게 된다.

모델을 덜 복잡하게 만들기 위한 것이 Regularization의 궁극적인 목표이다.

 

Hypothesis class 중에서 더 간단한 것을 선택하기 위해서

우리는 model에 penalty를 주는 것이라고 생각하면 된다.

 

{ L1 }

w에 0이 아닌 요소 많음 => 복잡

w에 0이 많음 => 단순

 

{ L2 }

w가 어느 쪽에 치중되어 있을 때 => 복잡

w의 요소가 퍼져있을 때 => 단순


< Softmax >

 

딥러닝에서 자주 사용하는 Multinomial logistic regression, 즉 softmax이다.

SVM보다 딥러닝에서 더 많이 쓰이는 개념이다.

 

지금까지 본 것은 SVM Loss를 Multi class로 확장시킨 아주 일반적인 손실함수일 뿐이다.

Multi class SVM은 score에 해석을 고려하지 않았다.

단지, 정답인 클래스가 정답이 아닌 클래스들 보다 더 높은 스코어를 내기 원하였다.

 

 

Softmax는 score 자체에 추가적인 의미를 부여한다.

특히, 저 수식을 이용해서 스코어를 가지고 클래스별로 확률 분포를 계산하게 될 것이다.

여기에서 softmax라고 불리는 함수를 사용할 것이다.

 

스코어를 전부 이용하는데, 스코어들에 지수를 취해서 양수가 되게 만든다.

그리고 그 지수들의 합으로 다시 정규화 시킨다.

 

그래서 우리는 softmax 함수를 거치게 되면 확률분포를 얻을 수 있고,

그것은 바로 해당 클래스일 확률이 된다.

확률값이므로 모든 값은 0~1의 값이고 전체합은 1이다.

 

Softmax의 Loss는 Cross-entropy Loss이다.

 

" - log (정답클래스확률) "

 

 

log는 단조 증가 함수이다.

그리고 log를 최대화 시키는 것이 그냥 확률값을 최대화 시키는 것보다 쉽다.

그러므로 log를 사용한다.

 

정답클래스인 log P를 최대화 시키는 것은 log P가 높았으면 좋겠다는 것이다.

그런데 손실함수는 "얼마나 좋은지"가 아닌 "얼마나 구린지"를 측정하는 것이기 때문에

log에 마이너스(-)를 붙힌다.

 

요약해보자면, 스코어가 있다면 softmax를 거치고, 나온 확률 값에 -log를 취하면 된다.

 

이를 softmax or multinomial logistic regression 이라고 한다.

 

 

softmax의 최소값 : 0

softmax의 최대값 : 무한대

 

log를 0으로 만드려면 입력값 즉, 정답스코어 값은 극단적으로 높아야한다. (무한대)

log안의 확률이 0, 1이 될 확률이 실제로 드물기 때문에 이론적으로 0~무한대라고 말한다.


 

두 손실 함수간의 차이는 다음과 같다.

 

SVM은 정답스코어와, 나머지 스코어 간의 margin(차이)를 신경썼다.

Softmax는 확률값으로 구해서 정답클래스(-log)에 신경썼다.

 

svm은 일정 선(=margin)을 넘기만 하면 더 이상 성능개선에 신경쓰지 않는다.

하지만, softmax는 최대한 확률을 1로 끌어올리려고 할 것이다.

즉, 더 좋은 성능을 내기 위해 노력할 것이다.

이것이 두 손실 함수간의 차이이다.


지금까지 한 것들을 정리해 보자면,

 

먼저, 입력 x로부터 score를 얻기 위해 Linear classifier를 사용할 것이다.

 

그리고 softmax, svm loss와 같은 손실함수를 이용해서,

모델의 예측값이 정답 값에 비해 "얼마나 구린지"를 측정한다.

 

그리고 모델의 "복잡함"과 "단순함"을 통제하기 위해

손실 함수에 regularization 항을 추가한다.

 

그리고 그걸 모두 합쳐서, 최종 손실 함수가 최소화가 되게 하는

가중치 행렬이자 파라미터인 행렬 W를 구하게 되는 것이다.

 

 

그렇다면

과연 어떻게 실제 Loss 값을 최소화하는 W를 찾을 수 있을까?

 

바로 "최적화(Optimization)"을 사용하면 된다.

 

최적화를 비유를 하자면,

우리가 엄청 큰 계곡을 걷고 있는 것과 같다.

여러분은 다양한 산과 계곡이 있는 골짜기를 거닐고 있는 것이다.

그리고 '산'과 '계곡'과 같은 풍경들이 파라미터 W이고,

여러분이 있는 곳의 '높이'가 바로 Loss이다.

Loss는 W에 따라 변하게 되고, 우리는 W를 찾아야 한다.

 

즉, 최종적으로 우리는 이 골짜기의 밑바닥을 찾아내는 것이다.

 

최적화하는 방법으로 우리는 다양한 iterative한 방법들을 사용한다.

이 방법들은 임의의 지점에서 시작해서 점차적으로 성능 향상시키는 방법이다.


1. 임의 탐색

 

임의로 샘플링한 W들을 엄청 많이 모아놓고 Loss를 계산해서

어떤 W가 좋은지를 살펴보는 것이다.

 

 

=> 절대 사용하면 안되는 방법. 정말 구리다.

이러한 방법이 있다는 것만 알고 넘어가도록 하자.


2. 경사하강법 (Gradient Descent)

 

slope(기울기)란 어떤 함수에 대한 미분값이다.

 

 

gradient는 벡터 x의 각 요소의 편도함수들의 집합니다.

(예를 들어, 입력이 3개면 gradient도 3개)

 

 

gradient의 각 요소가 알려주는 것은 바로

"우리가 그쪽으로 갈 때 함수 f의 경사가 어떤지"라는 것이다.

즉, 우리가 그 쪽 방향으로 조금 이동했을 때 Loss가 어떻게 변하는지이다.

 

이를 유한 차분법으로 계산이 가능하다.

Loss가 조금 감소했다. 계속 반복하여 gradient를 계산하는 것이다.

 

하지만 실제로 이런 식으로 계산하지 않는다.

왜냐하면 하나의 gradient를 얻기 위해 수천개의 함수값들을 일일이 다 계산해야하기 때문이다.

 

그러므로 Loss를 미분하여 gradient를 구한다.

 

 

실제 gradient를 구현할 때는 분석적인 방법을 이용한다.

(즉, 하나씩 찾지 않는다.)

 

gradient가 함수에서 증가하는 방향이기 때문에,

- gradient를 해야 내려가는 방향이 된다. (마이너스를 붙여줘야 한다.)

Learning rate (step size)와 곱해져서 빼주게 된다.

 

가장 기본적인 알고리즘은 바로

매 스텝마다 아래로 내려가는 것이다.

 

 

데이터가 너무 크면 Loss 구하기에 수백만번의 계산이 필요하다.

그러므로 매우 느릴 것이라고 예상된다.

 

그래서 Stochastic Gradient Descent (SGD)를 사용한다.

전체 데이터 셋의 gradient와 loss를 계산하기 보다는

Minibatch라는 작은 train 샘플 집합으로 나눠서 학습하는 것이다.

이는 2의 제곱수로 정해진다.

 

이를 통해 Loss의 전체 합의 "추정치"와 실제 gradient의 "추정치"를 계산하는 것이다.

 

 

이미지가 있으면 여러가지 특징 표현을 계산하는 것이다.

이미지의 모양새와 관련있을 수도 있다.

즉, 하나의 특징 벡터로 만드는 것이다.

 

 

데이터의 좌표계를 바꿈으로써 선형으로 분리할 수 있다.

이런 기법들은 문제를 풀 수 있도록 하려면 어떤 특징 변환이 필요한가를 알아내는 것들이다.

 

 

색에 해당하는 픽셀들을 양동이에 넣고, 양동이들로 이미지의 색을 유추할 수 있다.

 

 

CNN, DNN이 기존 방식과 유일하게 다른 점이 있다면,

이미 만들어 놓은 특징들을 사용하기 보다는

데이터로부터 특징들을 직접 학습하려 한다는 것이다.

 

그렇기에 raw 픽셀이 CNN에 그대로 들어가고

여러 layer들을 거쳐서 데이터를 통한 특징 표현을 직접 만들어 낸다.

 

따라서, Linear classifier만 훈련하는게 아니라 가중치 전체를 한꺼번에 학습하는 것이다.

 

오늘은 여기까지.. :)


(참고자료)

 

leechamin.tistory.com/search/cs231n

 

참신러닝(Fresh - Learning)

이차민의 iOS/ML/DL 공부 블로그 Computer Vision과 iOS개발에 대한 전반적인 공부를 합니다. "Be The First Penguin"

leechamin.tistory.com

lsjsj92.tistory.com/

 

꿈 많은 사람의 이야기

안녕하세요. 이수진이라고 합니다. 이 블로그는 AI(인공지능), Data Science(데이터 사이언스), Machine Learning, Deep Learning 등의 IT를 주제로 운영하고 있는 블로그입니다.

lsjsj92.tistory.com

taeyoung96.github.io/categories/CS231n/

 

CS231n Summary

한 발자국씩 앞으로 나아가자

taeyoung96.github.io

 

댓글