파이썬/머신러닝

[머신러닝] 과대적합과 과소적합 & KNN(K- 최근접 이웃 알고리즘)

취준생코린이 2021. 6. 4. 17:37
728x90

일반화

  • 훈련 데이터로 학습한 모델이 예측 데이터에 대해 정확히 예측하도록 하는것
  • trian(훈련)과 test(예측) 데이터의 비율이 7:3
  • 과대적합과 과소적합의 중간 같은 느낌
  • 예측률이 제일 높다

 

과대적합

  • 훈련 데이터에 너무 적합되어서 예측 데이터를 넣었을 때 제대로 작동되지 않음
  • 예를 들어 훈련 데이터로 축구공을 넣어서 학습시켜 둥글면 공이라고 인식시키고 싶은데
    훈련 데이터에 너무 적합되어 농구공, 야구공 같은 것들은 공이라고 인식하지 못하고
    축구공만 공이라고 인식한다.
  • 데이터의 칼럼수가 증가할 수 록 과대적합 확률 증가
  • train데이터가 많을수록 과대적합 확률 증가
  • 훈련 데이터에는 오차가 적은데 예측 데이터에는 오차가 증가하는 현상
    (여기서 오차는 실제 데이터랑 얼마나 다른지 정도로 보면 된다.)
    (훈련 데이터는 실제 데이터의 부분 집합)
  • 모형이 지나치게 복잡하여 관측값의 개수에 비해 상대적으로 너무 많은 매개변수를 포함할 때 발생한다.
    (매개변수는 규칙 정도로 생각하면 된다. 코끼리를 학습시키고 싶을 때 코끼리는 코가 길다. 몸집이 크다. 귀가 널찍하다. 이런 것들이 규칙(즉, 매개변수)이라고 생각하면 된다.)
  • 과대 적합은 좋지 않은 예측능력을 보여준다.

 

과소적합

  • 훈련 데이터를 충분히 반영하지 못해 훈련 데이터, 예측 데이터 모두에서 성능이 저하(예측률이 낮다)
  • test데이터가 많을수록 과소적합 황률 증가
  • 모델링을 너무 간단하게 해서 예측이 잘 안 나오는 현상
  • 관측값의 개수에 비해 매개변수가 너무 적을 때 발생한다.
  • 이때는 그냥 매개변수를 늘려주면 과소적합이 해결된다.

 

 

 

  훈련score 예측 score
과대적합 매우높음 훈련score에 비해 낮음
일반화 높음 가장높음
과소적합 다소 낮음 매우 낮음

 

 

해결방법

  • 주어진 훈련 데이터의 다양성이 보장되어야 한다. 다양한 데이터 포인트를 골고루 나타내어야 한다.
  • 일반적으로는 데이터의 양이 많으면 일반화에 좋다
    ( 하지만 데이터의 양이 너무 많으면 비용의 문제라든가 여러 문제가 있으니
    적정 수준의 데이터를 모으는 게 중요하다. )
  • 데이터의 양이 많더라도 편중된 데이터를 많이 모으는 것은 도움이 되지 않는다.
  • 규제를 통해 모델의 복잡도를 적정선으로 설정해야 한다.

 

분류

  • 정답(레이블)이 정해져있다
  • 정답의 종류가 정해져있다
  • 정답의종류 = class

 

회귀

  • 정답이 정해져있지않다
  • 연속적인 숫자로 표현된다
  • 미세한 차이(미세한 오차)가 중요하지않다

 

 

K-Nearest Neighbors(KNN) (K- 최근접 이웃 알고리즘)

  • 새로운 데이터 포인트와 가장 가까운 훈련 데이터셋의 데이터 포인트를 찾아 예측
  • k값에 따라 가까운 이웃의 수가 결정
  • 분류와 회귀에 모두 사용 가능하다
  • k값이 작을수록 모델의 복잡도(=결정경계)가 상대적으로 증가 (노이즈(noise) 값에 민감)
    (노이즈는 이상치이다.)
  • 반대로 k값이 커질수록 모델의 복잡도(=결정경계)가 낮아진다 (노이즈(noise) 값에 덜민감)
  • 100개의 데이터를 학습하고 k를 100개로 설정하여 예측하면 빈도가 가장 많은 클래스 레이블로 분류
  • 결정경계 = 모델의 복잡도
  • 이웃(k)의 수가 클수록 결정경계가 완만해지고 작을수록 각짐
  • 이웃(k)의 수가 클수록 이상치(노이즈)에 덜민감해지고 k가 작을 수록 이상치(노이즈)에 민감해진다.

 

  결정경계(모델의 복잡도) noise(이상치)
K값(이웃수) 높을수록 경계완만, 복잡도 낮음 이상치에 덜민감
K값(이웃수) 낮을수록 경계각짐, 복잡도 증가 이상치에 민감

 

 

 

K-Nearest Neighbors(KNN) (K- 최근접 이웃 알고리즘)에서 분류와 회귀에따른 차이

분류 : 이웃중에서 많은 비율을 예측값으로 사용

회귀 : 평균값을 예측값으로 사용

 

 

데이터 포인트(sample)사이 거리 값 측정 방법

  • 유클리디언 방식 : 최단거리로감(직선 C)
  • 맨하튼 방식 : 최단거리로 가지않고 a,b로 이동 , 즉 거리는 a+b만큼 이동한거다.

 

 

KNN의 장단점 및 주요 매개변수

  • 매개변수 : 거리 측정 방법, 이웃의 수, 가중치 함수
  • 이해하기 매우 쉬운 모델이다
  • 훈련 데이터 세트가 크면 (특성, 샘플의 수) 예측이 느려진다
  • 수백개 이상의 많은 특성을 가진 데이터 세트와 특성값 대부분이 0인 희소(sparse)한
    데이터 세트에는 잘 동작하지 않는다.
  • 거리를 측정하기 때문에 같은 scale을 같도록 정규화가 필요하다

 

KNN을 이용해서 머신러닝을 해보자!

  • 데이터는 붓꽃데이터(iris)를 이용

1. 문제정의

  • iris(붓꽃) 데이터를 활용
  • 꽃잎길이, 꽃잎 너비, 꽃받침 길이, 꽃받침 너비 4가지 특징을 통해서 3가지 품종을 구분
  • KNN모델의 이웃의 숫자를 조절해보자 (하이퍼파라미터 튜님)
  • 붓꽃은 분류문제에 해당한다

 

2. 데이터 수집

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris # sklearn에서 제공하는 붓꽃 데이터
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt

2. 데이터 수집
# sklearn에서 제공하는 붓꽃 데이터 사용
irisData = load_iris()
irisData

 

# keys() : 키값을 확인
irisData.keys()

# 데이터프레임의 실제값
# data가 문제(X)
irisData['data']

# 붓꽃품종의 종류를 0,1,2로 나눠서 기록
# 문제에 대한 정답(y)
irisData['target']

# 붓꽃의 종류
# 위의 target에 대한 실제값
# 0 : setosa, 1 : versicolor, 2 : virginica
irisData['target_names']

# 컬럼의 이름
# 각 특성을 설명하는 문자열 리스트
irisData['feature_names']

# 상세 정보
# print를 안쓰면 이스케이프 문자(\t, \n..)들이 문자로 보이고
# print를 쓰면 실행되서 보인다.
print(irisData['DESCR'])

# 데이터 경로
irisData['filename']

 

3. 데이터 전처리

# 데이터 구성하기
# 문제 데이터
irisDf = pd.DataFrame(irisData['data'], columns= irisData['feature_names'])


# 문제는 보통 2차원으로 구성, 정답은 1차원으로 구성됨
# 행렬은 수학에서 대문자로 표시, 따라서 문제는 대문자X
# 벡터 배열은 수학에서 소문자로 표시, 따라서 정답은 소문자y로 사용
X = irisDf # 문제
y = irisData['target'] # 정답


# 훈련과 테스트
#    7 : 3
# 데이터가 편중되지 않게 데이터를 섞어서 나누자 -> train_test_split사용
# X_train, X_test, y_train, y_test를 한번에 나눠준다.
X_train, X_test, y_train, y_test = train_test_split(X,y,
                                                   random_state=3,
                                                   test_size = 0.3)
# random_state : 데이터를 섞는 방식, 여기서는 3번방식을 사용함

 

4. 탐색적 데이터 분석

  • 지금은 Skip하겠다.

 

5. 모델선택 및 하이퍼파라미터 튜닝

  • from sklearn.neighbors import KNeighborsClassifier 사용
  • 이웃의 수(n_neighbors)를 설정안하면 디폴트는 5다
# KNN모델 받아오기
knn = KNeighborsClassifier()

 

6.학습

# X_train : 훈련용 문제
# y_train : 훙련용 정답
knn.fit(X_train,y_train)

 

7. 평가

# 예측 점수 : score(X_test, y_test)사용
# score는 정확도를 통해서 점수를 나타내줌
# X_test를 통해서 예측값을 출력하고
# 실제정답인 y_test롸 비교해서 전체 데이터중 맞춘 데이터의 비율을 이용해서
#점수를 나타내줌
knn.score(X_test, y_test)


# 예측값 : predict(X_test)사용
knn.predict(X_test)


y_test

 

하이퍼 파라미터(n_neighbors)에 변화를 주면 어떤 변화가 있는지 알아보자

  • 이웃의 수를 1~100까지 변화를 주자

 

# train 점수
trainList = []
for k in range(1,101):
    # 모델 선택 및 하이퍼 파라미터 튜닝
    model = KNeighborsClassifier(n_neighbors = k)
    # 모델학습
    model.fit(X_train, y_train)
    # 모델 평가
    trainList.append(model.score(X_train, y_train))
    print(f"이웃{k}개 : {trainList[k-1]}")

 

# test점수
testList = []
for k in range(1,101):
    # 모델 선택 및 하이퍼 파라미터 튜닝
    model = KNeighborsClassifier(n_neighbors = k)
    # 모델학습 : 학습은 무조건 train(훈련)으로 해야한다.
    model.fit(X_train, y_train)
    # 모델 평가
    testList.append(model.score(X_test, y_test))
    print(f"이웃{k}개 : {testList[k-1]}")

 

시각화를 해보자

# 시각화
plt.figure(figsize=(25,5)) # 출력할 그래프 크기
plt.plot(range(1, 101), # X데이터 = 1~100
        trainList,  # y데이터
         c="red")
plt.plot(range(1, 101), # X데이터 = 1~100
        testList,  # y데이터
         c="blue")

plt.xticks(range(1,101))
plt.grid() #기준선
plt.show()

 

그래프를 해석해보면 train과 test가 비슷한게 학습이 잘된거다.

그러면 9, 13, 18일때 학습이 잘됐음을 그래프로 확인할 수 있다.

 

 

맨하튼 거리 방식으로 예측을 해보자

위에서는 디폴트가 p=2라 전부 유클리드 거리방식으로 구하였다.

# 모델 선택 및 하이퍼 파라미터 튜닝
model = KNeighborsClassifier(n_neighbors = 9,
                            p=1,
                             weights="distance")
# 모델학습
model.fit(X_train, y_train)
# 모델 평가
train = model.score(X_train, y_train)
test = model.score(X_test, y_test)
print(f"이웃{9}train : {train}")
print(f"이웃{9}test : {test}")

 

# predict로 예측
model.predict([[1, 4, 2.5, 3]])

# 예측에 대한 정답으로 array([0])이 나옴
# 0이 의미하는건 품종 0이다. 
# 즉 setosa이다.
irisData['target_names']

 

728x90