적당량의 데이터로 머신러닝, 딥러닝을 돌려 성능을 내는것이 중요
비지도학습에서 군집화에 관심이 있다면 K-Means Clustering과 Hierarchical Clustering에 관해 공부
차원 축소에 관심이 있다면 주성분 분석(PCA) 혹은 t-SNE에 관해 공부
기본적인 강화 학습 알고리즘에는 Q-Learning, DQN 등에 관해 공부
🔥총 정리
모델링
성능 향상
- 어떤 모델을 쓰면좋을지 고민해볼때
- 모델 선택 후 어떠한 파라미터가 좋은지 고민해볼
클래스 불균형
지도학습
- | 분류 | 회귀 |
---|---|---|
알고리즘 | DecisionTreeClassifier KNeighborsClassifier LogisticRegression 나이브 베이즈(Naive Bayes) 서포트 벡터 머신(SVM) 신경망(ANN) 👇 앙상블 RandomForestClassifier XGBClassifier |
LinearRegression KNeighborsRegressor DecisionTreeRegressor RandomForestRegressor XGBRegressor Ridge Lasso |
평가 방법 | accuracy_score recall_score precision_score classification_report confusion_matrix |
mean_absolute_error mean_squared_error root mean_squared_error mean_absolute_percentage_error ☝️ 작을수록 좋은 지표들 👇 클수록 좋은 지표 r2_score |
- 평가방법
- model.predict_proba(x_test)
- 이 데이터셋이 y의 컬럼값일 확률
- model.score(feature, test)
- 모델의 적합도를 확인
- model.score(x_train, y_train)과 model.score(x_test, y_test)를 비교해서 과대적합, 과소적합을 확인가능
- 과대 적합 : train의 score는 높은데 test의 score는 떨어지는 경우
- 과소 적합 : train과 test 둘다 낮거나 train보다 test의 score가 훨씬 높은경우
- 분류
- accuracy, classification_report를 자주 사용(score 함수 시 accuracy_score)
- 회귀
- R2와 MAE를 자주 사용(score함수 시 r2 score)
- model.predict_proba(x_test)
비지도 학습
결측치_채우기
1. 필요없는 컬럼 제거
- 기준
- 결측치가 너무 많은데 이 결측치를 채울 방법이 없는 경우
- Unique한 값이고 그저 데이터를 구분하기 위한 값인 경우
- 사람 이름, 고객 번호
drop_cols = ['Cabin', 'PassengerId', 'Name', 'Ticket']
# 1. inplace, 이때 inplace를 쓰면 반환값은 None이라서 None을 대입하는 식의 선언은 사용하지 말것
# titanic = titanic.drop(drop_cols, axis=1, inplace=True) => 불가
titanic.drop(drop_cols, axis=1, inplace=True)
# 2. 앞에서 받기
titanic = titanic.drop(drop_cols, axis=1)
결측치
- 모든 결측치 제거
titanic.dropna(axis=0, inplace=True)
- 결측치 중 특정 컬럼의 결측치만 제거
- subset 매개변수 활용
titanic.dropna(subset=['Age'], axis=0, inplace=True)
- 특정 값으로 채우기
- 평균, 최빈값인 경우가 많음
- 단순한 평균이 좋을 수도 있지만 특정 카테고리별 평균을 채우는 것이 더 성능이 좋을 수 있음
- 모집단의 크기가 작아지고, 그 집단의 특성이 반영
- 다만 확실히 이게 좋다 라고 말하긴 힘듦
titanic['Age'].fillna(titanic['Age'].mean(), inplace=True)
- 평균, 최빈값인 경우가 많음
- 바로 앞의 값, 뒤의 값으로 대체
air['Ozone'].fillna(method='ffill', inplace=True)
air['Solar.R'].fillna(method='bfill', inplace=True)
- 선형값으로 채우기
air['Ozone'].interpolate(method='linear', inplace=True)
# 모든 결측치 제거
titanic.dropna(axis=0, inplace=True)
# Age 변수에 NaN이 포함된 행 제거
titanic.dropna(subset=['Age'], axis=0, inplace=True)
# NaN을 평균값으로 채우기
titanic['Age'].fillna(titanic['Age'].mean(), inplace=True)
# Embarked 변수 값 확인
titanic['Embarked'].value_counts(dropna=True)
# NaN 값을 가장 빈도가 높은 값으로 채우기
titanic['Embarked'].fillna('S', inplace=True)
# Ozone 변수 NaN 값을 바로 앞의 값으로 채우기
air['Ozone'].fillna(method='ffill', inplace=True)
# Solar.R 변수 NaN 값을 바로 뒤의 값으로 채우기
air['Solar.R'].fillna(method='bfill', inplace=True)
# 선형 보간법으로 채우기
air['Ozone'].interpolate(method='linear', inplace=True)
# 가장 가까운 이웃값으로 채우기
air['Ozone'].interpolate(method='nearest', inplace=True)
train_test_split
- stratify = y
- y를 기준으로 데이터를 분할
- y에서 특정 값이 적을 때 사용하면 좋음
# 모듈 불러오기
from sklearn.model_selection import train_test_split
# 7:3으로 분리
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1, stratify=y)
가변수화
범주형 값을 갖는 변수에 대한 가변수화를 진행
One-Hot Encoding
- 다중 공선성을 위해 drop_firtst =True
- 최소한의 컬럼만 가지고 가변수화 작업을 수행하는 것이 효과적
- 나중에 원핫 인코딩을 하게 되면 importance를 볼때 첫번째 컬럼은 볼 수 없음
- 가변수화를 통해 서로 독립적으로 만듦(독립변수를 만듦)
# 가변수 대상 변수 식별
dumm_cols = ['Pclass', 'Sex', 'Embarked']
# 가변수화
titanic = pd.get_dummies(titanic, columns=dumm_cols, drop_first=True)
Lable Encoding
- 문자열 카테고리 값을 숫자형 카테고리 값으로 변환
- 예) 상품 데이터
- 상품 구분 : TV, 냉장고, 전자레인지, 컴퓨터, 선풍기, 믹서
- 숫자로 변환 : TV->1, 냉장고->2, 전자레인지->3 등
- 주의! 01, 02는 문자열이므로 1, 2로 적어야 함
from sklearn.preprocessing import LabelEncoder
items = ['TV', '냉장고', '전자레인지', '컴퓨터', '선풍기', '선풍기', '믹서', '믹서']
# LabelEncoder 객체 생성한 후 fit()과 transfrom()메서드로 label 인코딩 수행
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
print('인코딩 변환값 :', labels)
인코딩 변환값 : [0 1 4 5 3 3 2 2]
# 인코딩 전 원래 값 확인 : classes_ 속성
print('인코딩 클래스 :', encoder.classes_)
인코딩 클래스 : ['TV' '냉장고' '믹서' '선풍기' '전자레인지' '컴퓨터']
# 인코딩 된 값 디코딩 하기 : inverse_transform()
# 어떤 순서로 디코딩 하냐에 따라 원본 값 순서가 달라짐
print('디코딩 원본 값 : ', encoder.inverse_transform([0, 1, 4, 5, 3, 3, 2, 2]))
print('디코딩 원본 값 : ', encoder.inverse_transform([4, 5, 2, 0, 3, 3, 1, 2]))
디코딩 원본 값 : ['TV' '냉장고' '전자레인지' '컴퓨터' '선풍기' '선풍기' '믹서' '믹서']
디코딩 원본 값 : ['전자레인지' '컴퓨터' '믹서' 'TV' '선풍기' '선풍기' '냉장고' '믹서']
주의점
- 간단하게 문자열 값을 숫자형 값으로 변환하지만 이는 단순 코드일 뿐 크기와는 상관 없음
- 일부 ML 알고리즘에서 이를 적용할 경우 예측 성능이 떨어지는 경우 발생
- 숫자 값의 크기 특성이 작용하기 때문
- 즉, 냉장고가 1 믹서가 2로 변환되면 1보다 2가 더 큰 값이므로 가중치가 부여되거나 더 중요하게 인식될 가능성이 발생함
- 이런 특성 때문에 레이블 인코딩은 선형 회귀와 같은 ML 알고리즘에서는 적용하지 않음
- 트리 류의 ML알고리즘은 숫자의 이런 특성을 반영하지 않기 때문에 레이블 인코딩 적용할 때 문제 없음
스케일링
크기를 조절하는 작업
- 거리를 따지는 경우 스케일링이 효과적일 확률이 높음
- 평가용 데이터도 학습용 데이터를 기준으로 스케일링을 수행함
- knn, support vector 머신의 경우 정규화 혹은 표준화가 사용됨
1. 정규화(Normalization)
- 각 변수의 값이 0과 1 사이의 값이 됨
$$\large X_{norm} = \frac{x-x_{min}}{x_{max}-x_{min}}$$
# 함수 불러오기
from sklearn.preprocessing import MinMaxScaler
# 정규화
scaler = MinMaxScaler()
scaler.fit(x_train)
x_train = scaler.transform(x_train)
x_test = scaler.transform(x_test)
2. 표준화(Standardization)
- 각 변수의 평균이 0, 표준편차가 1
$$\large X_Z = \frac{x-x_{mean}}{x_{std}}$$
from sklearn.preprocessing import StandardScaler
col_names = df.iloc[:, 1:-1].columns
scaler = StandardScaler()
df_standard = scaler.fit_transform(df.iloc[:, 1:-1])
standard = pd.DataFrame(df_standard, columns = col_names)
standard
3. Robust Scaler
- 각 feature의 median(Q2)에 해당하는 데이터를 0으로 잡고 Q1, Q3 사분위수와의 IQR 차이 만큼을 기준으로 정규화를 진행
- 평균 대신 Median을 사용하고 표준편차 대신 사분위값을 사용한다는 차이점
- 이상치에 매우 강함
$$\large x' = \frac{x-median(x)}{Q3-Q1}$$
- $x'$ : Robust Standardised Value
- x : Origin Value
- medina(x) : Sample Median
- Q3 - Q1 : IQR차이
from sklearn.preprocessing import RobustScaler
col_names = df.iloc[:,1:-1].columns
scaler = RobustScaler()
df_robust = scaler.fit_transform(df.iloc[:,1:-1])
robust = pd.DataFrame(df_robust, columns = col_names)
robust
4. MaxAbs Scaler
- 각 특성의 절댓값이 0과 1사이가 되도록 스케일링
- 모든 값은 -1과 1사이가 되며, 모든 데이터가 양수일 경우에는 MinMax Scaler와 같은 결과값
- 이상치에 매우 민감
$$\large x_{scaled} = \frac{x}{max(|x|)}$$
from sklearn.preprocessing import MaxAbsScaler
col_names = df.iloc[:,1:-1].columns
scaler = MaxAbsScaler()
df_maxabs = scaler.fit_transform(df.iloc[:,1:-1])
maxabs = pd.DataFrame(df_maxabs, columns = col_names)
maxabs
5. 로그 변환(Log transformation)
- 로그 변환을 통해 왜도와 첨도를 가진 변수를 정규분포에 가깝게 만들어 줌
- 큰 수치를 같은 비율의 작은 수치로 변환
- sns의 displot으로 왜도와 첨도 확인 필요
- 분포가 뾰족하고 한쪽으로 쏠려있다면 로그 변환 필요
- 로그변환과 스케일링 차이
- 로그 변환
- 개별 feature, target의 분포도가 skew가 심하면 log변환
- 스케일링
- 전체 데이터의 스케일링을 표준 정규 분포 형태로 맞추고 싶을 경우
- 로그 변환
sns.distplot(df['col'])
#로그변환
np.log1p(df['col])
🤔 평가용 데이터와 훈련용 데이터가 합쳐진 상태에서 스케일링하면 편하지 않을까?
- 평가용 데이터는 미래의 데이터
- 그렇기에 평가용 데이터와 훈련용 데이터가 합쳐진 상태에서는 스케일링을 하지 않고 분리 후 훈련용데이터를 기준으로 스케일링 후 평가용 데이터 또한 동일한 스케일러를 활용해야 함
🤔 그렇다면 평가용 데이터는 정규화를 할 경우 1을 넘을 수도 있지 않느냐?
- 어쩔 수 없다. 미래의 데이터는 우리는 모르는 것인데 미래에 들어온 값이 정확히 우리의 훈련용 데이터 셋의 범위 내에서 올 거라고는 장담할 수 없음
🤔 언제 스케일링을 진행해야 하나요??
- 주성분 분석(PCA)
주성분 분석에서 높은 분산/ 넓은 범위를 가지는 변수는 낮은 분산을 가지는 변수보다 주성분에서 큰 회귀계수를 가지게 된다. 이는 즉, 중요하지 않은 변수도 범위가 넓으면 주성분에서 중요한 변수로 간주된다는 것이다. 이는 모델링을 수행할 때 가장 치명적이다.
- 군집(clustering)
군집분석은 거리-기반 알고리즘으로 거리 측도(ex. 유클리디안, 맨하튼.. )를 이용하여 관측값 사이에서 유사성을 찾아 군집을 형성한다. 따라서, 넓은 범위를 가지는 변수는 군집에서 더 큰 영향력을 가지게 된다.
- k-최근접 이웃(knn)
k-최근접 이웃은 거리-기반 알고리즘으로 새로운 관측값의 주변에 있는 k개의 이웃(데이터)를 이용하여 유사성 측도에 기반해 관측값을 분류한다. 따라서, 유사성 측도에 모든 변수가 동일하게 기여할 수 있도록 스케일링을 수행해야한다.
- 서포트벡터머신(SVM)
서포트벡터 머신은 서포트벡터와 분류기(hyperplane) 사이 거리인 마진(margin)을 최대로 만들어주는 분류기를 찾는 알고리즘이다. 따라서, 큰 값을 가지는 변수가 거리 계산을 할 때 영향력을 많이 미치게 된다.
- 회귀
예측을 위한 회귀 모델링이 목적이라면 표준화를 수행할 필요가 없지만, 변수의 중요도나 다른 회귀계수들과 비교를 하는 것이 목적이라면 표준화를 수행하여야 한다.
- 릿지-라쏘
릿지-라쏘 모형은 회귀계수에 벌점을 가하는 방법으로 다중공선성과 과적합을 해결하기 위해 사용된다. 회귀계수의 크기를 벌점으로 사용하기 때문에 상대적으로 큰 분산을 가지는 변수가 회귀계수가 클 것이고 그 회귀계수를 벌점으로 사용하게 되면 작은 분산을 가지는 변수들보다 회귀계수를 더 축소시켜 최종적인 회귀계수는 작아지게 될 것이다. (회귀 계수에 영향이 가기에 스케일링 필요)
🤔 스케일링이 필요하지 않은 경우?
로지스틱 회귀나 트리 기반 모델인 의사결정나무, 랜덤 포레스트 , 그래디언트 부스팅은 변수의 크기에 민감하지 않으므로 표준화를 수행해줄 필요가 없다.
Modeling_Sequence
- 사용할 데이터 준비과정
- 충분한 탐색 및 전처리
- 사용할 알고리즘과 평가를 위한 함수 import
- 사용할 알고리즘용 함수로 모델 선언
- 모델.fit(x_train, y_train) 형태로 모델 학습시키기
- 모델.predict(x_test) 형태로 예측한 결과 변수로 저장
- 실제값과 예측값을 평가 함수에 전달해 성능 평가
# 0단계 : 데이터 준비하기
# 라이브러리 불러오기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 데이터 읽어오기
data = pd.read_csv('airquality.csv')
# x, y 분리
target = 'Ozone'
x = data.drop(target, axis=1)
y = data.loc[:, target]
# 학습용, 평가용 데이터 분리
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, shuffle=True, stratify=y)
# shuffle : 섞기 , stratify : 어떤 컬럼을 기준으로 테스트, 훈련 세트를 동일한 분포로 둘거냐?
# 1단계: 불러오기
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
# 2단계: 선언하기
model = LinearRegression()
# 3단계: 학습하기
model.fit(x_train, y_train)
# 4단계: 예측하기
y_pred = model.predict(x_test)
# 5단계: 평가하기
print(mean_absolute_error(y_test, y_pred))
분류 알고리즘
KNeighborsClassifier
의존성 주입
from sklearn.neighbors import KNeighborsClassifier
모델 선언 후 사용
# 선언하기
model = KNeighborsClassifier(n_neighbors=5)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
KNN(k-Nearest Neighbor) 기본 원리
k 최근접 이웃(가장 가까운 이웃 k개)
- k(탐색하는 이웃 개수)에 따라 데이터를 다르게 예측할 수도 있음
- 적절한 k 값을 찾는 것이 중요
- k를 1로 설정 안함→이웃 하나로 현재 데이터를 판단하기에는 너무 편향된 정보
- k를 홀수로 설정 → 짝수인 경우 과반수 이상의 이웃이 나오지 않을 수 있음
- k값의 크기가 클 수록 모델이 단순해짐
학습용 데이터에서 k개의 최근접 이웃의 값을 찾아 그 값들로 새로운 값을 예측하는 알고리즘
이해하기 쉽지만, 연산속도가 느림
KNN은 회귀와 분류에 둘 다 사용 가능
- 회귀면 이웃들의 평균을 구함
- 분류면 이웃들의 최빈값을 구함
거리 구하기
- 유클리드 거리
- 대각선 이동이 가능
- 최단 거리
$$\large d = \sqrt{\sum_{i=1}^n (q_i - p_i)^2}$$
- 맨하튼 거리
- 수직선을 따라서 이동
$$\large d = \sum_{i=1}^n |q_i - p_i|$$
DecisionTreeClassifier
의존성 주입
from sklearn.tree import DecisionTreeClassifier
모델 선언 후 사용
model = DecisionTreeClassifier()
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
# 평가하기
accuracy_score(y_test, y_pred)
유효 컬럼 확인하기
- model.feature_importances_
perf_dic = {'feature':list(x), 'importance': model.feature_importances_}
df = pd.DataFrame(perf_dic)
df.sort_values(by='importance', ascending=True, inplace=True)
시각화
# 트리를 직접 보기
# 시각화 모듈 불러오기
from sklearn.tree import export_graphviz
# 이미지 파일 만들기
export_graphviz(model,
filled=True,
feature_names=x.columns,
class_names=y.unique(),
rounded=True,
precision=3,
out_file='tree.dot')
!dot tree.dot -Tpng -otree.png -Gdpi=300
# 이미지 파일 로딩
from IPython.display import Image
Image(filename='tree.png', width=600)
# 변수 중요도 시각화
plt.figure(figsize=(6, 8))
plt.barh(list(x), model.feature_importances_)
plt.ylabel('Features')
plt.xlabel('Importances')
plt.show()
DecisionTree 기본원리
- 특정 변수에 대한 의사 결정 규칙을 나무가지가 뻗는 형태로 분류해 나감
- 스케일링 등의 전처리 영향도가 크지 않음
- 과적합으로 모델 성능이 떨어지기 쉬움
- 트리 깊이를 제한하는(=가지치기) 튜닝이 필요
- 분석 과정을 실제로 눈으로 확인할 수 있음 → 화이트 박스 모델
- 스무고개처럼 의미있는 질문을 먼저 하는것이 중요
용어
- Root Node(뿌리마디) : 전체자료를 갖는 시작하는 마디
- Child Node(자식마디) : 마디 하나로부터 분리된 2개 이상의 마디
- Parent Node(부모마디) : 주어진 마디의 상위 마디
- Terminal Node(끝마디) : 자식마디가 없는 마디(=LeafNode)
- Internal Node(중간마디) : 부모 마디와 자식 마디가 모두 있는 마디
- Branch(가지) : 연결되어 있는 2개 이상의 마디 집합
- Depth(깊이) : 뿌리 마디로부터 끝 마디까지 연결된 마디 개수
tree 보는법
- 분류
- sample : 해당 노드일 경우의 데이터의 수
- value : 각 샘플 별 값들의 개수
- class : 최빈값
- gini : 지니 불순도(낮을수록 순도가 높음)
- 특정 조건식 : 가지를 나눌 기준
불순도
- 순도의 반대말 = 불순도
- 순도가 높을 수록 분류가 잘 된것
- 불순도의 수치화 지표
- 지니 불순도
- 1 - ($양성 클래스비율^2 + 음성 클래스 비율^2$)
- $\large Gini = 1-\sum^c_{i=1}(p_i)^2$
- 지니 불순도가 낮을수록 순도가 높음
- 지니 불순도는 0~0.5 사이의 값(이진 분류의 경우)
- 엔트로피
- 지니 불순도
- 정보 이득
- 부모의 불순도에서 자식의 불순도를 뺀 값
- 클 수록 좋음
- $\large Gain(T,X) = Entropy(T) - Entropy(T, X)$
가지치기
- 여러 하이퍼 파라미터 값을 조정해 가지치기 할 수 있음
- max_depth, min_samples_leaf, min_samples_split
- 학습 데이터에 대한 성능은 낮아지나, 평가 데이터에 대한 성능을 높일 수 있음
주요 하이퍼파라미터
파라미터 | 설명 |
---|---|
max_depth | 트리의 최대 깊이(기본값 : None) 계속 분할되면 트리 깊이가 너무 깊어져 과적합이 발생 👉 적절한 값 설정 필요 |
min_samples_split | 노드를 분할하기 위한 최소한의 샘플 개수(기본값 : 2) 값을 작게 설정할 수록 계속 분할되어 트리 깊이가 깊어져 과적합 발생 |
min_samples_leaf | 리프노드가 되기위한 최소한의 샘플 수(기본값 : 1) 불균형 클래스인 경우 이를 고려해 작은겂을 설정할 필요가 있음 |
max_feature | 최선의 분할을 위해 고려할 feature 수(기본값 : None) 'sqrt' : feature수의 루트값 'log' : $log_2(전체 feature수)$ 정수형 : feature 수 실수형 : feature 비율 |
max_leaf_node | 리프 노드 최대 개수 |
LogisticRegression
의존성 주입
# 불러오기
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report
모델선언 후 사용
# 선언하기
model = LogisticRegression()
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
LogisticRegression 원리
$$\large p=\frac{1}{1+e^{-f(x)}}$$
- 로지스틱 함수(시그모이드 함수)
- 𝑝는 선형판별식값이 커지면 1 , 작아지면 0에 가까운 값이 됨
- f(x)는 선형 판별식
- (-∞,∞)범위를 갖는 선형판별식 결과로 (0,1) 범위의 확률값을 얻게 됨
- 기본적으로 확률값 0.5를 임계값(Threshold)로 하여 이보다 크면 1 , 아니면 0으로 분류
- 시그모이드 함수
- 해당 확률보다 낮으면 1 아니면 0(확률값을 받아서 그걸 기준으로 분류)
- 해당 확률보다 낮으면 1 아니면 0(확률값을 받아서 그걸 기준으로 분류)
RandomForestClassifier
개념
import
# 불러오기
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix, classification_report
모델 선언 후 사용
# 선언하기
model = RandomForestClassifier(max_depth=5, n_estimators=100, random_state=1)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
XGBClassifier
- light GMB 을 사용할 수도 있음(경량화된)
from lightgbm import LGBMClassifier
개념
import
# 불러오기
from xgboost import XGBClassifier
from sklearn.metrics import confusion_matrix, classification_report
모델 선언 후 사용
# 선언하기
model = XGBClassifier(max_depth=5, n_estimators=100, random_state=1)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
회귀 알고리즘
LinearRegression
의존성 주입
from sklearn.linear_model import LinearRegression
모델 선언 후 사용
model = LinearRegression()
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
# 평가
model.score(x_test, y_test)
# 혹은 MAE, R2 score를 직접 확인
# 회귀계수 확인
display(list(x_train))
display(model.coef_)
display(model.intercept_)
선형 계수 확인
display(list(x_train))
display(model.coef_)
display(model.intercept_)
plt.scatter(x_test, y_test.values)
plt.plot(speed, dist, color='r')
plt.show()
선형 회귀의 이해
데이터는 다양한 형태를 가질것이며 최선의 직선을 긋기가 쉽지 않음
과연 위 직선이 가장 최선의 직선일 것인가?
선형회귀 : 함수 𝑦 = 𝑎𝑥 + 𝑏에서 최선의 기울기 𝑎와 𝑦절편 𝑏를 결정하는 방법
- 최선의 회귀모델은 전체데이터의 오차 합이 최소가 되는 모델을 의미
- 이때의 최선의 직선을 회귀선이라고 부름
독립변수의 개수로 회귀분석을 분류
- 단순 회귀
- 독립변수 하나가 종속변수에 영향을 미치는 선형 회귀
- 𝑥값 하나만으로 𝑦값을 설명할 수 있는 경우
- $\large \hat{y} = w_0 + w_1x_1$
- 다중 회귀
- 여러 독립변수가 종속변수에 영향을 미치는 선형 회귀
- 𝑦값을 설명하기 위해서는 여러 개의 𝑥값이 필요한 경우
- $\large \hat{y} = w_0 + w_1x_1 + w_2x_2 ... + w_nx_n$
- 단순 회귀
$w_1$ : 기울기(또는 가중치) : coef_
$w_0$ : y절편(또는 편향) : intercept_
KNeighborsRegressor
의존성 주입
# 불러오기
from sklearn.neighbors import KNeighborsRegressor
모델 선언 후 사용
# 선언하기
model = KNeighborsRegressor(n_neighbors=5)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(mean_absolute_error(y_test, y_pred))
print(r2_score(y_test, y_pred))
KNN(k-Nearest Neighbor) 기본 원리
k 최근접 이웃(가장 가까운 이웃 k개)
- k(탐색하는 이웃 개수)에 따라 데이터를 다르게 예측할 수도 있음
- 적절한 k 값을 찾는 것이 중요
- k를 1로 설정 안함→이웃 하나로 현재 데이터를 판단하기에는 너무 편향된 정보
- k를 홀수로 설정 → 짝수인 경우 과반수 이상의 이웃이 나오지 않을 수 있음
- k값의 크기가 클 수록 모델이 단순해짐
학습용 데이터에서 k개의 최근접 이웃의 값을 찾아 그 값들로 새로운 값을 예측하는 알고리즘
이해하기 쉽지만, 연산속도가 느림
KNN은 회귀와 분류에 둘 다 사용 가능
- 회귀면 이웃들의 평균을 구함
- 분류면 이웃들의 최빈값을 구함
거리 구하기
- 유클리드 거리
- 대각선 이동이 가능
- 최단 거리
$$\large d = \sqrt{\sum_{i=1}^n (q_i - p_i)^2}$$
- 맨하튼 거리
- 수직선을 따라서 이동
$$\large d = \sum_{i=1}^n |q_i - p_i|$$
DecisionTreeRegressor
의존성 주입
# 불러오기
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error, r2_score
모델 선언 후 사용
# 선언하기
model = DecisionTreeRegressor(max_depth=5)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(mean_absolute_error(y_test, y_pred))
print(r2_score(y_test, y_pred))
유효 컬럼 확인하기
- model.feature_importances_
perf_dic = {'feature':list(x), 'importance': model.feature_importances_}
df = pd.DataFrame(perf_dic)
df.sort_values(by='importance', ascending=True, inplace=True)
시각화
- 트리의 각각의 샘플 수를 적는 기준은 알파벳이 빠른 순으로 작성하게 되므로 class_name을 작성할 때 알파벳 순으로 작성해줘야 함
- 원래 데이터에서 알파벳 순 정렬이고, 라벨은 본인이 보고 싶은 이름을 넣어주는 것
# 트리를 직접 보기
# 시각화 모듈 불러오기
from sklearn.tree import export_graphviz
# 이미지 파일 만들기
export_graphviz(model,
filled=True,
feature_names=x.columns,
class_names=y.unique(), # 무조건 문자열로
rounded=True,
precision=3,
out_file='tree.dot')
!dot tree.dot -Tpng -otree.png -Gdpi=300
# 이미지 파일 로딩
from IPython.display import Image
Image(filename='tree.png', width=600)
# 변수 중요도 시각화
plt.figure(figsize=(6, 8))
plt.barh(list(x), model.feature_importances_)
plt.ylabel('Features')
plt.xlabel('Importances')
plt.show()
DecisionTree 기본원리
- 특정 변수에 대한 의사 결정 규칙을 나무가지가 뻗는 형태로 분류해 나감
- 스케일링 등의 전처리 영향도가 크지 않음
- 과적합으로 모델 성능이 떨어지기 쉬움
- 트리 깊이를 제한하는(=가지치기) 튜닝이 필요
- 분석 과정을 실제로 눈으로 확인할 수 있음 → 화이트 박스 모델
- 스무고개처럼 의미있는 질문을 먼저 하는것이 중요
용어
- Root Node(뿌리마디) : 전체자료를 갖는 시작하는 마디
- Child Node(자식마디) : 마디 하나로부터 분리된 2개 이상의 마디
- Parent Node(부모마디) : 주어진 마디의 상위 마디
- Terminal Node(끝마디) : 자식마디가 없는 마디(=LeafNode)
- Internal Node(중간마디) : 부모 마디와 자식 마디가 모두 있는 마디
- Branch(가지) : 연결되어 있는 2개 이상의 마디 집합
- Depth(깊이) : 뿌리 마디로부터 끝 마디까지 연결된 마디 개수
tree 보는법
- 회귀
- sample : 해당 노드일 경우의 데이터의 수
- mse : mean squared error
- value : 반환할 값(예측값)
- 조건 : 트리의 분할 조건
불순도
- 순도의 반대말 = 불순도
- 순도가 높을 수록 분류가 잘 된것
- 불순도의 수치화 지표
- 지니 불순도
- 1 - ($양성 클래스비율^2 + 음성 클래스 비율^2$)
- $\large Gini = 1-\sum^c_{i=1}(p_i)^2$
- 지니 불순도가 낮을수록 순도가 높음
- 지니 불순도는 0~0.5 사이의 값(이진 분류의 경우)
- 엔트로피
- 지니 불순도
- 정보 이득
- 부모의 불순도에서 자식의 불순도를 뺀 값
- 클 수록 좋음
- $\large Gain(T,X) = Entropy(T) - Entropy(T, X)$
가지치기
- 여러 하이퍼 파라미터 값을 조정해 가지치기 할 수 있음
- max_depth, min_samples_leaf, min_samples_split
- 학습 데이터에 대한 성능은 낮아지나, 평가 데이터에 대한 성능을 높일 수 있음
주요 하이퍼파라미터
파라미터 | 설명 |
---|---|
max_depth | 트리의 최대 깊이(기본값 : None) 계속 분할되면 트리 깊이가 너무 깊어져 과적합이 발생 👉 적절한 값 설정 필요 |
min_samples_split | 노드를 분할하기 위한 최소한의 샘플 개수(기본값 : 2) 값을 작게 설정할 수록 계속 분할되어 트리 깊이가 깊어져 과적합 발생 |
min_samples_leaf | 리프노드가 되기위한 최소한의 샘플 수(기본값 : 1) 불균형 클래스인 경우 이를 고려해 작은겂을 설정할 필요가 있음 |
max_feature | 최선의 분할을 위해 고려할 feature 수(기본값 : None) 'sqrt' : feature수의 루트값 'log' : $log_2(전체 feature수)$ 정수형 : feature 수 실수형 : feature 비율 |
max_leaf_node | 리프 노드 최대 개수 |
RandomForestRegressor
개념
import
# 불러오기
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error, r2_score
모델 선언 후 사용
# 선언하기
model = RandomForestRegressor(max_depth=5, n_estimators=100, random_state=1)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(mean_absolute_error(y_test, y_pred))
print(r2_score(y_test, y_pred))
XGBRegressor
개념
import
# 불러오기
from xgboost import XGBRegressor
from sklearn.metrics import mean_absolute_error, r2_score
모델 선언 후 사용
# 선언하기
model = XGBRegressor(max_depth=5, n_estimators=100, random_state=1)
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(mean_absolute_error(y_test, y_pred))
print(r2_score(y_test, y_pred))
Ridge
import
from sklearn.linear_model import Ridge
모델 선언 후 사용
ridge = Ridge()
ridge.fit(x_train, y_train)
ridge.score(x_test, y_test)
train = []
test = []
alpha_list = [0.001., 0.01, 0.1, 1, 10 ,100]
for a in alpha_list:
ridge = Ridge(alpha = a, max_iter=10000)
ridge.fit(x_train, y_train)
train.append(ridge.score(x_train, y_train))
test.append(ridge.score(x_test, y_test))
plt.plot(np.log10(alpha_list), train)
plt.plot(np.log10(alpha_list), test)
plt.show()
Ridge 원리
- 선형회귀 모델에 규제를 합쳐서 만든 모델
- 규제 : 계수를 제곱한 값을 기준으로 규제 적용
Lasso
import
from sklearn.linear_model import Lasso
모델 선언 후 사용
lasso = Lasso()
lasso.fit(x_train, y_train)
lasso.score(x_test, y_test)
train = []
test = []
alpha_list = [0.001., 0.01, 0.1, 1, 10 ,100]
for a in alpha_list:
lasso = Lasso(alpha = a, max_iter=10000)
lasso.fit(x_train, y_train)
train.append(lasso.score(x_train, y_train))
test.append(lasso.score(x_test, y_test))
plt.plot(np.log10(alpha_list), train)
plt.plot(np.log10(alpha_list), test)
plt.show()
Lasso 원리
- 선형회귀 모델에 규제를 합쳐서 만든 모델
- 계수의 절댓값을 기준으로 규제 적용
하이퍼 파라미터
- alpha : 규제의 양
성능 평가
회귀의 성능평가
- 오차가 적은 것
- 정확도의 개념이 아님
실제값
$$\huge y$$
- 우리가 실제로 예측하고 싶은 값, Target, 목푯값
- 이값과 비교해 우리 모델의 성능을 평가할 것임
- 오차 : 실제값 - 예측값
예측값
$$\huge \hat{y}$$
- 우리모델로 새롭게 예측한 값
- 최소한, 아무리 못해도 평균값 보다는 좋아야 할 것
평균값
$$\huge \bar{y}$$
- 이미 알고 있는, 이미 존재하고 있는 평균으로 예측한 값
- 최소한 이 평균값 보다는 실젯값에 가까운 예측값을 원함
- SST = $\large\Sigma(y-\bar{y})^2$
- Sum Squared Total
- 전체 오차
- 우리에게 허용된 최대 오차(이 이상의 오차가 발생시 해당 모델을 쓸 이유가 없음)
- SST = SSR + SSE
- SSR = $\large\Sigma(\hat{y}-y)^2$
- Sum Squared Regression
- 평균(전체오차)보다 우리의 회귀식이 잡아낸 오차
- SSE = $\large \Sigma(\hat{y}-\bar{y})^2$
- Sum Squared Error
- 전체 오차 중 회귀식이 여전히 잡아내지 못한 오차
오차 제곱의 합
MSE
$$\large\frac{\Sigma(y-\hat{y})^2}{n}$$
- Mean SSE
- 오차 제곱의 합을 구한 후 평균을 구함
# 모듈 불러오기
from sklearn.metrics import mean_squared_error
# 성능 평가
print("MSE : ", mean_squared_error(y_test, y_pred))
RMSE
$$\large \sqrt \frac{\Sigma(y-\hat{y})^2}{n}$$
- Root MSE
- 오차의 제곱이르몰 루트를 사용해 일반적인 값으로 표현
# 모듈 불러오기
from sklearn.metrics import mean_squared_error
# 성능 평가
print("RMSE : ", mean_squared_error(y_test, y_pred)**(1/2))
print("RMSE : ", mean_squared_error(y_test, y_pred, squared=False))
오차 절대값의 합
MAE
$$\large \frac{\Sigma|y-\hat{y}|}{n}$$
- Mean Absolute Mean
- 오차 절대값의 합을 구한 후 평균을 구함
# 모듈 불러오기
from sklearn.metrics import mean_absolute_error
# 성능 평가
print("MAE : ", mean_absolute_error(y_test, y_pred))
MAPE
$$\large \frac{\Sigma|\frac{y-\hat{y}}{y}|}{n}$$
- Mean Absolute Percentage Mean
- 오차 비율을 표시하고 싶은 경우 MAPE를 사용함
# 모듈 불러오기
from sklearn.metrics import mean_absolute_percentage_error
# 성능 평가
print("MAPE : ", mean_absolute_percentage_error(y_test, y_pred))
결정계수
$$R^2 = \frac{SSR}{SST} = 1-\frac{SSE}{SST}$$
- model에서 score함수 사용 시 결정계수를 사용
- $R^2$(R-Squared)
- 모델성능을 잘 해석하기 위해서 만든 MSE의 표준화된 버전이 결정계수
- 전체 오차 중에서 회귀식이 잡아낸 오차 비율(일반적으로 0 ~ 1 사이)
- 오차의 비 또는 설명력 이라고도 부름
- $𝑅^2$ =1이면 𝑀𝑆𝐸=0이고 모델이 데이터를 완벽하게 학습한 것
- 클 수록 좋은 평가지표
- 만약 $R^2$의 값이 음의 값이 나온다면(비정상) 알고리즘 자체가 적합하지 않다는 뜻임
# 모듈 불러오기
from sklearn.metrics import r2_score
# 성능 평가
print("r2 : ", r2_score(y_test, y_pred))
분류 모델평가
혼돈행렬
ConfusionMatrix(오분류표)
T, F : 결과
- 예측이 맞음, 틀림
N, P :예측
- 1로 예측, 0으로 예측
TN(True Negative, 진음성): 음성으로 잘 예측한 것(음성을 음성이라고 예측한 것)
FP(False Positive, 위양성): 양성으로 잘 못 예측한 것(음성을 양성이라고 예측한 것)
FN(False Negative, 위음성): 음성으로 잘 못 예측한 것(양성을 음성이라고 예측한 것)
TP(True Positive, 진양성): 양성으로 잘 예측한 것(양성을 양성이라고 예측한 것)
# 모듈 불러오기
from sklearn.metrics import confusion_matrix
# 성능 평가
confusion_matrix(y_test, y_pred)
# 혼돈행렬 시각화
plt.figure(figsize=(2, 2))
sns.heatmap(confusion_matrix(y_test, y_pred),
annot=True,
cmap='Blues',
fmt='.2f',
annot_kws={'size':7},
square=True)
Accuracy
- 정확도(정분류율)
- 전체 중에서 예측이 맞은 확률
$$\frac{TN + TP}{TN + FP + FN + TP}$$
- 전체 중에서 예측이 맞은 확률
# 모듈 불러오기
from sklearn.metrics import accuracy_score
# 성능 평가
accuracy_score(y_test, y_pred)
Precision
- 정밀도
- 보통은 1의 정밀도
- 예측값이 분모
- 예측을 n으로 했는데 n이 실제로 n이었을 확률
- Positive로 예측한 것(FP + TP) 중에서 실제 Positive(TP)인 비율
- 비가 오지 않는데 비가 온다고 했으니 불필요한 우산을 챙기는 수고 발생
- 암이 아닌데 암이라 했으니 불필요한 치료 발생
$$\frac{TP}{FP + TP}$$
# 모듈 불러오기
from sklearn.metrics import precision_score
# 성능 평가
# 1의 precision
precision_score(y_test, y_pred)
# 각 값에 따른 precision 전체를 보여주기
precision_score(y_test, y_pred, average=None)
precision_score(y_test, y_pred, average='binary') # 이진 분류
precision_score(y_test, y_pred, average='macro') # 평균
precision_score(y_test, y_pred, average='weighted') # 개수를 반영한 평균
Recall
- 재현율(민감도)
- 실제 값이 n인데 예측값도 n인 확률
- 실제값이 분모
- 실제 Positive(FN + TP) 중에서 Positive로 예측한(TP)
- 비가 내리는 날 내리지 않을 것이라 했으니 우산을 챙기지 않아 비를 맞음
- 암인사람에게 암이 아니라했으니 심각한 결과 초래
$$\frac{TP}{FN + TP}$$
# 모듈 불러오기
from sklearn.metrics import recall_score
# 성능 평가
# 1의 recall
recall_score(y_test, y_pred)
# 모든 값에 대한 recall
recall_score(y_test, y_pred, average=None)
recall_score(y_test, y_pred, average='binary') # 이진 분류
recall_score(y_test, y_pred, average='macro') # 평균
recall_score(y_test, y_pred, average='weighted') # 개수를 반영한 평균
F1-Score
- 정밀도와 재현율의 조화 평균
- 정밀도, 재현율 둘 다 반영된 지표
- 분자가 같지만 분모가 다를경우, 즉 관점이 다른경우 조화평균이 큰 의미를 가짐
$$\large (F1-Score) = 2\frac{1}{\frac{1}{Precesion} + \frac{1}{Recall}} = \frac{2Precesion*Recall}{Precesion + Recall}$$
# 모듈 불러오기
from sklearn.metrics import f1_score
# 성능 평가
f1_score(y_test, y_pred)
classification_report
- macro avg : 0일때와 1일때의 precision, recall, f1-score의 평균
- weighted avg : 데이터의 개수를 고려해 가중치를 더해 평균을 구함
# 모듈 불러오기
from sklearn.metrics import classification_report
# 성능 평가
print(classification_report(y_test, y_pred))
K-FoldCrossValidation
# 1단계: 불러오기
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
# 2단계: 선언하기
model = DecisionTreeClassifier(max_depth=3)
# 3단계: 검증하기
cv_score = cross_val_score(model, x_train, y_train, cv=10,
scoring='accuracy_score')
# 확인
print(cv_score)
print(cv_score.mean())
$$\large 데이터 = 학습용 + 검증용 + 평가용$$
지금까지 모델을 선언하고 학습한 후 바로 평가를 진행함
일반화 성능, 즉 이후 새로운 데이터에 대한 모델의 성능을 예측하지 못한 상태에서 최종평가를 수행
검증용 데이터가 모델의 일반화된 성능을 예측할 수 있게 도와줌
- 하지만 이것 역시 단 하나의 데이터셋에 대한 추정일뿐
- 단 하나의 데이터셋에서 얻은 성능으로 정확도에 확신을 가질 수 없음
K-FoldCrossValidation
- 모든 데이터가 평가에 한번, 학습에 k-1번 사용
- K개의 분할(Fold)에 대한 성능을 예측→평균과 표준편차 계산→일반화 성능
- 단, k는 2이상이 되어야함(최소한 한개씩의 학습용, 검증용 데이터가 필요)
- k-분할은 어느정도의 점수가 나오는지 확인하는 정도의 용도고 실제 모델 학습 시에는 k 분할을 알고리즘 및 하이퍼 파라미터 튜닝을 통해 가장 효율적인 방법으로 해서 x_train 전체를(즉, 학습용과 검증용 나눈거 없이) 학습
#### 장점 + 모든 데이터를 학습과 평가에 사용할 수 있음 + 반복 학습과 평가를 통해 정확도를 향상시킬 수 있음 + 데이터가 부족해서 발생하는 과소적합 문제를 방지할 수 있음 + 평가에 사용되는 데이터의 편향을 막을 수 있음 + 좀 더 일반화된 모델을 만들 수 있음
단점
- 반복 횟수가 많아서 모델 학습과 평가에 많은시간이 소요됨
Hyperparameter
튜닝
과학이기보다는 예술(정해진 방법이 없음)
- 알고리즘을 사용해 모델링 할때 모델 성능을 최적화하기 위해 조절할 수 있는 매개변수
- KNN 알고리즘의 n_neighbors, Decision Tree 알고리즘의 max_depth 등
- 모델의 성능 향상을 위해 최선의 하이퍼파라미터값을 찾는 다양한 시도를 해야함
- Grid Search, Random Search
KNN
- n_neighbors에 따라 성능이 달라짐
- k값이 가장 클 때(=전체데이터개수) 가장 단순 모델
- →평균, 최빈값과 다를바 없음
- k값이 작을수록 복잡한 모델이 됨
Decision Tree
- max_depth
- 이 값이 작을 수록 트리 깊이가 제한되어 모델이 단순해 짐
- min_samples_leaf
- leaf가 되기 위한 최소한의 샘플 데이터 수
- 이 값이 클 수록 모델이 단순해 짐
- min_samples_split
- 노드를 분할하기 위한 최소한의 샘플 데이터 수
- 이 값이 클 수록 모델이 단순해짐
RandomSearch
- 성능을 테스트할 파라미터 값의 범위를 지정(딕셔너리 형태)
- 위 파라미터 값 범위에서 몇개 선택할지 정하여 RandomSearch 모델 선언 후 학습
- 학습 데이터에 대해 가장 좋은 성능을 보인 파라미터값으로 자동으로 학습함
- 이후 예측 및 평가과정을 바로 진행하면됨
- 지정한 개수의 임의의 값에 대해서만 성능 확인 가능
# 함수 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import RandomizedSearchCV
# 파라미터 선언
param = {'n_neighbors': range(1, 500, 10),
'metric': ['euclidean', 'manhattan']}
# 기본모델 선언
knn_model = KNeighborsClassifier()
# Random Search 선언
model = RandomizedSearchCV(knn_model, param,
cv=3,
n_iter=20)
# 학습하기
model.fit(x_train, y_train)
# 최적 파라미터
model.best_params_
# 최고 성능
model.best_score_
# 최고 성능의 모델
model.best_estimator_
GridSearch
- 성능을 테스트할 파라미터 값의 범위를 지정(딕셔너리 형태)
- 위 파라미터값 범위를 모두 사용하는 GridSearch모델 선언 후 학습
- 학습 데이터에 대해 가장 좋은 성능을 보인 파라미터값으로 자동으로 학습함
- 이후 예측 및 평가 과정을 바로 진행하면됨
- 모든 경우의 성능 확인 가능
- tip
- 넓은 범위와 큰 Step으로 설정한 후 범위를 좁혀나가는 방식으로 시간을 단축
# 함수 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
# 파라미터 선언
param = {'n_neighbors': range(1, 500, 10), 'metric': ['euclidean', 'manhattan']}
# 기본모델 선언
knn_model = KNeighborsClassifier()
# Grid Search 선언
model = GridSearchCV(knn_model,
param, # 파라미터 범위
cv = 5, #K-Fold 개수
n_iter=20, # 랜덤하게 선택할 파라미터(조합) 개수
scoring='r2', # 평가 지표
verbose = 2 # 모델 fit 과정 정보, 3은 실제 진행되는 과정까지 표현
)
# 학습하기
model.fit(x_train, y_train)
# 테스트로 얻은 성능
model.cv_results_['mean_test_score']
# 최적 파라미터
model.best_params_
# 최고 성능
model.best_score_
# 최고 성능의 모델
model.best_estimator_
# DecisionTree의 경우 시각화
plt.figure(figsize=(4, 4))
plt.barh(y = list(x), width=model.best_estimator_.feature_importances_)
plt.show()
- Grid Search. Random Search를 사용할 때 내부적인 K-Fold Cross Validation을 위해 cv 값을 지정하므로 실제 수행되는 횟수는 $파라미터 조합 수*cv$ 값이 됨
🤔 데이터 범위가 엄청 넓을때 Hyperparameter 값 찾는법
- Random Search와 Greed Search를 둘 다 사용
- 우선 Random Search를 통해 좋은 값을 찾기
- Grid Search로 Random Search로 찾은 좋은값의 주변값을 포함해서 검사
- 주변중에서 제일 좋은 값이 Random Search에서 나온값과 같다면 그대로 하이퍼 파라미터찾기 종료
- 바뀐다면 바뀐 값 기준으로 새롭게 Grid를 지정해서 좋은 값 찾기
🤔Hyperparameter 튜닝 시 주의사항
- 운영환경에서 성능이 보장되지 않음
- 과적합될 수 있음
- 미래에 발생할 데이터는 과거와 다를 수 있음
- 모델링 목표 : 완벽한 적절한 예측력을 위해 적절한 복잡도의 모델 완성
UnderSampling
- recall값이 비정상적임을 해결하기 위한 목적
- target값의 불균형 해결
- 실전에서는 높아진 recall로 인한 이득과 낮아진 accuracy로 인한 비용을 고려해서 진행
- confusion matrix의 각 부분을 돈으로 산정해서 얼마를 절감할 수 있는지 파악 후 절감이 가장 많이 되는 중요한 부분의 수치를 높이는데 집중해야함
# imblearn 설치
!pip install imbalanced-learn
#%%
# 불러오기
from imblearn.under_sampling import RandomUnderSampler
# Under Sampling
under_sample = RandomUnderSampler()
u_x_train, u_y_train = under_sample.fit_resample(x_train, y_train)
# 확인
print('전:', np.bincount(y_train))
print('후:', np.bincount(u_y_train))
sns.scatterplot(x='Age', y='MonthlyIncome', hue=u_y_train, data=u_x_train)
plt.show()
# 선언하기
model = RandomForestClassifier(max_depth=5, random_state=1)
# 학습하기
model.fit(u_x_train, u_y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
OverSampling
- target중 값이 적은 값을 똑같이 늘림
# 불러오기
from imblearn.over_sampling import RandomOverSampler
# Over Sampling
over_sample = RandomOverSampler()
o_x_train, o_y_train = over_sample.fit_resample(x_train, y_train)
# 확인
print('전:', np.bincount(y_train))
print('후:', np.bincount(o_y_train))
# 학습 데이터 분포 확인
sns.scatterplot(x='Age', y='MonthlyIncome', hue=o_y_train, data=o_x_train)
plt.show()
# 선언하기
model = RandomForestClassifier(max_depth=5, random_state=1)
# 학습하기
model.fit(o_x_train, o_y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
- SMOTE를 사용해서 값과 값 사이사이에 케이스를 늘림
# 불러오기
from imblearn.over_sampling import SMOTE
# Over Sampling
smote = SMOTE()
s_x_train, s_y_train = smote.fit_resample(x_train, y_train)
# 확인
print('전:', np.bincount(y_train))
print('후:', np.bincount(s_y_train))
# 학습 데이터 분포 확인
sns.scatterplot(x='Age', y='MonthlyIncome', hue=s_y_train, data=s_x_train)
plt.show()
# 선언하기
model = RandomForestClassifier(max_depth=5, random_state=1)
# 학습하기
model.fit(s_x_train, s_y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
ClassWeight
- 적절한 가중치를 두어 class 불균형 해소
# 선언하기
model = RandomForestClassifier(max_depth=5, random_state=1, class_weight='balanced')
# 학습하기
model.fit(x_train, y_train)
# 예측하기
y_pred = model.predict(x_test)
# 평가하기
print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))
앙상블(Ensembel)
앙상블 이해
- 통합은 힘이다(Unity is strength)
- 약한모델이 올바르게 결합하면 더 정확하고 견고한 모델을 얻을 수 있다!
- →여러 개의 모델을 결합하여 훨씬 강력한 모델을 생성하는 기법
- 캐글(Kaggle)과 같은 많은 기계학습경쟁에서 상위순위를 차지하고 있음
앙상블 방법
보팅(Voting)
- 여러모델들(다른 유형의 알고리즘 기반)의 예측결과를 투표를 통해 최종 예측결과를 결정하는 방법
- 하드 보팅 : 다수 모델이 예측한 값이 최종 결괏값
- 소프트 보팅 : 모든 모델이 예측한 레이블 값의 결정 확률 평균을 구한 뒤 가장 확률이 높은값을 최종선택
배깅(Bagging)
- Bootstrap Aggregating의 약자
- 데이터로부터 부트스트랩(중복된 복원추출) 한 데이터로 모델들을 학습시킨 후, 모델들의 예측 결과를 집계해 최종 결과를 얻는 방법
- 같은유형의알고리즘기반모델들을사용
- 데이터 분할시 중복을 허용(복원 랜덤 샘플링 방식이라고 함)
- 범주형 데이터(Categorical Data)는 투표방식(Voting)으로 결과를 집계
- 연속형 데이터(Continuous Data)는 평균으로 결과를 집계
- 대표적인 배깅 알고리즘 : RandomForest
랜덤 포레스트(Random Forest)
- 배깅의 가장 대표적 알고리즘
- 여러 Decision Tree 모델이 전체 데이터에서 배깅 방식으로 각자의 데이터 샘플링
- 모델들이 개별적으로 학습을 수행한 뒤 모든 결과를 집계하여 최종결과 결정
2가지 Random
- 랜덤하게 데이터를 샘플링
- 개별 모델이 트리를 구성할 때 분할기준이 되는 Feature를 랜덤하게 선정
Forest
- 나무가 모여 숲을 이루듯 Decision Tree가 여러 개 모여 Forest가 됨
주요 하이퍼파라미터
- 대부분 Decision Tree에서와 같은 하이퍼 파라미터를 가짐
파라미터 | 설명 |
---|---|
n_estimators | 만들어질 Decision Tree 개수 지정(기본값 : 100) 많이 지정할 수록 성능이 좋아질 것으로 기대하지만 무조건 그런것은 아님 너무 늘리면 학습속도가 너무 느려질 수 있음 |
max_depth | 트리의 최대 깊이(기본값 : None) 계속 분할되면 트리 깊이가 너무 깊어져 과적합이 발생 👉 적절한 값 설정 필요 |
min_samples_split | 노드를 분할하기 위한 최소한의 샘플 개수(기본값 : 2) 값을 작게 설정할 수록 계속 분할되어 트리 깊이가 깊어져 과적합 발생 |
min_samples_leaf | 리프노드가 되기위한 최소한의 샘플 수(기본값 : 1) 불균형 클래스인 경우 이를 고려해 작은겂을 설정할 필요가 있음 |
max_feature | 최선의 분할을 위해 고려할 feature 수(기본값 : None) 'sqrt' : feature수의 루트값 'log' : $log_2(전체 feature수)$ 정수형 : feature 수 실수형 : feature 비율 |
부스팅(Boosting)
- 같은 유형의 알고리즘 기반 모델 여러개에 대해 순차적으로 학습을 수행
- 이전 모델이 제대로 예측하지 못한 데이터에 대해서 가중치를 부여하여 다음모델이 학습과 예측을 진행하는 방법
- 계속하여 모델에게 가중치를 부스팅하며 학습을 진행해 부스팅 방식이라함
- 예측 성능이 뛰어나 앙상블 학습을 주도함
- 배깅에 비해 성능이 좋지만, 속도가 느리고 과적합 발생 가능성이 있음
- →상황에 맞게 적절히 사용해야 함
- 대표적인 부스팅 알고리즘 : XGBoost, LightGBM(XGBoost가 너무 느려서 나오게된 모델)
XGBoost(eXtreme Gradient Boosting)
- 부스팅을 구현한 대표적인 알고리즘중 하나가 GBM(GradientBoostMachine)
- GBM 알고리즘을 병렬학습이 가능하도록 구현한것이 XGBoost
- 회귀, 분류 문제를 모두 지원하며, 성능과 자원 효율이 좋아 많이 사용됨
- XGBoost장점
- 높은 예측 성능
- 빠른 수행 시간
- 과적합 규제 기능
- 가지치기
- 내장된 교차 검증
- 결측치 자체 처리
스태킹(Stacking)
- 여러 모델의 예측값을 최종 모델의 학습 데이터로 사용하여 예측 하는 방법
- 예를들면
- KNN, Logistic Regression, XGBoost 모델을 사용해 4종류 예측값을 구한 후
- 이 예측 값을 최종 모델인 Randomforest 학습 데이터로 사용
- 현실모델에서 많이 사용되지 않으며, 캐글(Kaggle)같은 미세한 성능 차이로 승부를 결정하는 대회에서 사용됨
- 기본 모델로 4개 이상 선택해야 좋은결과를 기대할 수 있음
K-meanClustering
- 정규화 필요
- k의 개수만큼 군집화 시킴
import
from sklearn.cluster import KMeans
모델 생성 후 사용
k = 3
model = KMeans(n_clusters=k, random_state=1)
model.fit(x_train)
data['cluster'] = model.fit(x_train)
군집된 값 시각화
plt.figure(figsize = (8, 8))
for i in range(k):
plt.scatter(df.loc[df['cluster'] == i, 'Annual Income (k$)'], df.loc[df['cluster'] == i, 'Spending Score (1-100)'],
label = 'cluster ' + str(i))
plt.legend()
plt.title('K = %d results'%k , size = 15)
plt.xlabel('Annual Income', size = 12)
plt.ylabel('Spending Score', size = 12)
plt.show()
k 결정 방법
from yellowbrick.cluster import KElbowVisualizer
model = KMeans()
visualizer = KElbowVisualizer(model, k=(1,10))
visualizer.fit(data_scale)
'데이터분석&ML&DL' 카테고리의 다른 글
YOLO 이미지 직접 Annotate하기 (0) | 2023.09.22 |
---|---|
YOLO 동영상 처리 (0) | 2023.09.22 |
시각지능 딥러닝 정리 (0) | 2023.09.22 |
넘파이, 판다스, 시각화, 데이터분석 방법론 정리 (0) | 2023.09.22 |