본문 바로가기
machine learning

[TENSORFLOW] Multiclass classifier

by 유주원 2017. 4. 13.

2017/04/10 - [machine learning] - [TENSORFLOW] Linear regression Classification

2017/04/11 - [machine learning] - [TENSORFLOW] Logistic regression Classification

2017/04/12 - [machine learning] - [TENSORFLOW] 다항 Logistic Regression

지금까지 이진 분류 문제만 다루어 봤다면 이제는 3개 이상의 분류 문제를 다루어 보자.

여기서는 mutliclass 분류에 적합한 Softmax regression에 대해 살펴볼 것이다.

우선 binary classification과 multinomial classification에 대해 차이를 살펴보자.

먼저 [x1, x2, x3]이라는 특징 벡터가 입력으로 들어왔을 때, 이게 class A인지, B인지, C인지를 binary classification을 써서 구분지으려면 아래의 그림처럼 진행이 되어야 한다.

공통 입력 [x1, x2, x3]에 대해 class A일때의 가중치 벡터 [w1, w2, w3], class B일때의 가중치 벡터 [w1, w2, w3], class C일때의 가중치 벡터 [w1, w2, w3]을 행렬 곱해서 가장 크게 나온 가중치일때의 class를 찾는 게 바로 binary classifier를 사용한 multi classification이다. 

반면에 '아 classifier 3개 두기 귀찮아.. 그냥 한번에 처리하자' 하고 나온게 multinomial classification이다.

binary classifier의 가중치들을 그냥 한꺼번에 처리한 차이밖에 없다. 이렇게 나온 결과 가중치들에 대해서 (예를 들어 2.0, 1.0, 0.1 이 나왔다면..) 이들의 결과 값의 합이 1이 되도록 확률 변환을 해주는 것이 바로 softmax이다.

식으로는 아래와 같이 표현할 수 있으며, 식에서도 알 수 있듯이 현재 결과 가중치를 전체의 가중치 합으로 나눠 normalize를 했음을 알 수가 있다.

그럼 softmax를 사용했을 때와 sigmoid를 사용했을 때 어떤 차이가 있을까??


sigmoid는 해당 뉴런의 input으로 들어오는 입력과 가중치 그리고 바이어스에 의해 출력이 결정되며, softmax 역시 같은 구조를 가진다. 차이라면 softmax는 다른 뉴런의 출력값도 비교를 한 후, 최종 출력 값을 결정한다는 점이 있다.

그럼 여기서 또한가지 의문이.. 그냥 가중치 젤 높은 값을 쓰면 되지 왜 구지 확률로 변경해야 할까?

아래 그림을 보면 그냥 2.0이 젤 높으니 이걸 쓰면 되지 않나? 이런 생각을 할 것이다.

물론 inference 과정에서는 구지 softmax를 사용해 주지 않아도 된다. 문제는 학습인데 학습시에 우리는 비용 함수가 필요하다고 언급했었고, Logistic regression에서는 비용함수로 cross entropy를 사용한다고 말했었다.

즉, 확률 모델간의 비교를 통해 차이 값을 구하고 해당 차이 값만큼 가중치를 조절해나가며 차이값이 최소가 되도록 훈련하는 것이 deep learning training의 목표이다. 그렇기 때문에 우리는 단순한 출력 값이 아닌 확률로 변환된 값들이 필요하게 되고 그래서 softmax로 확률 변환할 필요가 있는 것이다. 

위의 예에서 우리는 정답 label을 [1, 0, 0]의 확률로 정했고, softmax로 나온 예측 확률은 [0.7, 0.2, 0.1]이다. 만약에 확률로 변환하지 않는다면 [2.0, 1.0, 0.1]이 되는데 [2.0, 1.0, 0.1]과 [1, 0, 0] 사이에는 어떠한 연관 관계도 찾아볼 수 없다.

즉, training 시 가중치 업데이트를 위해 softmax가 필요하다.

그럼 이제 코드를 살펴보자. 우리는 아래의 데이터 분포를 가진 데이터들을 분류하는 것을 목표로 할 것이다.

train data와 test data를 만들자. 

traindata() 함수를 통해 train data를 만들고 data label도 함께 만들자. 

traindata() 함수내에서 사용되고 있는 생소한 함수만 몇가지 살펴보자면 우선 np.hstack은 가로 열을 추가해서 붙여 주는 것을 의미한다. 예를 들어 [[ 1.3 ], [2.4 ]] 란 벡터와 [[ 3.5 ], [ 1. 8]]이란 벡터에 대해 hstack을 실행하면, [[1.3 , 3.5], [2.4 , 1.8]]이 된다. 반대로 vstack은 세로로 행을 추가해 주는 것을 말한다. [[ 1.3 ], [2.4 ]], [[ 3.5 ], [ 1. 8]] 에 대한 vstack은 [[ 1.3], [2.4], [3.5], [1.8]] 이 된다. 

np.matrix는 행렬을 만들어주는 것을 말한다. 위의 코드를 예제로 보면 [1.,0.,0.] 벡터를 x1_label0의 길이만큼 만들고, 그 후로 [0.,1.,0.] 벡터를 x1_label1의 길이만큼 만든다. 이런식으로 만들면 [[1., 0., 0.], [1., 0., 0.], [1., 0., 0.] ....., [0., 1., 0.], [0., 1., 0.] ...... [0., 0., 1.], [0., 0., 1.]] 이렇게 행렬이 만들어진다.

np.arange는 입력 파라미터 만큼의 순차적인 숫자 리스트를 생성한다. 예를 들어 np.arange(3)을 하면 [0, 1, 2] 이렇게 숫자 리스트가 생성이 된다.

위 코드에서 이렇게 숫자 리스트를 생성한 이유는 train data와 label data를 섞기 위함이다. np.random.shuffle을 통해 만들어진 숫자 리스트를 뒤섞고 뒤섞은 숫자 리스트를 토대로 다시 train_data와 label_data를 재배치한다.

tf.Variable로 선언한 W와 b값을 보면, W는 입력 특징 벡터 개수 x label 개수만큼 행렬을 만들고, b는 label 개수만큼 행렬을 만들고 있으며, W는 가중치 b는 bias에 해당한다. 여기서는 x,y가 하나의 특징으로 잡혔고 label은 3이기 때문에 W는 2x3 행렬, b는 3x1 행렬을 가진다.  

y_model은 wx + b (w와 x, b 모두 행렬) 에 위에서 언급한 softmax를 씌운 형태를 모델로 정의했다.

cost는 cross entropy를 이용했으며, 훈련된 모델의 정확도를 측정하기 위해 accuracy operator를 추가했다. (http://yujuwon.tistory.com/entry/TENSORFLOW-Classification)

session에 X와 Y값을 이전에는 한 개씩 집어넣었다면, 여기서는 한꺼번에 집어넣는데(batch_size) 이렇게 한꺼번에 들어온 입력 값에 대한 cost의 합의 평균이 최소가 되도록 weight를 업데이트하게 된다.

(데이터를 한개씩 입력받아 weight를 수정하는 방식과 데이터 여러개를 한꺼번에 입력받아 cost의 평균으로 weight를 수정하는 방식, 둘 중 어떤게 더 빨리 수렴하는지를 확실히 모르겠지만 대부분의 open source에서는 batch_size로 학습하는 것을 선호하고있다.) 

위와 같이 batch_size로 훈련을 하게 되면 보다 빠르게 data를 training할 수가 있다.

마지막에 accuracy.eval 대신 sess.run(accuracy, feed_dict={X: test, Y: test_label})을 써도 정상적으로 동작한다.