5.3 NumPy·pandas·SciPy

실습 · 연습문제 2개

반복문 없는 쌍별 제곱 거리 pairwise-sq-dists

점 집합 X∈RN×D\mathbf{X} \in \mathbb{R}^{N \times D}와 Y∈RM×D\mathbf{Y} \in \mathbb{R}^{M \times D}의 모든 쌍에 대해 제곱 유클리드 거리

Sij=∥xi−yj∥2S_{ij} = \lVert \mathbf{x}_i - \mathbf{y}_j \rVert^2

를 담은 행렬 S∈RN×M\mathbf{S} \in \mathbb{R}^{N \times M}을 반환하는 함수 pairwise_sq_dists(X, Y)를 Python 반복문 없이 완성한다. 커널 방법, 최근접 이웃, 분자 사이의 거리 계산이 모두 이 연산에서 시작한다.

인자 shape
X (N, D)
Y (M, D)
반환 (N, M)

두 가지 방법이 있다.

  1. broadcasting: X[:, None, :] - Y[None, :, :]는 (N, M, D) 배열을 만든다. 메모리를 NMDNMD만큼 쓴다.
  2. 전개: ∥x−y∥2=∥x∥2−2x⊤y+∥y∥2\lVert \mathbf{x} - \mathbf{y} \rVert^2 = \lVert \mathbf{x} \rVert^2 - 2\mathbf{x}^\top\mathbf{y} + \lVert \mathbf{y} \rVert^2를 쓰면 행렬 곱 X @ Y.T 하나와 (N, 1), (1, M) 벡터의 broadcasting으로 끝난다. 메모리는 NMNM이다.

테스트는 다음을 확인한다.

  • 반복문으로 계산한 값과 같다.
  • 결과의 shape이 (N, M)이다. 특히 N == M일 때나 D == 1일 때도 맞아야 한다.
  • 2번 방법은 부동소수점 오차 때문에 아주 작은 음수를 만들 수 있다. 거리의 제곱은 음수일 수 없으므로 0 미만의 값은 0으로 잘라 낸다(np.maximum(S, 0)). 같은 점끼리의 거리는 정확히 0 이상이어야 한다.
  • X와 Y는 바뀌지 않는다.

원본을 바꾸지 않는 열별 표준화 standardize-columns

측정값 행렬 X∈RN×D\mathbf{X} \in \mathbb{R}^{N \times D}의 각 열(특성)을 평균 0, 표준편차 1로 바꾸는 함수 standardize(X)를 완성한다.

Zij=Xij−μjσj,μj=1N∑iXij,σj=1N∑i(Xij−μj)2Z_{ij} = \frac{X_{ij} - \mu_j}{\sigma_j}, \qquad \mu_j = \frac{1}{N}\sum_i X_{ij}, \qquad \sigma_j = \sqrt{\frac{1}{N}\sum_i (X_{ij} - \mu_j)^2}

함수는 (Z, mu, sigma)를 반환한다. 학습 데이터에서 구한 mu, sigma를 나중에 시험 데이터에 그대로 적용하기 위해서다(15.4장).

반환 shape
Z (N, D)
mu (D,)
sigma (D,)

이 장의 함정을 모두 피해야 테스트를 통과한다.

  • axis. 열마다의 통계량은 행 방향(axis=0)으로 줄여서 구한다.
  • broadcasting. (N, D)에서 (D,)를 빼면 각 행에서 같은 벡터를 뺀다. (N,)짜리를 빼려 하면 오류가 나거나(N != D) 조용히 틀린다(N == D).
  • 원본 보존. X -= mu처럼 제자리 연산을 쓰면 호출한 쪽의 데이터가 바뀐다. 입력은 바뀌면 안 된다.
  • 정수 입력. 정수 배열이 들어와도 결과는 실수여야 한다. 정수 배열에 제자리 연산으로 실수를 넣으면 잘린다.
  • 상수 열. 모든 값이 같은 열은 σj=0\sigma_j = 0이다. 0으로 나누지 말고 그 열의 sigma를 1로 바꾸어 Z의 해당 열이 0이 되게 한다.