챕터 7과 8은 패션 MNIST 데이터셋을 사용할 것 이다. 이 데이터셋은 10종류의 패션 아이템으로 구성되어 있다.
패션 MNIST 데이터는 워낙 유명하기 때문에 많은 딥러닝 라이브러리에서 이 데이터를 가져올 수 있는 도구를 제공한다. 텐서플로(TensorFlow)를 이용해 이 데이터를 불러올 것이다. 텐서플로도 코랩에서 바로 사용할 수 있다.
keras.datasets.fashion_mnist 모듈 아래 load_data() 함수는 훈련 데이터와 테스트 데이터를 나누어 반환한다. 이 데이터는 각각 입력과 타깃의 쌍으로 구성되어 있다.
훈련 데이터는 60000개의 이미지로 이루어져 있고 각 이미지는 28 * 28 크기이다. 타깃도 60000개의 원소가 있는 1차원 배열이다. 테스트 세트의 크기도 확인해보면 10000개의 이미지로 이루어진 것을 확인할 수 있다.
훈련 데이터에서 몇 개의 샘플을 ㄱ림으로 출력한 결과이다. 이 샘플들의 타깃값을 확인해보자.
패션 MNIST 타깃은 0~9까지의 숫자 레이블로 구성된다. 넘파이 unique()함수로 레이블 당 샘플 개수를 확인해 보자.
0~9까지 레이블마다 6000개의 샘플이 들어 있는 것을 볼 수 있다. 이 과정을 통해서 패션 MNIST 데이터셋을 저장했고, 어떤 종류의 이미지로 이루어져 있는지 알아보았다.
로지스틱 회귀로 패션 아이템 분류하기
이 훈련 샘플은 60000개나 되기 때문에 전체 데이터를 한꺼번에 사용하여 모델을 훈련하는 것보다 샘플을 하나씩 꺼내서 모델을 훈련하는 방법이 더 효율적일 것 같다. 이런 상황에 잘 맞는 방법이 확률적 경사 하강법이다. 확률적 경사 하강법은 여러 특성 중 기울기가 가장 가파른 방향을 따라 이동한다. 만약 특성마다 값의 범위가 많이 다르면 올바르게 손실 함수의 경사를 내려올 수 없기 때문에 각 픽셀이 0~255사이의 정숫값을 255로 나누어 0~1 사이의 값으로 정규화시켜주겠다. 이는 표준화는 아니지만 양수 값으로 이루어진 이미지를 전처리할 때 널리 사용하는 방법이다. reshape() 메서드를 사용해 2차원 배열인 각 샘플을 1차원 배열로 펼치겠다. SGDClassifier는 2차원 입력을 다루지 못하기 때문이다.
변환된 train_scaled의 크기를 확인하면 784개의 픽셀로 이루어진 60000개의 샘플이 준비된 것을 확인할 수 있다. cross_validate함수를 사용해 이 데이터에서 교차 검증으로 성능을 확인해보자.
반복 횟수를 늘려도 성능이 크게 향상되지는 않는다. 만족할 만한 수준은 아니다.
로지스틱 회귀 공식으로 보면 총 784개의 픽셀, 즉 특성이 있으므로 아주 긴 식이 만들어진다. 가중치 개수도 많아졌다. 티셔츠와 바지와.. 이렇게 타겟값이 달라질 때마다 가중치와 절편은 다른 값을 사용해야 한다. 티셔츠와 같은 가중치를 사용한다면 바지와 티셔츠를 구분할 수가 없기 때문이다. SGDClassifier 모델은 패션 MNIST 데이터의 클래스를 가능한 잘 구분할 수 있도록 이 10개의 방정식에 대한 모델 파라미터(가중치와 절편)를 찾는다.
이렇게 10개의 클래스에 대한 선형 방정식을 모두 계산한 다음에는 소프트맥스 함수를 통과하여 각 클래스에 대한 확률을 얻을 수 있다.
인공신경망
앞서 로지스틱 회귀를 표현한 그림과 비슷하다. 클래스가 총 10개이므로 z10까지 계산한다. z1~z10까지 계산하고 이를 바탕으로 클래스를 예측하기 때문에 신경망의 최종 값을 만든다는 의미에서 출력층이라고 부른다.
인공 신경망에서는 z값을 계산하는 단위를 뉴런이라고 부른다. 하지만 뉴런에서 일어나는 일은 선형 계산이 전부이다! 이제는 뉴런이란 표현 대신에 유닛이라고 부르는 사람이 더 많아지고 있다. 인공 신경망은 x1~x784를 입력층이라고 부른다.
텐서플로(Tensorflow)와 케라스(Keras)
텐서플로는 구글이 오픈소스로 공개한 딥러닝 라이브러리이다. 코랩에는 이미 텐서플로가 설치되어 있기 때문에 다음처럼 간단히 임포트하여 사용할 수 있다.
import tensorflow as tf
텐서플로에는 저수준 API와 고수준 API가 있다. 바로 케라스가 텐서플로의 고수준 API이다. 딥러닝 라이브러리가 다른 머신러닝 라이브러리와 다른 점 중 하나는 그래픽 처리 장치인 GPU를 사용하여 인공 신경망을 훈련한다는 것이다. GPU는 벡터와 행렬 연산에 미우 최적화되어 있기 때문에 곱셈과 덧셈이 많이 수행되는 인공 신경망에 큰 도움이 된다.
케라스 라이브러리는 직접 GPU 연산을 수행하지 않는다. 대신 GPU연산을 수행하는 다른 라이브러리를 백엔드로 사용한다. 예를 들면 텐서플로가 케라스의 백엔드 중 하나이다.
이제 케라스 API를 사용해 패션 아이템을 분류하는 가장 간단한 인공 신경망을 만들어 보자.
인공 신경망으로 모델 만들기
아까 로지스틱 회귀에서 만든 훈련 데이터를 그대로 사용하겠다. 로지스틱 회귀에서는 교차 검증을 사용해 모델을 평가했지만, 인공 신경망에서는 교차 검증을 잘 사용하지 않고 검증 세트를 별도로 덜어내어 사용한다. 이렇게 하는 이유는 딥러닝 분야의 데이터셋은 충분히 크기 때문에 검증 점수가 안정적이고 교차 검증을 수행하기에는 훈련 시간이 너무 오래 걸리기 때문이다. 검증 세트를 나누어 보자. 사이킷런의 train_test_split()함수를 사용하자.
사실 패션 MNIST 데이터는 이미 잘 섞인 데이터라서 섞지않고 앞이나 뒤에서 10000개 정도의 새믈을 덜어서 검증 세트로 만들어도 된다고 한다. 하지만 여기서는 일반적인 상황을 가정하여 데이터를 섞어서 나누었다. print를 확인해보면 훈련 세트 48000개 검증 세트 12000개로 분리된 것을 확인할 수 있다.
먼저 인공 신경망 그림의 오른쪽에 놓인 층을 만들어 보자. 이 층은 다음 그림처럼 10개의 패션 아이테을 분류하기 위해 10개의 뉴런으로 구성된다.
케라스의 레이어 패키지 안에는 다양한 층이 준비되어 있다. 가장 기본이 되는 층은 밀집층(dense layer)이다. 왜 밀집이라고 부를까? 784개의 픽셀과 오른쪽에 있는 10개의 뉴런이 모두 연결된 설을 생각해보면 정말 빽빽하다. 그래서 밀집층인 것이다. 이런 층을 양쪽의 뉴런이 모두 연결하고 있기 때문에 완전 연결층(fully connencted layer)라고도 부른다. 그럼 케라스의 Dense 클래스를 사용해 밀집층을 만들어 보자. 필요한 매개변수는 뉴런 개수, 뉴런의 출력에 적용할 함수, 입력의 크기이다.
첫 번째 매개변수로 뉴런 개수를 10개로 지정한다. 10개의 패션 아이템을 분류하기 때문이다. 10개의 뉴런에서 ㅍ출력되는 값을 확률로 바꾸기 위해서 소프트맥스 함수를 사용한다. 케라스 층에서는 activation 매개변수에 이 함수를 지정한다. 만약 이진 분류라면 시그모이드 함수를 사용하기 위해 activation='sigmoid'와 같이 설정한다. 마지막으로 세 번째 매개변수는 입력값의 크기이다. 여기서는 784개의 픽셀값을 받는다.
두번째 코드는 이 밀집층을 가진 신경망 모델을 만든 것이다. Sequential 클래스의 객체를 만들 때 앞에서 만든 밀집층의 객체 dense를 전달했다. 여기서 만든 model 객체가 바로 신경망 모델이다. 절편이 뉴런마다 더해진다는 사실을 잊지말자. 소프트맥스와 같이 뉴런의 선형 방정식 계산 결과에 적용되는 함수를 활성화 함수(activation function)라고 부른다.
인공 신경망으로 패션 아이템 분류하기
케라스 모델은 훈련하기 전에 설정 단계가 있다. 이런 설정을 model 객체의 compile()메서드에서 수행한다. 손실 함수는 꼭 지정해야 한다. 그다음 훈련 과정에서 계산하고 싶은 측정값을 지정한다.
model.compile(loss='sparse_categorical_crossentropy', metrics='accuracy')
sparse_categorical_crossentropy, 이름이 참 길다. 전에 이진 분류에서 이진 크로스 엔트로피 손실 함수를 사용한다고 했었다. 다중 분류에서는 크로스 엔트로피 손실 함수를 사용한다. 다중 분류일 때는 이진 분류와 달리 각 클래스에 대한 확률이 모두 출력되기 때문에 타깃에 해당하는 확률만 남겨 놓기 위해서 나머지 확률에는 모두 0을 곱한다. 이와 같이 타깃값의 해당 클래스만 1이고 나머지는 모두 0인 배열로 만드는 것을 원-핫 인코딩(one-hot encoding)이라고 한다. 따라서 다중 분류에서 크로스 엔트로피 손실 함수를 사용하려면 0, 1, 2와 같이 정수로 된 타깃값을 원-핫 인코딩으로 변환해야 한다. 그런데 패션 MNIST 데이터의 타깃값은 어떻게 되어있었을까?
하지만 텐서플로에서는 정수로 된 타깃값을 원-핫 인코딩으로 바꾸지 않고 그냥 사용할 수 있다. 정수로된 타깃값을 사용해 크로스 엔트로피 손실을 계산하는 것이 바로 sparse_categorical_crossentropy이다. 빽빽한 배열 말고 정숫값 하나만 사용한다는 뜻에서 sparse라는 이름을 붙인 것 같다.
이제 compile()메서드의 두 번재 매개변수인 metrics에 대해 알아보자. 케라스는 모델이 훈련할 때 기본으로 에포크마다 손실 값을 출력해 준다. 손실이 줄어드는 것을 보고 훈련이 잘되었다는 것을 알 수 있지만 정확도를 함께 출력하면 더 좋겠다. 이를 위해 metrics 매개변수에 정확도 지표를 의미하는 'accuracy'를 지정했다.
이제 모델을 훈련해 보자. 훈련하는 fit() 메서드는 사이킷런과 매우 비슷하다. 처음 두매개변수에 입력과 타깃을 지정한다. 그다음 반복할 에포크 횟수를 epochs 매개변수로 지정한다. 사이킷런의 로지스틱 모데로가 동일하게 5번 반복해 보겠다.
그럼 앞서 따로 때어놓은 검증 세트에서 모델 성능을 확인해 보자. 케라스에서 모델의 성능을 평가하는 메서드는 evaluate()메서드이다.
훌륭하다~!!
'AI > 혼공파 머신러닝+딥러닝' 카테고리의 다른 글
[ML 06-1] 군집 알고리즘 (0) | 2024.05.24 |
---|---|
[ML 05-1] 결정 트리 (0) | 2024.05.15 |
[ML 04-2] 확률적 경사 하강법 (0) | 2024.05.09 |
[ML 04-1] 로지스틱 회귀 (0) | 2024.04.04 |
[ML 03-3] 특성공학과 규제 - 릿지(Ridge), 라쏘(Lasso) (1) | 2024.03.31 |