에러함수의 에 대한 gradient를 구해보자.
라고 정의하면
우도함수
특성벡터 를 위한 목표벡터 는 클래스에 해당하는 하나의 원소만 1이고 나머지는 0인 1-of-K 인코딩 방법으로 표현된다.
는 를 원소로 가지고 있는 크기가 인 행렬
(예시)
따라서
음의 로그우도
에 대한 gradient를 구한다. 먼저 하나의 샘플 에 대한 에러
를 정의하면
다음 함수들 사이의 관계를 주목하자.
따라서
*batch : 전체 데이터를 모두 사용
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
import seaborn as sns
X, t = make_classification(n_samples=500, n_features=2, n_redundant=0, n_informative=1,
n_clusters_per_class=1, random_state=14)
t = t[:,np.newaxis]
sns.set_style('white')
sns.scatterplot(X[:,0],X[:,1],hue=t.reshape(-1));
이러한 두 개의 그룹으로 나누어져 있는 데이터가 주어졌을 때
로지스틱 회귀 모델 학습을 통해 얼마나 잘 구별해내는 지 살펴볼 것임
📌 sigmoid 함수 정의
def sigmoid(x):
return 1 / (1 + np.exp(-x))
📌 compute cost 함수 정의
cost를 계산하는 함수(에러함수) 정의 (w가 주어졌을 때 입력값 X와 목표값 t 에 대해)
코드에서는 1/N 을 곱함
def compute_cost(X, t, w):
N = len(t)
h = sigmoid(X @ w)
epsilon = 1e-5
cost = (1/N)*(((-t).T @ np.log(h + epsilon))-((1-t).T @ np.log(1-h + epsilon)))
return cost
📌 gradient descent 함수 정의
def gradient_descent(X, t, w, learning_rate, iterations):
N = len(t)
cost_history = np.zeros((iterations,1))
for i in range(iterations):
w = w - (learning_rate/N) * (X.T @ (sigmoid(X @ w) - t))
cost_history[i] = compute_cost(X, t, w)
return (cost_history, w)
📌 predict 함수 정의
입력 데이터에 대해서 파라미터 w 에대한 y 값을 구하는 함수
def predict(X, w):
return np.round(sigmoid(X @ w))
N = len(t)
X = np.hstack((np.ones((N,1)),X))
M = np.size(X,1)
w = np.zeros((M,1))
iterations = 1000
learning_rate = 0.01
initial_cost = compute_cost(X, t, w)
print("Initial Cost is: {} \n".format(initial_cost))
# Initial Cost is: [[0.69312718]]
(cost_history, w_optimal) = gradient_descent(X, t, w, learning_rate, iterations)
print("Optimal Parameters are: \n", w_optimal, "\n")
# Optimal Parameters are:
# [[-0.07024012]
# [ 1.9275589 ]
# [ 0.02285894]]
plt.figure()
sns.set_style('white')
plt.plot(range(len(cost_history)), cost_history, 'r')
plt.title("Convergence Graph of Cost Function")
plt.xlabel("Number of Iterations")
plt.ylabel("Cost")
plt.show()
👉 업데이트할 수록 비용이 줄어듦
Accuracy 계산
예측값이 타겟값과 얼마나 비슷한 지 계산
## Accuracy
y_pred = predict(X, w_optimal)
score = float(sum(y_pred == t))/ float(len(t))
print(score)
# 0.954
👉 95% 정도 일치
slope = -(w_optimal[1] / w_optimal[2])
intercept = -(w[0] / w_optimal[2])
sns.set_style('white')
sns.scatterplot(X[:,1],X[:,2],hue=t.reshape(-1))
ax = plt.gca()
ax.autoscale(False)
x_vals = np.array(ax.get_xlim())
y_vals = intercept + (slope * x_vals)
plt.plot(x_vals, y_vals, c="k");
📌 sgd(Stochastic Gradient Descent) 함수 정의
def sgd(X, t, w, learning_rate, iterations):
N = len(t)
cost_history = np.zeros((iterations,1))
for i in range(iterations):
i = i % N
w = w - learning_rate * (X[i, np.newaxis].T * (sigmoid(X[i] @ w) - t[i]))
cost_history[i] = compute_cost(X[i], t[i], w)
return (cost_history, w)
X, t = make_classification(n_samples=500, n_features=2, n_redundant=0, n_informative=1,
n_clusters_per_class=1, random_state=14)
t = t[:,np.newaxis]
N = len(t)
X = np.hstack((np.ones((N,1)),X))
M = np.size(X,1)
w = np.zeros((M,1))
iterations = 2000
learning_rate = 0.01
initial_cost = compute_cost(X, t, w)
print("Initial Cost is: {} \n".format(initial_cost))
# Initial Cost is: [[0.69312718]]
(cost_history, w_optimal) = sgd(X, t, w, learning_rate, iterations)
print("Optimal Parameters are: \n", w_optimal, "\n")
# Optimal Parameters are:
# [[-0.19304782]
# [ 2.5431236 ]
# [ 0.01130098]]
plt.figure()
sns.set_style('white')
plt.plot(range(len(cost_history)), cost_history, 'r')
plt.title("Convergence Graph of Cost Function")
plt.xlabel("Number of Iterations")
plt.ylabel("Cost")
plt.show()
👉 sgd의 경우 초기에 들쑥날쑥한 경향을 보임
Accuracy 계산
## Accuracy
y_pred = predict(X, w_optimal)
score = float(sum(y_pred == t))/ float(len(t))
print(score)
# 0.96
📌 batch gradient 함수 정의
batch 사이즈를 조절
def batch_gd(X, t, w, learning_rate, iterations, batch_size):
N = len(t)
cost_history = np.zeros((iterations,1))
shuffled_indices = np.random.permutation(N)
X_shuffled = X[shuffled_indices]
t_shuffled = t[shuffled_indices]
for i in range(iterations):
i = i % N
X_batch = X_shuffled[i:i+batch_size]
t_batch = t_shuffled[i:i+batch_size]
# batch가 epoch 경계를 넘어가는 경우, 앞 부분으로 채워줌
if X_batch.shape[0] < batch_size:
X_batch = np.vstack((X_batch, X_shuffled[:(batch_size - X_batch.shape[0])]))
t_batch = np.vstack((t_batch, t_shuffled[:(batch_size - t_batch.shape[0])]))
w = w - (learning_rate/batch_size) * (X_batch.T @ (sigmoid(X_batch @ w) - t_batch))
cost_history[i] = compute_cost(X_batch, t_batch, w)
return (cost_history, w)
X, t = make_classification(n_samples=500, n_features=2, n_redundant=0, n_informative=1,
n_clusters_per_class=1, random_state=14)
t = t[:,np.newaxis]
N = len(t)
X = np.hstack((np.ones((N,1)),X))
M = np.size(X,1)
w = np.zeros((M,1))
iterations = 1000
learning_rate = 0.01
initial_cost = compute_cost(X, t, w)
# Initial Cost is: [[0.69312718]]
print("Initial Cost is: {} \n".format(initial_cost))
# Optimal Parameters are:
# [[-0.06983134]
# [ 1.92943764]
# [ 0.01000487]]
(cost_history, w_optimal) = batch_gd(X, t, w, learning_rate, iterations, 32)
print("Optimal Parameters are: \n", w_optimal, "\n")
plt.figure()
sns.set_style('white')
plt.plot(range(len(cost_history)), cost_history, 'r')
plt.title("Convergence Graph of Cost Function")
plt.xlabel("Number of Iterations")
plt.ylabel("Cost")
plt.show()
Accuracy 계산
## Accuracy
y_pred = predict(X, w_optimal)
score = float(sum(y_pred == t))/ float(len(t))
print(score)
# 0.954
👉 처음에 실행했던 Gradient Descent (batch) 의 score와 같은 값이 나옴
🔥
준비
# Python ≥3.5 is required
import sys
assert sys.version_info >= (3, 5)
# Scikit-Learn ≥0.20 is required
import sklearn
assert sklearn.__version__ >= "0.20"
# Common imports
import numpy as np
import os
# to make this notebook's output stable across runs
np.random.seed(42)
# To plot pretty figures
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)
# Where to save the figures
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("Saving figure", fig_id)
if tight_layout:
plt.tight_layout()
plt.savefig(path, format=fig_extension, dpi=resolution)
MNIST 데이터
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, cache=True)
mnist.keys()
# dict_keys(['data', 'target', 'frame', 'categories', 'feature_names', 'target_names', 'DESCR', 'details', 'url'])
X, y = mnist["data"], mnist["target"]
X.shape
# (70000, 784)
X.values
# array([[0., 0., 0., ..., 0., 0., 0.],
# [0., 0., 0., ..., 0., 0., 0.],
# [0., 0., 0., ..., 0., 0., 0.],
# ...,
# [0., 0., 0., ..., 0., 0., 0.],
# [0., 0., 0., ..., 0., 0., 0.],
# [0., 0., 0., ..., 0., 0., 0.]])
X의 3번째 데이터 살펴보기
import matplotlib as mpl
import matplotlib.pyplot as plt
some_digit = X.loc[2].values
some_digit_image = some_digit.reshape(28, 28)
plt.imshow(some_digit_image, cmap=mpl.cm.binary)
plt.axis("off")
save_fig("some_digit_plot")
plt.show()
some_digit
👉 0은 흰색 부분, 0이 아닌값은 검은색 부분
y 변환(object → uint8)
y
# 0 5
# 1 0
# 2 4
# 3 1
# 4 9
# ..
# 69995 2
# 69996 3
# 69997 4
# 69998 5
# 69999 6
# Name: class, Length: 70000, dtype: category
# Categories (10, object): ['0', '1', '2', '3', ..., '6', '7', '8', '9']
y = y.astype(np.uint8)
y
# 0 5
# 1 0
# 2 4
# 3 1
# 4 9
# ..
# 69995 2
# 69996 3
# 69997 4
# 69998 5
# 69999 6
# Name: class, Length: 70000, dtype: uint8
여러 데이터 한번에 출력해보기
def plot_digit(data):
image = data.reshape(28, 28)
plt.imshow(image, cmap = mpl.cm.binary,
interpolation="nearest")
plt.axis("off")
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 = mpl.cm.binary, **options)
plt.axis("off")
plt.figure(figsize=(9,9))
example_images = X[:100].values
plot_digits(example_images, images_per_row=10)
save_fig("more_digits_plot")
plt.show()
y[0]
# 5
👉 첫번째 데이터는 5
이다.
학습 데이터, 테스트 데이터 나누기
X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]
👉 6만개는 학습데이터, 1만개는 테스트데이터로 나눔
문제를 단순화해서 숫자 5만 식별해보자.
y_train_5 = (y_train == 5)
y_test_5 = (y_test == 5)
y_train_5
# 0 True
# 1 False
# 2 False
# 3 False
# 4 False
# ...
# 59995 False
# 59996 False
# 59997 True
# 59998 False
# 59999 False
# Name: class, Length: 60000, dtype: bool
😀 로지스틱 회귀 모델 사용
from sklearn.linear_model import LogisticRegression
log_clf = LogisticRegression(random_state=0).fit(X_train, y_train_5)
log_clf.predict([X.loc[0],X.loc[1],X.loc[2]])
# array([ True, False, False])
교차 검증을 사용해서 평가
from sklearn.model_selection import cross_val_score
cross_val_score(log_clf, X_train, y_train_5, cv=3, scoring="accuracy")
# array([0.97525, 0.9732 , 0.9732 ])
👉 모든 교차 검증 폴드에 대해 정확도가 97% 이상이다.
과연 모델이 좋아 보일까?
무조건 5가 아니면 0을 돌려주는 함수 Never5Classifier 정의하여 다시 예측
from sklearn.base import BaseEstimator
class Never5Classifier(BaseEstimator):
def fit(self, X, y=None):
pass
def predict(self, X):
return np.zeros(len(X), dtype=bool)
never_5_clf = Never5Classifier()
cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring="accuracy")
# array([0.91125, 0.90855, 0.90915])
👉 90% 이상의 정확도로 큰 차이가 없어보임
never_5_clf.predict(X)
array([False, False, False, ..., False, False, False])
👉 이미지의 10%만 숫자 5이기 때문에 무조건 5가 아닌 것으로 예측하면 정확도는 90%가 된다.
💡 목표값(클래스)들이 불균형인 경우에 정확도(accuracy)는 좋은 지표가 아니다!
예측값 생성
from sklearn.model_selection import cross_val_predict
y_train_pred = cross_val_predict(log_clf, X_train, y_train_5, cv=3)
y_train_pred.shape
# (60000,)
from sklearn.metrics import confusion_matrix
confusion_matrix(y_train_5, y_train_pred)
# array([[54038, 541],
# [ 1026, 4395]], dtype=int64)
👉
confusion_matrix(y_train_5, y_train_pred)
# array([[54038, 541],
# [ 1026, 4395]], dtype=int64)
from sklearn.metrics import precision_score, recall_score
precision_score(y_train_5, y_train_pred) # 4395/(4395+541)
# 0.8903970826580226
recall_score(y_train_5, y_train_pred) # 4395/(4395+1026)
# 0.8107360265633647
confusion_matrix(y_train_5, never_5_clf.predict(X)[:60000])
# array([[54579, 0],
# [ 5421, 0]], dtype=int64)
precision_score(y_train_5, never_5_clf.predict(X)[:60000])
# 0.0
recall_score(y_train_5, never_5_clf.predict(X)[:60000])
# 0.0
👉
never_5_clf
의 경우 positive 부분이 0 이기 때문에 precision, recall 값이 0이다.🔥 Accuracy(정확도)만 사용하면 올바르지 못한 검증을 하게 되는 것이므로 대부분의 경우 confusion matrix를 사용하는 것이 좋음
errors = (y_train_pred != y_train_5)
errors
# 0 False
# 1 False
# 2 False
# 3 False
# 4 False
# ...
# 59995 False
# 59996 False
# 59997 False
# 59998 False
# 59999 False
# Name: class, Length: 60000, dtype: bool
error 인 경우를 출력
plt.figure(figsize=(9,9))
plot_digits(X_train[errors][:100].values, images_per_row=10)
save_fig("more_digits_plot")
plt.show()