[52일차] 다양한 분류 알고리즘

2023. 3. 8. 15:39데이터 엔지니어링 과정/python

1. 로지스틱 회귀

💻 데이터 준비

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

📍 read_csv() : pandas의 CSV 파일을 데이터프레임으로 변환해주는 함수

📌 k-최근접 이웃 분류기의 단점 : 훈련 세트의 범위를 벗어나면, 예측이 불가하다.

📍 unique() 함수 : 열이 갖고 있는 값을 중복값을 제외하고 알 수 있음
💻 생선의 종류 확인
# 어떤 종류의 생선 있는지 확인
print(pd.unique(fish['Species']))
>>> ['Bream' 'Roach' 'Whitefish' 'Parkki' 'Perch' 'Pike' 'Smelt']
 

💻 종류를 제외한 나머지를 훈련 데이터로 넘파이 배열로 생성

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

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()

 

💻 데이터 세트 2개 생성

# 데이터 세트 2개 만들기
from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(fish_input, fish_target, random_state=42)

 

💻 훈련 세트와 테스트 세트 표준화 전처리

from sklearn.preprocessing import StandardScaler
ss=StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

1. k-최근접 이웃 분류기의 확률 예측

💻 사이킷런의 KNeighborsClassifier 클래스 객체 만든 후, 훈련 후 score 확인

from sklearn.neighbors import KNeighborsClassifier
kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)

print(kn.score(train_scaled, train_target))
print(kn.score(test_scaled, test_target))
>>> 0.8907563025210085
>>> 0.85

📌 다중 분류(multi-class classfication): 타깃 데이터에 2개 이상의 클래스가 포함된 문제

 

📍 classes_ : KNeighborsClassifier에서 정렬된 타깃값을 알파벳 순으로 정렬

print(kn.classes_)
>>> ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']

 

💻 test_scaled 5개 데이터 확인 후 예측 종류 확인

test_scaled[:5]
>>> array([[-0.88741352, -0.91804565, -1.03098914, -0.90464451, -0.80762518],
           [-1.06924656, -1.50842035, -1.54345461, -1.58849582, -1.93803151],
           [-0.54401367,  0.35641402,  0.30663259, -0.8135697 , -0.65388895],
           [-0.34698097, -0.23396068, -0.22320459, -0.11905019, -0.12233464],
           [-0.68475132, -0.51509149, -0.58801052, -0.8998784 , -0.50124996]])
           
print(kn.predict(test_scaled[:5]))
>>> ['Perch' 'Smelt' 'Pike' 'Perch' 'Perch']

📍 predict_proba() 메서드 : 클래스별 확률값 반환

📍 round() 함수 : 넘파이의 함수로, 기본적으로 소수점 첫째 자리에서 반올림. decimals 매개변수로 유지할 소수점 아래 자릿수를 지정할 수 있음

import numpy as np
proba = kn.predict_proba(test_scaled[:5])

print(np.round(proba, decimals=4)) # 소수점 네 번째 자리까지 표기, 다섯 번째 자리에서 반올림
>>> [[0.     0.     1.     0.     0.     0.     0.    ]
     [0.     0.     0.     0.     0.     1.     0.    ]
     [0.     0.     0.     1.     0.     0.     0.    ]
     [0.     0.     0.6667 0.     0.3333 0.     0.    ]
     [0.     0.     0.6667 0.     0.3333 0.     0.    ]]

2. 로지스틱 회귀

  • 이름은 회귀이지만 분류 모델
  • 선형 방정식 학습 (선형 회귀와 동일)
  • 확률이 되려면 0~1 사이의 값이어야 함
    ➡ 가장 큰 음수일 때는 0, 가장 큰 양수일 때는 1이 되도록 해야 함
    시그모이드 함수를 사용하면 가능 (이중분류하려면!! 다중분류를 원하면 소프트맥스를 사용해야 함)

3. 로지스틱 회귀로 이진 분류 수행하기

💻 도미와 빙어에 대한 행 골라내기 feat.불리언 인덱싱

# 도미와 빙어 데이터로 이진분류
bream_smelt_indexes = (train_target == 'Bream') | (train_target == 'Smelt')
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]

🔎 대규모 데이터에서, 비트 연산의 속도는 압도적으로 빠르다!

 

💻 로지스틱 회귀 모델 훈련 후 예측

# 로지스틱 회귀 시작!
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']

print(lr.predict_proba(train_bream_smelt[:5]))
>>> [[0.99759855 0.00240145]
     [0.02735183 0.97264817]
     [0.99486072 0.00513928]
     [0.98584202 0.01415798]
     [0.99767269 0.00232731]]

 

💻 타깃값 확인

print(lr.classes_)
>>> ['Bream' 'Smelt']

🔎 첫 번째 열이 음성 클래스(0)이니까, Smelt(빙어)가 양성 클래스이군!

 

💻 계수 확인

print(lr.coef_,lr.intercept_)
>>> [[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]

 

💻 처음 5개의 샘플 z값

decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)
>>> [-6.02927744  3.57123907 -5.26568906 -4.24321775 -6.0607117 ]

📍 expit() : 사이파이의 시그모이드 함수

 

4. 로지스틱 회귀로 다중 분류 수행

📍 LogisticRegression 클래스

  • 반복적인 알고리즘 사용
  • max_iter 매개변수 : 반복 횟수 지정, 기본값은 100
  • 계수의 제곱 규제 (L2 규제)
    • 규제를 제어하는 매개변수는 C
      : C는 alpha와 반대로, 작을수록 규제 커짐

💻 로직스틱회귀 클래스로 다중 분류 모델 훈련 후 예측

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

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))
>>> 0.9327731092436975
>>> 0.925

# 예측 
# predict는 문자열로 반환
print(lr.predict(test_scaled[:5]))
>>> 'Perch' 'Smelt' 'Pike' 'Roach' 'Perch']

# predict_proba는 확률로 반환
proba = lr.predict_proba(test_scaled[:5])
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]]

 

💻 가지고 있는 클래스 값 확인 및 선형 방정식 확인

print(lr.classes_)
>>> ['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']

print(lr.coef_.shape, lr.intercept_.shape)
>>> (7, 5) (7,)

🔎 5개의 특성을 사용하므로 coef_ 배열의 열은 5개. 7은 z를 7개 계산한다는 의미

📍 softmax 함수 : 다중 분류에서 시그모이드 함수와 같은 역할

 

💻 테스트 세트의 처음 5개 샘플에 대한 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]]

 

2. 확률적 경사 하강법

1. 확률적 경사 하강법

훈련 세트에서 랜덤하게 하나의 샘플을 고르는 것

📌 에포크 epoch : 훈련 세트를 한 번 모두 사용하는 과정

 

2. 손실 함수

어떤 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지 측정하는 기준, 손실 함수의 값이 작을수록 좋음

 

3. 로지스틱 손실 함수 
: 이진 분류에서 사용, 다중 분류는 크로스엔트로피 손실 함수 사용

 

4. SGDClassifier

💻 판다스 데이터프레임 만들기

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

 

💻 입력 데이터와 타깃 데이터 정의 후 훈련 세트와  테스트 세트 분류

# Species 열을 제외한 남저ㅣ 5개 입력 데이터로 사용
# Species는 타깃데이터
fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()
fish_target = fish['Species'].to_numpy()

# 훈련 세트와 테스트 세트 나누기
from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
    fish_input, fish_target, random_state=42
)

 

💻 표준화 전처리

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

 

📍 SGDClassifier : 사이킷런에서 확률적 경사 하강법을 제공하는 대표적인 분류용 클래스

sc = SGDClassifier(loss='log', max_iter=10, random_state=42) # 로지스틱 손실 함수로 10번 훈련
sc.fit(train_scaled, train_target)

# 훈련 세트와 테스트 세트에서 정확도 점수 출력
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.773109243697479
>>> 0.775

📍 loss : 손실 함수의 종류 지정 (여기서는 로지스틱 손실 함수 지정)

              기본값은 'hinge'

    max_iter : 수행할 에포크 횟수 지정

 

📌 ConvergenceWarning 경고 : 훈련 부족. 모델이 충분히 수렴하지 않았다는 경고

 

📍 partial_fit() : 모델을 이어서 훈련

sc.partial_fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.8151260504201681
>>> 0.85

    

5. 에포크와 과대/과소적합

📌 에포프가 진행됨에 따라, 훈련 세트 점수는 꾸준히 증가하지만 테스트 세트 점수는 어느 순간부터 감소한다. 이 지점이 과대적합되기 시작하는 때. 과대적합이 시작하기 전에 훈련을 멈추는 것을 조기종료라고 한다.

 

💻 에포크마다 훈련 세트와 테스트 세트에 대한 점수 기록하기 위해 2개의 리스트 준비

import numpy as np
sc = SGDClassifier(loss='log', random_state=42)
train_score = []
test_score = []
classes = np.unique(train_target)

 

💻 300번의 에포크 동안 훈련 반복

for _ in range(0,300):
  sc.partial_fit(train_scaled, train_target, classes=classes)
  train_score.append(sc.score(train_scaled, train_target))
  test_score.append(sc.score(test_scaled, test_target))

 

💻 300번의 에포크 동안 기록한 훈련 세트와 테스트 세트의 점수 그래프

import matplotlib.pyplot as plt
plt.plot(train_score)
plt.plot(test_score)
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

🔎 백 번째 이후로는 과대적합이 시작된다. 백 번째 에포크가 적절한 반복 횟수..!🧐

 

💻 반복 횟수 100에 맞추고 모델 재훈련 후, 훈련 세트와 테스트 세트 평가

sc=SGDClassifier(loss='log', max_iter=100, tol=None, random_state=42)
sc.fit(train_scaled, train_target)

print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))
>>> 0.957983193277311
>>> 0.925