머신러닝 알고리즘은 문자열 데이터 속성을 입력받지 않으며 모든 데이터는 숫자형으로 표현되어야 한다.따라서 문자형 카테고리형 속성은 모두 숫자 값으로 변환/인코딩 되어야 한다.

scikit-learn을 사용한 변환 방식에는 대표적으로 2가지가 있다.

  • 레이블 인코딩 (Label Encoding)
  • 원-핫 인코딩(One-Hot Encoding)

이 두 가지의 차이점을 한눈에 살펴보면 다음과 같다.

이제 자세히 하나씩 살펴보자.


레이블 인코딩 (Label Encoding)

Label Encoding은 feature의 유형에 따라 숫자를 부여한다.

수행 방법

from sklearn.preprocessing import LabelEncoder
items=['트와이스','BTS','레드벨벳','신화','GOD','GOD']

# LabelEncoder를 객체로 생성 -> fit() -> transform()
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)
print('인코딩 변환값:',labels)

결과값

[4, 0, 2, 3, 1, 1]

레이블 인코딩의 문제점

  1. 일괄적인 숫자 값으로 변환되면서 예측 성능이 떨어질 수 있다.
    -> 숫자의 크고 작음에 대한 특성이 작용
  2. 선형 회귀와 같은 ML 알고리즘에는 적용하지 않아야 함 (트리 계열의 ML알고리즘은 숫자의 이러한 특성을 반영하지 않으므로 괜찮음)

원-핫 인코딩 (One-Hot Encoding)

원-핫 인코딩은 feature의 유형에 따라 새로운 피처를 추가해 고유 값에 해당하는 칼럼에만 1을 표시하고 나머지 칼럼에는 0을 표시하는 방법이다.

scikit-learn에서 하는 방법과 pandas에서 하는 방법 두가지가 있다.

1. sklearn을 이용한 원-핫 인코딩

원-핫 인코딩을 하기 전에 레이블 인코딩을 먼저 해야한다.

from sklearn.preprocessing import OneHotEncoder
import numpy as np
items=['트와이스','BTS','레드벨벳','신화','GOD','GOD']

# 먼저 LabelEncoder로 변환 
encoder = LabelEncoder()
encoder.fit(items)
labels = encoder.transform(items)

# 2차원 데이터로 변환 
labels = labels.reshape(-1,1)

# 원-핫 인코딩을 적용
oh_encoder = OneHotEncoder()
oh_encoder.fit(labels)
oh_labels = oh_encoder.transform(labels)

2. pd.get_dummies()

import pandas as pd
df = pd.DataFrame({'item':['트와이스','BTS','레드벨벳','신화','GOD','GOD'] })

# 원핫인코딩 실행
pd.get_dummies(df)

출처
https://john-analyst.medium.com/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC-%EB%A0%88%EC%9D%B4%EB%B8%94-%EC%9D%B8%EC%BD%94%EB%94%A9%EA%B3%BC-%EC%9B%90%ED%95%AB-%EC%9D%B8%EC%BD%94%EB%94%A9-f0220df21df1

반응형

회귀모델을 평가하는 평가지표

회귀모델은 그 모델이 잘 학습되어졌는지 확인하기 위한 회귀모델의 평가지표들이 4가지 있다. 이를 하나씩 살펴보자.

MAE(Mean Absolute Error)

  • 모델의 예측값과 실제값의 차이의 절대값의 평균
  • 절대값을 취하기 때문에 가장 직관적으로 알 수 있는 지표이다. (해석에 용이하다.)
  • 절대값을 취하기 때문에 모델이 Underperformance(실제보다 낮은 값으로 예측)인지 Overperformance(실제보다 높은 값으로 예측)인지 알 수 없다.
from sklearn.metrics import mean_absolute_error
mean_absolute_error(y, y_pred)

MSE(Mean Squared Error)

  • 제곱을 하기 때문에 MAE와는 다르게 모델의 예측값과 실제값 차이의 면적의(제곱)합이다.
  • 제곱을 하기 때문에 특이치(Outlier)에 민감하다.
from sklearn.metrics import mean_squared_error
mean_squared_error(y, y_pred)

RMSE(Root Mean Squared Error)

  • MSE에 루트를 씌워 사용한다.
  • RMSE를 사용하면 오류 지표를 실제값과 유사한 단위로 다시 변환하여 해석을 쉽게한다.
  • 예측 대상의 크기에 영향을 바로 받는다.
  • MAE보다 특이치에 Robust(강하다)하다.
from sklearn.metrics import mean_squared_error
rmse = mse**0.5

예시 네이버 주가와 삼성전자 주가

  • 삼성전자의 주가 : 2,389,000원
  • NAVER의 주가 : 778,000원
  1. 삼성전자의 주가를 예측하는 모델을 만들었다고 가정하자.
    ➡ 만든 모델로 삼성전자 주가를 예측해보았더니, RMSE가 500,000이 나왔다
  2. NAVER의 주가를 예측하는 모델을 만들었다고 가정하자
    ➡ 만든 모델로 NAVER 주가를 예측해보았더니, RMSE가 놀랍게도 똑같이 500,000이 나왔다

이 두 모델은 동일한 성능을 가지고 있는걸까?

분명 명확하게 이상한 점이 느껴진다. 삼성전자의 500,000와 NAVER의 500,000은 다르다.

에러가 예측하려는 값의 크기에 의존적이라는 면에서 RMSE는 크기 의존적 에러에 속하게 된다.

R-squared (Coefficient of determination, 결정계수)

  • SSE(Sum of Squares Error, 관측치와 예측치 차이)
  • SSR(Sum of Squares Regression, 예측치와 평균 차이)
  • SST(Sum of Squares Total, 관측치와 평균 차이)

R-squared는 현재 사용하고 있는 x변수가 y변수의 분산을 얼마나 줄였는가 이다.
단순히 y평균값 모델(기준모델)을 사용했을 때 대비 우리가 가진 x변수를 사용함으로서 얻는 성능 향상의 정도이다.

즉, R-squared 값이 1에 가까우면 데이터를 잘 설명하는 모델이고 0에 가까울수록 설명을 못하는 모델이라고 생각할 수 있다.

from sklearn.metrics import r2_score
r2 = r2_score(y, y_pred)

 

MAE vs RMSE

자, MAE와 RMSE 수학 공식을 자세히 살펴보자. 무언가가 비슷해보이지 않나..?

분명 중학교였나 고등학교 수학시간에 |X-Y| = (X-Y이라고 배웠어서 나는 두 평가지표 사이의 차이점이 궁금해졌다.

예를 들어보자.

  • 독립변수 = 주택의 노후도, 지하철 역과의 거리, 방 개수, 평수
  • 종속변수 = 집 값

이러한 상황이라면, 회귀 예측값이 집값이므로 200,000($), 470,000($) 등으로 현실적인 값을 갖게 된다.

이 때 "이 모델을 쓰면 집값 계산에 있어서 200$ 정도 틀리는군. 예산에 ±200$ 정도는 각오 해야겠어"라는 직관적인 판단을 할 수 있는 평가 지표를 쓰면 좋지 않을까?

MSE는 잔차값을 절대값 취하고 평균 낸 것이다.

위 표를 통해서라면 이 모델이 평균 25,200$씩 틀리는 모델이라는 걸 알 수 있다.

그러나 RMSE를 쓰면 잔차 계산이 조금씩 난해해진다.

먼저, MAE와 RMSE의 차이점을 비교해보자.

  1. 잔차를 제곱하고 다 더한 뒤 루트를 씌웠다는 점
  2. 평균을 낼 때  처리를 하는 게 아니라 처리를 한다는 것
  • 분모에 n보다 더 작은 sqrt(n)을 썼고, 분모가 작아지니 총 결과는 MAE보다 커진다.

그 결과 다음과 같은 차이가 발생한다.

RMSE로 구한 에러값 27,228을 통해 "이 모델은 27,228$만큼 틀린 모델이다"라고 표현할 수는 없다.
이미 실생활에서 쓰는 계산법을 벗어났기 때문이다.

그렇다면 왜 직관적이고 좋은 MAE가 아닌 RMSE를 쓰는걸까?

결론부터 말하자면, RMSE는 "큰 오류값 차이에 대해서 크게 패널티를 주는 이점"이 있기 때문이다.

다시 위 집 값 예측 예시에서 한 가지 케이스를 추가해보자.

4번째 집 값 데이터가 추가되었다. 다른 데이터와는 다르게 깡패 수준의 스케일을 가지고 있다.
모델은 나름 최선을 다해서 가격을 예측했지만, 잔차(차이)는 4백만$ 수준이나 나와버렸다..
이런 현상을 "큰 오류값이 났을 때"라고 볼 수 있다.

이 때 집 값을 3개만 예측했을 때와 큰 오류값이 추가됐을 때 MAE와 RMSE의 비율을 비교해보자.

MAE가 약 2%의 비율을 가질 때 RMSE는 절반 수준인 1.11%를 갖게 된다.
큰 오류가 나타났어도 RMSE는 제법 방어를 잘 해냈다고 이해하면 된다.

이는 RMSE가 MAE와의 차이 중 첫번째였던, "잔차를 제곱하고 다 더한 뒤 루트를 씌웠다는 점"을 통해 큰 오류값에 패널티를 크게 주었기 때문에 가능하다.

머신러닝의 '학습'에 있어서는 이러한 특이값에 휘둘리지 않는 게 굉장히 중요하다.

정리하자면,
RMSE는 MAE에 비해 직관성은 떨어지지만 Robust한 성격(극단적이지 않은 성격)에서 강점을 보인다.

출처
https://velog.io/@ljs7463/%ED%9A%8C%EA%B7%80%EB%AA%A8%EB%8D%B8-%ED%8F%89%EA%B0%80%EC%A7%80%ED%91%9Cevaluation-metrics
https://velog.io/@tyhlife/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%ED%9A%8C%EA%B7%80-%EB%AA%A8%EB%8D%B8%EC%9D%98-%ED%8F%89%EA%B0%80-%EC%A7%80%ED%91%9C

반응형

분류를 수행할 수 있는 기계 학습 알고리즘을 만들고 나면, 그 분류기의 예측력을 검증/평가 해봐야 한다.

모델의 성능을 평가하려면 모델을 생성하기 전부터 애초에 데이터를 학습 세트와 평가 세트로 분리해서, 학습 세트로 모델을 만들고 평가 세트로 그 모델의 정확도를 확인하는 절차를 거친다. (자세한 내용은 이 포스팅을 참고)

그런데 분류 모델 성능 평가 지표를 소개하기 전에 정답을 맞히거나 틀리는 경우의 수들을 먼저 이해해야 한다.

  • True Positive : 실제 Positive인 정답을 Positive라고 예측(True)
  • True Negative : 실제 Negative인 정답을 Negative라고 예측 (True)
  • False Positive : 실제 Negative인 정답을 Positive라고 예측 (False) - Type Ⅰ error
  • False Negative : 실제 Positive인 정답을 Negative라고 예측 (False) - Type Ⅱ error

여기서 그 유명한 1종 오류, 2종 오류의 개념이 등장한다. 이 두 유형의 오류는 분류 과제의 성격에 따라 경중이 다르다.

예를 들어 중고차 성능 판별 과제에서는 1종 오류(False Positive), 즉 좋은 자동차라 예측하고 구매하였지만 실제로는 좋지 않은 자동차였을 경우가 더 치명적이다. 좋은 중고차를 나쁜 차로 예측해서 구매하지 않더라도 손해볼 것이 없지만 나쁜 중고차를 좋은 차로 잘못 예측하여 구매하는 건 굉장한 손해니까.

그러나 분류 과제가 암환자 진단이라면 2종 오류(False Negative), 즉 암 환자를 건강하다고 판별하는 경우가 훨씬 치명적이다. 그 사람의 생명이 위험해질 수 있으니까.

아무튼 이 개념을 파이썬 코드로 표현해보면 아래와 같다. (정답 labels와 예측한 값 guesses이 있을 때)

계산된 값을 보면 각각 3, 0, 3, 4 순으로 나온다.

이제 이 개념들을 가지고 분류 모델의 성능을 평가할 수 있는 지표를 총 4개 소개하고자 한다.

  1. Accuracy
  2. Recall
  3. Precision
  4. F1 Score

하나씩 자세히 살펴보자.


1. Accuracy (정확도)

accuracy는 다음과 같이 정의할 수 있다.

전체 예측 건수에서 정답을 맞힌 건수의 비율이다. (여기서 정답을 맞힐 때 답이 Positive든 Negative든 상관 없다. 맞히기만 하면 된다.)

Accuracy의 단점

예를 들면 내일 서울에 시간당 1m 이상의 눈이 내릴지 여부를 예측한다고 해보자. 그땐 뭐 머신러닝이고 뭐고 할 거 없이 나 혼자서도 매우 정확한 분류기를 만들 수 있다. 그냥 무조건 Negative를 예측하면 이 분류기는 99.9%의 accuracy를 나타낼 거다. 그 정도 눈 내리는 날은 거의 없으니까. 이 분류기의 정답을 True Negative로만 잔뜩 맞히는 셈이다. True Positive는 하나도 발견하지 못하고.

이런 상황을 정확도 역설(Accuracy Paradox)라고 부른다.

그래서 이렇게 실제 데이터에 Negative 비율이 너무 높아서 희박한 가능성으로 발생할 상황에 대해 제대로된 분류를 해주는지 평가해줄 지표는 바로 recall(재현율)이다.


2. Recall (재현율)

recall은 다음과 같이 정의할 수 있다.

실제로 정답이 True인 것들 중에서 분류기가 True로 예측한 비율이다. 그래서 애초에 True가 발생하는 확률이 적을 때 사용하면 좋다.

그래서 위에서 예로 들었던 시간당 1m 이상의 눈이 내릴지 예측하는 과제에 적절한 거다. 그러면 실제로 그렇게 눈이 내린 날짜 중 몇 개나 맞히는지 확인할 수 있으니까. 만약 여기서 언제나 False로 예측하는 분류기가 있다면 accuracy는 거의 99%를 넘기겠지만, True Positive를 찾을 수 없으니 recall이 0이 된다.

Recall의 단점

그러나 안타깝게도 recall 또한 완벽한 통계 지표가 아니다. 시간당 적설량 1m 이상이 될지 분류하는 과제에서 언제나 True만 답하는 분류기가 있다고 해보자. 그러면 accuracy는 낮지만 눈이 많이 온 날에 대해서만큼은 정확하게 맞힐 수 있기 때문에 recall은 1이 된다. 이 역시 말이 안 되는 지표다.


3. Precision (정밀도)

precision은 다음과 같이 정의할 수 있다.

분류기가 눈이 많이 내릴 것이라고 예측한 날 중에 실제로 눈이 많이 내린 날의 비율을 구하는 것이다.

그래서 언제나 True만 답하는 분류기가 있다면 recall은 1로 나오겠지만, precision은 0에 가까울 거다. 시간당 1m 이상 눈 내리는 날이 며칠이나 되겠는가.

Precision의 단점

결국 precision을 사용할 때의 단점은 곧 recall을 사용할 때의 장점이 되기도 한다. recall과 precision은 서로 반대 개념의 지표이기 때문이다. 하나가 내려가면 다른 하나는 올라갈 수밖에 없다.

그래서 알고리즘을 평가할 때 recall과 precision 모두 나름대로 유용한 지표이긴 하지만, 그 알고리즘의 성능을 충분히, 효과적으로 표현하기에는 한계가 있다. 그래서 생겨난 게 recall과 precision의 조화평균, F1 Score다.


4. F1 Score

F1 score는 다음과 같이 정의할 수 있다.

조화평균에 대해서는 중고등학교 때 언젠가 배웠을 거다. 나도 가물가물하긴 한데 사실 잊어버렸더라도 상관없다. 컴퓨터가 알아서 계산해줄 테니 일단 개념만 알고 가면 되지 뭐.

아무튼 흔히 말하는 평균(산술평균)을 쓰지 않고 조화평균을 구하는 이유는 recall, precision 둘 중 하나가 0에 가깝게 낮을 때 지표에 그것이 잘 반영되도록, 다시말해 두 지표를 모두 균형있게 반영하여 모델의 성능이 좋지 않다는 것을 잘 확인하기 위함이다.

예를 들어 recall, precision이 각각 1과 0.01이라는 값을 가지고 있다고 하자.

산술평균을 구하면 (1+0.01)/2 = 0.505, 즉 절반은 맞히는 것처럼 보인다. 그러나 조화평균을 구해보면 2*(1*0.01)/(1+0.01) = 0.019가 된다. 낮게 잘 나온다.

이제야 분류기의 성능을 좀 제대로 확인할 수 있을 것 같다.


요약

본 포스팅에서는 분류 모델의 성능을 확인할 수 있는 다양한 지표들에 대해 다뤘다.

지표들을 계산하기에 앞서 정답을 맞히거나 틀리는 경우의 수들을 먼저 이해해야 한다.

  • True Positive (truth = 1, guess = 1)
  • True Negative (truth = 0, guess = 0)
  • False Positive (truth = 0, guess = 1) - Type Ⅰ error
  • False negative (truth = 1, guess = 0) - Type Ⅱ error

이제 이 경우의 수를 사용해서 모델 성능을 평가하는 네 개의 지표를 산출할 수 있는데, 각각의 특징과 장단점이 있다.

  • Accuracy : 모든 분류 건수 중에서 분류기가 몇개의 정답을 맞혔는가 (맞거나 틀리다고 정확히 분류했는가)
  • Recall : 맞다고 분류해야 하는 건수 중에서 분류기가 몇개나 제대로 분류했는가
  • Precision : 분류기가 맞다고 분류한 건수 중에서 실제로 맞는 건수가 몇개나 되는가

*** Recall과 Precision은 trade-off 개념이기 때문에 하나가 높아지면 하나는 낮아진다.

  • F1 Score : recall과 precision의 균형값 (조화평균)

어쨌든 분류기의 성능이 좋다는 뜻은 오류가 적다는 뜻이다. 오류가 발생하는 두 가지 경우를 잘 생각해보고, 그 과제의 경중에 따라 효과적인 지표를 사용해서 모델을 검증해야 한다.


scikit-learn에서 확인하는 방법

그리고 파이썬 라이브러리 scikit-learn에서는 이런 통계 지표들을 쉽게 확인할 수 있다. 이렇게.

그리고 분류 결과에 따라 나타나는 경우의 수를 Confusion Matrix(혼동 행렬)로 돌려주는 기능도 있다.

출처
https://hleecaster.com/ml-accuracy-recall-precision-f1/

반응형

이전 포스팅에서 K-Means 클러스터링의 개념이나 원리를 다뤘다.

K-Means 클러스터링 알고리즘은 소개된지 거의 반세기가 지났지만, 여전히 머신러닝에 가장 널리 사용되는 클러스터링 알고리즘 중 하나이다.

그러나 K-Means 알고리즘의 가장 큰 단점은 처음에 지정하는 중심점(centroid)의 위치를 무작위로 결정하기 때문에 최적의 클러스터로 묶어주는 데에는 한계가 있다는 것이다.

그래서 이번에는 K-Means의 새로운 버전이라고 할 수 있는 K-Means++ 알고리즘에 대해 살펴보려고 한다. K-Means++는 전통적인 K-Means의 문제, 즉 중심점 무작위 선정의 문제를 해결하기 위해 생겨난 거라고 보면 되겠다.

K-Means++ 클러스터링의 원리

전통적인 K-Means는 아래와 같은 원리로 진행된다.

  1. 일단 K개의 임의의 중심점(centroid)을 배치하고
  2. 각 데이터들을 가장 가까운 중심점으로 할당한다. (일종의 군집을 형성한다.)
  3. 군집으로 지정된 데이터들을 기반으로 해당 군집의 중심점을 업데이트한다.
  4. 2번, 3번 단계를 수렴이 될 때까지, 즉 더이상 중심점이 업데이트 되지 않을 때까지 반복한다.

그러나 K-Means++는 좀 다르다. K-Means에서 가장 첫번째 단계, 즉 중심점을 배치하는 걸 그냥 임의로 하는 대신 좀 더 신중하게(?) 하는 것이다.

  1. (일단 아무 공간에나 중심점을 k개 찍고 시작하는게 아니라) 가지고 있는 데이터 포인트 중에서 무작위로 1개를 선택하여 그 녀석을 중심점으로 지정한다.
  2. 나머지 데이터 포인트들에 대해 그 첫번째 중심점까지의 거리를 계산한다.
  3. 두번째 중심점은 각 점들로부터 거리비례 확률에 따라 선택한다. 즉, 이미 지정된 중심점으로부터 최대한 먼 곳에 배치된 데이터 포인트를 그 다음 중심점으로 지정한다는 뜻이다.
  4. 중심점이 k개가 될 때까지 2,3번을 반복한다.

scikit-learn 사용법

파이썬 라이브러리 scikit-learn을 사용하면 K-Means++를 매우 쉽게 적용할 수 있다.

K-Means를 사용할 때와 똑같고, 그냥 모델을 불러올 때 init = 'k-means++만 넣어주면 되는거다.

from sklearn.cluster import KMeans

model = KMeans(n_cluster = k, init = 'k-means++)

사실 기본값이 'k-means++'이라 따로 지정 안해주면 알아서 ++로 돌린다. 만약 전통적인 k-means로 중심점을 랜덤하게 지정하고 싶으면 init='random'을 써줘야 한다.

K-Means에 비해 가진 장점

  • 초기 중심점을 더욱 전략적으로 배치하기 때문에 전통적인 K-Means보다 더 최적의 군집화를 할 수 있다.
  • K-Means보다 알고리즘이 수렴하는 속도가 빠르다. 전통적인 K-Means를 사용하면 초기 중심점이 (재수없게) 잘못 지정되는 경우 알고리즘이 수렴하는 데 시간이 오래 걸릴 수 있기 때문이다.

출처
https://hleecaster.com/k-means-clustering-concept/

반응형

본 포스팅에서는 데이터 클러스터링(군집화)로 널리 사용되는 비지도학습 알고리즘 K-Means 클러스터링에 대해 최대한 쉽게 설명해보고자 한다. 파이썬 라이브러리 scikit-learn 사용법도 간략히 소개한다.

클러스터링, 군집화란 무엇인가

만약 우리가 다루는 데이터에 '레이블'이 붙어 있다면 지도학습, 즉 미리 가지고 있는 데이터와 레이블을 기반으로 예측이나 분류를 수행하는 모델을 만들 수 있다. 그러나 실제로는 레이블(분류)이 없는 경우가 더 많다. 물론 이렇게 별도의 레이블이 없는 데이터 안에서 패턴과 구조를 발견하는 비지도 학습도 머신러닝의 큰 축이고, 그 중 가장 대표적인 비지도 학습 기술이 바로 Clustering(군집화)이다.

참고로 지도학습 Classification(분류)과 엄연히 다른 거다. Classification은 미리 레이블이 붙어 있는 데이터들을 학습해서 그걸 바탕으로 새로운 데이터에 대해 분류를 수행하지만, Clustering은 레이블을 모르더라도 그냥 비슷한 속성을 가진 데이터들끼리 묶어주는 역할을 하기 때문이다.

아무튼 클러스터링, 군집화를 사용하는 예로는 아래와 같은 것들을 들 수 있다.

  • 추천 엔진 : 사용자 경험을 개인화하기 위해 비슷한 제품 묶어주기
  • 검색 엔진 : 관련 주제나 검색 결과 묶어주기
  • 시장 세분화(segmentation) : 지역, 인구 통계, 행동에 따라 비슷한 고객들 묶어주기

군집화의 목표

군집화의 목표는 서로 유사한 데이터들은 같은 그룹으로, 서로 유사하지 않은 데이터는 다른 그룹으로 분리하는 것이 된다.

그러면 자연스럽게 2개의 질문이 따라올 것이다.

  • 몇 개의 그룹으로 묶을 것인가
  • 데이터의 '유사도'를 어떻게 정의할 것인가 (유사한 데이터란 무엇인가)

이 두 질문을 해결할 수 있는 가장 유명한 전략이 바로 K-Means 알고리즘이다.

K-Means 군집화의 원리

  • "K"는 데이터 세트에서 찾을 것으로 예상되는 클러스터(그룹) 수를 말한다.
  • "Means"는 각 데이터로부터 그 데이터가 속한 클러스터의 중심까지의 평균 거리를 의미한다. (이 값을 최소화하는 게 알고리즘의 목표가 된다)

K-Means에서는 이걸 구현하기 위해 반복적인(iterative) 접근을 취한다.

  1. 일단 K개의 임의의 중심점(centroid)를 배치하고
  2. 각 데이터들을 가장 가까운 중심점으로 할당한다. (일종의 군집을 형성한다.)
  3. 군집으로 지정된 데이터들을 기반으로 해당 군집의 중심점을 업데이트한다.
  4. 2번, 3번 단계를 수렴이 될 때까지, 즉 더 이상 중심점이 업데이트 되지 않을 때까지 반복한다.

그림으로 보면 아래와 같다.

여기서 일단 k값은 2다. 그래서 (b)에서 일단 중심점 2개를 아무 데나 찍고, (c)에서는 각 데이터들을 두 개 점 중 가까운 곳으로 할당한다. (d)에서는 그렇게 군집이 지정된 상태로 중심점을 업데이트 한다. 그리고 (e)에서는 업데이트 된 중심점과 각 데이터들의 거리를 구해서 군집을 다시 할당하는 거다.

이걸 계속 반복한다.

아무튼 이렇게 군집화를 해놓으면 새로운 데이터가 들어와도 그게 어떤 군집에 속할지 할당해줄 수 있게 되는 셈이다.

만약 두 점 사이의 거리에 대해 좀 더 자세히 알고 싶다면 이 포스팅을 참고하자.

scikit-learn 사용법

파이썬 라이브러리 scikit-learn을 사용하면 K-means를 매우 쉽게 적용해볼 수 있다.

K값, 군집의 개수 정하기

군집화를 하기 위해서는 몇개의 군집이 적절할지 결정해야 하는데, 그러려면 일단 "좋은 군집"이란 무엇인지 정의할 수 있어야 한다.

만약 군집화가 잘 되었으면 각 군집의 샘플이 가까운 거리에서 오밀조밀하게 묶일 거다. 군집 내의 데이터들이 얼마나 퍼져 있는지 (혹은 얼마나 뭉쳐있는지) 응집도는 inertia값으로 확인한다. inertia는 각 데이터로부터 자신이 속한 군집의 중심까지의 거리를 의미하기 때문에 inertia값이 낮을수록 군집화가 더 잘됐다고 볼 수 있는 거다.

scikit-learn으로 모델을 만들었다면 이렇게 찍어보면 끝이다.

그래서 군집의 개수, 즉 k 값을 바꿔가면서 inertia를 그래프로 표시하면 보통 이런 모양새가 나온다.

k값이 증가하면 inertia 값은 감소하다가 어느정도 수준이 되면 거의 변화를 안보이게 된다. 대부분의 경우 너무 많지 않은 군집으로 분류하면서도 inertia 값이 작은 상태. 이게 그나마 최선이 될 거다. 위의 그래프를 예로 들면 최적의 클러스터 수는 3으로 보인다.

출처
https://hleecaster.com/ml-kmeans-clustering-concept/

 

반응형

이전 포스팅에서 분류에 널리 사용되는 머신러닝 알고리즘 의사결정나무(Decision Tree)에 대해 알아보았다.
의사결정나무는 매우 훌륭한 모델이지만, 학습 데이터에 오버피팅하는 경향이 있다. 가지치기(pruning)같은 방법을 통해 그런 부작용을 최소화하는 전략이 있긴하나 역시나 좀 부족하다.

그래서 본 포스팅에서는 의사결정트리의 오버피팅 한계를 극복하기 위한 전략으로 **랜덤 포레스트(RandomForest)라는 방법을 아주 쉽고 간단하게 소개하고자 한다.

내용이 짧으니 후딱 살펴보자.


랜덤 포레스트란

랜덤 포레스트는 분류, 회귀 분석 등에 사용되는 앙상블 학습 방법의 일종으로, 훈련 과정에서 구성한 다수의 결정 트리로부터 분류 또는 평균 예측치(회귀)를 출력함으로써 동작한다.

('무작위 숲'이라는 이름처럼) 랜덤 포레스트는 훈련을 통해 구성해놓은 다수의 나무들로부터 분류 결과를 취합해서 결론을 얻는, 일종의 인기 투표같은 거다.

아래 그림처럼.

물론 몇몇의 나무들이 오버피팅을 보일 수는 있지만 다수의 나무를 기반으로 예측하기 때문에 그 영향력이 줄어들게 되어 좋은 일반화 성능을 보인다.

이렇게 좋은 성능을 얻기 위해 다수의 학습 알고리즘을 사용하는 걸 앙상블(ensemble) 학습법이라고 부른다.

일단 랜덤 포레스트에서 각 나무들을 어떻게 생성하는지 알아야 한다. 결론부터 얘기하면.. 배깅(bagging)이라는 프로세스를 통해 나무를 만든다.

배깅(Bagging)

학습 데이터 세트에 총 1000개의 행이 있다고 해보자. 그러면 임의로 100개씩 행을 선택해서 의사결정트리를 만든게 배깅(bagging)이다. 물론 이런 식으로 트리를 만들면 모두 다르겠지만 그래도 어쨌거나 학습 데이터의 일부를 기반으로 생성했다는 게 중요하다.

그리고 이 때 중복을 허용해야 한다는 걸 기억하자.

1000개의 행이 있는 가방(bag)에서 임의로 100개를 뽑아 첫 번째 트리를 만들고 그 100개의 행은 가방에 도로 집어 넣는다. 그리고 다시 1000개의 행에서 또 임의로 100개를 뽑아 두 번째 트리를 만든 후 다시 가방에 집어넣고 뭐 이런 식.

Bagging Features

여기에 트리를 만들 때 사용될 속성(feature)들을 제한함으로써 각 나무들에 다양성을 줘야 한다.

원래는 트리를 만들 때 모든 속성들을 살펴보고 정보 획득량이 가장 많은 속성을 선택해서 그걸 기준으로 데이터를 분할했다. 그러나 이제는 각 분할에서 전체 속성들 중 일부만 고려하여 트리를 작성하도록하는 전략이다.

예를 들면 총 25개의 속성이 있는데, 그 중 5개의 속성만 뽑아서 살펴본 후 그 중 정보 획득량이 가장 높은 걸 기준으로 데이터를 분할하는 거다. 그 다음 단계에서도 다시 임의로 5개만 선택해서 살펴보고.. 이런 식이다.

그렇다면 몇개씩 속성을 뽑는 게 좋을까. 위 예처럼 총 속성이 25개면 5개, 즉 전체 속성 개수의 제곱근만큼 선택하는게 가장 좋다고, 경험적으로 그렇게 나타난다고 한다. (일종의 a rule of thumb이다.)

이제 서로 다른 트리를 만들 수 있게 되었으니 이것들을 모아 '숲'을 이루도록 하면 되는거다.

scikit-learn 사용법

랜덤 포레스트는 파이썬 라이브러리 scikit-learn을 사용하면 쉽게 구현할 수 있다.

sklearn.ensemble 모듈에서 RandomForestClassifier를 불러오면 된다. 단, 숲을 만들 때 나무의 개수 n_estimators라는 파라미터로 지정해주어야 한다.

RandomForestClassifier는 의사결정나무 DecisionTreeClassifier와 거의 똑같다. 당연히 .fit(), .predict(), .score()같은 메서드를 사용할 수 있다.

요약

  • 랜덤 포레스트는 앙상블 머신러닝 모델이다. 다수의 의사결정 트리를 만들고, 그 나무들의 분류를 집계해서 최종적으로 분류한다.
  • 오버피팅을 피하기 위해 임의(random)의 숲을 구성하는 거다. 다수의 나무들로부터 분류를 집계하기 때문에 오버피팅이 나타나는 나무의 영향력을 줄일 수 있다.
  • 랜덤 포레스트는 학습 데이터 세트에서 임의로 하위 데이터 세트를 추출하여 생성된다. 중복을 허용하기 때문에 단일 데이터가 여러번 선택될 수도 있다. 이 과정을 배깅(bagging)이라고 한다.
  • 나무를 만들 때는 모든 속성(feature)들에서 임의로 일부를 선택하고 그 중 정보 획득량이 가장 높은 것을 기준으로 데이터를 분할한다. 만약 데이터 세트에 n개의 속성이 있는 경우 n제곱근 개수만큼 무작위로 선택하는 것이 일반적이다.

출처
https://hleecaster.com/ml-random-forest-concept/

반응형

이전 포스팅에서 나이브 베이즈를 사용해서 텍스트를 어떻게 분류할 수 있는지 정말 간단한 예제를 통해 살펴보았다.

이번에는 파이썬 머신러닝 라이브러리 scikit-learn에서 실제로 어떻게 구현하고 동작하는지 코드를 알아볼 차례.

scikit-learn 사용법

1. CounterVectorizer

scikit-learn에서 Naive Bayes 분류기를 사용하기 전에 일단 자연어(텍스트)로 이루어진 문서들을 1과 0 밖에 모르는 컴퓨터가 이해할 수 있는 형식으로 변환해야 할 거다. feature extraction, 어휘(특성) 추출 과정이라 볼 수 있다.

.fit()

일단 CounterVectorizer라는 객체를 만든 후, fit() 메소드를 호출해서 학습 데이터 세트에 등장하는 어휘를 가르쳐놓아야 한다.

예를 들어 아래와 같이 코드를 작성하면

"첫번째", "문서", "테스트", "두번째" 이렇게 총 4개의 어휘를 학습한 CountVectorizer를 만들게 된다.

못 믿겠다면 vectorizer.vocabulary_를 출력해보면 된다. 이렇게 뜰 거다.

고유한 어휘가 딕셔너리처럼 각각의 인덱스를 가지고 있다.

.transform()

어휘 사전을 만들었으니 이제 .transform() 메서드를 호출할 수 있다. .transform()은 문자열 목록을 가져와 미리 학습해놓은 사전을 기반으로 어휘의 빈도를 세주는 거다.

아래와 같이 공백으로 구분되는 문자열로 가져와서 넣어주면

counts에는 각 어휘가 등장한 빈도수가 저장되는 거다. counts.toarray()를 출력해보면 [[1 0 1 2]]와 같은 array를 볼 수 있다. 위에서 학습된 인덱스에 따라 개수를 세주었다. 그리고 "직접"이라는 단어는 애초에 사전에 등록된 단어가 아니기 때문에 고려하지 않는다.

이렇게 텍스트를 컴퓨터가 이해할 수 있는 숫자로 변환시켜놓은 셈이니 이제 나이브 베이즈를 본격적으로 사용해볼 수 있다.

2. MultinomialNB

나이브 베이즈 분류기를 학습시킬 때는 당연히 2개의 파라미터가 필요하다. 위에서 여러 문서들을 .transform() 해놓은 문서-단어 행렬과 그 문서들이 어떤 분류에 속하는지 레이블을 준비해서 넣어주면 된다.

이제 새로운 문서가 등장했을 때 어떤 분류로 속할지 예측을 할 수 있다.

단! 텍스트를 바로 넣는다고 되는 게 아니다. 위에서 모델을 학습시키기 위해 학습 데이터를 문서-단어 행렬로 transform 한 것처럼 새로운 텍스트도 transform해서 넣어줘야 한다.

.predict_proba()는 각 레이블로 분류될 확률을 돌려준다. 베이즈 정리로 해당 문서가 어떤 분류에 속할 확률을 각각 다 계산할 수 있기 때문이다.


나이브 베이즈 분류기를 구현하는 방법은 일단 여기까지다.

주요 내용을 다시 간단히 요약해보자.

요약

  • 베이즈 정리에서 사용되는 확률을 계산하려면 레이블이 지정된 데이터 세트가 필요하다.
  • 데이터 세트에 포함된 문서에서 등장하는 어휘들이 곧 특성(feature)이다. 그리고 베이즈 정리를 적용하기 위해 이 특성(어휘)들은 모두 독립적인 것으로 가정한다.
  • 베이즈 정리를 사용하면 어떤 문서가 있을 때 그것이 특정 레이블에 속할 확률, P(레이블|문서)를 계산할 수 있으며, 이 중 확률이 가장 높은 레이블로 예측을 하게 된다.

뭐 이렇게 간단해보이긴 하지만 사실 텍스트(문서)를 나이브 베이즈로 제대로 분류하려면 그 전에 꽤 복잡한 과정을 거쳐 데이터를 잘 가공하는 게 정말 중요하다. garbage in, garbage out이라는 거..

뭐 일단 간단하고 일반적인 작업으로는 이런 게 있겠다.

  • 문장 부호, 불용어 등을 제거한다.
  • 모든 단어를 소문자로 통일한다. (한국어는 해당사항 없지만)
  • ngram 모델을 사용한다. 예를 들어 "이 음식은 너무 맛있다"는 문서가 있다면 여기서 feature는 "이", "음식은", "너무", "맛있다"가 될 것이다. 그러나 만약 bigram 모델을 사용하면 "이 음식은", "음식은 너무", "너무 맛있다"가 된다. 나이브 베이즈에서는 애초에 각 어휘들이 독립적이라고 가정하는데 이렇게 bigram 모델을 사용하면 연달아 등장하는 어휘를 고려하는 셈이니 조금 나아지는 편이다.
  • 형태소 단위로 쪼개서 집어 넣는다. 예를 들면 "공부하다", "공부했다", "공부하는" 등의 다양한 어휘가 등장할 때 이것들 각각을 어휘로 보지 않고 "공부하-"라는 대표형으로 묶어서 보는게 효과적이기 때문이다.
  • 특정 품사들만 선택해서 사용한다. 형태소 분석을 하면 각 어휘가 어떤 품사를 지니는지(태깅) 알 수 있기 때문에 이걸 바탕으로 일반명사, 고유명사, 형용사, 동사, 일반부사 위주로 사용하면 결과가 더 좋을 수도 있다. 물론 케바케다. 게다가 형태소를 나누는 기준이나 체계도 딱 정해져 있는 건 아니라..

출처
https://hleecaster.com/ml-naive-bayes-classifier-example/

반응형

나이브 베이즈 분류기(Naive Bayes Classifier)는 '베이즈 정리'를 활용하여 분류를 수행하는 머신러닝 지도학습 알고리즘이다. 특히 텍스트(문서)의 분류에 많이 사용되는데, 실제로 어떤 계산을 통해 분류하게 되는지 그 과정을 최대한 쉽게 소개해본다.

베이즈 정리

'베이즈 정리'에 대해서는 이 포스팅을 보자.

공식을 다시 살펴보자.

B라는 조건이 주어졌을 때 A의 확률을 구하는 거다.

그런데 여기서 만약 B를 데이터라고 생각하고, A를 레이블이라고 생각하면 일종의 분류기가 되는 셈이다.

B라는 데이터가 주어졌을 때 A라는 레이블로 분류될 확률을 계산하는 거니까.

본 포스팅에서는 어떤 쇼핑몰에 새로 작성된 리뷰를 읽고, 그게 긍정 리뷰인지 부정 리뷰인지 분류하는 예를 살펴보자. (텍스트를 분류하는 것이기 때문에 조금 생소할 수도 있다.)

"침대가 정말 부드럽다"라는 텍스트가 positive 리뷰인지 분류하려면 베이즈 정리를 바탕으로 계산한다.

이 식을 구성하는 것들을 하나씩 계산해보자.

P(positive)는 전체 리뷰 문서 중 긍정 리뷰의 비율이다. 간단하다.

P(review | positive)는 "침대가 정말 부드럽다"라는 review가 있을 때 이 텍스트를 구성하는 각 단어들이 positive 리뷰에서 등장할 확률을 의미한다.

그런데 이걸 찾으려면 일단 "각 단어들이 등장할 확률은 독립적이다"라는 가정을 해야 한다. 한 단어가 등장하는 게 다른 단어가 나타날 확률에 영향을 미치지 않는 뜻이다.

물론 실제 상황에서는 이럴 가능성이 매우 적으니까 엄밀히 말하면 옳은 가정은 아니다. 그런데 이런 무리한 가정에도 불구하고 나이브 베이즈가 매우 좋은 성능을 보이기 때문에 그냥 쓰는 거다. 그래서 나이브(naive)라는 표현을 붙여 놓은 거다. 순진한.. 뭔가 좀 투박하거나 어설픈..?

아무튼 그래서 이렇게 계산하면 된다.

P("침대가 정말 부드럽다" | positive) = P("침대가" | positive)  P("정말" | positive)  P("부드럽다" | positive)

각 단어가 positive 리뷰에서 등장할 확률을 곱해주는 거다.

예를 들어 P("침대가" | positive)는 positive 리뷰 데이터 세트 전체 단어 개수 중 "침대가"라는 단어 개수의 비율이 될 거다.

스무딩 (smoothing)

그러나 만약 "침대가"라는 단어가 긍정 리뷰 데이터 세트에서 한 번도 등장하지 않은 단어라면 어떻게 될까?
이 경우 P("침대가" | positive) 값은 0이 되기 때문에 P("이 침대는 정말 부드럽다" | positive)까지 0이 되어 버린다. 아주 희귀한(?) 단어나 오타 같은 게 있을 때 특히 문제다.

그래서 이 문제를 해결하기 위해 스무딩(smoothing)이라는 기술을 사용해야 한다. 뭐 별 건 아니다. 분자에는 1을 더하고 분모에는 N을 더해서 부드럽게(?) 만들어주는 거다. 여기서 N은 데이터 세트에 등장하는 고유한 단어 개수다.

아무튼 여기까지 하면 분자는 다 구할 줄 아는 거다.

이제 분모를 살펴보자.

P(review)는 사실 위에서 구한 P(review | positive)랑 똑같이 생각하면 된다. 리뷰 텍스트가 positive라고 가정하지 않는다는 차이만 있을 뿐이니까.

그리고 어차피 여기서는 positive 아니면 negative로 분류될 거라서, 각각의 레이블로 분류될 확률을 계산하고 비교하게 될 텐데.. 아래 식을 보면 알겠지만

분모가 어차피 동일하다. 무시해도 된다는 얘기다

여기까지 계산 할 줄 알면 일단 원리는 다 이해한 셈이다.

출처
https://hleecaster.com/ml-naive-bayes-classifier-concept/

반응형

+ Recent posts