Worth spreading

cs231n (2017) - lecture2 _ 1 (image classification) 본문

카테고리 없음

cs231n (2017) - lecture2 _ 1 (image classification)

annual 2017. 11. 15. 18:08

앞으로 쓸 강의노트들은 파이썬과 numpy에 어느정도 익숙하단 것을 가정하고 진행할 것입니다.


http://cs231n.github.io/python-numpy-tutorial/   // cs231n에서 제공하는 파이썬,numpy 튜토리얼 입니다. 

* 필요한 내용들로만 구성되어 길지 않으니 파이썬과 numpy가 익숙하지 않은 분들이나 오랜만에 다루시는 분들은 한 번씩 봐주시면 좋겠습니다.


 이번 강의에서는 이미지분류(image classification) 문제를 소개하겠습니다. 이미지분류는 이미지를 입력받아 카테고리 내에서 정답을 고르는 문제로 컴퓨터비전(computer vision) 분야에서 주요하게 다루는 core problem이라고 할 수 있습니다. 


가장 간단한 예를 보겠습니다.



우리 인간의 뇌 속에는 고도로 발달된 시각시스템이 탑재되어있기 때문에 우리는 위 사진을 보자마자 이것이 고양이라는 것을 쉽게 알 수 있습니다.

하지만 컴퓨터가 이미지를 바라보는 방식은 우리와는 조금, 아니 많이 다릅니다. 

컴퓨터는 우리처럼 전체적인 시각에서 이미지를 바라보는 것이 아니라 아주 많은 숫자들의 모임으로 바라볼 뿐입니다.

위 이미지는 800 x 600 x 3의 크기를 가지는 이미지입니다. 800x600은 이미지의 2차원 크기를 나타내고 뒤에 있는 3은 컴퓨터에서 색상을 표현할 때 주로 사용하는 방식인 RGB표현법에 의해 나타난 숫자입니다. (RGB : Red, Green, Blue의 조합으로 색상을 표현하는 방법) 

위 이미지의 800x600의 픽셀 하나하나가 3개의 RGB 값으로 이루어져있다는 뜻입니다.

정리하면 컴퓨터는 위 이미지를 1,440,000개(800x600x3)의 숫자로 인식합니다.


그렇다면 컴퓨터가 이 모든 pixel 값들을 가지고 있다면 고양이를 잘 분류할 수 있을까요? 그렇지 않습니다. 


여기서부터 computer vision 분야의 주요한 문제들을 만나게 됩니다.


가장 먼저 view point 문제입니다. 위 사진에 있는 고양이와 같은 고양이라고 하더라도 이 고양이를 다른 각도에서 찍게되면 이미지의 픽셀값은 완전히 다른 값들로 바뀌게 됩니다.

다음과 같은 문제들도 있습니다.



빛의 강도의 차이, 어수선한 배경, 부분적으로 가려지거나 희안한 포즈를 취하고 있는 상황


위의 모든 경우라도 인간은 위 이미지가 고양이라는 것을 쉽게 알 수 있습니다.

하지만 컴퓨터에게 이것을 인식하도록 만드는 것은 정말 골치아픈 일이 될 것이라고 느껴실 겁니다


즉, 우리는 이런 상황들에서도 고양이를 올바르게 인식할 수 있는 알고리즘을 설계해야합니다.(알고리즘을 robust하게 설계한다고 표현합니다.)


그렇다면 다양한 이미지들을 분류할 수 있는 알고리즘을 어떻게 설계해야 할까요? 

예를들어 비행기, 자동차, 새, 고양이, 사슴 이렇게 다섯 가지 사물과 동물을 구별해내는 프로그램을 만들어봅시다.

그런데 이건 정렬이나 최단경로찾기 같은 일반적인 알고리즘들과는 다른 것 같습니다... 무엇을 기준으로 할지도 정해져있지 않고 컴퓨터에게 이렇게 추상적인 걸 시키려고 하니까 딱히 뾰족한 수가 생각이 나지않네요.


그래서 우리는 어린 아이들이 학습을 하는 방법을 써 볼까 합니다. 

우선 컴퓨터에게 위에서 말한 비행기, 자동차, 새, 고양이, 사슴의 다양한 사진들을 각 카테고리( class라고 부르겠습니다 ) 별로 라벨( label )을 달아서 보여줄 겁니다.

그리고 나서 컴퓨터가 각 class별로 모아둔 사진을 보고 그 각각의 사진들을 학습하도록 할 것입니다.

이러한 접근법은 라벨이 달려있는 학습이미지 세트를 기반으로 하기 떄문에  데이터 주도 접근법( data-driven approach )라고 부릅니다. 

( 라벨이 달려있다는 것은 이것이 고양이인지 자동차인지 분류가 돼있다는 것입니다. 즉 이렇게 라벨이 달려있는(labeling 돼있다고 표현합니다) 이미지들을 컴퓨터에게 마구마구 주고 컴퓨터가 이 많은 사진들의 평균적인 특징을 뽑아내는 과정이 학습하는 과정이라고 생각하시면 됩니다).


이것이 앞으로 우리가 사용할 '학습하고 예측하기(Train & predict)' 작전입니다.



첫 번째 Learning Algorithm : KNN(K-Nearest Neighbor)


 KNN알고리즘은 입문용 알고리즘인 만큼 간단한 알고리즘입니다. 


먼저 우리가 사용할 학습 데이터(이미지) 세트를 소개하겠습니다.



우리가 사용할 이미지 데이터는 CIFAR10 이라는 이름을 가진 데이터 세트입니다.  (시파 텐 이라고 부릅니다)

CIFAR10은 딥러닝의 선구자 조프리 힌튼(Geoffrey Hinton) 교수님이 재직하고 계신 캐나다 토론토대학 컴퓨터과학부에서 제공하는 이미지 데이터세트입니다.

학습용으로 무료 배포되는 데이터이니 여러분들도 실습하실 때 유용하게 사용하시길 바랍니다. 

https://www.cs.toronto.edu/~kriz/cifar.html )


CIFAR10 데이터에는 10 개의 class가 있고 각 class마다 학습이미지 5000장, 테스트이미지 1000장 총 6000장씩 해서 총합60000장의 사진이 있습니다.

60000장의 이미지들의 정답이 저장돼있는 라벨도 물론 있구요

. 각 사진들은 32x32x3(RGB) 크기로 이루어져 있습니다. 


 자 그럼 본격적으로 KNN알고리즘을 배워보겠습니다.



KNN 알고리즘은 위와 같은 단계로 진행됩니다.

우선 'train'과정에서 모든 이미지와 라벨을 저장합니다.(CIFAR10에서는 60000만장의 이미지를 불러오는 

그리고 'predict'과정에서는 train을 했을 때 모아 둔 이미지 중에서  test_image와 가장 유사한 이미지를 골라서 반환해줍니다.


그렇다면 유사도를 어떻게 판단할까요?


첫 번째 방법은 L1 distance(Manhattan distance라고도 합니다)라고 불리는 방법으로 각 이미지의 픽셀 간의 차이를 기준으로 합니다.




위는 4x4 행렬을 예시로 L1 distance 연산을 한 결과입니다. test 이미지와 training image 사이의 대응되는 픽셀 값들을 빼고 절댓값을 취해준 후 이 값들을 모두 합친 것이  유사도를 나타내게 됩니다.

우리는 predict할 때 test데이터를 입력 받으면 test데이터 하나에 대한 모든 training 데이터와의 L1 distance를 구하고 이 값이 가장 작은 사진을 정답으로 선택할 것입니다.


이것을 수식으로 나타내면 다음과 같습니다.


d라는 함수는 I(1) 그리고I(2)를 input으로 받습니다. 여기서 I(1)은 test데이터, I(2)는 트레이닝 데이터가 됩니다.(순서가 바뀌어도 상관 없습니다. 절댓값을 취해주기 때문 ! )

그리고 오른쪽 시그마 밑에 있는 p는 픽셀의 개수를 나타냅니다.

위의 CIFAR10 예제의 경우 32x32x3이므로 픽셀의 개수p는 3072가 됩니다.

그러므로 각 픽셀에 대한 | I(1) - I(2) | 3072개를 다 더한 값이 유사도가 됩니다.


자 그럼 이제 파이썬으로 이것을 구현한 코드를 살펴보겠습니다.



우리가 위에서 봤던 대로 train과 predict 함수로 이루어져 있습니다. train 함수(method)애서는 데이터를 그대로 저장하기만 합니다

그리고 predict 함수에서 num_test 변수에 test이미지의 행(row)값을 numpy의 shape 함수를 이용해 구해서 저장해줬습니다.

그 밑의 for문은 행의 개수(num_test)만큼돌고 있으니 distances에는 열 단위(axis=1 부분)로 sum을 해주고 있습니다.


자, 이렇게 KNN 알고리즘을 L1 distance를 이용해 구현해봤습니다.

그럼 이제 우리 알고리즘의 성능을 평가해보겠습니다. 컴퓨터과학 분야에서 알고리즘의 성능평가는 최악의 경우를 나타내는 Big-O notation표기법을 이용해 나타냅니다.

성능에도 다양한 평가 요소가 있지만 보통은 시간복잡도(time complexity)라는 것을 척도로 합니다. 간단히 말해 시간이 얼마나 걸리는 지를 말하는 것입니다.

우리 알고리즘의 train 함수의 시간복잡도는 O(1)입니다. 빅오 1이라는 것은 상수 시간이 걸린다는 것으로 항상 최고의 퍼포먼스를 보여주는 좋은 성능이라고 생각하시면 됩니다. 우리는 이미지를 저장만 하면 되므로 당연히 train을 하는 것은 빠를 것입니다.


그렇다면 predict함수는 어떨까요? predict함수의 시간 복잡도는 O(n) 입니다. 여기서 n은 데이터의 개수를 말합니다. 우리는 predict를 할 때마다 데이터 전체를 훑어야 하기 때문에 빅오는 n이 됩니다.


우리는 L1 KNN 알고리즘의 성능을 평가해봤는데 결론부터 말하자면 위와 같은 성능은 좋지 않습니다.

왜냐하면 대부분의 사람들은 classifier 프로그램이 학습은 느리더라도 predict는 빠르게 해주길 바라기 때문입니다.

그런데 우리의 알고리즘은 학습은 빠른데 predict 가 느리죠.

( 사실 예측과정(predict)이 느리더라도 정확도(accuracy)만 높다면 모두들 환경하겠지만 말입니다 )


우리가 만든 KNN classifier가 어떻게 생겼는지 궁금하니까 한번 시각화해보겠습니다,



위 이미지는 CIFAR10처럼 10개가 아닌 5개의 카테고리를 가진 데이터 셋을 KNN으로 분류한 결과입니다.

다섯 가지 색상으로 각 카테고리를 표현했습니다. 각 색깔들 사이에는 자연스럽게 선이 생기게 되는데요, 이 선을 decision boundary라고 부릅니다.


 이 그래프에서 볼 수 있는 KNN classifier의 문제는 두 가지가 있습니다.

1) 초록색 영역 안에 있는 작은 노란색 섬(?) 

사실 저 노란 부분은 초록색이 돼야 하는게 맞다고 느껴지지 않으신가요?


2) 부분부분 들쭉날쭉한 decision boundary

부드럽지 않은 모습을 보이는 classifier는 예측을 할 때도 불안한 모습을 보여줄 수 있습니다.

위의 노란 섬 문제의 원인도 아마 들쭉날쭉한 decision boundary가 생긴 원인과 비슷할 것 같습니다


그러면 이 문제를 한번 해결해 봐야겠습니다.


아까 제가 KNN의 뜻을 설명해 줄 때 NN( Nearest Neighbor ) 의 뜻만 설명해드렸습니다. K는 뭘 뜻하는지 궁금하지 않으셨나요?

K는 Nearest Neighbour을 얼마나 뽑을지 나타내는 숫자입니다.

즉 가까운 이웃 셋을 고를 거라면 3 Nearest Neighbor이 됩니다. (우리가 지금까지 썻던 KNN은 사실 1NN이었던 거죠!)


여기서 K를 높은 수로 잡아주게 되면 더 많은 의견들을 들을 수 있게 되는 것과 비슷합니다!

아까는 1등의 의견만 들어서 삐죽삐죽한 결과과 나왔다고 볼 수 있지요.

그럼 K를 더 높은 수로 한 결과를 한번 보겠습니다.



위 슬라이드에 써있는 것 처럼 K가 1보다 클 때는 majority vote를 이용합니다. 다수결이란 뜻이지요. K가 10이라면 열 개의 결과 중 가장 많은 표를 받은 class를 정답으로 말하겠다는 것입니다.


자 이제 K=3 일 때를 보겠습니다. 보시다시피 K=1일 때 있던 노란 섬이 없어졌습니다! 그리고 삐죽삐죽했던 녀석들도 어느정도 덜 엇나간듯 보이네요.

K=5일 때는 더 부드러워 진 부분도 있지만 오히려 삐죽해진 부분도 있네요. K가 크다고 무조건 좋은 건 아닌 것 같습니다.


(K=3일 때부터 생기는 하얀 공간은 다수결에서 한 번도 승리하지 못한 부분을 나타냅니다.)




위 사진은 KNN을 CIFAR10 데이터 셋에 적용해 본 결과입니다

K=10으로 했고 왼쪽이 test data, 오른쪽이 predict 결과입니다. K=10이기 때문에 열 가지의 후보가 나왔네요.

초록색은 오답이고 빨간색은 정답입니다. 오답의 개수가 6개로 정답의 개수보다 많습니다.


그렇습니다. KNN은 별로 좋지 않습니다...


그래도 KNN에게 한번만 더 기회를 주도록 하겠습니다.

이번에는 L1 distance가 아닌 L2 distance(Euclidean distance)를 사용해보겠습니다,



수식을 보면 L1과 크게 다를 것은 없습니다. I(1) - I(2)해준 것에 제곱을 하고 다시 루트를 씌운 것 뿐입니다. 제곱을 해줬기 때문에 L1에서 있었던 절댓값 부호는 사라집니다.


또한 제곱을 해줌으로 그래프의 모양 또한 부드러워졌습니다. 두 식이 만들어내는 그래프의 모양이 다르므로 결과 또한 달라질 것입니다.

뭔가 더 안정된 결과를 보여줄 수 있을듯 합니다!(generalize 됐다고 표현합니다)

 ----------------- ----------------- ----------------- ----------------- -----------------

*  그래프가 둥글둥글하다고 L2가 무조건 좋은 것 또한 아닙니다.  *

-> L1 distance의 그래프를 보시면 좌표 축을 경계로 각이 져있습니다. 좌표축에 영향을 받는다는 것을 나타냅니다.

하지만 L2 distance는 좌표축에 상관 없이 일관된 원형을 보여주고 있습니다. 이것은 L2 distance보다 L1 distance가  입력 데이터 모양에 더 큰 영향을 받는다는 것을 보여준다고 해석할 수 있습니다.

따라서 입력데이터의 상세한 특징(feature) 가 중요한 의미를 지닌다면 L1 distance가 더 적합할 것이고    // L2는 부드럽게 펴버리기 때문에 상세한 데이터를 무시하는듯한 상황이 생길 수 있다)

그런 것보다는 그냥 평균적인 (general) 결과를 얻고 싶다면 L2 distance가 더 적합할 것입니다.

 ----------------- ----------------- ----------------- ----------------- -----------------

L1 distance와 L2 distance의 결과를 비교해보겠습니다.



두 이미지를 비교해보면 decision boundary의 모습이 살짝 다르다는 것을 알 수 있습니다.

L1 distance의 경우 decision boundary가 좌표축을 따라가는 성질을 보이고 있습니다. 위에서 말했듯이 L1은 좌표계에 종속적(dependent)하기 때문입니다

반면 L2 distance의 경우 좌표계에 큰 영향을 받지 않는 모습을 보여주고 있습니다.

이렇듯 서로 다른 알고리즘을 선택함으로써 나타나는 기하학적 변화를 시각화해 살펴보는 것은 직관적인 판단력을 늘려주는 좋은 방법 중 하나라고 생각합니다.


때문에 ! 저와 비슷한 생각을 가진 우리 스탠포드 컴퓨터비전 랩에서 KNN 데모 프로그램을 배포했습니다.

http://vision.stanford.edu/teaching/cs231n-demos/knn/


위 사이트로 들어가시면 위 사진과 같이 KNN 알고리즘에서 우리가 설정할 수 있는 숫자들(parameter라고 부릅니다)을 조절해보면서 그래프의 움직임을 실시간으로 볼 수 있습니다!

사이트에서 직접  파라미터를 조절해보면서 기하학적 변화를 느껴보시기 바랍니다.

KL1이나L2 선택에 decision boundary의 변화를 살펴보세요 !


자, 이번에는 머신러닝에서 가장 중요한 문제 중 하나인 하이퍼파라미터(Hyperparameters)문제를 살펴보겠습니다.

하이퍼파라미터 문제란 위 KNN 예제에서 어떤 K값이 가장 좋을지, L1 distance를 사용하는게 좋을지 L2 disetance를 사용하는게 좋을지 등을 판단하는 문제입니다.

이것은 머신러닝에서 아주 중요한 문제이지만 매우 problem-dependent 합니다. 즉 우리가 풀어야 할 문제들의 최적의 하이퍼파라미터는 문제마다 천차만별이라는 뜻입니다.

따라서 우리는 다양한 시도들을 해보고 가장 좋은 결과를 보여주는 파라미터를 사용해야 합니다.


하이퍼파라미터를 결정하는 문제에도 여러가지 방법이 있습니다.

가장 원초적인 것부터 살펴보겠습니다.


Idea#1: 우리가 가진 training dataset에 가장 좋은 결과를 보여주는 파라미터를 선택한다.


이 방법은 나쁘지 않은 성능을 낼 것 같지만 나쁜 성능을 낼 확률이 아주 높습니다. 

우리가 머신러닝 어플리케이션의 목표가 무엇일까요?

우리가 가진 데이터를 잘 예측하기 위해서 만드는 걸까요?

아닙니다. 우리는 한번도 보지 못한(unseen) 데이터를 잘 예측하기 위해서 만듭니다.

우리 머신을 우리 데이터에 너무 꼭 맞게 만들면 새로운 데이터를 보았을 때 오히려 나쁜 결과를 내놓을 수 있게 됩니다.

(training data에 오버피팅(overfitting)된다고 표현합니다.)


Idea#1은 매우 나쁜 아이디어로 절대 이 방법을 쓰면 안됩니다.


Idea #2: 우리가 가진 데이터를 train, test set으로 나누고 test set에 대해서 가장 좋은 성능을 보여주는  파라미터를 선택한다,

위 방법은 Idea #1을 개선한 것처럼 보이지만 사실 별반 다를 것이 없습니다. 다만 이번에는 test set에 오버피팅될 뿐입니다.

따라서 이 방법도 절대 쓰면 안됩니다.



Idea #3: train/validation/test set으로 나눈다. validation set으로 하이어파라미터를 결정하고 이를 test 에서 평가한다.


이 방법도 완벽한 방법은 아니지만 위의 두 아이디어보다는 괜찮습니다. 이번에는 test set을 하이퍼파라미터를 결정하는 도중에는 절대 건드리지 않습니다.

validation set을 이용해 하이퍼파라미터를 조절하고 이 결과를 test set에서 '평가' 합니다.  test set은 평가할 때 한번만 쓰게 됩니다.

결국 test set에 대한 정확도가 우리 머신러닝 어플리케이션의 성능이 됩니다.



Idea #4: Cross validation

training set을 여러 개로 나누고 (fold로 나눈다고 한다), 각 fold 돌아가면서 valdation set 역할을 합니다. 이렇게 해서 나온 결과들의 평균을 이용해 좋은 파라미터를 찾아가는 방법입니다.

ex) 첫 번째 training set은 fold 1,2,3,4 validation set은 fold 5,

      두 번째 training set은 fold 1,2,3,5 validation set은 fold 4,

      . . . . 

이런식으로 해서 나온 결과들의 평균을 취하는 방법이니다.


이 방법은 data set의 크기가 작을 때 유용하게 쓰입니다. 적은 data 개수에도 불구하고 최대한 generalize하게 적용될 수 있는 파라미터를 찾기 위해 노력하는 것이죠.




위 슬라이드에서는 다섯 가지 카테고리로 이루어진 image data set을 KNN으로 이용해 분류한 결과를 보여주고 있습니다.

x축은 k값을 나타내고 y축은 k값에 따른 accuracy의 변화를 나타내고 있습니다. 

hyperparameter setting 방법은 (하이퍼파라미터 튜닝이라고도 한다) Idea #4인 cross validation을 사용했네요. 

5개의 fold로 나누었기 때문에 각 k값마다 y축 방향으로 5개의 점들이 나타나고 있습니다.

그리고 위 그래프에는 이 5개의 점들의 평균값들이 선으로 이어져있습니다.

정확도(accuracy)가 가장 높은 부분은 K가 7 정도일 때로 나타나고 있습니다.

위에서 한번 언급했듯이 K가 높다고 무조건 좋은 건 아닙니다 !


*      위 처럼 우리의 알고리즘을 시각화 해보면 accuracy의 변화양상뿐만 아니라 fold의 분산(variance)도 눈으로 확인할 수 있습니다.

이처럼 알고리즘을 시각화하는 것은 다양한 측면에서 많은 도움을 줍니다.


하 지 만

KNN 알고리즘은 이미지 분류 분야에서 절대 쓰이지 않습니다.


KNN이 쓰이지 않는 첫 번째 이유는 prediction이 매우 느리다는 것이고 두 번째 이유는 픽셀들간의 차(difference)가 이미지의 유사성(혹은 차이)를 잘 나타내지 못하기 때문입니다.

위 슬라이드에는 오리지널 이미지와 살짝식 변화를 준 이미지들이 나타나 있습니다. 그런데 오리지널 이미지에 대한 오른쪽 세 개의 이미지들의 L2 distance 값은 모두 같게 나옵니다. 실제로는 어느정도 다르다고 볼 수 있는 사진들이 완전히 같은 사진들로 인식되는 상황이 발생한 것입니다.



또하나의 문제점은 '차원의 저주'Curse of dimensionality) 입니다. 

이 말은 우리 알고리즘이 잘 동작하도록 만들기 위해서는 우리가 다룰 data의 차원제곱만큼의 학습데이터(training data)가 필요하다는 것입니다.

여기서 차원은 dimension이라고도 하고 feature의 개수라고 볼 수도 있습니다. CIFAR10의 사진에서 dimension은 3072가 됩니다. 

즉 CIFAR10에서 카테고리의 개수가 10개이니 이 알고리즘을 잘 동작하도록 (여기서는 densely하게 구성한다고 표현하기도 합니다) 하려면 10의3072제곱만큼의 training data가 필요한 것입니다. 정말 말도 안되는 숫자이지요.

Computer Science 분야에서 뭐가 됐든 지수적으로 증가하는 것은 좋지 않습니다(exponential growth)



 오늘 배운 내용을 정리해보겠습니다.

* 우리는 이미지 분류를 할 때 라벨링이 돼있는 학습데이터로 학습을 하고 테이트셋을 이용해 우리 알고리즘의 성능을 평가합니다

* KNN classifier는 predict할 때 학습 데이터와 predict할 데이터간의 차이를 이용해 답을 찾습니다.

* 어떤 방법으로 데이터간의 차이를 결정할지, K를 몇으로 할지 이 두 가지가 KNN classifier에서의 하이퍼파라미터 입니다.

* 좋은 하이퍼파라미터를 찾기 위해서 validation set을 사용하고 test set은 마지막에 한 번만 사용합니다.


오늘 배운 KNN(K-Nearest Neighbor) 은 computer vision 분야에서 잘 쓰이지 않지만 머신러닝의 기본적인 특성을 가지고 있기 때문에 첫 시간에 다루기 좋은 주제였다고 생각합니다.

 여기서 또 짚고 넘어갈 것은 different distance matrix(위에서 말한 L1이나 L2) 이용함으로 우린 k-nearest neighbor classifier 알고리즘을 매우 많은 분야에 적용할 있습니다.(벡터나 이미지뿐만 아니라)


예를들어 문장이나 글을 분류하고 싶으면 문장간의 사이의 차이를 간단히 계산해냄으로 k-nearest 알고리즘을 있습니다.


아주 간단한 알고리즘이지만 새로운 문제를 만났을 가장 처음에 시도해보기 좋은 알고리즘으로 알려져있다.




다음 시간에는 선형분류기(Linear classifier)에 대해서 배워보겠습니다. 수고하셨습니다 !

Comments