본문 바로가기
코딩 독학/CS231n

[cs231n] 과제1. KNN classifiers

by 효니루 2020. 10. 29.

KNN classifiers two_loops , one_loops , no_loops distances

정답코드는 많은데, X_train과 X_test 간의 distance를 구할 때 각각의 행렬의 배열이 어떻게 변하는지 초보인 나로서는 이해가 잘 가지 않았다.

 

쉬운 배열을 임의로 만들어서 정답코드의 distance를 구하는공식이 X_train과 X_test간의 원소들간의 계산에 어떻게 적용되는지 눈으로 확인해보았다.

 

two_loops distances

>>실제코드

>>쉽게 이해해보자

1. 먼저 가상의 train, test 샘플을 만든다.

실제 CIFAR10의 train 샘플은 (50000, 32, 32, 3) 차원으로 [50000장의 사진x (32x32 픽셀) x3개의 RGB채널] 로 이루어져있고, 이를 (50000, 32x32x3) 으로 샘플마다 벡터화하여 행마다 서로 다른 사진의 샘플이 되도록 했다. 아래 예시에서는 train은 4개의 사진(행)이 각각 5개의 pixel값(열)을 가진다는 식으로 간단하게 구성해보았다.

 

one_loop distances

two_loops distances는 train샘플과 test샘플간의 L2 distance를 구할 때 , for 문이 2번 쓰인다. 

이를 더 효율적으로 for문을 1번만 써서 KNN classifier를 정의해본다.

>>실제정답코드

>> 쉽게 이해해보자

위의 X_test, X_train 데이터를 그대로 쓴다.

**np.sum(array, axis= )에서 axis=1 이면 같은행, 모든열의 값을 더한다는걸 반대로 생각해서 헤맴 ;;

 

헷갈려서 정리하자면,

sub 은 하나의 test벡터인 test[i]와 4개(모든)의 train벡터 ([0]~[3]) 간의 모든차원(픽셀값들)을 빼주는것이므로

two-loop distance에서 for i 문 안에서 for j문을 0~3까지 모두 실행한 경우의 sub을 한번에 한 것이다.

 

square는 sub의 각 원소를 제곱한 것이고, shape은 (4, 5) 로 4개의 각 행들은 test[i]와 각 train 샘플들간의 연산의 결과이고 열 5는 샘플 안의 픽셀들이 연산된 값을 의미한다.

 

dists[i, :] 에는 i번째 행에 test[i]와 각 train들 간의 최종 L2 distance값을 열에  배치하여 넣겠다는 것으로

np.sum(square, axis=1) 을 통해 square 행렬에서 각 행에서 모든 열의 값, 즉 sub/square를 거친 픽셀연산값들을

더해서 (4, 5) 배열에서 (4, ) 배열로 변환된다.  아래그림에서 시그마부분까지 해주는것이고, p와 q는 test와 train 샘플(벡터), 아래첨자 i=1~n 은 각 샘플의 픽셀(차원)이라고 보면된다.

L2 distance

no_loops distances

for문을 아예 없애고 벡터화시켜서 distance를 구해보자.

이 때는 일종의 트릭이 작용한다.

 

 

X_test의 행을 N개 (test샘플수), X_train을 M개라고 했을때, 최종 dists 행렬의 (N, M) 자리에는 각 N번째, M번째 test, train 샘플간의 distance가 오게 된다. 해당 l2 distance를 구하는 식은 위와 같고, 각 샘플의 d차원 (열) 원소들간에 연산이 일어나게 된다.

loop를 돌지 않기 위해서 L2 distance안의 기본식을 전개하면 원의방정식처럼 X2-2XY-Y2 형태가 나온다.

 

그래서 각각 (X_test)**2 - 2(X_train * X_test) + (X_train)**2 의 각 항을 구해서

행렬간 broadcasting이 일어날 수 있도록, 차원을 조정해주어 최종 dists 행렬을 만드는 것이다.

 

test_square_sum : X_test의 각 원소를 제곱해서 axis=1끼리 더해야 하나의 test샘플 (같은행)의 모든d차원의 픽셀 제곱값을 더한게 된다. 이 결과 (2, 1)행렬이 나오고, [1,1]은 test 1번, [2,1]은 test2번의 X2값이기 때문에, 하나의 test당 M개의 train 샘플과 distance를 구해야 하기 때문에 [:, np.newaxis]를 통해서 (2, 1) 차원을 (2, 4)차원으로 확장시킨다.

minus_2XY : test와 train 샘플들간의 원소끼리의 곱을 구하기 위해 train.T 하여 행렬곱

train_square_sum : X_train의 각 원소를 제곱해서 axis=1끼리 더함. (4, ) 행렬이 나오므로, dists = (2, 4)에서 broadcasting으로 더할 수 있으니 차원변환은 안 해도 된다.

 

댓글