인공지능/이론 정리

배치용 교차 엔트로피 오차 구현하기

고등어찌짐 2022. 1. 11. 00:56

밑바닥부터 시작하는 딥러닝 교재에서 미니배치로 학습할 시의 교차 엔트로피 오차 함수를 구현하면서, 이해되지 않았던 부분을 정리했다. 

 

현재 데이터셋의 shape 은 train_x : (60000, 748) , train_y : (60000, 10) 이다. 

미니배치 10의 사이즈로 학습하므로 한번 학습할 때의 학습, 평가 셋의 크기는 minibatch_train_x : (10, 784), 

minibatch_train_y : (10, 10) 에 해당한다. 


첫번째, 예측값과 레이블이 원 핫 인코딩으로 주어지는 경우

 

위와 같이 예측값 p 와 레이블 y 가 원 핫 인코딩의 shape으로 미니배치로 주어질 때 다음과 같이 구현할 수 있다. 

# y, p 의 형태가 원 핫 인코딩인 경우
def cross_entropy(y, p) :

  # 데이터가 1개만 존재하는 경우에도 계산가능하도록 reshape
  if p.ndim == 1 :
    p = p.reshape(1, p.size)
    y = y.reshape(1, y.size)

  batch_size = y.shape[0]  
  return - np.sum(y * np.log(p + le-7)) / batch_size

ndim == 1 인 경우를 체크하는 이유는 데이터가 1개만 존재하는 경우에도 데이터가 여러개 있을 때와 똑같이 계산할 수 있도록 reshape 해주는 과정이다. 

예를 들어서, n번째 데이터 하나에 대한 예측 결과값 pn 이 [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ] 라고 한다면 reshape 을 통해 

[[ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ]] 의 형태로 수정할 수 있다. 이는 예측해야할 데이터가 여러개 존재할 때의 넘파이 배열 형식, [[0번째 데이터의 원핫], [1번째 데이터의 원핫], .... ]과 같다. 

 


두번째, 정답은 정수형태로 출력은 원 핫 인코딩으로 주어졌을 때

 

처음에 책 예제를 실행하려 할 때 자꾸 넘파이 배열 크기가 맞지 않아 어디서 에러가 나는지 오랫동안 찾았다. 정답과 출력의 형태가 모두 정수로 주어진다고 착각했던 것이 에러의 이유였다. 이 경우는 레이블은 [ 1, 3, 4, 5... ] 와 같은 정수 형태, 모델의 예측 값은 위 첫번째 방법과 같은 원 핫 인코딩으로 주어졌을 때 구현하는 방법이다. 

# p 는 원 핫, y가 하나의 숫자로만 주어진 경우
def cross_entropy(y, p) :
  if p.ndim == 1 :
    p = p.reshape(1, p.size)
    y = y.reshape(1, y.size)

  batch_size = y.shape[0]
 
  return - np.sum(np.log(p[np.arange(batch_size), y] + 1e-7)) / batch_size

앞서 했던 착각때문에 마지막 줄의 p[np.arange(batch_size), y] 가 제대로 실행되지 않았고, 이해하는데 시간이 오래 걸렸다. 

 

이 식을 이해하기 위해 간단한 예시 데이터를 생성했다. 

# y 정답 레이블, p 예측값
y = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
p = np.array([1, 0, 2])

 

예시 데이터를 3개만 생성했으므로 배치 사이즈는 y.shape[0] 인 3 이 될 것이다. 

( 미니배치 개념을 적용한다면 무수히 많은 데이터 중, 3개의 데이터만 떼온 것이다. )

batch_size = y.shape[0]

 

이제 다음 식을 이해할 수 있었다. 

y[np.arange(batch_size), p]
# 결과값 array([2, 4, 9])

 

np.arange(batch_size) 는 batch_size 수 만큼 차례대로 수를 넣어 배열을 생성해준다. 예를 들어 batch_size 가 3 이라면 np.array([0, 1, 2]) 가 생성되는 것이다. 이는 배열 y 의 row 넘버를 가리키는 역할을 한다. 
비슷하게, p 의 역할은 배열 y 의 column 을 가리킨다. 즉, [ y[ 0, 1 ], y[ 1, 0 ], y[ 2, 2 ] ] 를 표현하게 된다.
더 자세하게 말하면 옆의 배열 중 1번째 원소 y[ 1, 0 ] 는 y 배열의 0번 행 중 1번째 인덱스 즉 1 을 의미하게 된다. 
그렇기때문에 실행 결과가 array([2, 4, 9]) 가 나오게 되는 것이다. 
 

# 참조

밑바닥부터 시작하는 딥러닝