[2024.10.30] 필수 온라인 강의 Part15 Machine Learning Basic CH04 지도학습 방법론
ML 방법론의 분류
- 학습 방법에 따른 ML방법론의 구분
- Supervision
- 입력 데이터(x)에 대해 어떤 결과가 정답으로 나와야하는지 직접 지정해주는 것( 사람이 설정한 라벨(y))
- 지도학습(Supervised Learning)
- 일반적인 ML모델의 학습 방식으로, 입력 데이터(x)에서 함수의 출력값이 라벨(y)에 가까워지도록 학습하는 방식
- 비지도학습(Unsupervised Learning)
- Supervision, 즉 레이블(y)를 활용하지 않고 입력 데이터(x)로만 모델을 학습하는 방법론 - 지도학습의 세부분류
- 회귀 모델 : 모델의 종속변수(y)가 연속형 변수인 모델
( 방 개수, 면적, 주변 지하철역과의 거리 등을 통해 집 가격을 예측,날씨 데이터를 보고 내일의 강수량을 예측)
회귀모델 방법론
- 선형회귀모델 (Linear Regression)
: f(x) = ax + b 꼴의 선형 모델을 사용
- 상관관계 분석 (Correlation Analysis)
: 선형회귀모델을 통해 데이터를 얼마나 잘 설명할 수 있는지 확인하는 상관관계 분석
분류 모델 : 모델의 종속변수(y)가 범주형 또는 이산형 변수인 모델
( 이메일의 내용을 보고 스팸메일인지 아닌지 구분, 손글씨 숫자 이미지를 보고 0~9 중 어느 숫자인지 구분)
- 로지스틱 회귀모델 (Logistic Regression)
: 이진 분류를 위한 모델링 방법론
- 회귀모델을 통해 확률에 대한 모델링이 가능하도록 만든 로지스틱 함수
- 어떤식으로 이를 확장해 3개 이상의 클래스를 분류하는 다중분류문제로 만드는지
- k-최근접이웃 (k-Nearest Neighbor) 분류기
- 결정트리 (Decision Tree)
- 서포트 벡터머신 (SVM, Support Vector Machine)
회귀모델의 정의와 개념 소개
- “회귀”라는 이름의 기원
- 프랜시스 골턴(F.Galton)의 1885년 논문, “Regression toward Mediocrity in Hereditary Stature” (유전에 의한, 평균 신장으로의 회귀)에서 유래한 이름
- 분석 과정에서 아무것도 회귀하지 않는다. - 회귀모델
- 입력값이 무엇이든, 출력값이 연속형 변수인 모델을 사용하는 방법론
- 부모의 키 → 자녀의 키, 오늘의 기온 → 내일의 기온
- 입력변수는 여러개거나 범주형이라도 상관
- 거주지역과 나이, 성별 → 연간 소득 금액, 특정 회사의 수익 관련 지표들 → 1주일 후 주식의 가격
- 출력변수가 여러개인 경우 있음
- 사람 얼굴 이미지 → 눈, 코, 입의 위치 좌표 - 회귀모델링 방법론의 세부 구분
- 입력변수의 개수
- 한개인 경우 → 단순 회귀분석
- 여러개인 경우 → 다중 회귀분석
- 출력변수는 여러개
- 한개인 경우 → (단변량) 회귀분석
- 여러개인 경우 → 다변량 회귀분석
- 사용하는 모델
- 선형 회귀분석
- 비선형 회귀분석 - Line Fitting 관점에서의 선형회귀모델
- 직선의 기울기(a)와 y절편(b)을 조절해 데이터(점들)와의 오차가 가장 작아지도록 만들기
- MSE 손실함수 : 오차는 목표값과 출력값 간 차이의 제곱으로 정의됨 - convex함수
- 최적화 문제로서의 선형회귀
- 이 관점에서 linear regression은 오른쪽과 같은 손실함수의 argmin(최소값이 되는 a, b)값을 찾는 컨벡스 최적화(Convex Optimization) 문제
- 최적화의 해석적 해법
- 빠르고 간편하게 정확한 값을 구할 수 있음
- 미리 해법이 알려진 경우에만 사용할수 있어 사용 조건이 까다롭거나, 아예 쓸수 없는 경우도 많음
: 인수분해, 근의 공식
- 최적화의 수치적 해법(Numerical Solution, Newton’s Method)
- 결과가 근사값으로 나오고, 여러 단계를 거쳐야해 시간이 많이 필요한 경우가 많음
- 대신 좀더 일반적으로, 많은 경우에 사용이 가능하다.
: 점진적으로, 근사적인 해를 찾는 방법
- 최소제곱 선형회귀분석의 해석적 해법
: 데이터 X, y가 다음과 같이 정의되고, MSE손실함수를 사용하는 다중 선형회귀 분석 - 상관분석과 상관계수
상관관계(Correlation)
- 한 변수가 변화할 때 다른 변수가 함께 변화하는 경향성을 보일 때 두 변수 사이의 관계를 상관관계
인과관계(Causation)
- 한 변수의 변화가 원인이 되어 그 결과로서 다른 변수를 변화시킬 때, 두 변수 사이의 관계를 인과관계 - 상관관계 분석(Correlation Analysis)
- 두 변수 사이의 상관관계를 판단하기 위한 분석과정
- 피어슨 상관계수(Pearson Correlation Coefficient) : 상관 분석의 결과
- 두 변수 사이에 어느 정도로 강한 선형상관관계가 있는지를 -1에서 1 범위의 숫자로 나타낸 것.
- 상관관계 분석은 다음의 4가지 기본가정을 전제
: 대상 변수들이 이 가정을 만족하지 않는다면 상관계수를 구해도 의미가 없음
1. 선형성: 두 변인 X와 Y의 관계가 직선적
- 오른쪽과 같은 데이터는 매우 강한 비선형적 상관관계가 있음
- 단순선형 회귀모델로 분석하면 0에 가까운 상관계수가 나옴
2. 등분산성: X의 값에 관계없이 Y의 분산이 일정
- 모든 입력범수의 범위에 대해 잔차의 분산이 동일해야 한다는 가정.
3. 정규성: 각 변인은 모두 정규분포
- 일반적으로 분석 전에 표준화(Standardization)방식의 전처리를 진행
4. 독립성: 각 샘플들은 모두 독립적
- 각 샘플들은 모두 독립적으로 추출 - 상관행렬(Correlation Matrix)
- 입력 피쳐에 포함된 모든 변수 쌍의 조합에 대한 상관계수를 행렬의 형태로 나타낸 것
- 계산 결과는 대칭행렬로 나오지만, 일반적으로 의미가 없는 중복값과 대각선 원소를 제거한 후, 다음과 같은 하삼각행렬의 형태로 시각화
: 데이터 탐색 시, 변수간의 상호 연관성을 파악하는데 유용
- 공선성(Collinearity) - 상관행렬을 시각화했을 때 몇몇 변수들간의 상관계수가 1.0이나 -1.0으로 나타나는 경우가 있는데, 이런 경우 두 변수 사이에 공선성이 있다고 표현
- 한 변수가 서로 다른 단위로 표기되어 데이터셋 내에 중복으로 포함되는 경우
- 같은 값이라고 생각함 : 즉, 의미가 없음
- 다중공선성(Multicollinearity) : 한 변수가 여러 변수들의 선형결합으로 나타나는 경우를 의미
- 다른 여러 변수들의 평균값이 별도 변수로 데이터셋에 포함되어있는 경우
* 공선성, 또는 다중공선성이 나타나지 않을 때까지 변수를 제거하는 방식의 전처리를 진행 - 상관계수는 직선의 기울기가 아니다!!!!!!!!!!!!!!
- 상관계수는 상관관계의 강도를 나타낸 것
- 회귀 직선의 기울기가 변해도, 그 부호가 바뀌지 않는 한 값이 변하지 않음
- 회귀분석의 결과로 나오는 회귀계수(결정계수)는, 직선의 기울기 값 그 자체를 의미하므로 데이터의 기울기가 변하면 따라서 변화
- 분석에 사용하는 변수들이 모두 평균 0, 표준편차 1인 표준정규분포를 따르는 경우에는, 상관계수와 회귀계수의 값이 일치
선형 회귀 모델링 방법론 실습
- 실습 데이터셋 준비
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
- 데이터셋을 불러와 기본정보 확인
- 데이터셋: Diabetes Data : sklearn에서 제공하는 당뇨병 진행도 예측 데이터셋
# 당뇨병 데이터셋을 로드
diabetes = load_diabetes(scaled=False)
# 데이터셋의 "설명"(description)섹션 확인
print(diabetes["DESCR"])
- 데이터셋의 전처리
- 평균, 표준편차가 각각 0, 1이 되도록 만드는 표준화(Standardization) 방식의 전처리를 별도로 진행
- 인풋 데이터를 pandas의 DataFrame 오브젝트로 변환, 표준화 전처리 적용
# 분석에 필요한 정보만 가져온 후, pandas 데이터프레임으로 변환
data = diabetes["data"]
data = pd.DataFrame(data, columns=diabetes["feature_names"])
# Standardization 방식의 전처리 적용을 위해 feature별 평균값, 표준편차를 계산
fts_mean = data.mean(axis=0)
fts_std = data.std(axis=0)
# 평균이 0, 표준편차가 1이 되도록 표준화
data = (data - fts_mean) / fts_std
# 결과 확인. 모든 변수가 평균 0, 표준편차 1로 조정된 것을 확인합니다.
data.describe()
- 목표값에도 같은 방식으로 전처리 적용
target = diabetes["target"]
# target 변수의 모양 확인
print(target.shape)
# 처음 10개 값들만 뽑아서 확인
print("Original target values: ", target[:10])
# target의 평균값, 표준편차를 계산
tgt_mean = target.mean()
tgt_std = target.std()
# 표준화 적용
target = (target - tgt_mean) / tgt_std
# 결과 확인
print("Scaled target values: ", target[:10])
print(f"Mean: {target.mean()}, Std: {target.std()}")
- 데이터셋 ㅜㄴ할
- Scikit learn에서 제공하는 train_test_split 함수를 활용
- 전체 데이터셋을 7:3비율로 분할해 학습용, 평가
# 재현성을 위해 random state를 지정
random_state = 1234
# train과 test를 7:3의 비율로 분할
train_data, test_data, train_target, test_target = train_test_split(data, target, test_size=0.3, random_state=random_state)
# 분할이 잘 이뤄졌는지 확인
print(f"train data: {train_data.shape}")
print(f"train target: {train_target.shape}")
print(f"test data: {test_data.shape}")
print(f"test target: {test_target.shape}")
- 사이킷런을 활용한 선형회귀 모델 학습
- scikit learn패키지에 포함된 LinearRegression모델을 불러와 초기화
- 학습용 데이터셋을 이용해 학습
# (다중) 선형회귀모델 초기화
multi_regressor = LinearRegression()
# 학습용 데이터셋을 활용해 학습 진행
multi_regressor.fit(train_data, train_target)
# 회귀식의 intercept(Ax+b에서 b부분) 확인
# 데이터을 평균이 0이 되도록 전처리했으므로 0에 가까운 값이 나온다.
print("intercept: ", multi_regressor.intercept_)
# 학습된 회귀식 확인
print("coefficients: ", multi_regressor.coef_)
- 학습용, 평가용 데이터셋에서 모델의 예측값 계산
- 계산된 예측값을 목표값과 비교해 MSE손실함수값을 확인
# 학습, 평가 데이터셋에서 회귀식의 예측값 계산
multi_train_pred = multi_regressor.predict(train_data)
multi_test_pred = multi_regressor.predict(test_data)
# 위의 예측값을 목표값과 비교해 MSE 손실함수의 값 계산
multi_train_mse = mean_squared_error(multi_train_pred, train_target)
multi_test_mse = mean_squared_error(multi_test_pred, test_target)
print(f"Train MSE: {multi_train_mse:.5f}")
print(f"Test MSE: {multi_test_mse:.5f}")
- 결과를 시각화
- 모델의 예측 결과가 얼마나 목표값과 비슷하게 나왔는지 확인
- 목표값을 x축, 예측값을 y축으로 하는 산점도
- 주황색으로 표시된 x=y 직선에 가까울수록 잘 예측
plt.figure(figsize=(4, 4))
plt.xlabel("target")
plt.ylabel("prediction")
# 테스트셋의 목표값(x축)과 모델의 예측값(y축)을 산점도로 도식화
y_pred = multi_regressor.predict(test_data)
plt.plot(test_target, y_pred, '.')
# y = x 직선을 플롯. 이 직선에 가까운 점일수록 정확한 예측이다.
x = np.linspace(-2.5, 2.5, 10)
y = x
plt.plot(x, y)
plt.show()
- 해석적 해법을 통해 파라미터를 계산
- 최소제곱 손실함수를 사용하는 선형회귀모델의 경우,
θ=(XTX)−1XTy 이라는 해석적 해법(Analytical Solution)이 알려져 있음
- 최소제곱 선형회귀문제의 해석적 해법을 이용해, numpy만으로 파라미터를 직접 계산
최소제곱 손실함수를 Ax=b 형태로 변환 후 numpy를 이용해 solve
# 위의 식을 일반적인 linear equation인 Ax=b 형태로 변형
A = train_data.T @ train_data
b = train_data.T @ train_target
# numpy를 활용해 linear equation 풀기
coef = np.linalg.solve(A, b)
# 학습된 parameter를 이용해 예측값을 내놓는 함수를 정의
def predict(data, coef):
return data @ coef
# 학습, 평가 데이터셋에서 회귀식의 예측값 계산
train_pred = predict(train_data, coef)
test_pred = predict(test_data, coef)
# 위의 예측값을 목표값과 비교해 MSE 손실함수의 값 계산
train_mse = mean_squared_error(train_pred, train_target)
test_mse = mean_squared_error(test_pred, test_target)
print(f"Multi Regression Train MSE is {train_mse:.5f}")
print(f"Multi Regression Test MSE is {test_mse:.5f}")
# scikit-learn 패키지를 활용한 학습 결과
print(multi_regressor.coef_)
# 해석적 해법을 이용해 직접 계산한 학습 결과
print(coef)
-> 해석적 해법을 통해 구한 파라미터값이 위에서 scikit learn 패키지를 사용해 구한것과 정확히 일치하는것을 확인
- 상관행렬의 시각화
- 상관계수는 두 변수 x, y 사이의 상관관계의 강도를 나타내는 수치
- 두 변수 사이의 단순선형회귀분석이 얼마나 효과적일지를 나타내는 것으로도 해석
- pandas DataFrame오브젝트의 상관행렬을 구하기
- 상관행렬의 중복되는 부분을 제거하고 나머지 부분을 시각화
# 데이터의 상관계수 행렬을 생성
corr = data.corr(numeric_only=True)
# figure에서 생략될 부분을 지정하는 mask 행렬을 생성
mask = np.ones_like(corr, dtype=bool)
mask = np.triu(mask)
# 시각화될 그림의 크기를 지정
# 히트맵 형태로 상관행렬 시각화하기
plt.figure(figsize=(13,10))
sns.heatmap(data=corr, annot=True, fmt='.2f', mask=mask, linewidths=.5, cmap='RdYlBu_r')
plt.title('Correlation Matrix')
plt.show()
- 상관계수와 회귀계수(결정계수)
- 상관행렬에서 두 변수를 선택해 단순선형회귀분석 모델을 학습
- 단순선형회귀분석을 진행한 결과, 회귀계수의 값이 두 변수의 상관계수와 매우 비슷한 값
- 전처리 파트에서 진행한것과 같이 각 변수들에 대해 평균이 0, 표준편차가 1이 되도록 표준화 전처리를 진행해준 경우 항상 이렇게 단순선형회귀의 계수는 상관계수와 같은 값이 나옴
# 단순선형회귀분석을 진행할 두 변수를 선정
x_feature = "s3"
y_feature = "s2"
# 모델 초기화 및 학습
simple_regressor = LinearRegression()
simple_regressor.fit(data[[x_feature]], data[[y_feature]])
# 결과 회귀계수 확인
coef = simple_regressor.coef_
print(coef)
분류 모델의 정의와 개념 소개
- 이진분류(Binary Classification)
- 출력값이 참 혹은 거짓(또는 0/1)두가지로만 나오는, 가장 간단한 형태의 분류모델
- 질병 검사의 양성/음성, 스팸메일 필터링
- 로지스틱 회귀는 이진 분류문제를 위한 모델 ( 결과값이 1일 확률을 예측하는 방식)
- 확률의 모델링과 로지스틱 함수
- 선형회귀모델의 결과를 그대로 확률로 해석할 경우, 문제가 생김
- 출력 결과가 0~1범위를 벗어날 수 있음 / 학습이 불안정 - 로지스틱(Logistic) 함수
- 인풋을 0과 1사이로 변환해 확률로 해석될 수 있는 결과를 내보내주는 함수
- 시그모이드(Sigmoid) 함수 : 오른쪽 그림과 같은 S자형 곡선그래프가 나오는 함수.
- 딥러닝에서는 시그모이드함수라고 하면 보통 로지스틱 함수
- 크로스엔트로피(Cross Entropy)
- 로지스틱 함수를 통과한, 모델의 출력값 확률에 대해 적용하도록 만들어진 손실함수
- 학습이 더 잘 됨 : 모델의 예측 결과가 완전히 틀린 경우에 BCE는 매우 큰 패널티를 주지만 MSE는 그렇지 않음
- 모델이 0.8등 정답에 어느정도 근접한 결과를 내놓은 경우 MSE는 로스값이 너무 작아져 학습이 매우 느려짐
- 로지스틱함수와 결합했을 때 미분식이 매우 간단 : 코드로의 구현이 쉽다. - 다중분류문제의 모델링
- 클래스가 k개인 다중분류 문제의 출력값은, i번째 원소가 i번째 클래스가 정답일 확률을 나타내는 k차 벡터의 형태
- 원 핫 인코딩(One Hot Encoding)
- 정답 클래스가 i번째일 경우, i번째 원소만 1이고 나머지 모든 값이 0인 벡터로 라벨을 표현하는 방식.
- 소프트맥스(Softmax)
- 다중분류 모델에서 결과 logit 벡터를 각각 클래스에 대한 확률을 나타내는 벡터로 변환해주도록 설계된 함수
- 크로스엔트로피(Cross Entropy)
- 로지스틱 함수를 통과한, 모델의 출력값 확률에 대해 적용하도록 만들어진 손실함수 - 다양한 방식의 분류문제 모델링
- kNN(k-Nearest Neighbor) 알고리즘
- 특정 인풋이 들어왔을 때, 학습 데이터셋에서 가장 근접한 k개의 라벨을 기준으로 출력값을 결정하는 간단한 방식 - k=3인 경우(실선): 빨간색 2개 / 파란색1개 → 빨강으로 분류됨.
- k=5인 경우(점선): 빨간색 2개 / 파란색3개 → 파랑으로 분류됨.
- 결정트리(의사결정나무, Decision Tree)
- 독립변수(X)내의 대소 관계나 특정 임계값(threshold)과의 비교 등의 판단을 계층적으로 적용하여 최종 결과를 분류하는 모델. (스무고개와 유사한 방식.)
- 불순도(Impurity)
- 한 범주 안에 데이터가 서로 얼마나 섞여 있는지를 측정하는 지표
- 결정트리의 학습과정에서 손실함수와 같은 역할
- 결정포레스트(Decision Forest or Random Forest)
- 랜덤한 구조의 결정트리를 여러개 생성한 뒤, 각 트리의 결과를 종합해 최종 출력값을 결정하는 방식.
- >> 이렇게 여러개 모델의 결과를 합쳐 더 나은 최종 성능을 내는 방법을 머신러닝 모델의 앙상블(Ensemble) - 서포트벡터머신(SVM)을 활용한 이진분류
- 결정경계(Decision Boundary)
- 이진분류 모델에서 판단의 기준이 되는 초평면 (Hyperplane). 결정경계를 기준으로 공간의 양쪽이 서로 다른 클래스로 분류
- 선형분리가능한(Linearly Separable) 데이터셋
- 하나의 결정경계를 통해 이진분류 데이터셋의 두 클래스가 완전히 나뉘어질 수 있을 때 이 데이터셋은 선형분리가 가능
- 서포트 벡터 머신 (SVM, Support Vector Machine)
- 선형분리 가능한 데이터셋에서, 데이터의 두 클래스를 가장 잘 분리하는 결정경계를 찾아내기 위한 방법론
- 각 클래스에 속하는 데이터포인트들과 결정경계 사이의 거리 인 마진 (Margin)값을 최대화하도록 학습
- 커널 트릭(Kernel Trick)
- SVM은 기본적으로 선형분리 가능한 데이터에 대해서만 사용할 수 있는 방법론
- 특정 커널함수를 통해 데이터를 고차원으로 변환해 선형분리가 가능해지도록 만들고 이후 SVM을 적용
분류문제 모델링 방법론 실습
- 실습 데이터셋 준비
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 데이터 관련 임포트
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer # 로지스틱 회귀와 결정트리 실습에 사용할 데이터셋
# SVM 실습에 사용할 합성 데이터 생성 및 시각화 툴
from sklearn.datasets import make_blobs
from sklearn.datasets import make_circles
# 로지스틱 회귀모델
from sklearn.linear_model import LogisticRegression
# 결정트리 모델과 시각화 관련 툴
import graphviz
from sklearn.tree import export_graphviz
from sklearn.tree import DecisionTreeClassifier
# SVM 모델
from sklearn.svm import SVC # SVM 분류기(classifier)
from sklearn.gaussian_process.kernels import RBF
- 데이터셋: Breast cancer wisconsin (diagnostic) dataset : 위스콘신 유방암 데이터셋
- 유방암이 악성(malignant)인지, 양성 (benign)인지 여부를 분류하는 이진분류(Binary Classification)문제를 실습
- 이진분류(Binary Classification)
- 종속변수가 범주형 변수(Categorical Variable)로 주어지는 분류문제(Classification)의 일종
- 결과가 1또는 0(True 또는 False) 두가지 범주중 하나로 결정되는 문제
- 데이터셋을 불러와 기본정보 확인
cancer = load_breast_cancer()
print(cancer["DESCR"])
- feature의 전체 수량과 종류 확인
- target의 전체 수량과 종류 확인
# 데이터셋의 입력값이 몇개의 샘플과 몇개의 변수로 구성되어있는지 확인
print(cancer["data"].shape)
# 데이터셋의 인풋값을 구성하는 feature들의 이름을 확인
print(cancer["feature_names"])
- target에서 각 범주별 빈도 확인
# 데이터셋의 target을 확인
print(cancer["target"].shape)
# target의 각 범주가 어떤 클래스에 해당하는지 확인
print(cancer["target_names"])
# 데이터셋의 target을 살펴봅시다.
cancer["target"]
# target내에 양성, 악성 샘플 수 확인
count_malignant, count_benign = np.bincount(cancer["target"])
print(f"총 {count_malignant}개의 악성 샘플과 {count_benign}개의 양성 샘플 포함.")
- 데이터셋의 시각화 및 특성 파악
- 본격적인 분류문제 모델링을 진행하기 이전에, 간단한 분석과 시각화를 통해 데이터의 특성을 살펴봄
- 각 피쳐들의 값에 따라 악성(malignant)과 양성 (benign)샘플이 어떻게 분포하는지 히스토그램을 통해 확인
- 각각 악성 또는 양성 중 한가지 샘플만 포함하도록 데이터셋을 둘로 분리
# 이후 코드가 간단해지도록 alias 설정
data = cancer["data"]
target = cancer["target"]
# 데이터에서 악성, 양성에 해당하는 샘플만 선택해 새로운 변수로 저장
data_malignant = data[target == 0]
data_benign = data[target == 1]
# 데이터셋 분리의 결과 확인
print("malignant(악성) 샘플 : ", data_malignant.shape)
print("benign(양성) 샘플 : ", data_benign.shape)
- 분리한 데이터셋을 활용해 한가지 변수에 대해 히스토그램 시각화
# 시각화에 사용할 변수의 인덱스를 지정
feature_idx = 0
# histogram의 형태로 악성, 양성 각각 샘플의 분포를 시각화.
plt.hist(data_malignant[:, feature_idx], bins=20, alpha=0.3)
plt.hist(data_benign[:, feature_idx], bins=20, alpha=0.3)
# 데이터셋의 피쳐, 클래스 이름 정보를 활용해 그래프에 정보를 추가
plt.title(cancer["feature_names"][feature_idx])
plt.legend(cancer["target_names"])
- 데이터셋 내의 모든 변수에 대해 같은 방식의 히스토그램 시각화 진행
# 30개의 전체 변수 각각에 대해 같은 방식의 시각화를 진행
plt.figure(figsize=[20,15])
for feature_idx in range(30):
plt.subplot(6, 5, feature_idx + 1)
# histogram의 형태로 악성, 양성 각각 샘플의 분포를 시각화.
plt.hist(data_malignant[:, feature_idx], bins=20, alpha=0.3)
plt.hist(data_benign[:, feature_idx], bins=20, alpha=0.3)
# 데이터셋의 피쳐, 클래스 이름 정보를 활용해 그래프에 정보를 추가
plt.title(cancer["feature_names"][feature_idx])
# 악성/양성에 대한 범례(legend)는 첫번째 histogram에만 표시
if feature_idx == 0:
plt.legend(cancer["target_names"])
plt.xticks([])
- 로지스틱 회귀(Logistic Regression)을 이용한 이진 분류
- 로지스틱 회귀는 이진분류 문제의 해결을 위한 방법론
- 데이터가 범주1(True)에 속할 확률을 0에서 1사이 값으로 예측
- 그 확률값이 정해진 한계값(Threshold)보다 큰지 작은지에 따라 해당 샘플이 둘중 어느 범주에 속하는지 분류해주는 지도 학습 알고리즘
- 데이터셋을 8:2로 랜덤 분할
- 모델을 초기화하고 학습 데이터셋을 fitting
- 평가 데이터셋에서 결과 스코어(정확도) 확인
# 결과 재현성을 위해 random seed를 설정합니다.
random_state = 1234
# 데이터셋을 8:2로 분할해 학습용 / 평가용으로 사용합니다.
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size=0.2, random_state=random_state)
# 모델을 초기화하고 학습 데이터에서 최적화를 진행합니다.
model = LogisticRegression(max_iter=5000)
model.fit(X_train, y_train)
# 테스트셋에 대한 결과 정확도를 계산합니다.
score = model.score(X_test, y_test)
print('scores =', score)
- 한계값 조정을 통한 결과 컨트롤
- 로 지스틱 회귀모델의 한계값(Threshold)을 조절해 모델의 출력값으로 나오는 악성/양성의 빈도를 조절
오류
- False Positive : 실제로는 암이 아닌데(negative), 암이라고 잘못 판단하는 경우
- False Negative : 실제로는 암인데(positive) 암이 아니라고 잘못 판단하는 경우 : 더 심각함
-> 한계값 조절을 통한 False Negative줄이기
- 사이킷런 모델에서 예측 결과를 확률값으로 받아오기
# 학습된 모델에서 결과 확률값을 그대로 가져오기
probs = model.predict_proba(X_test)[:, 1]
# 기본값인 0.5를 기준으로 판단한 결과는 원래 모델의 예측 함수(predict)와 동일
print("원래 예측값: \n", model.predict(X_test))
prediction = (probs > 0.5).astype(int)
print(f"한계값 0.5로 판단한 예측값: \n", prediction) # 한계값 = 0.5
# y_test == 0(양성)이지만 prediction == 1(음성)인 False Negative의 수를 계산해 출력
false_neg = ~y_test & prediction # ~, &는 각각 bitwise "not", "and" 연산자
print(f"위음성(False Negative) 개수: {false_neg.sum()} 개")
threshold = 0.7 # 한계값을 조절해 위음성 빈도를줄이기
prediction = (probs > threshold).astype(int)
print(f"한계값 {threshold}로 판단한 예측값: \n", prediction)
false_neg = ~y_test & prediction
print(f"위음성(False Negative) 개수: {false_neg.sum()} 개")
- 결정트리(Decision Tree)와 불순도(Impurity)
- 결정 트리
- 트리 구조를 사용하여 데이터를 분할하고 예측을 수행하는 머신러닝 모델
- 트리의 각 노드에서는 특정 기준에 따라 데이터를 이진분류하며, 입력 샘플이 여러 노드를 거쳐 최종적으로 리프 노드 (트리의 끝)에 도달하면, 최종적인 예측값이 결정
- 불순도
- 한 노드에 서로 다른 범주의 데이터가 얼마나 섞여 있는지를 나타내는 지표
- 불순도가 낮을수록 해당 노드의 데이터는 한 범주에 속하게 됨
- 순도를 측정하는 방법으로는 주로 지니 불순도(Gini impurity)나 엔트로피(Entropy)가 사용
- 의사결정트리 학습
# 결정트리 학습
dec_tree = DecisionTreeClassifier(max_depth=10, random_state=1234)
dec_tree.fit(X_train, y_train)
# 결과 정확도 출력
print(f"학습 데이터셋 분류 정확도: {dec_tree.score(X_train, y_train):.3f}")
print(f"평가 데이터셋 분류 정확도: {dec_tree.score(X_test, y_test):.3f}")
- graphviz 패키지를 활용해 저장한 파일을 시각화
# "tree.dot"이라는 이름으로, 학습한 그래프 파일을 저장
export_graphviz(dec_tree, out_file="tree.dot", class_names=cancer["target_names"],
feature_names=cancer["feature_names"], impurity=True, filled=True)
# 저장한 파일을 불러와 graphviz로 시각화
with open("tree.dot") as f:
dot_graph = f.read()
display(graphviz.Source(dot_graph))
- 결정트리의 시각화와 해석
- Feature Importance
- 결정트리에서 각각의 변수가 예측 결과에 얼마나 중요한 역할을 했는지를 나타내는 지표
- 결정트리의 노드에서 해당 변수를 활용해 불순도를 얼마나 감소시켰는지를 나타냄
- 랜덤으로 학습되는 개별 모델에 의존하는 값이므로, 이 값이 낮게 나타났다고 해서 그 변수가 전혀 중요치 않다는 결론을 내릴 수는 없음
- feature importance를 출력해 확인
print("Feature importances:") # feature importance를 출력해 확인
print(dec_tree.feature_importances_)
- feature importance를 출력해 확인
# feature별 importance값을 bar graph형태로 시각화
n_features = data.shape[1]
plt.barh(np.arange(n_features), dec_tree.feature_importances_, align='center')
plt.yticks(np.arange(n_features), cancer["feature_names"])
plt.xlabel("Feature importance")
plt.ylabel("Feature")
plt.ylim(-1, n_features)
- feature importance가 유능한지 확인
# 시각화를 수행할 변수를 지정합니다.
feature_name = "worst concave points" # feature importance 높은 node
feature_threshold = 0.142
# feature_name = "worst fractal dimension" # feature importance 낮은 node
# feature_threshold = 0.065
# 주어진 변수 이름을 통해 index를 찾고 feature importance값을 출력
list_feature_names = cancer["feature_names"].tolist()
feature_idx = list_feature_names.index(feature_name) # builtin list의 index함수
print("feature importance: ", dec_tree.feature_importances_[feature_idx])
# histogram의 형태로 악성, 양성 각각 샘플의 분포를 시각화.
plt.hist(data_malignant[:, feature_idx], bins=20, alpha=0.3)
plt.hist(data_benign[:, feature_idx], bins=20, alpha=0.3)
# 데이터셋의 피쳐, 클래스 이름 정보를 활용해 그래프에 정보를 추가
plt.axvline(feature_threshold)
plt.title(cancer["feature_names"][feature_idx])
plt.legend(["threshold"] + list(cancer["target_names"]))
plt.show()
- 사이킷런을 활용한 합성 데이터 생성
- SVM 모델링을 진행할 합성데이터를 생성하고, 시각화를 통해 간단히 특성
- make_blobs 함수를 사용해 랜덤 데이터 생성
- 생성된 데이터 시각화
# 재현성을 위한 랜덤시드 고정
random_state = 20
# 합성데이터 생성
X, y = make_blobs(
n_samples=100, # 샘플의 수
centers=2, # 클러스터의 수. 이진분류 실습이므로 2로 설정
cluster_std=1.2, # 샘플의 표준편차
random_state=random_state
)
plt.scatter(X[:,0], X[:,1], c=y, s=30)
plt.title('datasets random_state=20')
plt.show()
- blobs데이터를 활용한 SVM 분류모델의 학습
- 주어진 데이터를 두 개의 그룹으로 분리하는 방법
- 데이터들과 거리가 가장 먼 초평면(hyperplane)을 결정경계로 선택해 경계의 양쪽을 별개의 클래스로 분류하는 방법
- 주어진 각 클래스의 데이터셋들로부터 가장 멀리 떨어진 초평면을 결정경계로 선택하는 방식으로 학습
- 결정경계 시각화를 위한 보조함수 정의
def make_xy_grid(xlim, ylim, n_points):
# 1. x, y 각각이 일정 간격으로 변화하는 grid를 생성
xx = np.linspace(*xlim, n_points)
yy = np.linspace(*ylim, n_points)
YY, XX = np.meshgrid(yy, xx)
# 2. grid 위의 900개 점 좌표들을 순서대로 나타낸 array
xy = np.stack([XX.reshape(-1), YY.reshape(-1)], axis=1) # shape: (n^2, 2)
return XX, YY, xy
- 사이킷런의 SVM 분류기(Support Vector Classifier)를 학습한 후 분류 결과를 시각화
# 생성한 데이터로 SVM 분류기 모델 학습
clf = SVC(kernel='linear', C=1.0)
clf.fit(X, y)
# 데이터셋 산점도 시각화
plt.scatter(X[:,0], X[:,1], c=y, s=30, cmap=plt.cm.Paired)
# 시각화를 위해 x, y값 범위를 확인
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# 모델의 결정 경계 시각화
XX, YY, xy = make_xy_grid(xlim, ylim, 30)
Z = clf.decision_function(xy).reshape(XX.shape)
# 위에서 생성한 결정경계와 마진 시각화
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])
# 서포트 벡터를 표시
ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=100, linewidth=1, facecolors='none', edgecolors='k')
plt.title('C=1.0')
plt.show()
- 선형분리가능성(Linear separability)과 커널트릭(Kernel Trick)
- 선형분리가 불가능한 데이터셋이 주어졌을 때, 커널트릭을 활용해 SVM분류기를 학습하는 방법
- 커널 트릭 : 선형분리 가능한 고차원 공간으로 맵핑해주면 SVM을 사용해 이진분류를 수행
- RBF(Radial Basis Function)이라는 커널함수를 사용해 선형분리 불가능한 데이터셋에 대해 SVM 분류를 적용
- 사이킷런의 make_circles함수를 사용해 선형분리 불가한 데이터셋을 생성
- 생성한 데이터셋을 시각화
# 데이터 생성
X,y = make_circles(factor=0.1, noise=0.1) # factor: 생성할 원의 반지름 비율
# 생성한 데이터를 시각화
plt.figure(figsize=(4, 4))
plt.scatter(X[:,0], X[:,1], c=y, s=30, cmap=plt.cm.Paired)
plt.title('factor=0.1')
plt.show()
- 생성한 데이터셋의 인풋에 RBF 커널함수 적용
- 3D로 시각화
# RBF 커널함수 적용
z = RBF(1.0).__call__(X)[0]
# 3D 공간에 커널함수 적용된 데이터셋을 시각화
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:, 0], X[:, 1], z, c=y, s=30, cmap=plt.cm.Paired)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')
plt.title('RBF Kernel')
plt.show()
plt.clf()
- 사이킷런의 SVC함수에 내장된 커널함수 옵션을 활용해 데이터셋 분류
- 분류 결과 및 결정경계 시각화
def plot_svc_decision_function(model,ax=None):
if ax is None:
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
x = np.linspace(xlim[0],xlim[1],30)
y = np.linspace(ylim[0],ylim[1],30)
Y,X = np.meshgrid(y,x)
xy = np.vstack([X.ravel(), Y.ravel()]).T
P = model.decision_function(xy).reshape(X.shape)
# 결정경계 시각화
ax.contour(X, Y, P,colors="k",levels=[-1,0,1],alpha=0.5,linestyles=["--","-","--"])
ax.set_xlim(xlim)
ax.set_ylim(ylim)
#데이터셋 호출.
X,y=make_circles(factor=0.2,noise=0.1) #factor = R2/R1, noise=std
# 커널트릭을 적용한 SVM분류기 학습
#kernel을 rbf(Radial Basis Function)을 이용하여 SVC를 작동시켜보자.
clf = SVC(kernel="rbf").fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=30,cmap=plt.cm.Paired)
plot_svc_decision_function(clf)
plt.title('Train SVM')
plt.show()
'Study > 머신러닝' 카테고리의 다른 글
머신러닝 BASIC _ 모델 평가와 개선 (3) | 2024.11.04 |
---|---|
머신러닝 BASIC _ 다양한 데이터 처리 (13) | 2024.11.01 |
머신러닝 BASIC _ 비지도학습 방법론 (2) | 2024.10.31 |
머신러닝 BASIC _ 모델과 데이터 (2) | 2024.10.30 |
머신러닝 BASIC _ 머신러닝이란? (8) | 2024.10.29 |