ML,DL

[cs231n] 이미지 분류 : 데이터 기반 방법론, k-Nearest Neighbor, train/val/test 구분

yeeunnnn 2026. 1. 30. 15:01

🚨cs231n 2017 강의를 듣고 작성하였습니다.

🚨해당 게시글에 포함된 이미지 중 출처가 쓰여있지 않은 이미지는 모두 직접 그렸습니다.

 

1.1. Convolutional Neural Network

: CNN 또는 Deep Learning이라고 부른다.

  • DNN(Deep Neural Network)의 한 종류로, 하나 또는 여러 개의 convolutional layer와 pooling layer, fully connected layer들로 구성된 신경망(neural network)이다.
  • CNN은 2차원 데이터의 학습에 적합한 구조를 가지고 있다. 영상 내 객체 분류, 객체 탐지 등 다양한 응용 분야에 폭넓게 활용되는 DNN의 대표적 모델 중 하나이다.

🌟 앞으로 CNN이 무엇인지, 어떤 법칙이 있는지, 어떤 선례가 있는지, 이 모델의 최근 동향(2017년 기준)은 어떠한지를 살펴볼 것이다.

🌟 딥러닝에 관한 모든 알고리즘을 제대로 이해하는 것이 목표!

🌟 어떤 Neural network 모델을 사용했을 때 어떻게 작동하는지 정확하게 이해

🌟 아키텍쳐 선택이 어떤 영향을 미치는지 이해


1.2. Image classification(이미지 분류), Data-driven approach(데이터 기반 방법론), Pipeline(파이프라인)

1.2.1. Image Classification(이미지 분류)

출처 : https://cs231n.stanford.edu/
출처 : https://cs231n.stanford.edu/

위 그림의 이미지 분류 모델은 하나의 이미지와 여러개의 분류가능한 라벨이 있다.

이미지는 컴퓨터에서 3차원 배열의 값으로 나타난다.
이 예시에서 고양이 이미지는 가로 600픽셀, 세로 800픽셀로 구성되어있고, Red, Green, Blue(RGB) 3개의 색상 채널이 있다. 따라서 이 이미지는 600x800x3개의 픽셀로 구성되어있다. 각 픽셀의 값은 0~255범위의 정수값이다. → 이미지는 0~255 정수 범위의 값을 가지는 Width(너비)xHeight(높이)x3(RGB)크기의 3차원 배열이다.

이미지 분류 문제는 이 수많은 값들을 “cat”이라는 하나의 라벨로 변경하는 것이 목표이다.

 

이미지를 분류하는 일은 컴퓨터 비전의 관점에서 생각해보면 해결해야하는 문제들이 있다.

출처 :https://aibroker.tistory.com/entry/3-Image-Classification1-Introduction

  • Viewpoint variation(시점 변화) : 객체의 단일 인스턴스는 카메라에 의해 시점이 달라질 수 있다.
  • Scale variation(크기 변화) : 비주얼 클래스는 대부분 그것들의 크기의 변화를 나타낸다.
  • Deformation(변형) : 많은 객체들은 고정된 형태가 없고, 극단적인 형태로 변형될 수 있다.
  • Occlusion(폐색) : 객체들은 전체가 보이지 않을 수 있다. 때로는 물체의 매우 적은 부분(매우 적은 픽셀)만이 보인다.
  • Illumination conditions(조명 상태) : 조명의 영향으로 픽셀값이 변형된다.
  • Background clutter(배경 분규) : 객체가 주변환경에 섞여 알아보기 힘들게 된다.
  • Intra-class variation(내부클래스의 다양성) : 분류해야 할 클래스는 범위가 큰 것들이 많다. 예를 들어 의자의 경우, 매우 다양한 형태의 객체가 있다.

→ 좋은 이미지 분류기는 각 클래스 간의 감도를 유지하면서 동시에 이런 다양한 문제들에 대해 변함없이 분류할 수 있는 성능을 유지해야한다.

1.2.2. Data-driven approach(데이터 기반 방법론)

Data-driven approach (데이터 기반 방법론) : 먼저 컴퓨터에게 각 클래스에 대해 많은 예제를 주고 나서 이 예제들을 보고 시각적으로 학습할 수 있는 학습 알고리즘을 개발하는 것

  • 코드를 통해 직접적으로 모든 것을 카테고리로 분류하기보다는 좀 더 쉬운 방법이다.
  • 라벨링이 된 이미지들 training dataset(학습 데이터셋)이 처음 학습을 위해 필요하다.

1.2.3. The image classification pipeline(이미지 분류 파이프라인)

  • Input(입력) : 입력은 N개의 이미지로 구성되어있고, K개의 별개의 클래스로 라벨링되어있다. 이 데이터를 training set으로 사용한다.
  • Learning(학습) : 학습에서 할 일은 트레이닝 셋을 이용해 각각의 클래스를 학습하는 것이다. 이 과정을 training a classifier 혹은 learning a model이란 용어를 사용해 표현할 수 있다.
  • Evaluation(평가) : 새로운 이미지에 대해 어떤 라벨로 분류되는지 예측해봄으로써 분류기의 성능을 평가한다. 새로운 이미지의 라벨과 분류기를 통해 예측된 라벨을 비교할 것이다. 직관적으로 많은 예상치들이 실제 답과 일치하기를 기대하며, 이것을 ground truth(실측자료)라 한다.

1.3. Nearest Neighbor 분류기

1.3.1. Nearest Neighbor Classifier(최근접 이웃 분류기)

[이미지 분류 데이터셋의 예시]

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

CIFAR-10 : 60,000개의 작은 이미지로 구성되어 있고, 각 이미지는 32x32 픽셀 크기이다. 각 이미지는 10개의 클래스(airplane, automobile, bird, ...) 중 하나로 라벨링되어 있다. 이 60,000개의 이미지 중에 50,000개는 학습 데이터셋(트레이닝 셋), 10,000개는 테스트 셋으로 분류된다. 위 그림에서 각 10개의 클래스에 대해 임의로 선정한 10개의 이미지들의 예를 볼 수 있다.


첫 번째 열은 테스트 셋의 이미지 중 하나이며, 나머지 열(NN1~NN19)은 이 테스트 셋에 대해서 트레이닝 셋에 있는 이미지 중 픽셀값 차에 따른 상위 10개의 최근접 이웃 이미지이다.

50,000개의 CIFAR-10 트레이닝 셋이 주어진 상태에서 나머지 10,000개의 이미지에 대해 라벨링 하는 것을 가정해보자. 최근접 이웃 분류기는 하나의 테스트 이미지를 모든 학습 이미지와 비교하고 라벨 값을 예상한다. 4번째 행의 airplane 학습 이미지에 대한 결과를 확인해보면, 10개의 이미지 중 5개만이 같은 클래스로 검색된 반면, 5개의 이미지는 같은 클래스로 분류되지 않았다.

 

[두 개의 이미지를 분류하는 방법]

1. 이미지를 각각 픽셀값으로 비교하고 그 차이를 모두 더하는 것
→ 두 개의 이미지가 주어지고 그것들을 I1,I2 벡터로 나타냈을 때, 벡터간의 L1 distance(L1 거리)를 계산하는 것

(두 이미지 벡터(행렬)의 각 성분마다 차를 계산하고, 그 차를 전부 더해서 하나의 숫자를 얻는다. 두 이미지가 똑같을 경우에는 결과가 0)

2. 기하학적으로 두 벡터간의 유클리디안 거리를 계산하는 것

각 픽셀간의 차를 구하지만 각각에 제곱을 취하고 전부 더한 다음에 최종적으로 제곱근을 취한다.

 

 

[Nearest Neighbor Classifier 코드]

CIFAR-10 데이터를 메모리로 불러와 4개의 배열에 저장한다. 각각은 학습 데이터와 그 라벨, 테스트 데이터와 그 라벨이다.
아래 코드에 Xtr(50,000 x 32 x 32 x 3)은 트레이닝 셋의 모든 이미지를 저장하고 1차원 배열인 Ytr(길이 50,000)은 트레이닝 데이터의 라벨(0부터 9까지)을 저장한다.

Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # 제공되는 함수

# 모든 이미지가 1차원 배열로 저장
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # 50000 x 3072 크기의 배열
Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # 10000 x 3072 크기의 배열
nn = NearestNeighbor() # Nearest Neighbor 분류기 클래스 생성
nn.train(Xtr_rows, Ytr) # 학습 이미지/라벨을 활용하여 분류기 학습
Yte_predict = nn.predict(Xte_rows) # 테스트 이미지들에 대해 라벨 예측

print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) )

일반적으로 평가 기준으로서 accuracy(정확도)를 사용한다. 정확도는 예측값이 실제와 얼마나 일치하는지 그 비율을 측정한다.

앞으로 해당 강의를 통해 만드는 모든 분류기는 데이터(X)와 데이터의 실제 라벨(y)을 입력으로 받는 train(X,y) 형태의 함수가 있다. 또한, 새로운 데이터로부터 라벨을 예측하는 predict(X) 형태의 함수가 있다.

 

다음은 앞의 형식을 만족하는 L1 거리를 이용한 간단한 NNC의 구현이다.

import numpy as np

class NearestNeighbor(object):
  def __init__(self):
    pass

  def train(self, X, y):
    """ X is N x D where each row is an example. Y is 1-dimension of size N """
    # nearest neighbor 분류기는 모든 학습 데이터를 기억
    self.Xtr = X
    self.ytr = y

  def predict(self, X):
    """ X is N x D where each row is an example we wish to predict label for """
    num_test = X.shape[0]
    # 출력 type과 입력 type이 갖게 되도록 확인
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    for i in xrange(num_test):
      # i번째 테스트 이미지와 가장 가까운 학습 이미지를 L1 거리(절대값 차의 총합)를 이용하여 찾음
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      min_index = np.argmin(distances) # 가장 작은 distance를 갖는 인덱스를 찾음
      Ypred[i] = self.ytr[min_index] # 가장 가까운 이웃의 라벨로 예측

    return Ypred

 

위 코드를 실행해보면 해당 분류기는 CIFAR-10에 대해 정확도가 38.6% 밖에 되지 않는다는 것을 확인할 수 있다. 임의로 답을 결정하는 것(10개의 클래스가 있으므로 10%의 정확도)보다는 낫지만, 사람의 정확도(약 94%)나 최신 컨볼루션 신경망의 성능(약 95%)에는 훨씬 미치지 못한다.

1.3.2. K- Nearest Neighbor(KNN) 분류기

KNN 분류기의 방법은 학습 데이터 셋에서 가장 가까운 하나의 이미지만을 찾는 것이 아니라, 가장 가까운 k개의 이미지를 찾아서 테스트 이미지의 라벨에 대해 투표하도록 하는 것이다. (여기서 k=1인 경우, 원래의 Nearest Neighbor 분류기가 된다. 객관적으로 k값이 커질수록 분류기는 이상점(outlier)에 더 강인하고, 분류경계가 부드러워진다.

출처 : https://cs231n.stanford.edu/

Nearest Neighbor 분류기와 5-Nearest Neighbor 분류기의 차이 예시를 위 그림을 통해 확인해보자.

2차원 점(데이터)과 5개의 클래스(red,blue,green,yellow,purple)를 사용하였다. 색칠된 부분들은 해당 위치의 데이터가 어떤 클래스로 분류될 것인가를 L2 거리를 사용한 분류기를 통해 정해진 것이다.
NN 분류기의 경우 '결정 경계(decision boundary)'가 울퉁불퉁하게 형성된 것을 확인할 수 있으며, 5-NN 분류기는 일반화가 잘 되어 결정 경계가 부드러운 것을 확인할 수 있다.


1.4. Validation sets, Cross-validation, hyperparameter 튜닝

1.4.1. Hyperparameter 튜닝을 위한 검증 셋(validation set)

k-nearest neighbor 분류기는 k를 정해줘야한다. k와 같이 여러가지 거리함수(L1 norm, L2 norm, …)도 선택해야하고 이러한 다양한 선택들을 hyperparameter라 한다. 따라서 hyperparameter를 정하는 일은 문제의존적(problem-dependent)이다.

특히 hyperparameter 값을 조정하기 위해 테스트셋을 사용하면 절대 안된다. 실제로 알고리즘을 평가할 때인 맨 마지막 단 한 번을 제외하고는 절대 쳐다봐서는 안 된다. 만약 test set을 사용한다면 모델의 hyperparameter 들이 테스트 셋에서는 잘 동작하도록 튜닝이 되어 있지만, 실전에서 모델을 사용(deploy)할 때 상당히 성능이 낮아지는 것을 확인할 수 있다.(overfitting)

1.4.2. hyperparameter들을 튜닝하는 올바른 방법

출처 : https://cs231n.stanford.edu/

 

우리가 갖고있는 트레이닝 셋을 두 개로 쪼개서 검증 셋(validation set)으로 불리는 약간 적은 수의 트레이닝 셋과 나머지로 나눈다. 검증 셋은 hyperparameter들을 튜닝할 때, 가짜 테스트셋으로 활용한다.

→ 학습데이터셋을 트레이닝 셋과 검증 셋으로 나누고, 검증 셋을 활용하여 모든 hyperparameter들을 튜닝하자.

 

예를 들어, CIFAR-10에서는 다음과 같이 나타낼 수 있을 것이다. 아래의 과정을 통해 어떤 k 값이 잘 동작하는지를 그래프로 그려볼 수 있다. 그 뒤 가장 잘 동작하는 k 값으로 정하고, 실제 테스트 셋에 대해 한 번 평가를 하면 된다.

# Xtr_rows, Ytr, Xte_rows, Yte 는 이전과 동일하게 갖고 있다고 가정

Xval_rows = Xtr_rows[:1000, :] # 앞의 1000 개를 검증용으로 선택
Yval = Ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :] # 뒤쪽의 49,000 개를 학습용으로 선택
Ytr = Ytr[1000:]

# 검증 셋에서 가장 잘 동작하는 hyperparameter 들을 찾기
validation_accuracies = []

for k in [1, 3, 5, 10, 20, 50, 100]:
  # 특정 k 값을 정해서 검증 데이터에 대해 평가할 때 사용
  nn = NearestNeighbor()
  nn.train(Xtr_rows, Ytr)
  
  # k를 input으로 받을 수 있도록 변형된 NearestNeighbor 클래스가 있다고 가정
  Yval_predict = nn.predict(Xval_rows, k = k)
  acc = np.mean(Yval_predict == Yval)
  print 'accuracy: %f' % (acc,)

  # 검증 셋에 대한 정확도를 저장
  validation_accuracies.append((k, acc))

1.4.3. Cross- validation(교차 검증)

출처 : https://cs231n.stanford.edu/

 

학습 데이터 셋의 크기가 작을 경우, 조금 더 정교한 방식으로 교차 검증이라는 hyperparameter 튜닝 방법을 사용한다. 어떤 k값이 좋은지를 여러가지 검증셋에 대해 시험해보고 평균 성능을 확인한다.

위와 같이 5-fold 교차 검증에서는

1) 학습데이터를 5개의 동일한 크기의 그룹(fold)으로 쪼갠다.

2) 4개를 학습용으로, 1개를 검증용으로 사용한다.

3) 어떤 그룹을 검증셋으로 사용할지에 따라 iteration(반복), 성능을 평가, 각 그룹에 대해 평가한 성능을 평균낸다.

 

[실제 활용]

교차 검증은 계산량이 매우 많아지기 때문에, 실제로 사람들은 교차 검증보다 하나의 검증 셋을 정해놓는 것을 선호한다. 보통은 학습 데이터의 50~90% 정도를 학습 용으로 쓰고 나머지를 검증 데이터로 활용하는데, 검증 데이터셋의 크기는 여러 가지 변수들에 의해 영향을 받는다. 예를 들어, hyperparameter 개수가 매우 많다면, 검증 데이터셋의 크기를 늘리는게 좋을 것이다. 검증 셋에 있는 데이터의 개수가 매우 적다면 (수백 개 정도), 교차 검증 방법을 사용하는 것이 더 안전하다. 보통은 3-fold, 5-fold, 10-fold 교차 검증을 주로 많이 사용한다.


1.5. Nearest Neighbor의 장단점

  • 장점
    1. 방법을 이해하고 구현하는 것이 매우 쉽다.
    2. 분류기를 학습할 때 단순히 학습 데이터셋을 저장하고 기억만 해놓으면 되기 때문에 학습시간이 전혀 소요되지 않는다.
  • 단점
    1. 학습 시의 계산량이 없는 것은 테스트할 때 모든 학습 데이터 예시들과 비교를 해야되기 때문에 계산량이 매우 많아지는 것으로 보상된다.
    2. 이미지가 매우 고차원이기 때문에 실제 이미지 분류 문제 세팅에서는 효과적이지 않다.

→ 보통 테스트할 때 얼마나 효율적인지에 관심이 많이 있고, 학습에 소요되는 시간이 얼마인지는 크게 중요하게 생각하지 않았다. 뉴럴 네트워크는 학습할 때 매우 많은 계산량을 필요로 하지만, 학습이 끝나면 새로운 테스트 샘플을 분류하는데 매우 적은 계산만으로도 수행할 수 있다. 실제 환경에서는 후자가 더 바람직하다.