목차
Overview
이번 과제에서는 원본 픽셀을 그대로 사용하는 대신, 이미지에서 hand-crafted feature를 추출한 뒤 분류기를 학습한다.
사용한 feature는 Histogram of Oriented Gradients, HOG와 HSV color histogram이다.
전체 흐름은 다음과 같다.
- 각 이미지에서 HOG feature를 추출한다.
- 각 이미지에서 HSV hue channel 기반 color histogram을 추출한다.
- 두 feature vector를 이어 붙여 하나의 feature vector로 만든다.
- 추출된 feature 위에서 Softmax linear classifier를 학습한다.
- 같은 feature 위에서 TwoLayerNet을 학습한다.
핵심은 이미지 전체를 픽셀값 그대로 넣는 것이 아니라, 분류에 도움이 될 만한 정보를 먼저 뽑아낸 뒤 모델을 학습한다는 점이다.
핵심 아이디어
HOG는 이미지의 경계와 방향 정보를, HSV histogram은 색상 분포 정보를 담는다. 두 feature를 합치면 texture와 color 정보를 함께 사용할 수 있다.
원본 픽셀 기반 입력은 이미지의 의미를 직접 담고 있지 않다.
고양이가 조금 이동하거나 배경이 바뀌어도 픽셀 값은 크게 달라질 수 있고, 반대로 의미가 다른 이미지라도 픽셀 분포가 비슷할 수 있다.
Feature extraction은 이 문제를 조금 줄이기 위한 방법이다.
이미지를 그대로 벡터화하기 전에, 이미지에서 분류에 유용할 만한 패턴을 먼저 계산한다.
이번 과제에서 사용한 feature는 다음 두 가지이다.
- HOG: edge, contour, gradient orientation 같은 형태 정보를 담음
- HSV color histogram: hue channel 기반의 색상 분포 정보를 담음
HOG는 색상 정보는 거의 무시하고 texture와 shape에 가까운 정보를 본다.
반대로 color histogram은 texture는 무시하고 색상이 어떻게 분포하는지를 본다.
따라서 두 feature를 concatenate하면 다음처럼 하나의 feature vector가 된다.
여기서 는 입력 이미지이고, 는 이미지에서 추출한 최종 feature vector이다.
모델은 이제 원본 이미지 가 아니라 feature vector 를 입력으로 받는다.
구현
Feature extraction
hog_feature와 color_histogram_hsv는 각각 이미지 하나를 입력으로 받아 feature vector 하나를 반환한다.
extract_features는 여러 이미지에 대해 feature function들을 적용한 뒤, 각 이미지의 feature들을 이어 붙여 feature matrix를 만든다.
형태를 간단히 쓰면 다음과 같다.
원본 이미지 하나가 형태였다면, feature extraction 이후에는 각 이미지가 하나의 feature vector로 표현된다.
여기서 은 이미지 개수이고, 는 HOG feature dimension과 HSV histogram feature dimension을 합친 값이다.
Feature를 추출한 뒤에는 feature별 평균과 표준편차를 이용해 표준화한다.
여기서 는 번째 feature의 평균이고, 는 번째 feature의 표준편차이다.
이렇게 하면 각 feature가 대략 비슷한 scale을 갖게 되어 학습이 더 안정적으로 진행된다.
Feature 기반 Softmax 튜닝
먼저 추출한 feature 위에서 Softmax classifier를 학습했다.
이전 softmax 과제와 마찬가지로 validation set을 이용해 learning rate와 regularization strength를 튜닝한다.
from cs231n.classifiers.linear_classifier import Softmax
# validation set으로 learning rate와 regularization strength를 튜닝
learning_rates = [1e-6, 2e-6]
regularization_strengths = [5e4, 6e4, 6.5e4, 7e4, 7.5e4]
results = {}
best_val = -1
best_softmax = None
for lr in learning_rates:
for reg in regularization_strengths:
softmax = Softmax()
loss_history = softmax.train(
X_train_feats,
y_train,
learning_rate=lr,
reg=reg,
num_iters=170,
)
y_train_pred = softmax.predict(X_train_feats)
train_accuracy = np.mean(y_train == y_train_pred)
y_val_pred = softmax.predict(X_val_feats)
val_accuracy = np.mean(y_val == y_val_pred)
results[(lr, reg)] = (train_accuracy, val_accuracy)
if val_accuracy > best_val:
best_val = val_accuracy
best_softmax = softmax
# 결과 출력
for lr, reg in sorted(results):
train_accuracy, val_accuracy = results[(lr, reg)]
print(
"lr %e reg %e train accuracy: %f val accuracy: %f"
% (lr, reg, train_accuracy, val_accuracy)
)
print("best validation accuracy achieved: %f" % best_val)
가장 좋은 결과는 다음 설정에서 나왔다.
lr 1.000000e-06 reg 6.000000e+04 train accuracy: 0.411939 val accuracy: 0.431000
원본 픽셀 기반 linear classifier보다 feature 기반 Softmax가 더 좋은 성능을 낸다.
다만 여전히 linear classifier이기 때문에 feature space에서 선형 결정 경계만 만들 수 있다는 한계가 있다.
Misclassification 결과 해석
오분류 결과를 보면 완전히 이상한 결과라기보다는, feature의 한계가 드러나는 경우가 많다.
예를 들어 고양이나 개 이미지가 truck으로 분류되는 경우가 있었다.
모델이 객체의 의미를 이해하는 것이 아니라 HOG와 색상 histogram을 보고 판단하기 때문에, 배경 색상이나 윤곽 패턴이 비슷하면 이런 오분류가 생길 수 있다.
또 하늘을 나는 bird가 airplane으로 분류되는 경우도 자연스럽다.
두 클래스 모두 파란 하늘 배경에 작은 물체가 놓인 형태로 나타날 수 있기 때문이다.
즉 hand-crafted feature는 edge와 color 분포를 잘 잡아내지만, 객체의 semantic meaning까지 직접 이해하지는 못한다.
Feature 기반 TwoLayerNet 학습
다음으로 같은 HOG + HSV feature 위에서 TwoLayerNet을 학습했다.
입력 차원은 원본 픽셀 차원인 3072가 아니라, 추출된 feature dimension이다.
from cs231n.classifiers.fc_net import TwoLayerNet
from cs231n.solver import Solver
input_dim = X_train_feats.shape[1]
hidden_dim = 500
num_classes = 10
data = {
"X_train": X_train_feats,
"y_train": y_train,
"X_val": X_val_feats,
"y_val": y_val,
"X_test": X_test_feats,
"y_test": y_test,
}
net = TwoLayerNet(
input_dim,
hidden_dim,
num_classes,
weight_scale=1e-2,
reg=1e-3,
)
best_net = None
solver = Solver(
data=data,
model=net,
update_rule="sgd",
optim_config={
"learning_rate": 1e-1,
},
lr_decay=0.95,
num_epochs=30,
batch_size=200,
print_every=500,
)
solver.train()
best_net = solver.model
최종 학습 기록은 다음과 같다.
(Epoch 29 / 30) train acc: 0.711000; val_acc: 0.610000
(Epoch 30 / 30) train acc: 0.716000; val_acc: 0.615000
test set에서는 다음 결과가 나왔다.
y_test_pred = np.argmax(best_net.loss(data["X_test"]), axis=1)
test_acc = (y_test_pred == data["y_test"]).mean()
print(test_acc)
0.606
Feature 기반 Softmax의 validation accuracy가 약 43.1%였고, feature 기반 TwoLayerNet의 test accuracy가 약 60.6%까지 올라갔다.
같은 feature를 사용하더라도 비선형 layer를 추가하면 더 복잡한 decision boundary를 만들 수 있기 때문에 성능이 크게 좋아진다.
추가 개념들
초기 학습 실패: regularization을 그대로 가져오면 안 됨
처음에는 선형 Softmax 분류기에서 사용했던 regularization strength를 그대로 TwoLayerNet에 적용했다.
reg = 6e4
이때 학습 결과는 다음과 비슷했다.
초기 loss: 약 2687
이후 loss: 2.302585에 고정
train / validation accuracy: 약 10%
10개 클래스에 같은 확률을 출력하면 softmax loss는 다음 값이 된다.
따라서 loss가 에 고정되고 accuracy가 약 라면, 모델이 사실상 무작위 추측을 하고 있다는 뜻이다.
원인은 reg=6e4가 TwoLayerNet에는 지나치게 큰 값이었던 것이다.
강한 regularization gradient가 weight를 거의 0으로 밀어버리면 모든 class score가 비슷해지고, softmax 결과도 거의 uniform distribution이 된다.
선형 분류기에서 찾은 regularization strength는 그 모델의 weight scale, parameter 수, loss 구현에 맞는 값이다.
따라서 다른 구조의 neural network에 그대로 재사용하면 안 된다.
Regularization과 weight initialization 조정
TwoLayerNet에서는 regularization strength를 크게 낮추고, weight initialization scale을 명시했다.
weight_scale = 1e-2
reg = 1e-3
weight_scale은 초기 weight의 표준편차를 결정한다.
- 너무 작으면 activation과 gradient가 작아져 학습이 느리거나 거의 진행되지 않을 수 있음
- 너무 크면 activation과 score가 과도하게 커져 학습이 불안정해질 수 있음
따라서 weight_scale도 learning rate, regularization strength와 함께 튜닝해야 하는 하이퍼파라미터이다.
작은 learning rate에서의 학습
처음에는 다음 설정을 사용했다.
learning_rate = 1e-3
weight_scale = 1e-2
reg = 1e-3
num_epochs = 10
학습은 정상적으로 진행되었지만 매우 느렸다.
초기 validation accuracy: 약 10%
10 epoch validation accuracy: 약 35.7%
loss도 천천히 감소했다.
2.306 -> 2.289 -> 2.268
정확도가 계속 상승했으므로 모델이 완전히 망가진 것은 아니다.
다만 learning rate가 너무 작아 update 크기가 작았고, 목표 성능에 도달하기 어려웠다.
가중치 업데이트는 다음과 같다.
여기서 가 learning rate이다.
가 너무 작으면 gradient 방향이 맞더라도 한 번의 update에서 이동하는 거리가 지나치게 작다.
Learning rate 증가
이후 learning rate를 다음처럼 키웠다.
learning_rate = 1e-1
기존 에서 로 올렸기 때문에, 같은 gradient에 대한 update 크기는 100배 커진다.
그 결과 초기 몇 epoch만에 validation accuracy가 빠르게 상승했다.
Epoch 1: 49.9%
Epoch 3: 53.5%
Epoch 5: 56.7%
Epoch 8: 58.5%
Epoch 10: 59.6%
Epoch 13: 60.8%
Epoch 15: 62.1%
중간 실험에서는 validation accuracy가 약 까지 올라갔다.
최종 기록 코드에서는 30 epoch 학습 기준으로 validation accuracy , test accuracy 를 확인했다.
큰 learning rate가 안정적으로 동작한 이유
이번 feature에는 학습 전에 표준화를 적용했다.
표준화를 하면 feature별 scale이 대략 평균 0, 표준편차 1 수준으로 맞춰진다.
feature scale 차이가 크면 특정 방향의 gradient만 지나치게 커져 큰 learning rate에서 발산하기 쉽다.
반대로 표준화를 적용하면 각 feature 방향의 gradient scale이 비교적 균형을 이루기 때문에 더 큰 learning rate를 안정적으로 사용할 수 있다.
또 HOG와 HSV histogram은 원본 픽셀보다 이미 분류에 유용한 정보를 담고 있다.
- HOG: 경계, 윤곽, 방향 정보
- HSV histogram: 색상 분포 정보
따라서 TwoLayerNet은 원본 픽셀에서 특징을 처음부터 찾는 것이 아니라, 이미 추출된 feature들의 비선형 조합을 학습하면 된다.
이 때문에 적절한 learning rate를 사용했을 때 빠르게 높은 정확도에 도달할 수 있었다.
하이퍼파라미터별 역할
learning_rate는 한 번의 gradient update 크기를 결정한다.
- 너무 작음: loss와 accuracy가 매우 천천히 개선됨
- 적절함: 빠르고 안정적으로 수렴
- 너무 큼: loss가 진동하거나 발산할 수 있음
이번 실험에서는 보다 이 훨씬 적절했다.
weight_scale은 초기 weight의 표준편차를 결정한다.
- 너무 작음: activation과 gradient가 작아져 학습이 느릴 수 있음
- 너무 큼: activation과 score가 과도하게 커질 수 있음
이번 실험에서는 가 정상적인 학습을 가능하게 했다.
reg는 weight가 지나치게 커지는 것을 억제한다.
- 너무 큼: weight가 0에 가까워져 underfitting 발생
- 너무 작음: train accuracy만 높아지고 overfitting 가능
- 적절함: 일반화 성능 개선
선형 분류기에서 사용한 reg=6e4는 TwoLayerNet에 지나치게 강했다.
이번 실험에서는 reg=1e-3 수준에서 정상적으로 학습되었다.
num_epochs는 전체 학습 데이터를 반복해서 학습하는 횟수이다.
하이퍼파라미터가 잘못된 상태에서는 epoch를 늘려도 의미가 없다.
먼저 loss가 정상적으로 감소하고 validation accuracy가 상승하는 설정을 찾은 뒤 epoch 수를 늘려야 한다.
최종 정리
- 원본 픽셀 대신 HOG와 HSV histogram feature를 사용했다.
- HOG는 형태와 방향 정보를, HSV histogram은 색상 정보를 담는다.
- 두 feature를 concatenate하면 texture와 color 정보를 함께 사용할 수 있다.
- 선형 Softmax에서는 validation accuracy 약 를 얻었다.
- 선형 분류기에서 찾은
reg를 TwoLayerNet에 그대로 사용하면 안 된다. - loss가 에 고정되고 accuracy가 라면 모델이 무작위 추측 상태일 가능성이 높다.
reg,weight_scale,learning_rate는 서로 영향을 주므로 함께 튜닝해야 한다.- 표준화된 HOG + HSV feature에서는 비교적 큰 learning rate를 안정적으로 사용할 수 있었다.
- Feature 기반 TwoLayerNet에서는 최종 test accuracy 약 를 얻었다.