로지스틱 회귀(이진분류, 다항분류)를 모델이 제공하는 절편과 기울기로 z 값을 구하고, sigmod(), softmax()를 이용해서 확률 값을 직접 구해 보면서 깊게 공부를 하였군요.

(softmax 정의시 np.sum() 대신 sum()을 사용하여 왜 안되나 고민하였는데.. 혼공머신에서 질문올리고 답변을 받아서 해결을 했네요... 질문을 하고 질문을 받아 줄 수 있는 환경이 좋아요 ^^)

학습한 모델이 기울기와 절편을 제대로 제공한다면 테스트 데이타를 잘 예측할 수 있을 것 같군요.  어떻게 잘 기울기와 절편을 찾을까?? 에러 코스트를 낮게 찾아야 하고 경사하강법을 사용한다...

 

 

 

혼공머신 4장을 참고하였습니다.

https://hongong.hanbit.co.kr/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%94%A5%EB%9F%AC%EB%8B%9D/

 

혼자 공부하는 머신러닝+딥러닝

혼자 공부하는 머신러닝 딥러닝, 무료 동영상 강의, 머신러닝+딥러닝 용어집을 다운로드 하세요. 포기하지 마세요! 독학으로 충분히 하실 수 있습니다. ‘때론 혼자, 때론 같이’ 하며 힘이 되겠

hongong.hanbit.co.kr

 

4.1 다항 분류

  • 이진분류 (0 또는 1, 1장에서처럼 빙어/도미 분류)를 학습하고 확장된  3개 이상 분류 기법을 다룬다 
  • KNN, 로지스틱 회귀를 학습함.

 

4.2 해결할 문제

  • 7종의 물고기에서 어떤 특성 (Test Data)가 주어질 떄 어떤 물고기에 속할까
  • 7종의 물고기 종류 ['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']
  • 데이타 특성은 5개이다. 
    • Weight,Length,Diagonal,Height,Width
  • 데이타 샘플
    Species Weight  Length  Diagonal    Height  Width
0   Bream   242.0   25.4    30.0    11.5200 4.0200
1   Bream   290.0   26.3    31.2    12.4800 4.3056
2   Bream   340.0   26.5    31.1    12.3778 4.6961
3   Bream   363.0   29.0    33.5    12.7300 4.4555
4   Bream   430.0   29.0    34.0    12.4440 5.1340

 

4.3 KNN

  • 이웃을 선택하고 (k=1,3,5) 선택된 이웃과 가까운 거리에 있는 것을 선택
  • 그림은 2개의 feature로 3개의 class (원, 세모, 네모)로 분류. 책은 5개 특성, 7개 class

 

4.3.1 KNN 모델

 

KNN 모델도 일반적인 머신러닝 모델과 같이 학습,평가,예측을 한다.

from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

#모델 선택
kn = KNeighborsClassifier(n_neighbors=3)

#모델 학습
kn.fit(train_scaled, train_target)
#모델 평가
print(f'Test Score : {kn.score(test_scaled,test_target)}')

#모델 예측
y_pred = kn.predict(test_scaled)
print(f"Predict 5 Items: {kn.predict(test_scaled[:5])}")

# 정확도 계산
acc = accuracy_score(test_target,y_pred)
print(f'accuracy: {acc}')

 

85% 정확도로, Test Data 5개를 잘 분류 하였다.

Test Score : 0.85

Predict 5 Items: ['Perch' 'Smelt' 'Pike' 'Perch' 'Perch'] 

accuracy: 0.85

 

4.3.2 KNN 예측 결과는 어떻게 산출되었을까?

predict()은 class 값을 전달한다. predict_proba()는 class의 확률값을 전달한다.

 

예제 5개의 sample을 예측할 때, predict_proba()는 5개의 class에 속할 확률을 나타낸다.

predict() class에 속할 확률 속에서 높은 class를 선택한다.

 

첫번째 sample data(빨간색)는 Perch가 선택이 되었다. Perch의 확률은 0.66이고, Roach는 0.33이다. 높은 Perch로 선택했다.

 

즉, Pearch로 판정을 했지만, 100%가 아닌 66%로 확률로 판정을 했다. 모든 데이타를 100% 판정한다는 것은 가능할까? 오류를 인정해야 한다.

import pandas as pd
import numpy as np

proba = kn.predict_proba(test_scaled[3:8])
df = pd.DataFrame(data=np.round(proba, decimals=4),columns=kn.classes_)
df['Predict'] = kn.predict(test_scaled[3:8])
print(df)

 

   Bream  Parkki   Perch  Pike   Roach  Smelt  Whitefish Predict
0    0.0     0.0        0.6667   0.0  0.3333    0.0        0.0       Perch
1    0.0     0.0        0.6667   0.0  0.3333    0.0        0.0       Perch
2    1.0     0.0        0.0000   0.0  0.0000    0.0        0.0       Bream
3    0.0     0.0        0.0000   0.0  0.0000    1.0        0.0       Smelt
4    0.0     0.0        0.3333   0.0  0.6667    0.0        0.0       Roach

 

 

4.4 로지스틱 회귀

  •  선형 방정식을 학습하지만, 방정식의 연속될 결과값을 분류= 카타고리=클라스로 표현한다.
  • 클라스가 2개인 경우 선형 방정식의 결과 값을 sigmod() 함수를 클라스의 확률을 구하고 확률에 따라  이진 분류한다.
  • 클라스가 3개 이상인 경우 각 클라스의 수 만큼 선형 방정식의 값이 계산되고, SoftMax()로 각 클라스의  
  • 로지스틱은 2개 클라스(=카타고리)를 분류, SoftMax회귀는 3개 이상 클라스를 분류
  • 선형 방정식을 학습하므로 연속형 값이 나오며, 이를 클라스로 매핑해야 한다.
  • 로지스틱은 선형 방정식의 값(z)을 sigmod 함수와 threadhold에 따라 클라스를 구분
  • SoftMax은 선형 방적식의 값(z)가 클라스 수 만큼 산출이 되고, softmax에 의해 각 클라스의 확률을 산출한다. 최종 확률이 높은 것을 선택한다.

 

4.4.1 로지스틱 모델 (멀티클라스)

 

로지스틱 모델도 일반적인 머신러닝 모델과 같이 학습,평가,예측을 한다.

#모델 선택
lr = LogisticRegression(C=20, max_iter=1000)

#모델 학습
lr.fit(train_scaled, train_target)

#모델 평가
print(f'Train Score : {lr.score(train_scaled, train_target)}')
print(f'Test  Score : {lr.score(test_scaled, test_target)}')

#모델 예측
y_pred = lr.predict(test_scaled)

#정확도 계산
acc = accuracy_score(test_target,y_pred)
print(f'accuracy: {acc}')

 

실행 결과

Train Score : 0.9327731092436975
Test  Score : 0.925
accuracy: 0.925

 

 

4.4.2 로지스틱 모델(멀티클라스)은 어떻게 Test data로  예측할까?

  • 학습에 의해 클래스 숫자만큼 기울기와 한개의 절편을 구한다.
  • 예측데이타와 (구한)기울기로 클래스 숫자만큼 z 값을 구한다.
  • (멀티class인경우) maxsoft로 각 클래스의 확률을 구한후, 확률이 가장 큰 클라스를 선택한다.
  • (binaryclass인경우) sigmod로 2개의 클래스의 확률을 구한후, 확률이 가장 큰 클라스를 선택한다.

test 데이타 1개를 이용하여 기울기와 절편, SoftMax를 이용하여 클라스별 확률을 구한다.

  • test 데이타는 일때
    •  [-0.88741352 -0.91804565 -1.03098914 -0.90464451 -0.80762518]
  • 모델 학습에 의한 기울기와 절편으로 클라스별 z값을 구한다.
    • [-6.5   1.03  5.16 -2.73  3.34  0.33 -0.63]
  • z값들에 대한 확률
    • Class 별 확률값
                Bream   Parkki  Perch   Pike    Roach   Smelt   Whitefish
      0        0.0         0.014   0.841   0.0        0.136   0.007   0.003
 
#예측할 데이타
print(f'예측할 Feature {test_scaled[:1]},정답 {test_target[:1]}')

#학습으로 예측한 값
print(f'학습으로 예측한 값 : {lr.predict(test_scaled[:1])}')
#학습으로 구한 기술기와 절편의 크기
print(f'학습으로 구한 기술기 크기 : {lr.coef_.shape}, 절편의 크기 {lr.intercept_.shape}')

#sklearn lib으로 구한 z 값들
decision = lr.decision_function(test_scaled[:1])
print(f'sklearn 제공 class별 z 값 : {np.round(decision, decimals=2)}')

#직접 z 값을 구한다. 기울기와 절편을 이용한다.
my_data = np.dot(lr.coef_ , test_scaled[:1].reshape(5,-1))
my_data = my_data.reshape(1,-1) +lr.intercept_
my_data = np.round(my_data,decimals=2)
print(f'직접 계산한  class별 z 값  : {my_data}')

from scipy.special import softmax

# 각 Class별 z 값들에 대한 Softmax로 확률을 구한다.
proba = softmax(decision, axis=1)
proba = np.round(proba, decimals=3)
df = pd.DataFrame(data=proba, columns=lr.classes_)
print('Class 별 확률값 Using SoftMax Lib')
print(df)

#SoftMax를 직접 정의하여 확률을 구해보자.
myProba = np.exp(decision)/np.sum(np.exp(decision))
myProba = np.round(myProba,decimals=3)
df1 = pd.DataFrame(data=myProba, columns=lr.classes_)
print('\nClass 별 확률값 Defining SoftMax func()')
print(df1)

 

수행 결과

 

예측할 Feature [[-0.88741352 -0.91804565 -1.03098914 -0.90464451 -0.80762518]],정답 ['Perch']
학습으로 예측한 값 : ['Perch']
학습으로 구한 기술기 크기 : (7, 5), 절편의 크기 (7,)
sklearn 제공 class별 z 값 : [[-6.5   1.03  5.16 -2.73  3.34  0.33 -0.63]]
직접 계산한  class별 z 값  : [[-6.5   1.03  5.16 -2.73  3.34  0.33 -0.63]]
Class 별 확률값 Using SoftMax Lib
     Bream  Parkki  Perch   Pike  Roach  Smelt  Whitefish
0      0.0      0.014   0.841   0.0     0.136  0.007      0.003

Class 별 확률값 Defining SoftMax func()
     Bream  Parkki   Perch  Pike  Roach  Smelt  Whitefish
0    0.0       0.014    0.841   0.0    0.136    0.007      0.003

 

4.4.3 로지스틱 모델 (바이너리클라스)

  • 데이타 분류값이 2개만 다를뿐이지 모델 학습 방법,평가, 예측은 멀티클라스와 동일하다.
  • 멀티 클라스는 각각의 클라스의 z 값을 구하고, 클라스별 확률을 구했지만, 바이너리클라스는 1개(양성클라스)의 z값과 확률만 구하고 다른 1개(음성클라스는)의 확률은 1에서 뺴준다 .. (음성,양성 클라스 확률의 합계는 1이다.)
  •  
from sklearn.linear_model import LogisticRegression
#모델선택
lr = LogisticRegression()

#모델학습
lr.fit(train_bream_smelt, target_bream_smelt,)

#모델평가
print(f'Train Score : {lr.score(train_bream_smelt, target_bream_smelt)}')

#모델예측 ,,,학습에 사용된 데이타를 예측하는 것은 좋은 방법은 아니다.
y_pred=lr.predict(train_bream_smelt[:1])

print(f'이진분류의 category {lr.classes_}')
print(f'예측할 데이타 {train_bream_smelt[:1]} 예측결과 {y_pred}')

 

실행결과

Train Score : 1.0
이진분류의 category ['Bream' 'Smelt']
예측할 데이타 [[0.91965782 0.60943175 0.81041221 1.85194896 1.00075672]] 예측결과 ['Bream']

 

4.4.4 로지스틱 모델(바이너리클라스)은 어떻게 Test data로  예측할까?

  • 실행결과를 보면 예측할 데이타로 Bream을 예측하였다. lib가 제공하는 predict_proba()의 결과는 클라스별 확률을 구하고, 높은 확률인 클라스를 선택하는 알고리즘이다.
  • 음성 클라스 확률과 양성 클라스 확률의 합을 구해 보고 1이 되는 것을 확인할 수 있다.
bin_test_data = lr.predict_proba(train_bream_smelt[:1])
bin_df = pd.DataFrame(columns=lr.classes_,data= bin_test_data)
bin_df['sum of prob'] = bin_df['Bream'] + bin_df['Smelt']
bin_df
 
       Bream         Smelt    sum of prob
0   0.997599    0.002401    1.0

 

  • 모델이 학습한 기울기와 절편으로 확률을 직접 구해보자!!
    • z 값을 test 데이타 * 기울기 + 절편으로 구한다.
    • z 값을 sigmod 함수로 0~1 사이의 값을 구한다. 이 값은 양성 클라스의 확률이 된다. 음성 클라스의 확률을 1을 뺴준다.
# 모델 학습으로 산출된 기울기와 절편으로 확률을 구해보자
print(f'기울기 {lr.coef_} 절편 { lr.intercept_}')
print(f'예측할 데이타  {train_bream_smelt[:1]}')

#sklearn lib으로 구한 z 값
bin_lib_z = lr.decision_function(train_bream_smelt[:1])
print(f'sklearn 제공 z 값 : {bin_lib_z}')

# z값을 기울기와 절편으로 구하자
test_data = train_bream_smelt[:1].reshape(5,1)
my_data = np.dot(lr.coef_,test_data) + lr.intercept_
print(f'직접 계산한 z 값  : {my_data}')

# z값을 0~1 값으로 변환한다 이진분류에서는 sigmod 함수를 사용한다.
prob1 = 1 / (1+ np.exp(-bin_lib_z))
print(f'양성의 확률 : {prob1}')
print(f'양성인 확률 : {expit(bin_lib_z)}')
print(f'음성인 확률 {1-prob1}')
 
===== 실행 결과 =========
기울기 [[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] 절편 [-2.16155132]
예측할 데이타  [[0.91965782 0.60943175 0.81041221 1.85194896 1.00075672]]
sklearn 제공 z 값 : [-6.02927744]
직접 계산한 z 값  : [[-6.02927744]]
양성의 확률 : [0.00240145]
양성인 확률 : [0.00240145]
음성인 확률 [0.99759855]

+ Recent posts