cs

완전 나잖아?

 

너무나 감사한 면접의 기회가 와서 면접을 진행해봤는데, 정말 많이 배웠다.. 

 

결과를 떠나서 정말 더 열심히 해야겠다는 생각만 들었..ㅠㅠ (너무 가고싶은 회사였어서 그른가 내가 더 자책하게 된다..)

 

오늘 저녁에 스터디 카페 등록해서 더 열공해야겠다.

 

그냥 블로그에 이렇게 글 올리는게 아니고 진짜 노트에 써가면서 머리속에 다 집어넣어야지.. 

 

(일단 병원치료... 이석증?인지 뭔지 치료좀 받고, 계속 머리가 핑핑 돈다@@@@@@ 스트레스로 병을 몇개를 얻을 수 있는걸까?)

 


K-최근접 이웃 회귀

먼저 지도학습 알고리즘은 크게 분류랑 회귀로 나눠진다. 

이미지로 설명하는 방식을 좀 배워야할듯..

분류는 말 그대로 나누어서 클래스 하나로 분류하는 것이고, 

 

회귀는 클래스중 하나로 분류 하는 것이 아니라 임의의 어떤 숫자를 예측하는 것이다. 

 

책에서 내가 좋아하는 수학사를 살짝 이야기해주는데, 

Sir   Francis Galton

19세기 통계학자이자 사회학자인 프랜시스 골턴님께서, 키큰 사람의 아이가 부모보다 더 크지 않는다는 사실을 관측하고? 이를 '평균으로 회귀한다'라고 표현을 하면서 회귀 분석의 시작이라고 한다. 

 

다시말해, 두 변수 사이의 상관관계를 분석하는 방법이 회귀이다. 

 

자 다시, 회귀는 알겠고, 그럼 책에서 소개하는 k-최근접 이웃 회귀는 무엇일까?

 

K-최근접 이웃 분류

간단하게 생각하면 된다. 

 

예측하려는 샘플에 가장 가까운 샘플 k개를 선택하고, 클래스를 확인하여 다수 클래스를 새로운 샘플의 클래스로 예측을 한다. 

 

예를들어 위의 그림처럼 k=3(샘플이 3개)라고 생각한다면, 사각형이 다수인 2개이기때문에 x는 네모로 분류가 된다. 

 

K-최근접 이웃 회귀

뭐 회귀도 여기서 크게 다를게 없다. 

 

동일하게 가장 가까운 샘플 k개를 선택하는건 동일하지만 타겟값이 클래스가 아닌 수치이다. 

 

해당 값들의 평균을 구하면서 값을 산출 할 수 있게된다. 

 

예제로 걍 해보는게 빠르당..

 

데이터 준비

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

이전에 했었던 모델을 기초로 사용하니까 

 

농어의 길이가 특성이고 무게가 타겟이라고 생각하면 된다. 

 

해당 데이터를 가지고 한번 어떤 형태를 보이는지 산점도로 확인을 해보자. 

plt.scatter(perch_length, perch_weight)
plt.xlabel('length')
plt.ylabel('weight')
plt.show()



그리지 않더라도,, 대충 생각해도 대충 봐도 뭔가 길이가 길어짐에 따라 무게가 늘어나는 것이 보인다.

 

지난번에 한것처럼, 우린?.. 나는 모델을 만드는 것이니 훈련세트와 테스트 세트로 나눌것이다. 

 

#train, test set

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)

 

계속 사용하고 있는, sklearn의 train_test_split함수를 사용한다. 

 

다시 언급을 해주는데, sklearn에서 사용할 때의 훈련 세트는 2차원 배열이여야한다. 

 

즉 perch_length가 1차원이기 때문에 이를 나눈 train_input, test_input 도 1차원 배열이다. 

 

이걸 2차원으로 바꿔줘야한다. 

 

그 선형대수에서 보는 것 처럼 [1, 2, 3] 이런 느낌을 새로로 [[1], [2], [3]]이런식으로 정리가 되어야한다. 

 

다시 책의 그림을 보자면,,

 

 

배열이 (3, ) 이였던것이 2차원으로 변경시켜주면서 (3, 1)로 이쁘게 정렬이 되었다. 

 

지난번에는 2개의 특성 (무게와 길이)를 사용해서 자연스럽게 2개인 2차원배열을 썼는데, 

 

이번에는 길이만 주었기 때문에 수동으로?2차원 배열을 만들어야 한다. 

 

reshape()함수를 사용해보자.

 

현재 train_input의 크기는 (42, ) 인것을 2차원인 (42, 1)로 변경하려면

 

train_input.reshape(42, 1)을 하면 되는데,

 

크기자리에 -1을 지정하게 되면 나머지 원소 개수로 모두 채우라는 의미가 된다. 

 

즉, 첫번째 크기를 나머지 원소 개수로 채우고, 두번째 크기를 1로 하면 

 

train_input.reshape(-1, 1)처럼 사용하면 된다. 

 

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

 

결정계수(R^2)

사이킷런에서 k-최근접 이웃 회귀 알고리즘을 구현한 클래스는 KNeighborsRegressor이다. 

 

KNeighborsClassifier과 비슷하고, 동일하게 객체를 생성하고 fit()으로 회귀 모델을 훈련시켜야한다. 

 

from sklearn.neighbors import KNeighborsRegressor

knr = KNeighborsRegressor()

#k-최근접 이웃 회귀 훈련시키기 
knr.fit(train_input, train_target)

print(knr.score(test_input, test_target))

0.992809406101064

 

분류의 경우에는 테스트 세트에 있는 샘플을 분류한 개수의 비율, 즉 정확도이다.

 

회귀의 경우에는 다른 값으로 평가를 하는데, 그것이 결정계수라고 부른다. 

 

  • RSS(sum of squares of residuals): (타깃 - 예측)^2의 합
  • TSS(total sum of squares) (타깃 - 평균)^2의 합

 

만약 타깃의 평균정도를 예측 하는 수준이라면 R^2는 0에 가까워지고, 예측이 타깃에 아주 가까워지면 1에 가까운 값을 가지게 된다.

 

근데 0.99라는 수치는 말도 안되게 좋은 값인데, 정확도처럼 R^2가 얼마나 좋은지 이해하기 힘드니 다른 값을 계산 하기로 한다 .

 

타깃과 예측한 값 사이의 차를 구하며 ㄴ어느정도 예측이 벗어났는지 가늠하기 좋다고 한다?! 

 

그래서 sklearn.metric 패키지에서 여러 측정도구를 사용해 확인를 해보자. 

 

from sklearn.metrics import mean_absolute_error

#테스트 셋에 대한 예측 
test_prediction = knr.predict(test_input)

#테스트 셋에 대한 평균 절대값 오차
mae = mean_absolute_error(test_target, test_prediction)
print(mae)

19.157142857142862

 

 

저 결과값이 의미하는 것은?

 

결과에서 예측이 평균적으로 19.15g정도 타깃값과 다르다는 것을 의미한다. 

 

이 전까지는 훈련 세트를 사용해서 모델을 훈련하고, 테스트 셋으로 모델을 평가했는데,,, 

 

훈련세트를 사용해서 평가하면 어떤 값을가질까?

 

 

 

과대적합 vs 과소적합

앞에서 훈련한 모델을 사용해 훈련세트의 R^2를 확인 해보자.

 

print(knr.score(train_input, train_target))

0.9698823289099254

 

앞서서 점수를 구했던 테스트 세트의 점수와,  방근 구한 훈련세트에서의 점수를 다시 보자

  • test set: 0.9928...
  • train set: 0.9698...

보통의 경우에는 훈련세트에서 모델을 만들면 훈련세트의 점수가 조금 더 높게 나오는게 당연하다. 

 

만약! 

 

훈련세트에서의 점수가 굉장히 좋았다가, 테스트 세트에서 점수가 나쁘게 나온다면, 과대적합이라고 말을 한다. 

 

즉, 훈련세트에만 잘 맞는 모델이라서 테스트 세트와 진짜 실제 데이터에 대한 예측을 만들 때 동작을 잘 하지 못할것이다.

 

반대로, 훈련 세트보다 테스트 셋이 더 높게 나왔을때는?? 두 점수 다 낮다면??

 

이런 경우는 대부분 훈련세트에 과소적합이 되었다고 한다.

 

즉, 모델이 너무 단순해서 훈련세트에 적절히 훈련되지 않은 경우이다.

 

훈련 세트가 전체 데이터를 대표한다고 가정하기 때문에 훈련 세트를 잘 학습 하는것이 중요하다. 

 

 

train_set vs test_set의 점수 비교를 했을 때:

  • 과대적합 (overfitting): train_set 점수가 더 높은 경우이며, 훈련세트에서만 잘 맞는 모델이라 실전 데이터를 투입하게 되었을때 잘 작동하지 않을 수 있음 
  • 과소적합 (underfitting): 점수가 모두 낮거나, test_set점수가 더 높을경우이며, 모델이 적절히 훈련되지 않았음, 즉 최적화가 되지 않았다고 할 수 있음 

Techniques to fight underfitting and overfitting (extended). Image by Author

(조금 더 디테일하게 보려고 자료를 찾아봤는데, 위의 테이블의 정리가 아주 아주 깔끔하댱.. 참고로 어제 면접에서 질문받은 L1, L2내용이 들어가 있긴 하지만,,,, 좀 더 저 테이블 완벽하게 이해하려면 더 노력해야겠드아..ㅠㅠ  )

 

 

그럼 우리가 가지고 있는 결과값 같은 경우에는 test_set의 점수가 더 높으니 과소적합이 문제가 있다..! 

 

테이블에서는 다양한 방법으로 해결할 수 있는데, 

 

책에서는 모델을 조금 더 복잡하게 만드는 것을 선택했다. 

 

복잡하게 라고 하면, k-최근접 이웃 알고리즘으로 모델을 더 복잡하게 방법이 있는데, 이웃인 k의 갯수를 줄이면 된다고 한다.

 

이유는 이웃의 개수를 줄이면 훈련 세트에 있는 국지적인 패턴에 민감해지고, 이웃의 갯수를 늘리면 데이터 전반에 있는 일반적인 패턴을 따르기 때문이다..!! 

 

sklearn에서의 기본적인 k의 개수는 5개인데, 3개로 낮춰서 실행해보즈아..

#모델이웃을 3으로 재조정해주고
knr.n_neighbors=3

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

0.9804899950518966

 

이야... 진짜 순식간에 높아졌다.. 

(생각보다 단순하게 스코어를 조정할수있는 방법이 있는지는 몰라땨..)

 

 

+ Recent posts