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: 리프 노드 최대 개수

 

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()