[CS231N] 3. Loss Functions and Optimization

7 분 소요

💡 Iamge Classification
Stanford University “CS231N” 강의를 듣고 정리하였습니다.

자막 : https://github.com/visionNoob/CS231N_17_KOR_SUB (크롬 확장 프로그램 Subtitles for Youtube)
2017 버전 강의 목차 및 슬라이드 : http://cs231n.stanford.edu/2017/syllabus.html
2017 버전 강의 동영상 링크 : https://www.youtube.com/playlist?list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv
2022 버전 강의 목차 및 슬라이드 : http://cs231n.stanford.edu/schedule.html
실습 과제 : https://github.com/cs231n/cs231n.github.io/tree/master/assignments

Lecture 3 : Loss Functions and Optimization

L3_01

2강에서는 가중치 행렬 $W$의 각 행들이 해당하는 플래스의 템플릿이 되고 행렬 $W$의 각 행은 이미지의 픽셀 값해당 클래스사이의 가중치가 되고 이것을 통해 각 이미지에 해당하는 10개의 클래스 스코어를 계산

이 클래스 스코어를 가지고 이미지를 분류하는 것은 좋은 방법이 아님 (개구리 이미지의 경우 개구리 클래스 점수가 $-4.34$ )

어떤 행렬 $W$가 가장 좋은 점수를 내는지 정량화할 필요가 있음 → 행렬 $W$를 입력으로 받아서 각 스코어를 확인하고 얼마나 좋고 나쁜지 정략적으로 말해주는 것이 손실함수

L3_02

트레이닝 데이터 $X$는 알고리즘의 입력값인 이미지이고 $Y$는 예측하고자 하는 값인 레이블(타켓)로 카테고리의 갯수 사이의 정수 값이 됨

다음으로 예측함수 $f$ 와 정답 값 $Y$를 입력받아 얼마나 예측을 못하는지 정량화 하는 손실함수 $L_i$를 정의 → 평균내면 최종 loss인 $L$

\[L=\frac{1}{N}\sum_i L_i(f(x_i, W), y_i)\]


Multi-class SVM

Image Classification에 적합한 손실함수

\[\begin{align} L_i &=& &\sum_{j\ne y_i}\begin{cases}0 &\mbox if s_{y_i} \geq s_j +1 \\ s_i-s_{y_i} +1 &\mbox otherwise\end{cases}\\ &=& &\sum_{j \ne y_i} max(0, s_j-s_{y_i}+1) \end{align}\\\]

$L_i$ 구하는 방법

  1. True인 카테고리를 제외한 나머지 카테고리 $Y$ 즉, 맞지 않는 카테고리 전부 합침
  2. 올바른 카테고리의 스코어와 올바르지 않은 카테고리의 스코어 비교
  • 올바른 카테고리의 점수가 올바르지 않은 카테고리의 점수+margin($S_j+1$)보다 높으면 True인 스코어가 다른 False 카테고리보다 훨씬 더 크다는 것을 의미 → Loss는 0이 됨
  1. 0과 다른 값의 최댓값 $Max(0, value)$의 식으로 손실함수를 만듦

L3_03

이러한 손실함수를 Hinge loss

  • 정답 카테고리의 점수가 올라갈수록 Loss가 선형적으로 줄어듦 → Loss가 0이라는 건 클래스를 잘 분류한 것

L3_04

$L_{cat}=max(0, 5.1-3.2+1)+max(0,-1.7-3.2+1)=2.9+0=2.9$

$L_{car}=max(0, 1.3-4.9+1)+max(0,2.0-4.9+1)=0+0=0$

$L_{frog}=max(0, 2.2+3.1+1)+max(0,2.5+3.1+1)=6.3+6.6=12.9$

\[\begin{align} L &=& & \frac{1}{N}\sum_{i=1}^N L_i \\ &=& & (2.9+0.0+12.9)/3=5.27 \end{align}\]

분류기가 5.27 만큼 이 트레이닝 셋을 안 좋게 분류하고 있다는 정량적 지표

Question : SVM Loss가 가질 수 있는 최댓값과 최솟값은

  • 손실함수가 Hinge Loss 모양이라는 것을 생각해 보면 $\infty$, 0

Question : 파라미터를 초기화하고 처음부터 학습시킬 때, 보통 행렬 $W$를 임의의 작은 수로 초기화 하는데 그렇게 되면 처음 학습시에는 결과 스코어가 임의의 일정한 값을 갖게 됨. 만약 모든 스코어 $S$가 거의 0에 가깝고 값이 서로 비슷하다면 Loss가 어떻게 될까

  • Loss를 계산할 때 정답이 아닌 클래스를 순회하기 때문에 $C-1$ 클래스를 순회하게 되고 비교하는 두 스코어가 비슷하니 Margin 때문에 1이라는 값을 얻게 됨

→ 트레이닝을 처음 시작할때 Loss가 $C-1$이 아니라면 디버깅 문제가 있다는 걸 의미

Question : 손실 함수가 $\sum_{j \ne y_i} max(0, s_j-s_{y_i}+1)$가 아닌 $\sum_{j \ne y_i} max(0, s_j-s_{y_i}+1)^2$이라면 어떻게 될까

  • Hinge Loss는 조금 잘못된 에러와 많이 잘못된 에러를 크게 신경 쓰지 않지만 Squared Hinge Loss는 에러의 값도 제곱이 되기 때문에 에러도 곱절로 정량화 할 수 있음
  • 손실함수를 만들 때 어떤 에러를 신경쓰는지, 어떤 에러가 트레이드 오프 되는 것인지에 따라 손실함수를 설계해야함

Regularization

L3_05

  1. 트레이닝 셋(파란색 점)에 맞춘 분류기(파란색 선)
  2. 새로 들어온 테스트 셋(녹색 네모) → 트레이닝 셋에 맞춘 분류기에만 맞춘 분류기는 테스트 셋을 맞추지 못함

  3. 우리가 의도한 건 녹색 선 → 트레이닝 셋을 잘 맞추지 못하더라도 테스트 셋에서의 높은 성능을 보이는 분류기

이러한 문제를 해결하는 방법이 Regularization

L3_06

Data Loss Term에서는 분류기가 트레이닝 데이터에 fit하게 하고 Loss Function에서 Regularization을 추가해 모델이 좀 더 단순한 W를 찾도록 도와줌

Regularization의 역할

  1. 모델이 더 복잡해지지 못하게 하거나
  2. 모델은 여전히 복잡하지만 sort penalty를 추가하거나 (복잡한 모델을 계속 쓰고싶으면 penalty를 감수해야 함을 의미)

    모델이 트레이닝 데이터 셋에 완벽히 fit되지 않도록 penalty를 부여

오컴의 면도날(Occam’s Razor) : Among competing hypotheses, the simplest is the best

  • 더 일반적인 (더 단순한) 것이 미래에 일어날 현상을 잘 설명할 가능성이 높기 때문

In common use Regularizations

\[L=\frac{1}{N}\sum_{i=1}^N\sum_{j\ne y_i}max(0, f(x_i;W)_j-f(X_i;W)y_i+1)+\lambda R(W)\]
  1. L2 Regularization : $R(W)=\sum_k\sum_lW_{k,l}^2$

    • 가중치 행렬 $W$에 penalty를 부여
  2. L1 Regularization : $R(W)=\sum_k\sum_l|W_{k,l}|$

    • 행렬 $W$가 희소행렬(행렬의 값이 대부분 0인 행렬)
  3. Elastic net (L1+L2) : $R(W)=\sum_k\sum_l\beta W_{k,l}^2+|W_{k,l}|$

예시 $x=[1\ 1\ 1\ 1], w_1=[1 \ 0 \ 0 \ 0], w_2=[0.25 \ 0.25 \ 0.25 \ 0.25]$

  • Linear Regression은 $x$와의 내적이 서로 같기 때문에 $w_1, w_2$는 같음

  • L2 Regresstion에서는 어떤것이 더 조잡한지(매끄러워야함)에 따라 결정되기 때문에 $w_2$가 norm이 작기 때문에 $w_2$를 선호 → $x$의 모든 요소가 영향을 끼치길 바람

  • L1 Regresstion은 행렬 $w$에 0의 갯수에 따라 복잡도가 결정되기 때문에 $w_1$를 선호

Softmax Classifier (Multinomial Logistic Regression)

Multi-class SVM에서는 스코어 자체를 신경 쓰지 않았지만 Multinomial Logistic Regression에서는 스코어 자체에 추가적인 의미를 부여함

socore은 클래스의 정규화 되지 않은 로그확률을 의미하고 수식을 이용해서 스코어를 통해 클래스 별 확률 분포를 계산함

\[P(Y=k|X=x_i)=\frac{e^{s_k}}{\sum_j e^{s_j}}\quad \text{where}\quad s=f(x_i;W)\\ \\ L_i=-\log P(Y=y_i|X=x_i)\\ \\ \text{in summary : } \quad L_i=-\log (\frac{e^{s_{y_i}}}{\sum_j e^{s_j}})\]
  • 스코어 전부를 지수를 취해서 양수가 되고 만들고 $\sum$으로 다시 정규화 시킴
  • softmax에서 나온 확률이 정답 클래스에 해당하는 클래스의 확률, 즉 1로 나타나게 하고 싶기 때문에 $Loss$는$\log(\text{probability of the true class})$ → 손실 함수는 얼마나 잘 못 맞추는지를 측정하는 함수이기 때문에 마이너스를 붙임
  • softmax 함수를 거치면 해당 클래스의 확률 분포를 얻을 수 있음

L3_07

과정 : 스코어 → 지수화 → (합이 1이 되도록) 정규화 → 정답 스코어에만 $-\log$를 씌움

Question : Softmax Loss의 최댓값과 최솟값은

  • 확률 분포를 생각해 보면 정답 클래스의 확률은 1이 되길 원하고 정답이 아닌 클래스는 0이 되길 원함. 정답 클래스에 대한 $\log$확률이기 때문에 $\log(1)=0, -\log(1)=0$ → 고양이를 완벽하게 분류했다면 Loss는 0이 됨
  • Loss가 0이 되려면 실제 스코어는 무한대에 가깝게 높아야하고 지수화와 정규화를 하기 때문에 확률 1(정답), 0(그외)를 얻으려면 정답 클래스의 스코어는 $+\infty$, 나머지는 $-\infty$이 되어야 함
  • 최댓값은 없음

Question : SVM Loss와 마찬가지로 $s$가 모두 0 근처에 모여있는 작은 수일때의 Loss는

  • $-\log(\frac{1}{C}=\log(C))$ → 마찬가지로 초기에 $\log(C)$가 아니라면 디버깅 문제가 있다는 걸 의미

SVM Loss vs Softmax Loss

L3_08

  • SVM Loss : 정답 스코어, 정답이 아닌 스코어 간의 margin에 집중
  • Softmax Loss : 확률을 구해서 $-\log(\text{probability of the true class})$에 집중

  • Multi-class SVM Loss은 앞에 예시에서 car 클래스에서 car score가 다른 클래스보다 훨씬 높았고 그렇기 때문에 car score를 조금 높인하고해서 SVM Loss가 변하진 않음(SVM Loss는 오직 정답과 그 외 클래스의 마진이 얼마나 되는지에만 관심이 있기 때문)
  • 하지만 Softmax Loss는 정답 스코어가 충분히 높고 다른 클래스 스코어가 충분히 낮은 상태에서도 최대한 정답 클래스에 확률을 몰아 넣으려고(언제나 확률을 1로 만듦) 노력하기 때문에 정답 클래스는 양의 무한대로, 그외 글래스는 음의 무한대로 보내려고 노력함
  • SVM의 경우 일정 margin을 넘기면 더이상 성능개선에 신경쓰지 않지만, Softmax의 경우 계속해서 성능을 개선해나감

Optimization

어떻게 해야 실제 Loss를 줄이는 $W$를 찾을 수 있을지를 고민하는 것이 최적화

Strategy #1 : Random Search(임의 탐색)

임의로 샘플링한 $W$를 많이 모아두고 Loss를 계산해서 어떤 $W$가 좋은지 살펴보는 것 → 아주 안 좋은 방법

# assume X_train is the data where each column is an example (e.g. 3073 x 50,000)
# assume Y_train are the labels (e.g. 1D array of 50,000)
# assume the function L evaluates the loss function

bestloss = float("inf") # Python assigns the highest possible float value
for num in range(1000):
  W = np.random.randn(10, 3073) * 0.0001 # generate random parameters
  loss = L(X_train, Y_train, W) # get the loss over the entire training set
  if loss < bestloss: # keep track of the best solution
    bestloss = loss
    bestW = W
  print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)

# prints:
# in attempt 0 the loss was 9.401632, best 9.401632
# in attempt 1 the loss was 8.959668, best 8.959668
# in attempt 2 the loss was 9.044034, best 8.959668
# in attempt 3 the loss was 9.278948, best 8.959668
# in attempt 4 the loss was 8.857370, best 8.857370
# in attempt 5 the loss was 8.943151, best 8.857370
# in attempt 6 the loss was 8.605604, best 8.605604
# ... (trunctated: continues for 1000 lines)

# Assume X_test is [3073 x 10000], Y_test [10000 x 1]
scores = Wbest.dot(Xte_cols) # 10 x 10000, the class scores for all test examples
# find the index with max score in each column (the predicted class)
Yte_predict = np.argmax(scores, axis = 0)
# and calculate accuracy (fraction of predictions that are correct)
np.mean(Yte_predict == Yte)
# returns 0.1555

→ CIFAR-10에서 클래스는 10개 이므로 임의 확률은 10%가 되고 무작위 시행을 거치게 되면 이 예시에서는 15%정도의 정확도를 보임

Strategy #2 : Follow the slope

골자기에서 땅의 경사(Slope)를 느끼면서 어느 방향으로 내려가야 산 아래로 도착하는지 조금씩 걸어 내려가는 방법

Slope(경사)

\[\frac{df(x)}{dx}=\lim_{h \rightarrow 0}\frac{f(x+h)-f(x)}{h}\]

1차원 공간에서의 slope는 어떤 함수에 대한 미분 값을 의미하지만 $x$는 스칼라가 아니라 벡터이기 때문에 다변수함수로 확장시켜야 함

L3_09

Current $W$에 아주 작은 값인 $h$를 더하고 다시 Loss를 구하면 $1.25347$에서 $1.25322$로 감소하고 극한식을 이용해서 FDM으로 근사시킨 gradient를 구함 → 이 과정을 반복

  • 하지만 이 방법은 많은 시간이 걸리기 때문에 사용하지 않음

The loss is just a function of $W$

\[\begin{align} &L=\frac{1}{N}\sum_{i=1}^N L_i+\sum_k W_k^2\\ &L_i=\sum_{j \ne y_i}max(0, s_j-s_i+1)\\ &s=f(x;W)=Wx\\ \\ &\text{want}\ \nabla_W L \end{align}\]

L3_10


$W$의 모든 원소를 순회하는 것이 아니라 gradient를 나타내는 식이 무엇인지 먼저 찾아내고 수식으로 한번에 gradient $dW$를 한번에 계산

앞에서 다룬 수치적인 gradient는 실제로는 사용하지 않지만 디버깅 툴로는 유용할 수 있음 (하지만 수치적 gradient는 느리고 부정확하기 때문에 디버깅에 이용할 때는 파라미터의 스케일을 줄여야 함)

Gradient Descent

# Vanilla Gradient Descent

while True:
  weights_grad = evaluate_gradient(loss_fun, data, weights)
  weights += - step_size * weights_grad # perform parameter update

Loss와 gradient를 계산한 뒤에 $W$를 gradient 반대 방향으로 step_size = Learning rate를 조절하며 최적화 시키는 방법

L3_11

댓글남기기