판다스를 이용하여 csv파일을 읽어보자!

import pandas as pd
fish = pd.read_csv("https://bit.ly/fish_csv_data")
fish.head()

species에는 어떠한 종류의 생선들이 있는지 확인하기 위해 unique() 함수를 사용해보자.

pd.unique(fish["Species"])

도미,빙어를 비롯해 총 7가지 종류의 생선이 있다.

이제 Species 열을 타겟으로 만들고 나머지 5개 열을 입력 데이터로 사용해보자.

fish_input = fish[["Weight","Length","Diagonal","Height","Width"]].to_numpy()

잘 입력되었는지 처음 5개 행을 출력해보자!

print(fish_input[:5])

array([[242.    ,  25.4   ,  30.    ,  11.52  ,   4.02  ],
       [290.    ,  26.3   ,  31.2   ,  12.48  ,   4.3056],
       [340.    ,  26.5   ,  31.1   ,  12.3778,   4.6961],
       [363.    ,  29.    ,  33.5   ,  12.73  ,   4.4555],
       [430.    ,  29.    ,  34.    ,  12.444 ,   5.134 ]])

이제 타겟 데이터를 준비하자. ( 종류 ) 

fish_target = fish["Species"].to_numpy()

이제 훈련,테스트 세트로 나눠보자.

from sklearn.model_selection import train_test_split()
train_input, train_target, test_input, test_target = train_test_split(fish_input,fish_target,random_state=42)

 

이제 데이터 전처리 (표준화)를 하고 훈련시켜보자 !

from sklearn.preprocessing import StandardScaler
ss = StandardSclaer()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

K-최근접 이웃 분류기로 확률 예측

from sklearn.neighbors import KNeighborsClassifier
Kn = KNeighborsClassifier()
Kn.fit(train_scaled,train_target)
print(Kn.score(train_scaled,train_target))
print(kn.score(test_scaled,test_target))

0.8907563025210085
0.85

이제 타겟값을 어떻게 예측하는지 알아보자.

print(kn.classes_)

array(['Bream', 'Parkki', 'Perch', 'Pike', 'Roach', 'Smelt', 'Whitefish'],
      dtype=object)

'Bream'이 첫 번째 클라스, 'Parkki'가 두 번째 클라스.. 방식 predict()메소드를 이용해서 타깃값으로 예측을 출력해보자.

Kn.predict(test_scaled[:5])

array(['Perch', 'Smelt', 'Pike', 'Perch', 'Perch'], dtype=object)

predit_proba()메소드클래스별 확률값을 반환한다.

import numpy as np
proba = Kn.predict_proba(test_scaled[:5])
print(np.round(proba, decimals=4)     #np.round() = 반올림 , decimals = n >>소수 n번째 자리까지 표시

해석 => 첫 번째 열 : 'Bream'일 확률, 두 번째 열 : 'Parkki'일 확률 ,,,,,,,이다.

확률을 봤으니 직접 샘플의 최근접 이웃 (3개)가 무엇인지 살펴보자.

distances, indexes = Kn.kneighbors(test_scaled[3:4])
print(train_scaled[indexes])

[['Roach' 'Perch' 'Perch']]

1/3 = 0.33333333      2/3 = 0.66666667 

기준 샘플 수가 3개이다 보니 확률이 0, 0.3, 0.6, 1 네 개의 숫자 밖에 나오지 않는다...  더 좋은 방법은 없을까?

 

로지스틱 회귀

로지스틱 회귀 (logistic regression)는 이름은 회귀이지만 분류 모델이다.

또한 로지스틱 회귀는 선형 회귀와 마찬가지로 선형 방정식을 학습한다.

 

변수 a,b,c,d,e는 가중치/계수.

z의 값은 0~1로 나오면 확률로 표현할 수 있다. 이를 위해 시그모이드 함수를 사용하면 된다!

import numpy as np
import matplot.pyplot as plt
z = np.arange( -5, 5 , 0.1)
phi = 1 / ( 1 + np.exp(-z))
plt.plot(z , phi)
plt.xlabel('z')
plt.ylabel('phi')
plt.show()

정말 0 ~ 1로 출력된다!

 

# 로지스틱 회귀로 이진 분류 수행해보자

이진 분류이므로 불리언 인덱싱을 통해 해결 ( T / F)

char_arr = np.array(['A','B','C','D','E'])
print(char_arr[[True,False,True,False,False]])

['A' 'C']

위와 같은 방식을 통해 훈련 세트에서 도미,빙어의 행만 골라 내자

bream_smelt_indexes = (train_target == "Bream" ) | (train_target == "Smelt")
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_scaled[bream_smelt_indexes]

# | = or 과 같음

위 코딩 => 도미,빙어에는 True값이 들어가있고 나머지는 모두 False가 들어가 있다.

따라서 이 배열을 사용해 train_scaled와 train_target 배열에 불리언 인덱싱을 적용하면

손쉽게 도미와 빙어 데이터만 골라낼 수 있을것 같다.

 

이제 준비된 데이터로 로지스틱 회귀 모델을 훈련해보자.

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)

print(lr.predict(train_bream_smelt[:5]))

['Bream' 'Smelt' 'Bream' 'Bream' 'Bream']

두 번째 샘플을 빼고 모두 도미로 예측했다.! 

KNeighborsClassifier()과 마찬가지로 예측확률은 preidct_proba() 메소드에서 제공한다.

lr.predict_proba(train_bream_smelt[:5])

array([[0.99759855, 0.00240145],
       [0.02735183, 0.97264817],
       [0.99486072, 0.00513928],
       [0.98584202, 0.01415798],
       [0.99767269, 0.00232731]])

샘플마다 두개의 확률이 출력되었다. !! 첫 번째 열 = 음성 클래스(0), 두 번째 열 = 양성 클래스(1)

무엇이 0, 1인지는 classes_ 를 통해 알아보자.

lr.classes_

array(['Bream', 'Smelt'], dtype=object)

빙어가 양성이다 ! 

 

이제 계수까지 확인해보자.

print( lr.coef_, lr.intercept_ )

[[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]

이 계수를 통해 보면 방정식은 다음과 같다.

위 방정식에서 z값을 구해보고싶다!!

= decision_function()메소드로 출력할 수 있다.

lr.decision_function(train_bream_smelt[:5])

array([-6.02927744,  3.57123907, -5.26568906, -4.24321775, -6.0607117 ])

이 z값을 시그모이드 함수에 통과시키면 확률을 얻을 수 있다!!

파이썬의 사이파이 라이브러리를 통해 시그모이드함수(expit()) == np.exp()함수를 이용할 수 있다.

 

from scipy.special import expit
expit(decision)

[0.00240145 0.97264817 0.00513928 0.01415798 0.00232731]

predict_proba()메소드를 사용했을 때의 두 번째 열과 동일하다. 

즉, decision_function() 메소드는 양성 클래스(빙어)에 대한 z값을 반환함을 알 수 있다.

 

이제 다중 분류 문제로 넘어가보자.

 

로지스틱 회귀로 다중 분류 수행하기

 

lr = LogisticRegression(C=20, max_iter = 1000)
lr.fit(train_scaled, train_target)

lr.score(train_scaled, train_target)
lr.score(test_scaled, test_target)

0.9327731092436975
0.925

처음 5개 샘플에 대한 예측을 출력해보자.

lr.predict(test_scaled[:5])

array(['Perch', 'Smelt', 'Pike', 'Roach', 'Perch'], dtype=object)

이번에는 테스트 세트의 처음 5개 샘플에 대한 예측확률을 출력해보자!

proba = lr.predict_proba(test_scaled[:5])
np.round(proba, decimals = 3)

array([[0.   , 0.014, 0.841, 0.   , 0.136, 0.007, 0.003],
       [0.   , 0.003, 0.044, 0.   , 0.007, 0.946, 0.   ],
       [0.   , 0.   , 0.034, 0.935, 0.015, 0.016, 0.   ],
       [0.011, 0.034, 0.306, 0.007, 0.567, 0.   , 0.076],
       [0.   , 0.   , 0.904, 0.002, 0.089, 0.002, 0.001]])

5개 샘플에 대한 예측이므로 5개의 행이 출력, 7개의 생선에 대한 확률을 계산했으므로 7개의 열이 출력되었다.

 

첫 번째 샘플을 보면  세 번째 열이 가장 높다. (Perch:농어)

lr.classes_

array(['Bream', 'Parkki', 'Perch', 'Pike', 'Roach', 'Smelt', 'Whitefish'],
      dtype=object)

3번째 열이 농어임을 확인!!!

 

 

이제 다중 분류일 경우의 선형방정식을 살펴보자.

소프트맥스함수

시그모이드함수는 하나의 선형방정식의 출력값을 0~1로 반환하는 반면,

소프트맥스함수는 여러개의 방정식의 출력값을 0~1로 압축하고 전체합이 1이 되게한다.

소프트맥스함수를 정규화된 지수함수라고 부르기도 한다.

 

 

먼저 decision_function()메소드를 통해 z1 ~ z7까지의 값을 구하고,

소프트맥스함수를 이용해 확률로 바꿔보자.

decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))

[[ -6.5    1.03   5.16  -2.73   3.34   0.33  -0.63]
 [-10.86   1.93   4.77  -2.4    2.98   7.84  -4.26]
 [ -4.34  -6.23   3.17   6.49   2.36   2.42  -3.87]
 [ -0.68   0.45   2.65  -1.19   3.26  -5.75   1.26]
 [ -6.4   -1.99   5.82  -0.11   3.5   -0.11  -0.71]]

 

from scipy.special import softmax
proba = softmax(decision, axis = 1)
print(np.round(proba, decimals = 3))

[[0.    0.014 0.841 0.    0.136 0.007 0.003]
 [0.    0.003 0.044 0.    0.007 0.946 0.   ]
 [0.    0.    0.034 0.935 0.015 0.016 0.   ]
 [0.011 0.034 0.306 0.007 0.567 0.    0.076]
 [0.    0.    0.904 0.002 0.089 0.002 0.001]]

앞서 구한 decision()배열을 softmax함수에 전달했다.

axis= 의 매개변수는 소프트맥스를 계산할 축을 지정한다.

여기서는 각 행(샘플)에 대해 소프트맥스를 계산한다. !

+ Recent posts