mnist dataset 사용하여 숫자 클래스를 분류하는 내용을 다루어 볼 것이다.
import numpy as np
#mnist dataset 불러오기
from sklearn.datasets import fetch_openml
mnist = fetch_openml(‘mnist_784’, version=1)
mnist.keys()
#그 중 data : 하나의 행이 하나의 샘플 - 784가지 특성 담겨있음 (까만색, 흰색 채워져있는거), target : 샘플의 label
X, y= mnist[“data”], mnist[“target”]
X.shape
import matplotlib as mpl
import matplotlib.pyplot as plt
some_digit = X.to_numpy()[0] #X[0] 로 작성하니 오류가 난다. to_numpy함수 사용으로 해결
some_digit_image = some_digit.reshape(28,28)
plt.imshow(some_digit_image, cmap = “binary”)
plt.axis(“off”)
plt.show()
y[0]
#y의 label을 정수형으로 변환해 줄 것이다
y = y.astype(np.uint8)
#X와 y의 train, test set 나누기
X_train , X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
5이다, 5가 아니다 두 클래스로 분류해주는 알고리즘 만들기 (5-detector)
y_train_5 = (y_train == 5) #y가 그냥 label이었다면 - 5이면 true로, 5가 아니면 false로 판단하게 바꿈
y_test_5 = (y_test == 5)
from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state = 42)
sgd_clf.fit(X_train, y_train_5)
sgd_clf.predict([X.to_numpy()[0]])
from sklearn.model_selection import cross_val_score
cross_val_score(sgd_clf, X_train, y_train_5, cv =3, scoring=“accuracy”)
# train set (X, y)를 가지고 3개로 나눠 두묶음으로 sgd_clf 훈련 후 한 묶음으로 MSE 구하는 식
from sklearn.base import BaseEstimator
class Never5Classifier(BaseEstimator):
def fit(self, X, y = None): return self
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”)
#5가 아니라고 무조건 분류하는 분류기도 정확도가 90% 이상 #0~9까지의 이미지가 있는데 그럼 그 중 5는 10%에 불과, 따라서 5가 아니라고 무조건 분류하는 분류기도 정확도가 90% 이상임 - 정확도는 적합하지 않은 성능 측정 지표
#오차행렬 이용
#만약 이진분류 아니라면 i행 i열 제외한 나머지가 오차인 것들
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
#y_train_pred의 오차행렬 출력
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
y_train_perfect_predictions = y_train_5
#제대로 분류되어있으면 대각성분 제외 0 가짐
confusion_matrix(y_train_5, y_train_perfect_predictions)
#정밀도 5라고 분류한것 중 진짜 5인 수의 비율
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred)
#정밀도 직접 계산
3530 / (3530 + 687)
recall_score(y_train_5, y_train_pred)
#재현율 직접 계산
3530 / (3530 + 1891)
#정밀도, 재현율의 조화평균인 F1점수 코드
from sklearn.metrics import f1_score
f1_score(y_train_5, y_train_pred)
3530 / (3530 + (1891 + 687)/2)
#F1 점수 직접 계산
y_scores = sgd_clf.decision_function([some_digit])
y_scores
#임계값을 0으로 정할 경우 모든 데이터 양의 클래스로(5라고) 판별
threshold = 0
y_some_digit_pred = (y_scores > threshold)
y_some_digit_pred
#임계값을 너무 크게 잡으면 음의 클래스로 판별할 가능성 높아진다
threshold = 200000
y_some_digit_pred = (y_scores > threshold)
y_some_digit_pred
#cv로 각 샘플의 예측값 대신 결정점수를 반환받아 저장
y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3, method=“decision_function”)
#60000개의 train set 모두의 예측된 결정점수 받음
y_scores.shape
#반환받은 결정점수를 이용해 모든 임계값에 대한 정밀도, 재현율을 계산할 것
#input = train set의 실제y값(T/F), 샘플의 결정점수
#output = 각 임계값에 따른 정밀도, 재현율
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)
확률이나 결정점수를 input하면 제일 큰값부터 threshold로 부여한다. 그에 따라 정해지는 precision과 recall을 저장한다
그 다음 값을 threshold로 사용해 앞의 과정 반복해서 세개의 array로 반환해준다
#threshold에 따른 정밀도, 재현율 plot하기
def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):
plt.plot(thresholds, precisions[:-1], “b–”, label=“precision”, linewidth=2)
plt.plot(thresholds, recalls[:-1], “g-”, label=“recall”, linewidth=2)
plt.xlabel(“threshold”, fontsize=16)
plt.legend(loc=“upper left”, fontsize=16)
plt.axis([-50000,50000,0,1])
plt.figure(figsize=(8, 4))
plot_precision_recall_vs_threshold(precisions, recalls, thresholds)
plt.show()
#재현율에 따른 정밀도 곡선
def plot_precision_vs_recall(precisions, recalls):
plt.plot(recalls, precisions, “b-”, linewidth=2)
plt.xlabel(“recall”, fontsize=16)
plt.ylabel(“precision”, fontsize=16)
plt.axis([0, 1, 0, 1])
plt.figure(figsize=(8, 6))
plot_precision_vs_recall(precisions, recalls)
plt.show()
#재현율 80% 근처에서 정밀도 급강 - 그 전 지점 트레이드오프로 선택?
threshold_90_precision = thresholds[np.argmax(precisions >= 0.90)]
threshold_90_precision
y_train_pred_90 = (y_scores >= threshold_90_precision)
precision_score(y_train_5, y_train_pred_90)
recall_score(y_train_5, y_train_pred_90)
high precision, but low recall 이면 유용하지 않을것
정밀도 조정에서 재현율 - 정밀도 곡선처럼 재현율 조정을 위해 사용되는 다른 곡선
위양성비율에 대한 진양성비율(재현율) 곡선
roc_curve 함수
input = 훈련용 라벨데이터, y의 결정점수
output = 거짓양성비율, 진짜양성비율, 임계값
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)
def plot_roc_curve(fpr, tpr, label=None):
plt.plot(fpr, tpr, linewidth=2, label=label)
plt.plot([0, 1], [0, 1], ‘k–’)
plt.axis([0, 1, 0, 1])
plt.xlabel(‘False Positive Rate’, fontsize=16)
plt.ylabel(‘True Positive Rate’, fontsize=16)
plt.figure(figsize=(8, 6))
plot_roc_curve(fpr, tpr)
plt.show()
from sklearn.metrics import roc_auc_score
roc_auc_score(y_train_5, y_scores)
# 랜덤포레스트와 SGD 비교 - ROC 곡선 이용
# 랜덤포레스트는 결정점수 대신 확률을 점수 대신으로 이용한다.
from sklearn.ensemble import RandomForestClassifier
forest_clf = RandomForestClassifier(n_estimators=10, random_state=42)
y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3, method=“predict_proba”)
y_scores_forest = y_probas_forest[:, 1] #양성 클래스의 확률
fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, “b:”, linewidth=2, label=“SGD”)
plot_roc_curve(fpr_forest, tpr_forest, “RandomForest”)
plt.legend(loc=“lower right”, fontsize=16)
plt.show()
roc_auc_score(y_train_5, y_scores_forest)
#랜덤포레스트의 분류기가 AUC값이 더 커 좋은 분류기라고 비교할 수 있다.
#서포트벡터머신 분류기 불러오기
#SVC함수 fit input = X_train, y_train(label : 정수값)
#predict 는 label의 정수값을 리턴한다.
from sklearn.svm import SVC
svm_clf = SVC(gamma=“auto”, random_state = 42)
svm_clf.fit(X_train[:1000], y_train[:1000])
svm_clf.predict([some_digit])
#샘플 하나 당 10개의 결정점수 반환 - 각 클래스 분류 기준이 될 점수
#그 중 가장 높은 점수를 가진 클래스에 할당한다.
some_digit_scores = svm_clf.decision_function([some_digit])
some_digit_scores
np.argmax(some_digit_scores)
svm_clf.classes_
svm_clf.classes_[5]
#SVC에서 OvR기법 사용하는 다중 분류기를 만들고 싶을 때
from sklearn.multiclass import OneVsRestClassifier
ovr_clf = OneVsRestClassifier(SVC(gamma=“auto”, random_state = 42))
ovr_clf.fit(X_train[:1000], y_train[:1000])
ovr_clf.predict([some_digit])
#OvO기법 사용하도록 강제하는 경우
from sklearn.multiclass import OneVsOneClassifier
ovo_clf = OneVsOneClassifier(SGDClassifier(max_iter=5, random_state=42))
ovo_clf.fit(X_train, y_train)
ovo_clf.predict([some_digit])
len(ovo_clf.estimators_)
sgd_clf.fit(X_train, y_train)
sgd_clf.predict([some_digit])
sgd_clf.decision_function([some_digit])
#분류기 정확도 측정해보기
cross_val_score(sgd_clf, X_train, y_train, cv = 3, scoring = “accuracy”)
#정확도가 더 높아질 수 있도록 X_train의 스케일 조정한다.
#너무 오래걸려 중단…
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))
cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring=“accuracy”)
#오차행렬 측정
#y_train_pred 에 sgd 다중분류를 실행했을 때 예측값들을 저장
#이러한 y_train_pred의 오차행렬을 출력 (10x10 행렬)
y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)
conf_mx = confusion_matrix(y_train, y_train_pred)
conf_mx
def plot_confusion_matrix(matrix):
""“컬러 오차 행렬을 원할 경우”""
fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111)
cax = ax.matshow(matrix)
fig.colorbar(cax)
plt.matshow(conf_mx, cmap=plt.cm.gray)
plt.show()
#대각선부분이 잘 분류되었다는 것을 나타내는 지표 - 밝을수록 데이터 수가 많다는 뜻
#대각선이 어두우면 데이터의 양이 적거나 잘 분류되지 않았다는 것이라 생각할 수 있다.
row_sums = conf_mx.sum(axis=1, keepdims=True)
norm_conf_mx = conf_mx / row_sums
np.fill_diagonal(norm_conf_mx, 0)
plt.matshow(norm_conf_mx, cmap=plt.cm.gray)
plt.show()
#숫자그림 위한 추가 함수
def plot_digits(instances, images_per_row=10, **options):
size = 28
images_per_row = min(len(instances), images_per_row)
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)
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 = matplotlib.cm.binary,** options)
plt.axis(“off”)
#훈련데이터중 label이 각각 3이나 5, 예측된 클래스가 3이나 5로 분류된 데이터들을 각각 경우에 따라 출력
#label, predict
# X_11 = (3,3) X_12 = (3,5) X_21 = (5,3) X_22 = (5,5)
cl_a, cl_b = 3, 5
X_aa = X_train[(y_train == cl_a) & (y_train_pred == cl_a)]
X_ab = X_train[(y_train == cl_a) & (y_train_pred == cl_b)]
X_ba = X_train[(y_train == cl_b) & (y_train_pred == cl_a)]
X_bb = X_train[(y_train == cl_b) & (y_train_pred == cl_b)]
plt.figure(figsize=(8,8))
plt.subplot(221); plot_digits(X_aa[:25], images_per_row=5)
plt.subplot(222); plot_digits(X_ab[:25], images_per_row=5)
plt.subplot(223); plot_digits(X_ba[:25], images_per_row=5)
plt.subplot(224); plot_digits(X_bb[:25], images_per_row=5)
plt.show()
from sklearn.neighbors import KNeighborsClassifier
#타깃 레이블 정하기 (T/F)
y_train_large = (y_train >= 7)
y_train_odd = (y_train % 2 == 1)
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])
#분류기 평가하기 - 모든 레이블에서의 F1 점수 평균으로 평가
y_train_knn_pred = cross_val_predict(knn_clf, X_train, y_multilabel, cv=3, n_jobs=-1)
f1_score(y_multilabel, y_train_knn_pred, average=“macro”)
# 다중 출력 분류 - 다중레이블 분류 + 이진분류가 아닌 다중분류
# 이미지 픽셀 강도에 잡음 추가해주는 코드
noise = np.random.randint(0, 100, (len(X_train), 784))
X_train_mod = X_train + noise
noise = np.random.randint(0, 100, (len(X_test), 784))
X_test_mod = X_test + noise
y_train_mod = X_train
y_test_mod = X_test
def plot_digit(data):
image = data.reshape(28,28)
plt.imsow(image, cmap = mpl.cm.binary, interpolation = “nearest”)
plt.axis(“off”)
some_index = 5500
plt.subplot(121); plot_digit(X_test_mod[some_index])
plt.subplot(122); plot_digit(y_test_mod[some_index])
plt.show()
knn_clf.fit(X_train_mod, y_train_mod)
clean_digit = knn_clf.predict([X_test_mod[some_index]])
plot_digit(clean_digit)