이전 글) 시퀀스 API를 사용하여 이미지 분류기 만들기 (1)
2021/02/02 - [Deep learning - TensorFlow] - [TensorFlow] 시퀀스 API를 사용하여 이미지 분류기 만들기 (1)
이어서 이미지 분류기 만들기 두 번째. 이번에는 모델을 컴파일해보고 새로운 샘플에 대하여 예측을 시행해 보겠다.
⊙ 모델 컴파일
compile() 메서드를 이용하여 손실 함수와 옵티마이저(Optimizer)를 지정하여 컴파일한다. 부가적으로 훈련과 평가 시에 계산할 지표를 추가할 수 있다.
model.compile(loss="sparse_categorical_crossentropy", optimizer="sgd", metrics=["accuracy"])
먼저 샘플들의 정답을 나타내는 레이블은 0에서 9까지의 정수로 이루어져 있다. (책에서는 타깃 클래스 인덱스라 설명되어있어 이해하기 어려웠다...) 이 클래스는 각 샘플들에 대하여 배타적(독립적)이므로 손실 함수를 "sparse_categorical_crossentropy"(다중 분류 손실 함수)를 사용한다. 배타적이지 않는 경우(ex. One-Hot-Vector[0., 0., 0., 1., 0., 0., 0., 0., 0.])에 대하여 "categorical_crossentropy"를 사용한다. 두 손실 함수는 계산 과정이 같아 실측값은 같게 나오기 때문에 클래스의 배타성 여부에 따라 편한 손실 함수를 선택하면 된다. 대신 다중 분류(Multiclass classification) 문제를 해결하기 위해 출력층에 "softmax"를 넣어 줘야 한다.
만약 (이진 레이블에 대한) 이진 분류를 수행한다면 출력층에 "softmax" 대신 "sigmoid"함수를 사용하고 "binary_crossentropy" 손실을 사용해야 한다.
label | loss | |
binary_crossentropy | 0 or 1 | sigmoid cross entropy |
categorical_crossentropy | [0, 1] or [1, 0] | softmax cross entropy |
sparse_categorical_crossentropy | 0 or 1 | softmax cross entropy |
"binary_crossentropy"는 "logistic regression"이 아니면 거의 쓸 일이 없고, 웬만하면 "categorical_crossentropy"나 "sparse_categorical_crossentropy"를 이용한다.
옵티마이저에 "sgd"를 지정하여 기본 확률적 경사 하강법을 사용하여 모델을 훈련한다. 즉, 후진 자동 미분과 경사 하강법을 이용한 역전파 알고리즘을 수행한다. 마지막으로 분류기이므로 훈련과 평가 시에 정확도를 측정하기 위해 "accuracy"로 지정한다.
⊙ 모델 훈련과 평가
모델을 훈련하기 위해 fit() 메서드를 호출한다.
history = model.fit(X_train, y_train, epochs=30, validation_data=(X_valid, y_valid))
입력 특성(X_train)과 타깃 클래스(y_train), 훈련할 에포크 횟수(default : 1), 검증 세트를 인수로 전달한다.
케라스는 한 에포크가 끝날 때마다 검증 세트를 사용해 손실과 추가적인 측정 지표를 계산한다. 이 지표는 모델이 얼마나 잘 수행되는지 확인하는데 유용하다. 훈련 세트 성능이 검증 세트보다 월등히 높다면 아마도 모델이 훈련 세트에 과대 적합(Overfitting)되었을 수 있다.
신경망을 훈련하면 위의 결괏값을 볼 수 있다. 훈련 에포크마다 케라스는 처리한 샘플 개수와 샘플마다 걸린 평균 훈련 시간(second us/step), 훈련 세트와 검증 세트에 대한 손실(loss/val_loss)과 정확도(accuracy/val_accuracy)를 출력한다. 위의 결과를 확인하자. 먼저 신경망이 훈련될 때마다 손실이 줄어드는 것을 알 수 있다. 30번의 에포크 이후 검증 정확도(val_accuracy)가 88.96% 달성했다. 훈련 정확도와 2.98% 차이로 크지 않아서 과대 적합이 많이 일어나지 않았다.
검증 세트가 없을 경우?)
검증 세트가 없을 경우 어떻게 해야 할까? 검증 세트를 따로 만드는 대신 훈련 세트의 일부분을 이용할 수 있다. validation_data 매개변수에 검증 세트를 전달하는 대신 케라스가 검증에 사용할 훈련 세트의 비율을 지정한다. 예를 들어 validation_split = 0.1로 쓰면 케라스는 검증에 (섞기 전의) 데이터의 마지막 10%를 사용한다.
훈련 세트의 편중)
어떤 클래스는 많이 등장하고 다른 클래스는 조금 등장하여 훈련 세트가 편중되어있으면 모델의 훈련이 제대로 이루어지지 않아 정확도가 낮아진다. 이를 방지하기 위해 fit() 메서드를 호출할 때 class_weight 매개변수를 지정하는 것이 좋다. 적게 등장하는 클래스는 높은 가중치를 부여하고 많이 등장하는 클래스는 낮은 가중치를 부여한다. 케라스는 손실을 계산할 때 이 가중치를 사용한다. 또한 샘플별로 가중치를 부여하고 싶다면 sample_weight 매개변수를 지정한다. (class_weight와 sample_weight가 모두 지정되면 케라스 두 값을 곱하여 사용한다.)
fit() 메서드가 반환하는 history 객체에는 훈련 파라미터(history.params), 수행된 에포크 리스트(history.epoch)가 포함된다. 이 객체의 가장 중요한 속성은 에포크가 끝날 때마다 훈련 세트와 검증 세트에 대한 손실과 측정한 지표를 담은 딕셔너리(history.history)이다. 이 딕셔너리를 이용해 우리가 훈련한 모델에 대한 pandas 데이터 프레임을 만들어 plot() 메서드를 이용하여 학습 곡선을 관찰하자.
pd.DataFrame(history.history).plot()
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()
위의 <그래프1>을 보자. 훈련하는 동안 훈련 정확도와 검증 정확도가 계속 상승하고, 손실과 검증 손실이 감소하는 것으로 보아 모델의 훈련이 제대로 이루어졌다. 또한 검증 곡선과 훈련 곡선이 매우 가까워 과대 적합이 발생하지 않았다. 그래프를 자세히 관찰하면 훈련 초기에 모델이 훈련 세트보다 검증 세트에서 더 좋은 성능을 낸 것처럼 보인다. 하지만 검증 손실은 한 에포크를 마칠 때마다 계산하고, 훈련 손실은 에포크가 진행되는 동안 계산하기 때문에 이는 정확한 해석이 아니다. 훈련 곡선을 에포크의 절반만큼 -x방향으로 평행 이동한 상태에서 관찰해야 정확한 해석이 가능하다.
일반적으로 충분히 오래 훈련하면 훈련 세트의 성능이 검증 세트의 성능을 앞지른다.(훈련 세트로 계속 훈련하기 때문에) 따라서 검증 손실이 여전히 감소한다면 모델이 아직 완전히 수렴되지 않았다고 볼 수 있다. 그러면 계속 훈련을 시켜야 되는데, 케라스에서 fit() 메서드를 다시 호출하면 중지되었던 곳에서부터 훈련을 이어갈 수 있다.
만약 모델의 성능이 만족스럽지 않다면 처음으로 돌아가서 하이퍼파라미터의 값을 변경해야 한다. 가장 먼저 확인해야 할 것은 학습률이다. 학습률이 도움이 되지 않으면 다른 옵티마이저로 테스트 후 학습률을 재조정해야 한다. 여전히 성능이 높지 않다면 층 개수, 층에 있는 뉴런 개수, 은닉층이 사용하는 활성화 함수와 같은 모델의 하이퍼파라미터를 변경한다. 배치 크기와 같은 하이퍼파라미터를 조정할 수 있다.(fit() 메서드를 호출할 때 batch_size 매개변수를 지정할 수 있으며 default 값은 32이다.)
모델의 검증 정확도가 만족스럽다면 배포하기 전 테스트 세트로 모델을 평가하여 일반화 오차를 추정해야 한다. 이때 evaluate() 메서드를 이용한다.(batch_size, sample_weight과 같은 매개변수를 지원한다.)
print(model.evaluate(X_test, y_test))
검증 세트보다 정확도가 더 낮게 나오는 것을 알 수 있다. 검증 세트에서 모델의 하이퍼파라미터를 조정하므로 검증 세트의 정확도가 높은 것이 당연하다. 테스트 세트의 정확도가 낮다고 해서 그것에 맞게 하이퍼파라미터를 조정하면 안 된다. 테스트 세트에 훈련하는 결과가 발생하므로 객관적으로 모델을 평가하기 어렵다.
⊙ 모델을 사용한 예측
모델의 predict() 메서드를 이용해 새로운 샘플에 대해 예측을 만들 수 있다. 새로운 샘플이 없기에 테스트 세트의 처음 3개를 이용했다.
X_new = X_test[:3]
y_proba = model.predict(X_new)
print(y_proba.round(2))
각 샘플에 대해 0에서 9까지 클래스마다 각각의 확률을 모델이 추정했다. 예를 들어 첫 번째 이미지의 경우 클래스 9(앵클부츠)의 확률이 98%, 클래스 5(스니커즈)의 확률이 1%로 추정했다. 클래스 5의 확률은 무시할 정도로 작다고 생각할 수 있다. 만약 가장 높은 확률을 가진 클래스에만 관심이 있다면 predict_classes() 메서드를 사용할 수 있다.
y_pred = model.predict_classes(X_new)
print(y_pred)
print(np.array(class_names)[y_pred])
y_new = y_test[:3]
print(y_new)
이 분류기는 세 개의 이미지를 정확하게 분류했다.
전체 소스 코드)
github.com/Chocochip101/TensorFlow/blob/main/sequence_API(2).py
지금까지 시퀀설 API를 사용해 이미지 분류기를 만들어 보았다. 다음에는 시퀀설 API를 활용해 회귀용 다층 퍼셉트론을 만들어 보자.
⊙ 해당 문서는 '핸즈온 머신러닝(2판) - 오렐리앙 제롱 저'의 책을 공부하며 정리한 글입니다.
book.naver.com/bookdb/book_detail.nhn?bid=16328592
'ML & DL (Machine Learning&Deep Learning) > Deep Learning (DL)' 카테고리의 다른 글
[Deep Learning] ResNet - Deep Residual Learning for Image Recognition (CVPR 2016) (0) | 2021.11.11 |
---|---|
[TensorFlow] Data Scaling의 중요성 (0) | 2021.09.17 |
[TensorFlow] 시퀀스 API를 사용하여 이미지 분류기 만들기 (1) (0) | 2021.02.02 |
[TensorFlow] 경사 하강법(Gradient Descent) (0) | 2021.02.01 |
[TensorFlow] TensorFlow&Keras 개발환경 구축 (0) | 2021.02.01 |
댓글