본문 바로가기
AI

[AI-3Team] 소프트맥스 회귀(Softmax Regression) 다중 클래스 분류

by 나 왜 인공지능전공 2021. 1. 13.

1. 다중 클래스 분류(Multi-class Classification)                                          


세 개 이상의 선택지 중 하나를 고르는 문제를 다중 클래스 분류라고 한다.

입력된 샘플 데이터에 대해서 각 정답지에 대해서 시그모이드 함수를 적용해 보자. 그렇게 한다면, 첫번째가 정답일 확률은 0.7, 두번째가 정답일 확률은 0.6, 세번째가 정답일 확률은 0.4 등과 같은 출력을 얻게된다.

 

그런데 이렇게 되면 확률로 나타내었을때 뭔가 보기가 이쁘지가 않다. 그래서 이 전체 확률의 합계가 1이 되도록 하고 싶은데  그렇게 해주는 것이 바로 소프트맥스 함수 이다.

 

2. 소프트 맥스 함수(Softmax function)


소프트맥스 함수는 분류해야하는 정답지(클래스)의 총 개수를 k라고 할 때, k차원의 벡터를 입력받아 각 클래스에 대한 확률을 추정한다.

 

그림을 통해 알아보자

위의 그림을 보면 2가지의 질문이 있습니다. 먼저, 소프트 맥스 함수의 입력에 대한 질문이다.

모델은 4차원 벡터를 입력으로 받는다. 그런데 소프트맥스의 함수의 입력으로 사용되는 벡터는 벡터의 차원이 분류하고자 하는 클래스의 개수가 되어야 하므로 어떤 가중치 연산을 통해 3차원 벡터로 변환되어야 한다. 그 과정은 다음과 같다.

 

샘플 데이터 벡터를 소프트맥스 함수의 입력 벡터로 차원을 축소하는 방법은 간단하다. 소프트맥스 함수의 입력 벡터z의 차원수만큼 결과값이 나오도록 가중치 곱을 진행하면 된다. 위의 그림에서 화살표는 총 (4 × 3 = 12) 12개이며 전부 다른 가중치를 가지고, 학습 과정에서 점차적으로 오차를 최소화하는 가중치로 값이 변경된다.

 

두번째 질문은 오차 계산 방법에 대한 질문이다. 소프트맥스 회귀에서는 출력되는 실제값을 다음 그림 처럼원-핫 벡터로 표현한다.

위의 그림은 소프트맥스 함수의 출력 벡터의 첫번째 원소p1이 virginica가 정답일 확률, 두번째 원소p2가 setosa가 정답일 확률, 세번째 원소p3가 versicolor가 정답일 확률을 의미한다고 하였을 때, 각 실제값의 정수 인코딩은 1, 2, 3이 되고 이에 원-핫 인코딩을 수행하여 실제값을 원-핫 벡터로 수치화한 것을 보여준다.

 

전체적인 과정을 보자

소프트 맥스 함수를 통해 나온 예측값과 실제값 사이에 오차가 발생하고

해당 오차로부터 가중치를 업데이트 한다.

 

 

3. 비용 함수(Cost function)


소프트맥스 회귀에서는 비용 함수로 크로스 엔트로피 함수를 사용한다.

크로스 엔트로피 함수

아래에서y는 실제값을 나타내며,k는 클래스의 개수로 정의한다.yj는 실제값 원-핫 벡터의j번째 인덱스를 의미하며,pj는 샘플 데이터가j번째 클래스일 확률을 나타낸다. 

이를nn개의 전체 데이터에 대한 평균을 구한다고 하면 최종 비용 함수는 다음과 같다.

 

4. 실습


 

 

 

iris.csv
0.00MB

import pandas as pd
data = pd.read_csv('/content/iris.csv',encoding='latin1')

print(len(data)) # 총 샘플의 개수 출력
print(data[:5]) # 샘플 중 5개 출력

print("품종 종류:", data["variety"].unique(), sep="\n")
# 중복을 허용하지 않고, 있는 데이터의 모든 종류를 출력

import seaborn as sns
#del data['Id'] # 인덱스 열 삭제
sns.set(style="ticks", color_codes=True)
g = sns.pairplot(data, hue="variety", palette="husl")
#데이터의 특성에 따른 분포 그래프

 

from sklearn.model_selection import train_test_split
data_X = data[['sepal.length', 'sepal.width', 'petal.length', 'petal.width']].values # X 데이터에 해당됩니다. X는 총 4개입니다.
data_y = data['variety'].values # Y 데이터에 해당됩니다. 예측해야하는 값입니다.

print(data_X[:5]) #X에 해당되는 데이터를 10개만 출력합니다.
print(data_y[:5]) #y에 해당되는 데이터를 10개만 출력합니다.

(X_train, X_test, y_train, y_test) = train_test_split(data_X, data_y, train_size=0.8, random_state=1)
# 훈련 데이터와 테스트 데이터를 8:2로 나눕니다. 또한 데이터의 순서를 섞습니다.
from tensorflow.keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
# 훈련 데이터와 테스트 데이터에 대해서 원-핫 인코딩
print(y_train[:5])
print(y_test[:5])

from tensorflow.keras.models import Sequential # 케라스의 Sequential()을 임포트
from tensorflow.keras.layers import Dense # 케라스의 Dense()를 임포트
from tensorflow.keras import optimizers # 케라스의 옵티마이저를 임포트

model=Sequential()
model.add(Dense(3, input_dim=4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam',metrics=['accuracy'])
# 옵티마이저는 경사하강법의 일종인 adam을 사용합니다.
# 손실 함수(Loss function)는 크로스 엔트로피 함수를 사용합니다.
history=model.fit(X_train,y_train, batch_size=1, epochs=200, validation_data=(X_test, y_test))
# 주어진 X와 y데이터에 대해서 오차를 최소화하는 작업을 200번 시도합니다.

epochs = range(1, len(history.history['accuracy']) + 1)
plt.plot(epochs, history.history['loss'])
plt.plot(epochs, history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.show()