AI/혼자 공부하는 머신러닝+딥러닝 책

챕터 03 - 1

codehunter 2023. 7. 10. 22:32

미리 결론부터 말하면 이번 챕터도 머신러닝의 학습과정이 익숙해지는 연습이다.

또한 앞의 두 챕터는 무게와 길이로 도미인지 빙어인지 분류하는 작업이었고 

이번 챕터는 주어진 데이터로 농어의 무게를 예측하는 문제를 풀어나가는 챕터이다. 즉 이번 챕터는 특정값을 예측하는 문제(회귀 regression)이다.

 

알고리즘 또한 1,2장에서 썼던 k-최근접 이웃 알고리즘을 여기서도 똑같이 적용해보면서 익혀보자.

 

전체과정을 아예 외우기 위해 여기까지 학습한 작업 순서대로 나열해보자면

 

1. 데이터 준비 및 데이터 패턴 파악하기 ( 주로 그래프들을 그려가며 대략적인 추세 파악 )

2. 만약 스케일이 안맞는 데이터들이라면 스케일 맞추기

3. 훈련세트와 테스트세트 준비하기

4. 특정 알고리즘 (여기선 k-최근접 이웃 알고리즘 ) 으로 훈련하기

5. 평가

6. 예측 및 사용하기

 

 

 

 

1. 데이터 준비 및 데이터 패턴 파악하기 ( 주로 그래프들을 그려가며 대략적인 추세 파악 )

2. 만약 스케일이 안맞는 데이터들이라면 스케일 맞추기

이번 챕터는 이 과정이 빠진다.

 

3. 훈련세트와 테스트세트 준비하기

4. 특정 알고리즘 (여기선 k-최근접 이웃 알고리즘 ) 으로 훈련하기

k-최근접 이웃 알고리즘으로 회귀를 적용한 클래스는 KNeighborsRegressor인다. 이는 도미와 빙어 분류시에 사용했던 KNeightborsClassifier와 비슷하다. 훈련을 해서 테스트를 하면 아래와 같은 점수가 나오는데 

분류에서는 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율이고 정확도라고 불리운다.

회귀에서는 정확한 숫자를 맞히는것은 거의 불가능하고 이 점수를 결정계수 ( R^2​ )라고 부른다. 계산식은 아래와 같다.

만약 타킷의 평균 정도를 예측하는 수준이라면 결정계수( R^2​ )는 0에 가까워지고,

예측이 타킷에 아주 가까워지면 (분자가 0에 가까워지기 때문에) 1에 가까운 값이 된다고 함. 

그러므로 1에 가까운 값이 나와야 좋은 것임.

0.99... 면 아주 좋은 값이다. 하지만 이 값이 직감적으로 얼마나 좋은지 이해하기는 어렵고 대신 다른 값을 알아보자.

mean_absolute_error 라는 함수는 타깃과 예측의 절대값 오차를 평균하여 반환한다. 위 결과에서 19라는 값은 예측한 무게값과 19g 정도 차이가 있다는걸 알 수 있다.

 

그런데 이 시점에 책에서는 훈련세트로 훈련하고 테스트 세트로 모델을 평가했는데 갑자기 훈련세트를 사용해 평가하는건 어떻겠냐고 한다. 분류에서는 당연히 100% 정확도가 나왔겠지만 회귀에서는?... 예상으로는 좋은 점수가 나와야하는데...

오히려 테스트 세트보다 점수가 떨어진 현상이 발생했다. 이렇게 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 너무 낮은경우를 과소적합 되었다고 말한다고 한다. 그 반대로 훈련 세트에서 점수가 굉장히 좋았는데 테스트 세트에서 점수가 굉장히 나쁘다면 과대적합 되었다고 한다.

 

과소적합이 일어나는 현상으로는 훈련 세트와 테스트 세트의 크기가 매우 작기 때문이라고 한다.

이럴때는 해결책으로 모델을 조금 더 복잡하게 만들면 된다고 함. KNeighborsRegressor 모델에서는 이웃의 개수를 줄이면 복잡해진다고 함. 기본값으로 5였는데 3으로 줄여서 다시 훈련하고 점수를 내보면

점수가 조금 올라간걸 알수 있다. 바로 테스트 세트도 다시 점수를 내보면 

훈련세트 보다는 낮아졌으므로 과소적합 문제는 해결했다.

 

확인문제 

2. 과대적합과 과소적합에 대한 이해를 돕기 위해 복잡한 모델과 단순한 모델을 만들겠습니다. 앞서 만든 k-최근접 이웃 회귀 모델의 k값을 1, 5, 10으로 바꿔가며 훈련해 보세요. 그다음 농어의 길이를 5에서 45까지 바꿔가며 예측을 만들어 그래프로 나타내 보세요. n이 커짐에 따라 모델이 단순해지는 것을 볼 수 있나요?

# k-최근접 이웃 회귀 객체를 만듭니다.
knr = KNeighborsRegressor()

# 5에서 45까지 x 좌표를 만듭니다.
x = np.arange(5, 45).reshape(-1, 1)

# n = 1, 5, 10일 때 예측 결과를 그래프로 그립니다.
for n in [1, 5, 10]:
  # 모델을 훈련합니다.
  knr.n_neighbors = n
  knr.fit(train_input, train_target)

  # 지정한 범위 x에 대한 예측을 구합니다.
  prediction = knr.predict(x)

  # 훈련 세트와 예측 결과를 그래프로 그립니다.
  plt.scatter(train_input, train_target)
  plt.plot(x, prediction)
  plt.title('n_neightbors={}'.format(n))
  plt.xlabel('length')
  plt.ylabel('weight')
  plt.show()

 

.

이웃이 많아지면 변화량이 적어지는데 아마 평균위치의 변화량이 점점 작아져서 그런건 아닌지...

 

최종코드

## k-최근접 이웃 회귀
### 데이터 준비

import numpy as np

perch_length = np.array(
    [8.4, 13.7, 15.0, 16.2, 17.4, 18.0, 18.7, 19.0, 19.6, 20.0,
     21.0, 21.0, 21.0, 21.3, 22.0, 22.0, 22.0, 22.0, 22.0, 22.5,
     22.5, 22.7, 23.0, 23.5, 24.0, 24.0, 24.6, 25.0, 25.6, 26.5,
     27.3, 27.5, 27.5, 27.5, 28.0, 28.7, 30.0, 32.8, 34.5, 35.0,
     36.5, 36.0, 37.0, 37.0, 39.0, 39.0, 39.0, 40.0, 40.0, 40.0,
     40.0, 42.0, 43.0, 43.0, 43.5, 44.0]
     )
perch_weight = np.array(
    [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )

### 추세 파악

import matplotlib.pyplot as plt
plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()

from sklearn.model_selection import train_test_split
train_input, test_input, train_target, test_target = train_test_split(
    perch_length, perch_weight, random_state=42
)
print(test_input.shape)


### 사이킷런에 입력하기 위해 데이터 형태변경 테스트

test_array = np.array([1,2,3,4])
print(test_array.shape)
print(test_array)

test_array = test_array.reshape(2,2)
print(test_array.shape)
print(test_array)

train_input = train_input.reshape(-1, 1)
test_input = test_input.reshape(-1, 1)
print(train_input.shape, test_input.shape)

### 결정계수

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor()

# k-최근접 이웃 회귀 모델을 훈련합니다.
knr.fit(train_input, train_target)

print(knr.score(test_input, test_target))

from sklearn.metrics import mean_absolute_error

# 테스트 세트에 대한 예측을 만듭니다.
test_prediction = knr.predict(test_input)

# 테스트 세트에 대한 평균 절대값 오차를 계산한다.
mae = mean_absolute_error(test_target, test_prediction)
print(mae)

### 과대적합 vs 과소적합

print(knr.score(train_input, train_target))

### 과소적합 해결법 (모델을 조금 복잡하게 만들기)

# 이웃의 개수를 3으로 설정한다.
knr.n_neighbors = 3

# 모델을 다시 훈련한다.
knr.fit(train_input, train_target)
print(knr.score(train_input, train_target))

print(knr.score(test_input,test_target))

## 확인문제 2

# k-최근접 이웃 회귀 객체를 만듭니다.
knr = KNeighborsRegressor()

# 5에서 45까지 x 좌표를 만듭니다.
x = np.arange(5, 45).reshape(-1, 1)

# n = 1, 5, 10일 때 예측 결과를 그래프로 그립니다.
for n in [1, 5, 10]:
  # 모델을 훈련합니다.
  knr.n_neighbors = n
  knr.fit(train_input, train_target)

  # 지정한 범위 x에 대한 예측을 구합니다.
  prediction = knr.predict(x)

  # 훈련 세트와 예측 결과를 그래프로 그립니다.
  plt.scatter(train_input, train_target)
  plt.plot(x, prediction)
  plt.title('n_neightbors={}'.format(n))
  plt.xlabel('length')
  plt.ylabel('weight')
  plt.show()

'AI > 혼자 공부하는 머신러닝+딥러닝 책' 카테고리의 다른 글

챕터 03 - 3  (0) 2023.07.15
챕터 03 - 2  (0) 2023.07.15
챕터 02 - 2  (1) 2023.07.08
챕터 02 - 1  (0) 2023.07.06
챕터 01  (0) 2023.07.02