KT 에이블스쿨 복습
[0329 복습] 기본 알고리즘_Linear Regression(선형회귀), K-Nearest Neghbor(KNN), 정규화(스케일링), Decision Tree(의사결정나무, 결정트리)
리니끄적
2024. 3. 29. 22:58
CH1. Linear Regression(선형회귀)
최적의 회귀모델
- y = ax +b (a는 기울기(가중치), b는 y절편(편향))
- 다양한 형태를 가진 데이터에서 최선의 직선을 긋는 것! (오차의 합=MSE이 최소가 되는 직선)
단순 회귀
- 독립변수가 하나면 단순 회귀, 여러 개이면 다중 회귀!
- x 값 하나만으로 y 값을 설명할 수 있는 경우
- 모델 학습 후 회귀계수 확인 가능
- 회귀식: y = w0 + w1x1
- coef_: 회귀계수(=가중치)
- intercept_: 편향
다중 회귀
- 여러 독립변수가 종속변수에 영향을 미치는 선형 회귀
- y값을 설명하기 위해서는 여러 개의 x값이 필요한 경우!
- 모델 학습 후 회귀계수 확인 가능
- 회귀식: y = w0 + w1x1 + w2x2 + w3x3 + w4x4 ... + wnxn
- 각 독립변수의 최선의 가중치(w1, w2...wn)와 편향(w0)을 찾음!
- 회귀계수가 여럿이므로, 독립변수 이름과 함께 확인! list(x_train) 활용
# 모델링
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score # 일반적으로 회귀문제에서는 MAE, R2로 평가
model = LinearRegression()
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
print('MAE:', mean_absolute_error(y_pred, y_test))
print('R2:', r2_score(y_pred, y_test))
# 회귀식 확인
a = model.coef_
b = model.intercept_
speed = np.array([x_test.min(), x_test.max()])
# np.linspace 1번째 요소와 2번째 요소 사이에서 알아서 값을 3번째 요소(n개)만큼 만들어 줌
speed = np.linspace(x_test.min(), x_test.max(),2)
dist = a * speed + b
# 시각화
plt.figure(figsize=(5,3))
plt.scatter(x_test, y_test) # 평가 데이터 x_test의 최솟값과 최대값을 구하면 선을 그릴 수 있음!
plt.scatter(x_train, y_train) # 학습 데이터
plt.plot(speed, dist, color='r') # 회귀선 그리기, 학습 데이터에 대한 최선의 직선
plt.show()
- 회귀계수 시각화
# 회귀계수 시각화
plt.figure(figsize=(3,5))
df = pd.DataFrame()
df['feature'] = list(x)
df['coef'] = model.coef_
# 정렬 barh 큰거부터 작은 거로 출력하려면 ascending=True로 줘야 함 / seaborn은 반대
# 선형회귀는 변수가 많을수록 모델이 복잡해지고 과적합이 될 가능성이 높아짐! -> 그래서 꼭 불필요한 변수를 제거해야 함!
df.sort_values(by='coef', ascending=True, inplace=True)
# 시각화
plt.barh(y=df['feature'], width=df['coef'])
plt.show()
CH2. K-Nearest Neighbor(KNN)
KNN
- k-Nearest Neighbor: k 최근접 이웃(가장 가까운 이웃 k개)
- 학습용 데이터에서 k개의 최근접 이웃의 값을 찾음 → 그 값들로 새로운 값을 예측하는 알고리즘
- 회귀와 분류 모두 사용 가능!
- 다른 알고리즘에 비해 이해하기 쉬우나, 연산 속도 느림
k값의 중요성
- k의 개수에 따라 데이터 다르게 예측할 수도 있음
- k값에 따라 예측 값이 달라짐 → 적절한 k값을 찾는 것이 중요 (기본값 =5)
- 일반적으로 k는 홀수로 설정, 1로는 설정하지 않음!
- 만약 k=데이터 개수라면 전체를 나타내기 때문에 → 최빈값, 평균과 같은 값으로 나타남
- k=1 모델이 가장 복잡함, k가 작을수록 모델이 복잡해지며 과적합 발생 가능성이 높다!
- 참고) 맨하튼 거리는 유클리드 거리보다 항상 크거나 같다
대표적인 스케일링 방법
- 독립변수와 종속변수의 값 범위가 차이가 많이 나면 knn 모델 성능이 달라질 수 있음!
1. 정규화(Nomalization)
- 각 변수의 값이 0과 1사이 값이 됨
2. 표준화(Standardization)
- 각 변수의 평균이 0, 표준편차가 1이 됨
** 평가용 데이터에도 학습용 데이터를 기준으로 스케일링을 수행!
- KNN 회귀모델 구현
# 정규화 전 값의 범위 확인: 각자 범위가 다름!
plt.boxplot(x_train, vert=False, labels=list(x))
plt.show()
# 최댓값, 최솟값 구하기
x_max = x_train.max()
x_min = x_train.min()
# 공식 사용해서 정규화
x_train = (x_train - x_min) / (x_max - x_min)
x_test = (x_test - x_min) / (x_max - x_min)
# 모듈 불러오기
from sklearn.preprocessing import MinMaxScaler
# 정규화
scaler = MinMaxScaler()
# 학습 데이터로 피팅한 기준을 가지고 transform(학습 데이터의 최대값, 최소값 구하기)
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
# 일반적으로 정규화한 x_train, x_test를 가지고 있는 것이 좋으므로, 다른 변수로 선언해주는 것을 권고!
# scaler.fit(x_train)
# x_train_S = scaler.transform(x_train)
# x_test_S = scaler.transform(x_test)
- 이후 모델링은 모두 같은 방식 → 정규화 후 성능이 조금 더 좋아짐!
- KNN 분류모델 구현
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix, classification_report # 분류 모델 성능평가는 혼동행렬, 분류 리포트 활용
model = KNeighborsClassifier()
model.fit(x_train, y_train)
y_pred = model.predict(x_test)
print(confusion_matrix(y_pred, y_test))
print(classification_report(y_pred, y_test))
CH3. Decision Tree(결정트리)
Decision Tree
- 결정 트리, 의사 결정 나무: 특정 변수에 대한 의사결정 규칙을 나무 가지 뻗는 형태로 분류함
- 분류와 회귀 문제에 모두 사용되는 지도학습 알고리즘
- 스케일링 등의 전처리 영향도가 크지 않고, 분석 과정 눈으로 확인할 수 있음(화이트박스 모델)
- 스무고개처럼 의미 있는 질문을 먼저 하는 것이 중요!
- 훈련 데이터에 대한 제약 사항이 거의 없는 유연한 모델
- 과적합으로 모델 성능이 떨어지기 쉬움 → 트리 깊이를 제한하는 튜닝 필요(max_depth)
Decision Tree 용어
- Root Node(뿌리 마디): 전체 자료 시작하는 마디
- Child Node(자식 마디): 마디 하나로부터 분리된 2개 이상의 마디
- Paret Noed(부모 마디): 주어진 마디의 상위 마디
- Terminal Node(끝 마디): 자식 마디가 없는 마디 (=Leaf Node)
- Internal Node(중간 마디): 부모 마디와 자식 마디가 모두 있는 마디
- Branch(가지): 연결되어 있는 2개 이상의 마디 집합
- Depth(깊이): 뿌리 마디로부터 끝 마디까지 연결된 마디 개수 → 작을수록 좋음
분류와 회귀
- 분류: 마지막 노드에 있는 샘플들의 최빈값을 예측값으로 반환 (불순도)
- 회귀: 마지막 노드에 있는 샘플들의 평균을 예측값으로 반환 (MSE)
불순도
- 순도의 반대말 = 불순도 / 순도가 높을수록 분류가 잘 된 것!
- 불순도를 수치화할 수 있는 지표 = 지니 불순도(Gini Impurity), 엔트로피(Entropy)
- 지니 불순도 = 1 - (양성 클래스 비율^2 + 음성 클래스 비율 ^2)
- 지니 불순도가 낮을수록 순도가 높으며, 0~0.5 사이의 값(이진 분류의 경우!)
- 완벽하게 분류되면 → 0 / 완벽하게 섞이면 → 0.5
- 지니 불순도가 낮은 속성으로 의사결정 트리 노드 결정!
- 엔트로피 = -음성클래스 비율 * log2(음성클래스 비율) - 양성클래스 비율 * log2(양성클래스 비율)
- pi: 집합 안에서 속성 i의 확률을 나타냄
- ex) pi =1이면 집합 안의 모든 항목이 i 속성을 가진 경우
- 엔트로피는 0 ~ 1 사이의 값: 순수하게 분류되면 → 0, 완벽하게 섞이면(50:50) → 1
정보 이득
- 엔트로피는 단지 속성의 불순도를 표현
- 우리가 알고 싶은 것 = 어떤 속성이 얼마나 많은 정보를 제공하는가!
- 정보 이득 공식
- Gain(T, X) = Entropy(T) - Entropy(T,X)
- 정보 이득이 크다 = 어떤 속성으로 분할할 때 불순도가 줄어든다!
가지치기
- 가지치기 안하면 depth(깊이)가 너무 깊어서 모델이 학습 데이터에는 매우 잘 맞지만, 평가 데이터에는 잘 맞지 않음
- 여러 하이퍼파라미터 값을 조정해 가지치기 할 수 있음! (가장 적절한 하이퍼파라미터 값을 찾도록 노력)
- max_depth, min_samples_leaf, in_samples_split 등
- 확률에 근거해 예측!
- 파라미터
- max_depth: 트리의 최대 깊이 (기본값: None)
→ 노드의 샘플 개수가 min_samples_split 설정 값보다 작아질 때까지 계속 분할 - min_samples_split: 노드를 분할하기 위한 최소한의 샘플 개수 (기본값: 2)
- min_samples_leaf: 리프 노드가 되기 위한 최소한의 샘플 수 (기본값: 1)
- max_feature: 최선의 분할을 위해 고려할 feature 수(기본값:None)
- max_leaf_node: 리프 노드 최대 개수
- max_depth: 트리의 최대 깊이 (기본값: None)
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import confusion_matrix, classification_report
# 2단계: 선언하기
# 독립변수 개수를 제한, 변수가 20개가 있을 때 최대한 불순도가 낮은 질문을 하며 가지를 치는데
# 만약 불순도의 값이 같으면 변수들 중 하나를 랜덤으로 고름 → random_state줘야 같은 결과를 도출할 수 있음
# max_depth를 주면 복잡성을 줄이고 과적합을 줄여서 성능이 더 좋아짐!
model = DecisionTreeClassifier(max_depth=5, 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))
- 시각화
# 시각화 모듈 불러오기
from sklearn.tree import export_graphviz
from IPython.display import Image
# 이미지 파일 만들기
export_graphviz(model, # 모델 이름
out_file='tree.dot', # 파일 이름
feature_names=list(x), # Feature 이름
class_names=['die', 'survived'], # Target Class 이름, 회귀문제이면 class가 없기 때문에 안 씀
rounded=True, # 둥근 테두리
precision=2, # 불순도 소숫점 자리수
max_depth=3, # 표시할 트리 깊이 (실제로는 5로 만들었지만, 보여주는 거는 3만)
filled=True) # 박스 내부 채우기
# 파일 변환
!dot tree.dot -Tpng -otree.png -Gdpi=300
# 이미지 파일 표시
Image(filename='tree.png')
- 변수 중요도 시각화
# 변수 중요도, Embarked_Q Parch에는 질문에 안 쓰인 것!
# depth=5로했을 때, max_depth를 주면 변수 중요도가 없는 변수들이 생김
plt.figure(figsize=(5, 5))
plt.barh(y=list(x), width=model.feature_importances_)
plt.show()
- 변수 중요도 정렬
# 데이터프레임 만들기
df = pd.DataFrame()
df['feature'] = list(x)
df['importance'] = model.feature_importances_
df.sort_values(by='importance', ascending=True, inplace=True)
# 시각화
plt.figure(figsize=(5, 5))
plt.barh(df['feature'], df['importance'])
plt.show()