9.1 부동소수점과 수치 오차

실습 · 연습문제 2개

Kahan 보정 합 kahan-sum

float32 배열의 원소를 앞에서부터 하나씩 더하면, 합이 커질수록 작은 원소의 아래쪽 자릿수가 반올림으로 사라진다. Kahan 보정 합(compensated summation)은 덧셈마다 잃은 부분을 보정 변수 cc에 모아 다음 덧셈에서 되돌려 준다.

y=xi−c,t=s+y,c=(t−s)−y,s=t.y = x_i - c, \qquad t = s + y, \qquad c = (t - s) - y, \qquad s = t.

t−st - s는 이번 덧셈에서 실제로 더해진 양이고, 여기서 yy를 빼면 반올림으로 잃은 부분(의 음수)이 남는다.

함수 kahan_sum(a)를 완성한다.

  • a는 1차원 float32 배열이다. 모든 중간 계산을 float32로 한다(np.float32(0)에서 시작하고 원소를 float64로 바꾸지 않는다).
  • 반환값은 float32 스칼라다.
  • np.sum, math.fsum을 쓰지 않는다.

테스트는 float64로 계산한 합과 비교해, 순서대로 더한 합보다 오차가 크게 작은지 확인한다.

주의: 보정 식 c = (t - s) - y를 대수적으로 정리하면 0이 된다. 수학적으로 같은 식이 부동소수점에서는 다르다는 것이 이 알고리즘의 핵심이다.

안정한 log-softmax stable-log-softmax

분류 모델의 cross-entropy는 logits z∈RK\mathbf{z} \in \mathbb{R}^K의 log-softmax

log⁡softmax(z)k=zk−log⁡∑j=1Kezj\log \mathrm{softmax}(\mathbf{z})_k = z_k - \log \sum_{j=1}^{K} e^{z_j}

를 쓴다. 정의대로 np.log(np.exp(z) / np.exp(z).sum())을 계산하면 logits가 크면 exp가 overflow해 nan이, 작으면 underflow해 -inf가 나온다.

함수 log_softmax(z, axis=-1)를 완성한다.

  • z는 임의 shape의 배열이고, axis 방향으로 log-softmax를 계산한다. 반환 shape은 z와 같다.
  • z = [1000, 1001, 1002]나 z = [-1000, 0]처럼 큰 값·작은 값이 섞여도 nan이나 inf 없이 정확한 값을 내야 한다.
  • scipy.special.logsumexp를 쓰지 않고 numpy만으로 구현한다.

힌트: 모든 zjz_j에서 같은 상수 mm을 빼도 log-softmax는 변하지 않는다. m=max⁡jzjm = \max_j z_j로 고르면 지수의 인자가 모두 0 이하가 된다. 축을 유지하려면 keepdims=True를 쓴다.