가중치 업데이트 →wj:=wj+Δwj →가중치 업데이트 값인 Δwj=η(y(i)−ˆy(i))x(i)j →η는 학습률로 0.0~0.1 사이의 값 →y(i)와 ˆy(i)는 각각 i번째 훈련 샘플의 정답 레이블, 예측 레이블
- 가중치 벡터의 모든 가중치는 동시에 업데이트
- 모든 가중치가 각자의 업데이트 값 Δwj에 의해 업데이트되기 전에 예측 레이블 ˆy(i)를 다시 계산하지 않음 → Δw0=η(y(i)−output(i)) → Δw1=η(y(i)−output(i))x(i)1 → Δw2=η(y(i)−output(i))x(i)2
- 퍼셉트론이 클래스 레이블을 정확히 예측하면 가중치 유지(업데이트 값은 0) → y(i)=−1,ˆy(i)=−1,Δwj=η(−1−(−1))x(i)j=0 → y(i)=1,ˆy(i)=1,Δwj=η(1−1)x(i)j=0 - 퍼셉트론이 클래스 레이블을 잘못 예측하면 가중치를 양성 또는 음성 타겟 클래스 방향으로 이동시킴 → y(i)=1,ˆy(i)=−1,Δwj=η(1−(−1))x(i)j=η(2)x(i)j → y(i)=−1,ˆy(i)=1,Δwj=η(−1−1)x(i)j=η(−2)x(i)j
- 가중치 업데이트는 x(i)j값에 비례하여, 이 값을 조절해 결정경계를 더 크게 움직이거나 더 작게 움직일 수 있음
- 퍼셉트론은 두 클래스가 선형적으로 구분되고 학습률이 충분히 작을 때만 수렴이 보장됨
- 선형 결정 경계로 나눌 수 없다면 훈련 데이터셋을 반복할 최대 횟수(epoch)를 지정해 분류 허용 오차 지정, 그렇지 않으면 퍼셉트론은 가중치 업데이트를 멈추지 않음
퍼셉트론 알고리즘
4. 파이썬으로 퍼셉트론 학습 알고리즘 구현
- 퍼셉트론 클래스 정의
import numpy as np
classPerceptron(object):def__init__(self, eta = 0.01, n_iter = 50, random_state = 1):
self.eta = eta
self.n_iter = n_iter
self.random_state = random_state
deffit(self, X, y):
rgen = np.random.RandomState(self.random_state) # 난수 생성기 객체 생성(격리된 시드 부여 가능, 이전과 동일한 랜덤 시드 재현 가능)
self.w_ = rgen.normal(loc = 0.0, scale = 0.01,
size = 1 + X.shape[1]) # 평균 0, 표준편차 0.01의 정규분포에서 훈련 데이터 개수(X.shape[1]) + 절편(1)만큼의 난수 생성
self.errors_ = [] # 훈련 중 error의 감소 확인을 위한 객체for _ inrange(self.n_iter):
errors = 0for xi, target inzip(X, y):
update = self.eta * (target - self.predict(xi)) # 가중치 w_j의 변화량
self.w_[1:] += update * xi # 각 훈련 데이터에 가중치값 곱하기
self.w_[0] += update # 가중치값 업데이트
errors += int(update != 0.0) # 업데이트 된 값이 0이 아니면(실제값과 예측값이 다르면) errors에 1 더함
self.errors_.append(errors) # 전체 훈련 데이터에서 예측값이 실제값과 다르게 나온 횟수 추가return self
# 최종입력인 Z 계산defnet_input(self, X):return np.dot(X, self.w_[1:]) + self.w_[0]
# 결정함수를 임계값인 0과 비교, 크거나 같으면 1, 작으면 -1로 반환defpredict(self, X):return np.where(self.net_input(X) >= 0, 1, -1)
- fit 메서드 내의 w_객체에 가중치를 초기화하여 선언 - 가중치가 0이 아니어야 학습률(eta)가 분류 결과에 영향을 줄 수 있어 0이 아닌 랜덤 값으로 초기화 - 가중치가 0이면 eta는 가중치 벡터의 방향이 아니라 크기에만 영향을 미침
- 붓꽃 데이터셋에서 퍼셉트론 훈련 - Perceptron은 이진분류이므로 setosa와 versicolor에 대해서만 분류 진행 - 또한, 특성은 첫번째 특성(꽃받침 길이)와 세번째 특성(꽃잎 길이)을 사용
import pandas as pd
import matplotlib.pyplot as plt
s = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
df = pd.read_csv(s, header = None, encoding = 'utf-8')
# 처음 100개의 데이터(setosa 50개와 versicolor 50개)의 다섯번째 열인 예측 대상을 y로 선언
y = df.iloc[0:100, 4].values
# setosa이면 -1, versicolor이면 1로 변경
y = np.where(y == 'Iris-setosa', -1, 1)
# 처음 100개의 데이터에서 예측에 사용하고자 하는 특성인 첫번재 열과 세번째 열만 추출
X = df.iloc[0:100, [0, 2]].values
# 산점도로 시각화
plt.scatter(X[:50, 0], X[:50, 1], color = 'red', marker = 'o', label = 'setose')
plt.scatter(X[50:100, 0], X[50:100, 1], color = 'blue', marker = 'x', label = 'versicolor')
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc = 'upper left')
plt.show()
# Perceptron 알고리즘 훈련
ppn = Perceptron(eta = 0.1, n_iter = 10)
ppn.fit(X, y)
# 훈련 과정에서 각 반복에서 나온 오차의 개수 시각화
plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker = 'o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')
plt.show()
from matplotlib.colors import ListedColormap
# 결정경계 시각화 함수defplot_decision_regions(X, y, classifier, resolution = 0.02):
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# 두 특성의 최소값과 최대값에 각각 -1, +1을 한 값을 정의
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1# 위에서 구한 최소값과 최대값을 시작으로 하는 meshgrid 생성(최소값과 최대값 사이에 0.02의 간격으로 데이터 생성)
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
# ravel 함수로 meshgrid를 1차원으로 평평하게 펴고, xx1, xx2를 각각 X, y로 Perceptron에 넣어 예측
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
# 예측된 값을 다시 meshgrid 형태로 변경
Z = Z.reshape(xx1.shape)
# contourf 함수로 등고선 그래프 그리기(등고선이 두 집단의 경계로 그려짐)# 분류된 값이 -1인 것과 1인 것을 각각 빨간색과 파란색으로 구분
plt.contourf(xx1, xx2, Z, alpha = 0.3, cmap = cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
# 훈련 데이터를 빨간색 네모 모양와 파란색 x모양으로 구분for idx, cl inenumerate(np.unique(y)):
plt.scatter(x = X[y == cl, 0], y = X[y == cl, 1],
alpha = 0.8, c = colors[idx], marker = markers[idx], label = cl, edgecolor = 'black')
plot_decision_regions(X, y, classifier = ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc = 'upper left')
plt.show()
- OvR(One-versus-Resr)기법을 사용하면 이진 분류기로 다중 클래스 문제 해결 가능 →클래스마다 하나의 분류기를 훈련(각 클래스를 양성으로 분류하고 나머지는 음성으로 분류) →레이블이 없는 새로운 데이터 샘플 분류 시에는 클래스 레이블의 개수와 같은 n개의 분류기 사용 → 신뢰도가 가장 높은 클래스 레이블(최종 입력의 절댓값이 가장 큰 클래스)을 분류하려는 샘플의 레이블로 선택
- 퍼셉트론의 가장 큰 문제는 수렴 → 구 클래스가 선형적인 초평면으로 구분될 수 있을 때 수렴하지만, 선형 결정 경계로 완벽하게 구분되지 않으면 최대 epoch를 지정하지 않는한 가중치 업데이트가 멈추지 않음
● 예시로 '월간 데이콘 쇼츠 - 초전도체 임계온도 예측 AI 헤커톤'의 데이터 사용(train, test, submission 파일 3개 존재)
1. 모듈 임포트
import os
# Seed 고정defseed_everything(seed):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
seed_everything(42)
# warning 무시import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.tree import DecisionTreeClassifier, DicisionTreeRegressor
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score, mean_squared_error, mean_absolute_error, r2_score
2. 데이터 불러오기
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('sample_submission.csv')
3. 데이터 탐색
train.head() # 처음 5개의 데이터 (전체적인 데이터 구조 확인)
train.info() # 데이터에 대한 개략적인 정보 (행과 열 개수, non-null값 개수, 데이터 타입)
train.describe() # 숫자형 데이터 요약 (개수, 평균, 표준편차, 최소값, 25%, 50%, 75%, 최대값)
train.isna().sum() # 각 데이터의 결측값 개수# 이외에도 이해가 가지 않는 행이나 열만 따로 뽑아서 탐색, 데이터 도메인 지식 등을 찾아보며 데이터에 대한 이해 필요
train = train.drop('ID', axis = 1) # train 데이터 탐색 결과 단순 식별자 변수인 ID는 필요 없는 것으로 판단, 삭제
feature = train.copy() # 밑에서 target변수를 제거하고 feature 변수만 남긴 데이터
target = feature.pop('critical_temp') # target 데이터 추출, train에는 feature 데이터만 남음
- train.head(): 처음 5개의 데이터 (전체적인 데이터 구조 확인)
- train.info(): 데이터에 대한 개략적인 정보 (행과 열 개수, non-null값 개수, 데이터 타입)
scaler = StandardScaler() # 박스플롯 출력 전 하나의 박스플롯에서 데이터를 한눈에 보기 위해 같은 범위로 임시 스케일링
plt.figure(figsize = (15, 10))
sns.boxplot(scaler.fit_transform(feature))
- 각 feature 데이터의 바이오린 플롯과 히스토그램 출력하여 데이터 분포 확인
for col in train.columns:
fig, axs = plt.subplots(nrows = 1, ncols = 2, figsize = (10, 4))
sns.violinplot(x = col, data = train, kde = True, ax = axs[0])
sns.histplot(x = col, data = train, kde = True, ax = axs[1])
# 방법 1. 결측값이 있는 행 제거
feature = feature.dropna(axis = 0, how = 'any') # 각 행에서 하나의 값이라도 NaN이면 해당 행 제거
feature = feature.dropna(axis = 0, how = 'all') # 각 행의 모든 값이 NaN이면 해당 행 제거# 방법 2. 결측값 채우기(특정 값으로 채우기)
feature = feature.fillna(0) # 0으로 채우기
feature = feature.fillna(method = 'pad') # 바로 앞의 값으로 채우기
feature = feature.fillna(method = 'bfill') # 바로 뒤의 값으로 채우기
feature = feature.fillna(feature.mean()) # 해당 열의 평균값으로 채우기# 방법 3. 결측값 채우기(보간법)
feature = feature.interpolate(method = 'time') # 인덱스가 날짜/시간일 때 날짜나 시간에 따라 보간
feature = feature.interpolate(method = 'nearest', limit_direction = 'forward') # 가장 가까운 값으로 보간
feature = feature.interpolate(method = 'linear', limit_direction = 'forward') # 양 끝값에 따라 선형식에 적용하여 보간
feature = feature.interpolate(method = 'polynomial', order = 2) # 양 끝값에 따라 다항식에 적용하여 보간 (order은 다항식 차수)# 방법 4. 결측값이 있는 열을 해당 열의 결측값이 아닌 행과 다른 feature 변수들로 예측하여 채우기
feature_target = feature.pop('결측값 열') # 다른 행들로부터 결측값을 예측할 목표열 분리
target_index = feature_target[feature_target['결측값 열'] == np.nan].index # 목표열 중 결측값인 행의 인덱스 추출
feature_train = feature.iloc[~target_index] # 목표열이 아닌 다른 열에서 결측값이 있는 행의 인덱스가 아닌 행 추축(train 용)
feature_test = feature.iloc[target_index] # 목표열이 아닌 다른 열에서 결측값이 있는 행의 인덱스인 행 추출(test 용)
feature_target_train = feature_target[~target_index] # 목표열 중 결측값이 아닌 행 추출(train 용)
rf = RandomForestRegressor() # 랜덤포레스트로 예측
rf.fit(feature_train, feature_target_train) # 랜덤포레스트에 목표열 중 결측값이 아닌 행을 target 변수, 다른 열의 해당 행들을 feature 변수로 모델 피팅
pred = rf.predict(feature_test) # 피팅된 모델로 목표열에서 결측값인 행 예측
feature.iloc[target_index]['결측값 열'] = pred # 원래 데이터프레임에서 목표열의 결측값인 값을 위에서 예측한 값으로 대체
# 방법 1. 문자형 변수 라벨 인코딩
encoder = LabelEncoder()
feature[[feature.select_dtypes(object).columns]] = encoder.fit_transform(feature.select_dtypes(object))
test[[test.select_dtypes(object).columns]] = encoder.transform(test.select_dtypes(object))
# 방법 2. 문자형 변수 원핫 인코딩
encoder = OneHotEncoder()
feature[[feature.select_dtypes(object).columns]] = encoder.fit_transform(feature.select_dtypes(object).toarray())
test[[test.select_dtypes(object).columns]] = encoder.transform(test.select_dtypes(object).toarray())
- 라벨 인코딩은 문자형 변수 고윳값의 개수에 따라 0부터 n-1까지의 숫자로 변환되어 ML에서 왜곡된 결과를 불러올 수 있음
- 원핫 인코딩은 이런 문제를 해결하기 위해 고윳값의 개수만큼 새로운 feature 변수를 생성하여 각 행에서 고윳값에 해당하는 feature만 1, 나머지는 0으로 표시
9. 모델 피팅 및 예측
# feature 변수와 target 변수 분리
X = feature
y = target
# 학습용 데이터(X_train, y_train)와 검증용 데이터(X_test, y_test)로 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)
# 모델 선언(사용할 모델로 선정, 예시로는 RandomForestRegressor 선정)
model = RandomForestRegressor()
# 모델 학습
model.fit(X_train, y_train)
# 학습된 모델로 검증용 데이터 예측
y_pred = rf.predict(X_test)
# 실제 검증용 데이터와 예측한 검증용 데이터 비교하여 점수 출력print(f'mse: {mean_squared_error(y_test, y_pred)}\nmae: {mean_absolute_error(y_test. y_pred)}\naccuracy: {accuracy_score(y_test, y_pred)}')
- KFold와 cross_val_score에서 각각 평균 정확도가 다른 이유는 cross_val_score가 StratifiedKFold를 이용해 폴드 세트를 분할하기 때문
# 방법 3. GridSearchCV
params = {'max_depth': [2, 3, 5, 10], 'min_samples_split': [2, 3, 5], 'min_samples_leaf': [1, 5, 8]} # 사용할 모델에 맞는 하이퍼 파라미터 중 탐색하고 싶은 범위 지정
grid_model = GridSearchCV(model, param_grid = params, scoring = 'accuracy', cv = 5, verbose = 3) # GridSearchCV에 model, 하이퍼 파라미터, 점수, 교차검증 횟수 등을 파라미터로 지정
grid_model.fit(X_train, y_train) # GridSearchCV 모델에 train 데이터 피팅print(f'GridSearchCV 최적 하이퍼 파라미터: {grid_model.best_params_}') # 최적 하이퍼 파라미터print(f'GridSearchCV 최고 정확도: {grid_model.best_score_:.4f}') # 최적 하이퍼 파라미터일 때 나온 최고 점수
best_model = grid_model.best_estimator_ # 최적 하이퍼 파라미터로 최고 점수가 나온 모델을 best_model로 저장
y_pred = best_model.predict(X_test) # best_model로 test 데이터 예측
accuracy = accuracy_score(y_test, y_pred) # 점수 계산print(f'테스트 세트에서의 model 정확도: {accuracy:.4f}')
11. 기본 분석 이후
- pycaret, autogluon 등 auto ML을 사용해 최적의 모델 선택
- 데이터에 대한 깊은 이해를 통해 더 최적의 변수 선택 및 전처리, 차원 축소 및 군집화 등으로 파생변수 생성
U, S, V = randomized_svd(adj_matrix, n_components = 2)
S = np.diag(S)
print(U.shape)
print(S.shape)
print(V.shape)
# 출력 결과
(943, 2)
(2, 2)
(2, 1682)
# 분해 후 재조합# (U * S) * V
np.matmul(np.matmul(U, S), V)
# 출력 결과
array([[ 2.75413997e+00, 1.40492906e+00, 7.42675835e-01, ...,
-8.18516364e-04, 1.56868753e-02, 1.47148277e-02],
[ 1.37986536e+00, 9.01276577e-02, 4.55027384e-01, ...,
1.09681871e-02, 4.52128919e-04, -1.05261346e-03],
[ 9.93032597e-01, 7.39687516e-03, 3.35229530e-01, ...,
8.95866035e-03, -3.68132502e-04, -1.54632039e-03],
...,
[ 6.28183642e-01, 6.62904209e-02, 2.03737923e-01, ...,
4.52498630e-03, 5.10681060e-04, -1.32467414e-04],
[ 9.59658161e-01, 4.03024146e-01, 2.70469585e-01, ...,
1.31860581e-03, 4.42188957e-03, 3.93973413e-03],
[ 1.80307089e+00, 1.01280345e+00, 4.73641731e-01, ...,
-2.26049256e-03, 1.13925590e-02, 1.09104420e-02]])
- 사용자 기반 추천
나와 비슷한 취향을 가진 다른 사용자의 행동을 추천
사용자 특징 벡터의 유사도 사용
my_id, my_vector = 0, U[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector inenumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과# 무려 99% 유사
Best Match: 0.9999996213542565, Best Match ID: 639
recommend_list = []
for i, log inenumerate(zip(adj_matrix[my_id], adj_matrix[best_match_id])):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천if log1 < 1.and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[300, 301, 303, ..., 1227, 1243, 1257]
- 항목 기반 추천
내가 본 항목과 비슷한 항목을 추천
항목 특징 벡터의 유사도 사용
my_id, my_vector = 0, V.T[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector inenumerate(V.T):
# user_id와 my_id가 다르면 코사인 유사도 계산if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과# 더 높은 유사도
Best Match: 0.9999999998965627, Best Match ID: 1425
recommend_list = []
for i, user_vector inenumerate(adj_matrix):
if adj_matrix[i][my_id] > 0.9:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[0, 1, 4, ..., 935, 937, 940]
my_id, my_vector = 0, U[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector inenumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.9999996213542565, Best Match ID: 639
recommend_list = []
for i, log inenumerate(zip(adj_matrix[my_id], adj_matrix[best_match_id])):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천if log1 < 1.and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[300, 301, 303, ..., 1227, 1243, 1257]
항목 기반 추천
my_id, my_vector = 0, V.T[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector inenumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.9999999998965627, Best Match ID: 1425
recommend_list = []
for i, user_vector inenumerate(adj_matrix):
if adj_matrix[i][my_id] > 0.9:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[0, 1, 4, ..., 935, 937, 940]
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris, fetch_olivetti_faces
from sklearn.decomposition import PCA, IncrementalPCA, KernelPCA, SparsePCA
from sklearn.decomposition import TruncatedSVD, DictionaryLearning, FactorAnalysis
from sklearn.decomposition import FastICA, NMF, LatentDirichletAllocation
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
iris, labels = load_iris(return_X_y = True)
faces, _ = fetch_olivetti_faces(return_X_y = True, shuffle = True)
iris 그래프 그리기 함수
defplot_iris(iris, labels):
plt.figure()
colors = ['navy', 'purple', 'red']
for xy, label inzip(iris, labels):
plt.scatter(xy[0], xy[1], color = colors[label])
faces 그리기 함수
defshow_faces(faces):
plt.figure()
# 2*3의 배열로 표현
num_rows, num_cols = 2, 3# 총 6개씩 뜨게 됨for i inrange(num_rows * num_cols):
plt.subplot(num_rows, num_cols, i+1)
plt.imshow(np.reshape(faces[i], (64, 64)), cmap = plt.cm.gray)
위의 함수 사용해서 그래프 그리기
plot_iris(iris[:, :2], labels)
show_faces(faces)
1. 주성분 분석(Principal Component Analysis, PCA)
PCA를 사용해 iris 데이터 변환
150×4 크기의 데이터를 150×2 크기의 행렬로 압축
# 기존 iris 데이터의 shape
iris.shape
# 출력 결과
(150, 4)
# PCA 변환 후 iris 데이터의 shape
model = PCA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# PCA로 변환한 iris 데이터 시각화
plot_iris(transformed_iris, labels)
더 명확하게 분류됨
PCA를 통해 학습된 각 컴포넌트(6개)
각 컴포넌트는 얼굴의 주요 특징을 나타냄
# 기존 faces 데이터의 shape
faces.shape
# 출력 결과
(400, 4096)
# PCA 변환 후 faces 데이터의 shape
model = PCA(n_components = 6, random_state = 0)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# PCA로 변환한 faces 데이터 시각화
show_faces(faces_components)
기존 얼굴에서 주요 특징만 남음
2. Incremental PCA
PCA는 SVD 알고리즘 실행을 위해 전체 학습용 데이터 셋을 메모리에 올려야 함
Incremental PCA는 학습 데이터를 미니 배치 단위로 나누어 사용
학습 데이터가 크거나 온라인으로 PCA 적용이 필요할 때 유용
model = IncrementalPCA(n_components = 2)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
일반 PCA로 변환한 것과 큰 차이는 없음
model = IncrementalPCA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
3. Kernel PCA
비선형적인 형태가 Kernel로 표현될 수 있음
차원 축소를 위한 복잡한 비선형 투형
model = KernelPCA(n_components = 2, kernel = 'rbf', random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = KernelPCA(n_components = 6)
model.fit(faces)
# Kernel PCA는 components_를 출력할 수 없어 오류가 발생함
faces_components = model.components_
4. Sparse PCA
PCA의 주요 단점 중 하나는 주성분들이 보통 모든 입력 변수들의 선형 결합으로 나타난다는 점
희소 주성분 분석은 몇 개의 변수들만의 선형결합으로 주성분을 나타냄으로써 이러한 단점을 극복
model = SparsePCA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = SparsePCA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
아예 특성이 없어져 흰색이나 검은색으로 나타난 얼굴도 있는 모습
5. Truncated Singular Value Decomposition(Truncated SVD)
PCA는 정방 행렬에 대해서만 행렬 분해 가능
SVDs는 정방 행렬 뿐만 아니라 행과 열이 다른 행렬도 분해 가능
PCA는 밀집 행렬(Dense Matrix)에 대한 변환만 가능하지만, SVD는 희소 행렬(Sparse Matrix)에 대한 변환도 가능
전체 행렬 크기에 대해 Full SVD를 사용하는 경우는 적음
특이값이 0인 부분을 모두 제거하고 차원을 줄인 Truncated SVD를 주로 사용
model = TruncatedSVD(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = TruncatedSVD(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
6. Dictionary Learning
Sparse code를 사용하여 데이터를 가장 잘 나타내는 사전 찾기
Sparse coding은 overcomplete 기저 벡터(basis vector)(기저보다 많은 수의 함수로 프레임 표현하는 )를 기반으로 데이터를 효율적으로 표현하기 위한 개발
기저 벡터는 벡터 공간에 속하는 벡터의 집합이 선형 독립이고, 다른 모든 벡터 공간의 벡터들이 그 벡터 집합의 선형 조합으로 나타남
이웃한 픽셀들의 가능한 모든 조합으로 이미지를 재정의
픽셀 정보를 넘어서 더 풍부한 표현력으로 이미지를 설명, 인식의 정확성 향상
model = DictionaryLearning(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
기저 벡터만 남음
model = DictionaryLearning(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
7. Factor Analysis
요인 분석은 변수들 간의 상관관계를 고려하여 저변에 내재된 개념인 요인들을 추출해내는 분석방법
요인 분석은 변수들 간의 상관관계를 고려하여 서로 유사한 변수들끼지 묶어주는 방법
PCA에선느 오차(error)를 고려하지 않고, 요인 분석에서는 오차(error)를 고려
model = FactorAnalysis(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = FactorAnalysis(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
8. Independent Component Analysis(ICA)
독립 성분 분석은 다변량의 신호를 통계적으로 독립적인 하부 성분으로 분리하는 계산 방법
ICA는 주성분을 이용하는 점은 PCA와 유사하지만, 데이터를 가장 잘 설명하는 축을 찾는 PCA와 달리 가장 독립적인 축, 독립성이 최대가 되는 벡터를 찾음
model = FastICA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = FastICA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
9. Non-negative Matrix Factorization
음수 미포함 행렬 분해는 음수를 포함하지 않은 행렬 V를 음수를 포함하지 않은 행렬 W와 H의 곱으로 분해하는 알고리즘
숫자 5를 분해하면 2+3, 1+4 등으로 분리 가능 -> 이것과 마찬가지로 행렬을 어떤 두 개의 행렬의 곱으로 분해
model = NMF(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = NMF(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
어느정도 형체가 살아있음
10. Latent Dirichlet Allocation(LDA)
잠재 디리클레 할당은 이산 자료들에 대한 확률적 생성 모형
디리클레 분포에 따라 잠재적인 의미 구조를 파악
디리클레 분포(Dirichlet distribution)는연속 확률분포의 하나로, k차원의 실수 벡터 중 벡터의 요소가 양수이며 모든 요소를 더한 값이 1인 경우 (이를 k−1차원단체라고 한다)에 대해 확률값이 정의되는 분포이다.
디리클레 분포는베이즈 통계학에서다항 분포에 대한사전 켤레확률이다. 이 성질을 이용하기 위해, 디리클레 분포는 베이즈 통계학에서의사전 확률로 자주 사용된다.
model = LatentDirichletAllocation(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
디리클레 분포에 따라 일자로 나열됨
model = LatentDirichletAllocation(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
노이즈가 낀 형태,
11. Linear Discriminant Analysis(LDA)
LDA는 PCA와 유사하게 입력 데이터 세트를 저차원 공간에 통해 차원을 축소
LDA는 지도 학습 분류에서 사용하기 쉽도록 개별 클래스를 분별할 수 있는 기준을 최대한 유지하면서 차원 축소
정답이 있는 지도학습에서 사용하기 때문에 faces 데이터에 대해서는 사용 불가능
model = LinearDiscriminantAnalysis(n_components = 2)
# 정답인 labels를 같이 모델에 넣어 학습시켜 줘야함
model.fit(iris, labels)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
12. 압축된 표현을 사용한 학습
원래의 digits 데이터와 분해(decomposition)된 digits 데이터의 cross val score 비교
- digits 데이터 및 학습 모델 라이브러리 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import cross_val_score
# min_max 스케일링 하는 함수defmin_max_scale(x):
min_value, max_value = np.min(x, 0), np.max(x, 0)
x = (x - min_value) / (max_value - min_value)
return x
# digits 데이터 그래프 그리는 함수defplot_digits(digits, labels):
digits = min_max_scale(digits)
ax = plt.subplot(111, projection = '3d')
for i inrange(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], digits[i, 2],
str(labels[i]), color = plt.cm.Set1(labels[i] / 10.),
fontdict = {'weight': 'bold', 'size': 9})
ax.view_init(4, -72)
# digits 데이터 불러온 뒤 NMF로 분해# 기존 데이터와 분해된 데이터 shape 비교
digits = load_digits()
nmf = NMF(n_components = 3)
nmf.fit(digits.data)
decomposed_digits = nmf.transform(digits.data)
print(digits.data.shape)
print(decomposed_digits.shape)
print(decomposed_digits)
# 출력 결과
(1797, 64)
(1797, 3)
[[0.483926210.1.24523912]
[0.58296151.46767560.07150889]
[0.615158821.109632070.387782 ]
...
[0.552726651.260565190.72094739]
[0.78725620.27898731.04952028]
[0.785074120.672508840.92677982]]
# 분해 전
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.947222220.955555560.966573820.980501390.9637883 ]
mean cross val score: 0.9627282575054161 (+/- 0.011168537355954218)
# 분해 후
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.547222220.580555560.640668520.596100280.56267409]
mean cross val score: 0.5854441349427422 (+/- 0.03214521445075084)
- SVC
# 분해 전
svm = SVC()
score = cross_val_score(
estimator = svm,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.961111110.944444440.983286910.988857940.93871866]
mean cross val score: 0.9632838130609718 (+/- 0.02008605863225686)
# 분해 후
svm = SVC()
score = cross_val_score(
estimator = svm,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.613888890.622222220.660167130.601671310.59888579]
mean cross val score: 0.6193670690188796 (+/- 0.022070024720937543)
- Decision Tree
# 분해 전
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.783333330.697222220.788300840.832869080.78830084]
mean cross val score: 0.7780052615289385 (+/- 0.04421837659784472)
# 분해 후
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.572222220.508333330.579387190.58217270.52924791]
mean cross val score: 0.5542726709996905 (+/- 0.0298931375955385)
- Random Forest
# 분해 전
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.938888890.905555560.972144850.966573820.91086351]
mean cross val score: 0.9388053234292789 (+/- 0.02745509790638632)
# 분해 후
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.588888890.602777780.640668520.590529250.54874652]
mean cross val score: 0.594322191272052 (+/- 0.029463634023029848)
- 전반적으로 분해한 데이터에서 성능이 떨어진 모습
13. 복원된 표현을 사용한 학습
분해 후 복원된 행렬을 사용해 학습
- 데이터 행렬 복원
# 분해된 행렬에 곱하기 연산을 통해 원래의 행렬로 복원
components = nmf.components_
reconstructed_digits = decomposed_digits @ components
print(digits.data.shape)
print(decomposed_digits.shape)
print(reconstructed_digits.shape)
# 출력 결과
(1797, 64)
(1797, 3)
(1797, 64)
# reconstructed digits 시각화
plt.figure(figsize = (16, 8))
plt.suptitle('Re-Constructed digits')
for i inrange(10):
plt.subplot(2, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.imshow(reconstructed_digits[i].reshape(8, 8))
- KNN
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.541666670.594444440.662952650.576601670.57381616]
mean cross val score: 0.5898963169297431 (+/- 0.04029722337499952)
- SVM
svm = SVC()
score = cross_val_score(
estimator = svm,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.627777780.605555560.660167130.610027860.5821727 ]
mean cross val score: 0.6171402042711235 (+/- 0.025969174809053776)
- Decision Tree
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.577777780.516666670.534818940.568245130.55153203]
mean cross val score: 0.5498081089445992 (+/- 0.0221279380012718)
- Random Forest
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.580555560.558333330.651810580.596100280.57660167]
mean cross val score: 0.592680284741566 (+/- 0.031916555892366645)
- 새로 복원한 데이터에서도 큰 성능 향상점은 없음
14. 이미지 복원
# faces 데이터를 train와 test 데이터로 분리from sklearn.model_selection import train_test_split
train_faces, test_faces = train_test_split(faces, test_size = 0.1)
show_faces(train_faces)
show_faces(test_faces)
# 테스트 데이터에는 랜덤한 점에 0값을 주어 검은색의 점으로 노이즈를 생성
damaged_faces = []
for face in test_faces:
idx = np.random.choice(range(64 * 64), size = 1024)
damaged_face = face.copy()
damaged_face[idx] = 0.
damaged_faces.append(damaged_face)
show_faces(damaged_faces)
# 노이즈를 준 test 데이터를 NMF 모델에 넣어 분해하고 다시 복원하는 과정으로 노이즈 제거# damaged_faces의 형식을 float32로 변환하는 과정 필요
damaged_faces = np.asarray(damaged_faces, dtype = np.float32)
matrix1 = nmf.transform(damaged_faces)
matrix2 = nmf.components_
show_faces(matrix1 @ matrix2)
# 분해 components를 조절하여 정교함 조절 가능, 더 높은 수로 분해할수록 더 정교하게 복원할 수 있음
nmf = NMF(n_components = 100)
nmf.fit(train_faces)
matrix1 = nmf.transform(damaged_faces)
matrix2 = nmf.components_
show_faces(matrix1 @ matrix2)
차원 축소 과정에서 중요하지 않은 정보는 버려지고 중요한 정보만 남기 대문에 데이터 정제에 활용 가능
데이터 생성 및 시각화 함수
# 필요 라이브러리import numpy as np
import matplotlib.pyplot as plt
from sklearn import manifold
from sklearn import random_projection
from sklearn import datasets
# s_curve 데이터셋 생성
s_curve, color = datasets.make_s_curve(1000, random_state = 0)
# 손글씨 데이터셋 생성
digits, labels = datasets.load_digits(return_X_y = True)
# projection으로 만들기
rand_proj = random_projection.SparseRandomProjection(n_components = 3, random_state = 0)
projected_digits =rand_proj.fit_transform(digits)
# min_max_scale 하는 함수defmin_max_scale(x):
min_value, max_value = np.min(x, 0), np.max(x, 0)
x = (x - min_value) / (max_value - min_value)
return x
# s_curve 시각화 함수defplot_s_curve(s_curve, color, position, projection):
s_curve = min_max_scale(s_curve)
if projection == '3d':
ax = plt.subplot(position, projection = projection)
ax.scatter(s_curve[:, 0], s_curve[:, 1], s_curve[:, 2], c = color, cmap = plt.cm.Spectral)
ax.view_init(4, -72)
elif projection == '2d':
ax = plt.subplot(position)
ax.scatter(s_curve[:, 0], s_curve[:, 1], c = color, cmap = plt.cm.Spectral)
# 손글씨 시각화 함수defplot_digits(digits, labels, position, projection):
digits = min_max_scale(digits)
if projection == '3d':
ax = plt.subplot(position, projection = projection)
for i inrange(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], digits[i, 2], str(labels[i]),
color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})
ax.view_init(4, -72)
elif projection == '2d':
ax = plt.subplot(position)
for i inrange(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], str(labels[i]),
color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})
- 데이터 시각화
# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(3차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '3d')
plot_digits(projected_digits, labels, 122, '3d')
# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(2차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '2d')
plot_digits(projected_digits, labels, 122, '2d')
쿨백-라이블러 발산(Kullback–Leibler divergence,KLD)은 두 확률분포의 차이를 계산하는 데에 사용하는 함수로, 어떤 이상적인 분포에 대해, 그 분포를 근사하는 다른 분포를 사용해 샘플링을 한다면 발생할 수 있는정보 엔트로피차이를 계산, 상대 엔트로피(relative entropy), 정보 획득량(information gain), 인포메이션 다이버전스(information divergence)라고도 한다.
import numpy as np
import matplotlib.pyplot as plt
from sklearn import cluster
from sklearn import mixture
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
예시 데이터 생성
# 그래프 그리는 함수 작성defplot_data(datasets, position, title):
X, y = datasets
plt.subplot(position)
plt.title(title)
plt.scatter(X[:, 0], X[:, 1])
# 랜덤한 예시 데이터 생성을 위한 파라미터
np.random.seed(0)
n_samples = 1500
random_state = 0
noise = 0.05# 여러 구조의 예시 데이터 작성
circles = datasets.make_circles(n_samples = n_samples, factor = 0.5, noise = noise, random_state = random_state)
moons = datasets.make_moons(n_samples = n_samples, noise = noise, random_state = random_state)
blobs = datasets.make_blobs(n_samples = n_samples, random_state = random_state)
no_structures = np.random.rand(n_samples, 2), None# 그래프 그리는 함수로 예시 데이터 시각화
plt.figure(figsize = (12, 12))
plot_data(circles, 221, 'Circles')
plot_data(moons, 222, 'Moons')
plot_data(blobs, 223, 'Blobs')
plot_data(no_structures, 224, 'No structures')
XGBoost는 GBM 기반이지만, GBM의 단점인 느린 수행 시간과 과적합 규제 부재 등의 문제를 해결
병렬 CPU 환경에서 빠르게 학습 가능
필요 라이브러리
from sklearn.datasets import load_iris, load_breast_cancer, load_wine, load_diabetes
from sklearn.model_selection import train_test_split, cross_validate
from sklearn.metrics import accuracy_score, precision_score, recall_score
import xgboost as xgb
from xgboost import XGBClassifier, XGBRegressor
from xgboost import plot_importance, plot_tree
import graphviz
import matplotlib.pyplot as plt
1. 파이썬 기반 XGBoost
- 유방암 데이터로 연습
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size = 0.2, random_state = 42)
# XGBoost는 XGBoost만의 데이터 형식인 DMatrix 형식을 사용하므로 다음과 같이 train과 test 데이터를 변환
dtrain = xgb.DMatrix(data = X_train, label = y_train)
dtest = xgb.DMatrix(data = X_test, label = y_test)
# 파라미터
params = {
'max_depth': 3,
'eta': 0.1,
'objective': 'binary:logistic',
'eval_metric': 'logloss',
'early_stopping': 100
}
num_rounds = 400# 모델 생성
evals = [(dtrain, 'train'), (dtest, 'eval')]
xgb_model = xgb.train(params = params, dtrain = dtrain, num_boost_round = num_rounds, early_stopping_rounds = 100, evals = evals)
- xgb_model 생성 결과, logloss가 더 이상 감소되지 않을 때까지 모델 최적화
- 계속 감소한다면 사전에 정해둔 round 횟수(num_rounds = 400)만큼만 진행한 뒤 모델 생성 종료
- 가장 마지막 모델로 생성
- train 데이터로 생성한 모델에 test 데이터를 넣어 예측값 출력(상위 10개만)
import numpy as np
predicts = xgb_model.predict(dtest)
print(np.round(predicts[:10], 3))
- 정확도, 정밀도, 재현율 출력
# 데이터 생성 결과를 이원화(0 또는 1로)하여 2*2 테이블 상에서 정확도, 정밀도, 재현율을 계산할 수 있도록 변경
preds = [1if x > 0.5else0for x in predicts]
print(preds[:10])
print("정확도: {}".format(accuracy_score(y_test, preds)))
print("정밀도: {}".format(precision_score(y_test, preds)))
print("재현율: {}".format(recall_score(y_test, preds)))
# 출력 결과
정확도: 0.9736842105263158
정밀도: 0.9722222222222222
재현율: 0.9859154929577465