- 공통 코드
import numpy as np import pandas as pd import matplotlib.pyplot as plt import platform from matplotlib import font_manager, rc import seaborn as sns import scipy as sp import scipy.stats import os # 시각화에서 한글 설정 if platform.system() == 'Darwin': rc('font', family='AppleGothic') elif platform.system() == 'Windows': font_path = "c:/Windows/Fonts/malgun.ttf" font_name = font_manager.FontProperties(fname=font_path).get_name() rc('font', family=font_name) # 시각화에서 음수 표현 설정 plt.rcParams['axes.unicode_minus'] = False # Jupyter Notebook 출력 소수점 이하 3자리로 제한 %precision 3 pd.options.display.precision = 3 # 이미지 저장 경로 설정 PROJECT_ROOT_DIR = "." CHAPTER_ID = "classification" IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID) os.makedirs(IMAGES_PATH, exist_ok=True) def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300): path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension) print("그림 저장:", fig_id) if tight_layout: plt.tight_layout() plt.savefig(path, format=fig_extension, dpi=resolution) # 시각화 설정 mpl.rc('axes', labelsize=14) mpl.rc('xtick', labelsize=12) mpl.rc('ytick', labelsize=12) # 음수 부호 표시 오류 제어 mpl.rcParams['axes.unicode_minus'] = False # Seaborn 및 Scipy 임포트 import seaborn as sns import scipy as sp from scipy import stats # 사이킷런 버전 확인 및 설정 import sklearn assert sklearn.__version__ >= "0.20" # 결과 재현을 위한 난수 시드 설정 # 모델을 만드는 작업을 여러 번에 걸쳐서 하는 경우 # 시드가 변경이 되어 훈련용 데이터가 자주 변경된다면 # 결국 모든 데이터를 가지고 모델을 생성하는 결과를 초래함 # Overfitting 문제 발생 np.random.seed(42)
# data load from sklearn.datasets import fetch_openml mnist=fetch_openml('mnist_784', version=1, as_frame=False) print(mnist)
# feature와 target을 분리해보자. X,y=mnist['data'],mnist['target'] # 데이터는 784 픽셀을 가진 흑백 이미지이며, # 실제 크기는 28*28이 된다. # 각 픽셀은 0 ~ 255 까지의 값을 가진다. print(X.shape) print(y.shape)
# 이미지 하나를 출력해보자. some_digit=X[0] # 이 이미지는 784 픽셀로 구성 # 2차원 이미지로 변환하자. some_digit_img=some_digit.reshape(28,28) plt.imshow(some_digit_img, cmap=mpl.cm.binary) plt.axis('off')
# 출력한 이미지의 레이블 확인 print(y[0])
#숫자 그림을 위한 추가 함수 #여러 개의 이미지 데이터를 행 단위로 출력하는 함수 #instance는 출력할 이미지, images_per_row는 열의 수 def plot_digits(instances, images_per_row=10, **options): #이미지 크기 설정 size = 28 #열의 개수 설정 images_per_row = min(len(instances), images_per_row) #이미지 전체를 순회하면서 28 * 28로 설정 images = [instance.reshape(size,size) for instance in instances] #행의 개수 구하기 n_rows = (len(instances) - 1) // images_per_row + 1 #이미지 들을 저장할 리스트 row_images = [] n_empty = n_rows * images_per_row - len(instances) #0으로 가득채운 행렬을 만들어서 images 에 저장 images.append(np.zeros((size, size * n_empty))) #행 단위로 순회하면서 이미지를 추가 for row in range(n_rows): rimages = images[row * images_per_row : (row + 1) * images_per_row] row_images.append(np.concatenate(rimages, axis=1)) image = np.concatenate(row_images, axis=0) #이미지 출력 plt.imshow(image, cmap = mpl.cm.binary, **options) plt.axis("off")
- 여러 개의 이미지 출력 함수 확인
plt.figure(figsize=(9,9)) example_images = X[:100] plot_digits(example_images, images_per_row=5) save_fig("more_digits_plot") plt.show()
- 이진 분류를 위한 데이터를 생성하자.
# 훈련 데이터와 테스트 데이터 분리 # mnist는 섞여있다. X_train, X_test, y_train, y_test= X[:60000], X[60000:], y[:60000], y[60000:] # 이진 분류는 True와 False로 분류 # 이진 분류의 경우는 Target이 bool y_train_5=(y_train==5) y_test_5=(y_test==5)
- sklearn의 SGDClassifier(Stochastic Gradient Descent) - 확률적 경사 하강법 클래스
- 경사 하강법(목표 지점까지 도달하기 위해서 작은 단위로 분할을 한 뒤, 그 분할 단위에서 최선의 방법을 선택)을 이용해서 분류를 처리
- 한 번에 하나씩 훈련 샘플을 선택해서 수행하기에, 데이터의 크기가 수시로 변할 수 있는 온라인 학습에 적합하다.from sklearn.linear_model import SGDClassifier # 훈련에 사용할 모델을 생성 - 하이퍼 파라미터를 설정 # max_iter : 최대 반복 횟수 , max이기에 이 함수는 생각보다 빨리 멈출 수도 있다. # tol 은 정밀도 이다. sgd_clf=SGDClassifier(max_iter=1000,tol=1e-3,random_state=42) # 훈련 sgd_clf.fit(X_train,y_train_5) # 예측 - feature는 2차원 배열 이상이어야 합니다. sgd_clf.predict([some_digit])
#5인 데이터를 분류하는 분류기 from sklearn.base import BaseEstimator class Never5Classifier(BaseEstimator): def fit(self, X, y=None): pass def predict(self, X): return np.zeros((len(X), 1), dtype=bool) #분류기 생성 never_5_clf = Never5Classifier() #새로 만든 분류기의 정확도 확인 cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring="accuracy")
- 분류의 평가 지표
- 오차 행렬
- 정답이 True와 False로 나누어져 있고, 분류 모델 또한 True와 False의 답을 내놓으면 2*2 행렬로 case를 표현- 이름을 만들 떄는 실제 정답에 관련디 것을 관련된 것을 먼저 부이고, 다음에는 예측한 내용을 붙입니다.
- sklearn에서는 confusion matrix라는
(TP+TN)/(TP+FN+FP+TN)
TP/(TP+FP)
TP/(TP+FN)
2*((Precision*Recall)/(Precision*recal))
accuracy_score, precision_score, recall_score, f1_score
사용from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score print("accuracy: %.2f" %accuracy_score(y_train_5, y_train_pred)) print("Precision : %.3f" % precision_score(y_train_5, y_train_pred)) print("Recall : %.3f" % recall_score(y_train_5, y_train_pred)) print("F1 : %.3f" % f1_score(y_train_5, y_train_pred))
이진 분류에서 사용하는 도구
정밀도 / 재현율 곡선은 정밀도에 대한 재현율 곡선이고, 거짓 양성 비율(FPR)에 대한 진짜 양성 비율(TPR - 재현율)의 곡선
FPR
- 1에서 음성으로 정확하게 분류한 음성 샘플의 비율인 음성 비율(True Negative Rate - TNR)를 뺀 값
- TNR을 specificity(특이도) 라고도 한다.
ROC Curve는 1-특이도 그래프이다.
점선이 완전 랜덤 뷴리기
점선 으로 부터 멀어지면 멀어질수록 더 좋은 분류기
y_train_pred=cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3) conf_mx=confusion_matrix(y_train,y_train_pred) print(conf_mx)
# 오차 행렬을 시각화 plt.matshow(conf_mx,cmap=plt.cm.gray) plt.show()
- 오차 행렬의 결과를 확인해서 오류가 많이 발생하는 특별한 상황이 있다면, 이에 대한 해결책을 제시할 수 있습니다.
- 오류가 많이 발생하는 클래스가 있다면, 그 클래스의 데이터를 조금 더 넣어 모델을 만들거나, 결과가 나온 데이터를 가지고 다시 한 번 학습하는 모델을 만든다거나 전처리 작업을 수행함
# 오차 행렬을 각 행의 합계로 나누고 대각선을 0으로 채워서 오류를 조금 더 눈에 띄게 출력 # 분류 모델에서는 이 행렬을 반드시 출력해보자 # 잘못 분류된 모델을 확인할 수 있기 떄문이다. row_sums=conf_mx.sum(axis=1, keepdims=True) norm_conf_mx=conf_mx/row_sums # 주 대각선 방향을 0으로 채움 np.fill_diagonal(norm_conf_mx,0) plt.matshow(norm_conf_mx,cmap=plt.cm.gray) plt.show()
average='weighted'
를 설정해주면 된다.# 출력해야 할 레이블이 여러 개 인 경우 # 이미지를 가지고 7보다 큰지, 그리고 홀수인지 여부를 같이 리턴하는 경우라면? from sklearn.neighbors import KNeighborsClassifier y_train_large=(y_train>=7) y_train_odd=(y_train%2==1) # 2개를 가지고 MulitiLabel 생성 y_multilabel=np.c_[y_train_large,y_train_odd] knn_clf=KNeighborsClassifier() knn_clf.fit(X_train,y_multilabel) knn_clf.predict([some_digit])
정리
- 분류 방법
- 이진 분류 : 참과 거짓으로 분류
- 카테고리 분류 : 2개 이상으로 분류
- 다중 레이블 분류 : 결과가 2개 이상인 경우- 평가 지표
- 오차 행렬
- 정확도
- 정밀도, 재현율, F1 Score
- 정밀도와 재현율 트레이드오프(PR)
- ROC 곡선(AUC)- 교차 검증
- 스케일링 작업을 하면 일반적으로 평가지표가 좋아짐
# borrowscore 와 pyment_inc_ratio에 따른 columns 선형 판별 분석 loan3000=pd.read_csv('./data/loan3000.csv') loan3000.head()
loan3000.outcome=loan3000.outcome.astype('category') # 숫자 컬럼들의 상관 계수를 전부 출력 print(loan3000.corr()) # 독립 변수와 종속 변수를 설정 predictors=['borrower_score','payment_inc_ratio'] outcome='outcome' #독립션수 -feature x=loan3000[predictors] # 종속 변수 - target y=loan3000[outcome] print(x) print(y)
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis loan_lda = LinearDiscriminantAnalysis() loan_lda.fit(X, y) #최적의 borrower_score 와 payment_inc_ratio 값 구하기 print(pd.DataFrame(loan_lda.scalings_, index=X.columns))
# 처음 5개의 데이터 판별 pred=pd.DataFrame(loan_lda.predict_proba(loan3000[predictors])) pred.head() # default와 paid_off의 확률을 확인
from sklearn.neighbors import KNeighborsClassifier loan200=pd.read_csv('./data/loan200.csv') # loan200.head() predictors=['payment_inc_ratio','dti'] outcome='outcome' newloan=loan200.loc[0:0,predictors] X=loan200.loc[1:,predictors] y=loan200.loc[1:,outcome] print(X) print(y)
knn=KNeighborsClassifier(n_neighbors=20) # 판별할 이웃의 개수 설정 # 데이터를 가지고 모델을 훈련 knn.fit(X,y) # 예측 print(knn.predict(newloan)) # 확률 확인 print(knn.predict_proba(newloan))
- 표준화를 수행한 경우와의 차이
loan_data=pd.read_csv('./data/loan_data.csv.gz') loan_data=loan_data.drop(columns=['Unnamed: 0', 'status']) loan_data['outcome']=pd.Categorical(loan_data['outcome'], categories=['paid off', 'default'], ordered=True) predictors=['payment_inc_ratio','dti', 'revol_bal', 'revol_util'] outcome='outcome' # 예측을 위해 떼놓은 데이터 newloan newloan=loan_data.loc[0:0, predictors] print(newloan)
X=loan_data.loc[1:, predictors] y=loan_data.loc[1:,outcome] knn=KNeighborsClassifier(n_neighbors=5) knn.fit(X,y) nbrs=knn.kneighbors(newloan) print(X.iloc[nbrs[1][0],:])
- 표준화를 하지 않으면, 이웃을 찾을 때 특정 특성이 영향을 많이 미치게 됩니다.
- 상대적으로 revol_bal이 가까운 데이터가 선택이 되었습니다.
이제 정규화를 해보자.
# 정규화 수행 후 이웃 구하기 from sklearn import preprocessing scaler=preprocessing.StandardScaler() scaler.fit(X*1.0) # 정수를 실수로 바꾸면서 정규화 X_std=scaler.transform(X*1.0) newloan_std=scaler.transform(newloan*1.0) knn=KNeighborsClassifier(n_neighbors=5) knn.fit(X_std,y) nbrs=knn.kneighbors(newloan_std) print(X.iloc[nbrs[1][0],:])
- 이전에 비해서 revo_bal을 제외한 속성드의 거리들이 가까워졌음
KNN은 구현이 간단하고 직관적(알아보기 쉬움)인데 반해서 성능 면에서는 다른 복잡한 알고리즘에 비해서 떨어지는 편입니다.
- 전체 데이터 14개, 이 중 맑은 날은 4일
- 전체 데이터에서 경기를 한 날은 9일, 맑은날 중 경기를 한 날은 4일
- 단순히 보면 그냥 100%이다.
- 나이브 베이즈 사전 확률
- 맑은 날 : 4/14 : 0.29
- 경기 : 9/14 : 0.64- 나이브 베이즈 사후 확률
- 맑은 날의 개수를 경기를 한 날로 나누기
- 4/9 : 0.44- 베이즈 공식에 대입하기
- 0.44*0.64/0.29 = 0.98
Odds Ratio=성공율/실패율=성공율/(1-성공율)
# 데이터 가져오기 from sklearn import datasets iris=datasets.load_iris() # list(iris.keys()) # 피쳐 생성 X=iris['data'][:,3:] y=(iris['target']==2).astype(np.uint8) # print(X) # print(y)
from sklearn.linear_model import LogisticRegression #분류 모델 생성 log_reg=LogisticRegression(solver='lbfgs',random_state=42) # 모델 훈련 log_reg.fit(X,y) #샘플 데이터를 1000개 생성해서 예측하기 X_new=np.linspace(0,3,1000).reshape(-1,1) # 각 샘플의 확률 계산 y_proba=log_reg.predict_proba(X_new) # 경계점수 decision_boundary=X_new[y_proba[:,1]>=0.5][0] print(decision_boundary) # 예측 print(log_reg.predict([[1.7],[1.5]]))