본문 바로가기
Study/혼자 공부하는 판다스

혼자 공부하는 판다스 - 머신러닝 (KNN, SVM)

by Wanooky 2022. 4. 29.

분류 알고리즘은 예측하려는 대상의 속성(설명 변수)을 입력 받고, 목표 변수가 갖고 있는 카테고리(범주형)

 

값 중에서 어느 한 값으로 분류하여 예측한다.

 

훈련 데이터에 목표 변수 값(0 또는 1)을 함께 입력하므로 지도 학습 유형에 속한다.

 


KNN

 

K-Nearest-Neighbors로 k개의 가까운 이웃이라는 뜻이다. 새로운 관측값이 주어지면 기존 데이터에서 가장 속성이

 

비슷한 k개의 이웃을 먼저 찾고, 가까운 이웃들이 갖고 있는 목표 값과 같은 값으로 분류하여 예측하는 것이다.

 

import pandas as pd
import seaborn as sns

df = sns.load_dataset('titanic')

print(df.head())
print('\n')

pd.set_option('display.max_columns', 15)
print(df.head())

   survived  pclass     sex   age  sibsp  parch     fare embarked  class  \
0         0       3    male  22.0      1      0   7.2500        S  Third   
1         1       1  female  38.0      1      0  71.2833        C  First   
2         1       3  female  26.0      0      0   7.9250        S  Third   
3         1       1  female  35.0      1      0  53.1000        S  First   
4         0       3    male  35.0      0      0   8.0500        S  Third   

     who  adult_male deck  embark_town alive  alone  
0    man        True  NaN  Southampton    no  False  
1  woman       False    C    Cherbourg   yes  False  
2  woman       False  NaN  Southampton   yes   True  
3  woman       False    C  Southampton   yes  False  
4    man        True  NaN  Southampton    no   True

info() 메소드로 일부 열에 누락 데이터가 있는지 확인해본다.

 

print(df.info())


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
 #   Column       Non-Null Count  Dtype   
---  ------       --------------  -----   
 0   survived     891 non-null    int64   
 1   pclass       891 non-null    int64   
 2   sex          891 non-null    object  
 3   age          714 non-null    float64 
 4   sibsp        891 non-null    int64   
 5   parch        891 non-null    int64   
 6   fare         891 non-null    float64 
 7   embarked     889 non-null    object  
 8   class        891 non-null    category
 9   who          891 non-null    object  
 10  adult_male   891 non-null    bool    
 11  deck         203 non-null    category
 12  embark_town  889 non-null    object  
 13  alive        891 non-null    object  
 14  alone        891 non-null    bool    
dtypes: bool(2), category(2), float64(2), int64(4), object(5)
memory usage: 80.7+ KB
None

deck열에 유효한 값이 203개에 불과하고, 688개의 누락 데이터가 있는데 전체 891명의 승객 중 688명의 데이터가

 

존재하지 않는다는 뜻이다. 따라서 deck 열ㅇ르 제거하도록 하고, embark_town 열은 embarked 열과 동일한 의미가

 

갖기 때문에 중복을 없애기 위해 열 자체를 제거한다..

rdf = df.drop(['deck', 'embark_town'], axis=1)

 

age열에 누락 데이터가 177개 포함되어있다. 분석에 포함시켜야하는 중요한 속성으로 판단될 경우에 예측 결과에

 

영향을 최소화하는 방법을 선택해야 한다. 평균 나이로 치환하는 방법도 가능하지만 누락 데이터가 있는 행을 모두

 

제거하도록 하자.

 

rdf = rdf.dropna(subset=['age'], how='any', axis=0)
print(len(rdf))

714

 

embarked 열에는 승객들이 탑승한 도시명의 첫 글자가 들어있다. 누락 데이터가 2개이므로 가장 많이 탑승한

 

도시명으로 치환한다.

 

most_freq = rdf['embarked'].value_counts(dropna=True).idxmax()
print(most_freq)
print('\n')

print(rdf.describe(include='all'))
print('\n')

rdf['embarked'].fillna(most_freq, inplace=True)

          survived      pclass   sex         age       sibsp       parch  \
count   714.000000  714.000000   714  714.000000  714.000000  714.000000   
unique         NaN         NaN     2         NaN         NaN         NaN   
top            NaN         NaN  male         NaN         NaN         NaN   
freq           NaN         NaN   453         NaN         NaN         NaN   
mean      0.406162    2.236695   NaN   29.699118    0.512605    0.431373   
std       0.491460    0.838250   NaN   14.526497    0.929783    0.853289   
min       0.000000    1.000000   NaN    0.420000    0.000000    0.000000   
25%       0.000000    1.000000   NaN   20.125000    0.000000    0.000000   
50%       0.000000    2.000000   NaN   28.000000    0.000000    0.000000   
75%       1.000000    3.000000   NaN   38.000000    1.000000    1.000000   
max       1.000000    3.000000   NaN   80.000000    5.000000    6.000000   

              fare embarked  class  who adult_male alive alone  
count   714.000000      712    714  714        714   714   714  
unique         NaN        3      3    3          2     2     2  
top            NaN        S  Third  man       True    no  True  
freq           NaN      554    355  413        413   424   404  
mean     34.694514      NaN    NaN  NaN        NaN   NaN   NaN  
std      52.918930      NaN    NaN  NaN        NaN   NaN   NaN  
min       0.000000      NaN    NaN  NaN        NaN   NaN   NaN  
25%       8.050000      NaN    NaN  NaN        NaN   NaN   NaN  
50%      15.741700      NaN    NaN  NaN        NaN   NaN   NaN  
75%      33.375000      NaN    NaN  NaN        NaN   NaN   NaN  
max     512.329200      NaN    NaN  NaN        NaN   NaN   NaN

 

다음은 속성을 선택한다. 예측 변수로는 생존 여부를 나타내는 survived 열을 추가하고, 나머지 6개의 열을 포함하자.

 

ndf = rdf[['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'embarked']]
print(ndf.head())

   survived  pclass     sex   age  sibsp  parch embarked
0         0       3    male  22.0      1      0        S
1         1       1  female  38.0      1      0        C
2         1       3  female  26.0      0      0        S
3         1       1  female  35.0      1      0        S
4         0       3    male  35.0      0      0        S

 

sex와 embarked 열의 범주형 데이터를 숫자형 데이터로 변환하는데, 이를 0과 1로 변환할 것이다.

 

이 과정을 더미 변수를 만든다고 하고 원핫인코딩이라고도 부른다.

 

onehot_sex = pd.get_dummies(ndf['sex'])
ndf = pd.concat([ndf, onehot_sex], axis=1)

onehot_embarkde = pd.get_dummies(ndf['embarked'], prefix='town')
ndf = pd.concat([ndf, onehot_embarked], axis=1)

ndf.drop(['sex', 'embarked'], axis=1, inplace=True)
print(ndf.head())

   survived  pclass   age  sibsp  parch  female  male  town_C  town_Q  town_S
0         0       3  22.0      1      0       0     1       0       0       1
1         1       1  38.0      1      0       1     0       1       0       0
2         1       3  26.0      0      0       1     0       0       0       1
3         1       1  35.0      1      0       1     0       0       0       1
4         0       3  35.0      0      0       0     1       0       0       1

 

훈련/검증 데이터 분할을 해보자. 예측 변수인 'survived' 열을 변수 y에 저장하고

 

나머지 열을 설명 변수 x에 할당한다. 그리고 데이터의 상대적 크기 차이를 없애기 위해 정규화 과정을 거친다.

 

x = ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male', 'town_C', 'town_Q', 'town_S']]
y = ndf['survived']

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
x = ss.fit(x).transform(x)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=10)

print(x_train.shape)
print(x_test.shape)

(499, 9)
(215, 9)

 

분류 모형의 예측력을 평가하는 지표가 있는데

 

Confusion Matrix (오차행렬)가 있다. 

 

 

정확도는 True 예측 대상 중에 실제값이 True인 비율이다 ( Precision = TP / (TP + FP) )

 

재현율은 실제 값이 True인 분석대상 중에서 True로 예측하여 모형이 적중한 비율이다 ( Recall = TP / (TP + FN) )

 

F1-score은 정확도와 재현율이 균등하게 반영될 수 있도록 정확도와 재현율의 조화평균을 계산한 값으로, 모형의 예측력을 종합적으로 평가하는 지표이다. ( F1 Score = 2*(Precision*Recall) / (Precision + Recall) )

 

모형을 학습하고 검증해보자. 사이킷런의 neighbors 모듈을 사용한다.

 

from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier()

kn.fit(x_train, y_train)
kn.score(x_train, y_train)

0.8537074148296593

 

모형을 가지고 y_hat을 예측해보자.

 

y_hat = knn.predict(x_test)

print(y_hat[0:10])
print(y_test.values[0:10])

[0 0 1 0 0 1 1 1 0 0]
[0 0 1 0 0 1 1 1 0 0]

 

모형의 예측 능력을 평가해보자. metrics 모듈의 confusion_matrix() 함수를 사용하여 오차 행렬을 계산해보자.

 

from sklearn import metrics
knn_matrix = metrics.confusion_matrix(y_test, y_hat)
print(knn_matirx)

[[111  14]
 [ 24  66]]

왼쪽 위에부터 TP, FP, FN, TN 이다.

 

이번에는 모형의 예측 능력을 평가하는 지표를 계산해보자. metrics 모듈의 classification_report() 함수를 사용하면 

 

precision, recall, f1-score 지표를 출력할 수 있다.

 

knn_report = metrics.classification_report(y_test, y_hat)
print(knn_report)

              precision    recall  f1-score   support

           0       0.82      0.89      0.85       125
           1       0.82      0.73      0.78        90

    accuracy                           0.82       215
   macro avg       0.82      0.81      0.82       215
weighted avg       0.82      0.82      0.82       215

SVM

 

Support Vector Machine의 약자로 벡터 개념을 가져와서 사용하는 알고리즘이다.

 

각 열은 열 벡터 형태로 구현이 되고, 열 벡터들이 각각 고유의 축을 갖는 벡터 공간을 만들게 된다.

 

분석 대상이 되는 개별 관측값은 모든 속성(열 벡터)에 관한 값을 해당 축의 좌표로 표시하여 벡터 공간에서의 위치를

 

나타낸다. 속성이 2개 존재하면 2차원 평면 공간에 표시, 3개면 3차원 공간에 표시하고 고차원 벡터 공간의 좌표도 사용이 가능하다

 

SVM 모형은 벡터 공간에 위치한 훈련 데이터의 좌표와 각 데이터가 어떤 분류 값을 가져야 하는지 정답을 입력 받아서

 

학습하는 것으로, 같은 분류 값을 갖는 데이터끼리 같은 공간에 위치하도록 벡터 공간을 여러조각을 나눌 수 있다면,

 

새로운 데이터에 대해서도 어느 공간에 위치하는지 분류할 수 있다.

 

df = sns.load_dataset('titanic')

pd.set_option('display.max_columns', 15)

rdf = df.drop(['deck', 'embark_town'], axis=1)

rdf = rdf.dropna(subset=['age'], how='any', axis=0)

most_freq = rdf['embarked'].value_counts(dropna=True).idxmax()
rdf['embarked'].fillna(most_freq, inplace=True)


ndf = rdf[['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'embarked']]

onehot_sex = pd.get_dummies(ndf['sex'])
ndf = pd.concat([ndf, onehot_sex], axis=1)

onehot_embarked = pd.get_dummies(ndf['embarked'], prefix='town')
ndf = pd.concat([ndf, onehot_embarked], axis=1)

ndf.drop(['sex', 'embarked'], axis=1, inplace=True)

x = ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male', 'town_C', 'town_Q', 'town_S']]
y = ndf['survived']

from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(x)
x = ss.transform(x)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=10)

print(x_train.shape)
print(x_test.shape)

(499, 9)
(215, 9)

데이터를 준비했고, 모형 학습 및 검증을 진행해보자.

 

sklearn의 svm 모듈의 SVC() 함수를 사용하여 모형 객체를 생성한다.

 

이때 데이터를 벡터 공간으로 매핑하는 함수를 커널이라고 하는데, kernel='rbf' 옵션으로 RBF 함수를 적용한다.

 

이 외에도 Linear, Polynomial, Sigmoid 등의 커널이 있다.

 

from sklearn import svm

svm_model = svm.SVC(kernel='rbf')

svm_model.fit(x_train, y_train)

y_hat = svm_model.predict(x_test)

print(y_hat[0:10])
print(y_test.values[0:10])

[0 0 1 0 0 0 1 0 0 0]
[0 0 1 0 0 1 1 1 0 0]

예측값인 y_hat 과 실제 데이터인 y_test를 비교하면 10개 중 8개가 일치한다.

 

이 모형의 오차 행렬을 출력해보자.

 

from sklearn import metrics
svm_matrix = metrics.confusion_matrix(y_test, y_hat)
print(svm_matrix)
print('\n')

svm_report = metrics.classification_report(y_test, y_hat)
print(svm_report)

[[120   5]
 [ 35  55]]


              precision    recall  f1-score   support

           0       0.77      0.96      0.86       125
           1       0.92      0.61      0.73        90

    accuracy                           0.81       215
   macro avg       0.85      0.79      0.80       215
weighted avg       0.83      0.81      0.81       215

표 좌측 상단부터 TP, FP, FN, TN 이다.