AI/딥러닝파이토치교과서

[ML] 3. 머신 러닝 핵심 알고리즘: 지도학습

inthyes 2024. 3. 2. 21:53

3.1 지도학습

지도 학습 : 정답을 컴퓨터에 미리 알려주고 데이터를 학습시키는 방법

구분 분류 회귀
데이터 유형 이산형 데이터 연속성 데이터
결과 훈련 데이터의 레이블 중 하나를 예측 연속된 값을 예측
예시 학습 데이터를 A-B-C 그룹 중 하나로 매핑 결과값이 어떤 값이든 나올 수 있음

3.1.1 K-최근접 이웃

왜 사용할까?

→ 주어진 데이터에 대한 분류

 

언제 사용하면 좋을까?

→ K-최근접 이웃은 직관전이며 사용하기 쉽기 때문에 초보자가 쓰기에 좋다. 또한, 훈련 데이터를 충분히 확보할 수 있는 환경에서 사용하면 좋다.

 

K-최근접 이웃(K-nearst neighbor)은 새로운 입력을 받았을 때 기존 클러스터에서 모든 데이터와 인스턴스 기반 거리를 측정한 후 가장 많은 속성을 가진 클러스터에 할당하는 분류 알고리즘이다.

과거 데이터를 사용하여 미리 분류 모델을 만드는 것이 아니라, 과거 데이터에 저장해 두고 필요할 때마다 비교를 수행하는 방식이다.

따라서 K값의 선택에 따라 새로운 데이터에 대한 분류 결과가 달라질 수 있음에 유의해야 한다.

 

위 이미지에 대해 설명하자면

새로운 입력 1은 주변 범주 세 개가 주황색이기 때문에 주황색으로 분류된다.

새로운 입력 2는 주변 범주 두 개가 주황색, 한 개가 녹색이므로 주황색으로 분류된다.

새로운 입력 3은 주변 범주 두 개가 녹색, 한 개가 주황색이므로 녹색으로 분류된다.

 

K-최근접 이웃 실습

 

1. 라이브러리 호출 및 데이터 준비

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn import metrics

names = ['sepal-length','sepal-width','petal-length','petal-width','Class']

dataset = pd.read_csv('iris.data', names = names)

 

2. 훈련과 테스트 데이터셋 분리

X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, 4].values

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20)

from sklearn.preprocessing import StandardScaler

# StandardScaler 객체 생성
s = StandardScaler()

# 훈련 데이터에 대해 StandardScaler를 적합하고 변환
X_train = s.fit_transform(X_train)

# 테스트 데이터에 대해 훈련 데이터에 적합한 StandardScaler를 사용하여 변환
X_test = s.transform(X_test)

 

3. 모델 생성 및 훈련

K = 50인 K-최근접 이웃 모델을 생성한 후 모델을 훈련한다.

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 50)
knn.fit(X_train, y_train)

 

4. 모델 정확도

from sklearn.metrics import accuracy_score
y_pred = knn.predict(X_test)
print("정확도 : {}".format(accuracy_score(y_test, y_pred)))

5. 최적의 K 찾기

for문을 이용하여 K값을 1부터 10까지 순환하면서 최적의 K값과 정확도를 찾는다.

k = 10
acc_array = np.zeros(k)

for k in np.arange(1, k+1, 1):
  # for문을 반복하면서 k값 변경
  classifier = KNeighborsClassifier(n_neighbors = k).fit(X_train, y_train)
  y_pred = classifier.predict(X_test)
  acc = metrics.accuracy_score(y_test, y_pred)
  acc_array[k-1] = acc

max_acc = np.amax(acc_array)
acc_list = list(acc_array)
k = acc_list.index(max_acc)
print("정확도", max_acc, "으로 최적의 k는", k+1, "입니다.")

K값이 50일 때 정확도가 83%였다면 K값이 1일 때는 정확도가 100%로 높아졌음을 확인할 수 있다.

K-최근접 이웃 알고리즘은 K값에 따라 성능이 달라질 수 있으므로 초기 설정이 매우 중요하다.


3.1.2 서포트 벡터 머신

왜 사용할까?

→ 주어진 데이터에 대한 분류를 하기 위해

 

언제 사용하면 좋을까?

→ 서포트 벡터 머신은 커널만 적절히 선택한다면 정확도가 상당히 좋기 때문에 정확도를 요구하는 분류 문제를 다룰 때 사용하면 좋다. 또한 텍스트를 분류할 때도 많이 사용한다.

 

서포트 벡터 머신(Support Vector Machine, SVM)은 분류를 위한 기준선을 정의하는 모델이다.

즉, 분류되지 않은 새로운 데이터가 나타나면 결정 경계(기준선)를 기준으로 경계의 어느 쪽에 속하는지 분류하는 모델이다.

서포트 벡터 머신 이해에 중요한 "결정 경계"는 데이터를 분류하기 위한 기준선을 말한다.

 

결정 경계는 데이터가 분류된 클래스에서 최대한 멀리 떨어져 있을 때 성능이 가장 좋다. 

마진은 결정 경계와 서포트 벡터 사이의 거리르 의미한다. 

서포트 벡터란 결정 경계와 가까이 있는 데이터들을 의미하며 이 데이터들이 경계를 정의하는 결정적인 역할을 한다고 보면 된다.

최적의 결정 경계는 마진을 최대로 해야한다.

 

이를 종합하여 보면 서포트 벡터 머신은 데이터들을 올바르게 분리하면서 마진 크기를 최대화해야하는데, 이때 이상치를 허용하지 않는 것을 하드 마진이라고 하며, 이상치들이 마진 안에 포함되는 것을 허용한다면 소프트 마진이라고 한다.

 

서포트 벡터 머신 실습

 

1. 라이브러리 호출

from sklearn import svm
from sklearn import metrics
from sklearn import datasets
from sklearn import model_selection
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

 

TF_CPP_MIN_LOG_LEVEL이라는 환경 변수를 사용하여 로깅을 제어한다.

기본값은 0으로 모든 로그가 표시되며 INFO 로그를 필터링하려면 1, WARNING 로그를 필터링하려면 2, ERROR 로그를 추가로 필터링하려면 3으로 설정하면 된다.

 

2. iris 데이터를 준비하고 훈련과 테스트 데이터셋으로 분리

iris = datasets.load_iris()
X_train, X_test, y_train, y_test = model_selection.train_test_split(iris.data,
                                 iris.target,
                                 test_size = 0.6,
                                 random_state = 42)

 

3. SVM 모델에 대한 정확도

먼저 사이킷런으로 SVM 모델을 생성 및 훈련시킨 후 테스트 데이터셋을 이용한 예측을 수행한다.

# 선형 SVM 모델 생성
svm_model = svm.SVC(kernel='linear', C=1.0, gamma=0.5)

# 훈련 데이터로 SVM 모델 훈련
svm_model.fit(X_train, y_train)

# 테스트 데이터에 대한 예측
predictions = svm_model.predict(X_test)

# 정확도 계산
score = metrics.accuracy_score(y_test, predictions)
print('정확도 : {0:f}'.format(score))

SVM은 선형 분류와 비선형 분류를 지원한다. 비선형에 대한 커널은 선형으로 분류될 수 없는 선형으로 분류될 수 없는 데이터들 때문에 발생됐다. 비선형 문제를 해결하는 가장 기본적인 방법은 저차원 데이터를 고차원으로 보내는 것인데, 이는 많은 수학적 계산이 필요하기 때문에 성능에 문제를 줄 수 있다.

이를 해결하기 위해 도입된 것이 '커널 트릭(kernel trick)이다. 선형 모델을 위한 커널에는 선형 커널이 있고, 비선형을 위한 커널에는 가우시안 RBF 커널과 다항식 커널(poly)이가 있다. 가우시안 RBF 커널과 다항식 커널은 수학적 기교를 이용하는 것으로, 벡터 내적을 계산한 후 고차원으로 보내는 방법으로 연산량을 줄였다.

 

코드에 작성된 C값은 오류를 어느정도 허용할지 지정하는 파라미터이며, C값이 클수록 하드마진, 작을수록 소프트 마진이다. 

감마(gamma)는 결정 경계를 얼마나 유연하게 가져갈지 지정한다. 즉 훈련 데이터에 얼마나 민감하게 반응할지 지정하기 때문에  C와 개념이 비슷하다.

감마 값이 높으면 훈련 데이터에 많이 의존하기 때문에 결정 경계가 곡선 형태를 띠며 과적합을 초래할 수 있으니 주의해야 한다.


3.1.3 결정 트리

왜 사용할까?

→ 주어진 데이터에 대한 분류

 

언제 사용하면 좋을까?

→ 결정 트리는 이상치가 많은 값으로 구성된 데이터셋을 다룰 때 사용하면 좋다. 또한, 결정 과정이 시각적으로 표현되기 때문에 머신러닝이 어떤 방식으로 의사 결정을 하는지 알고 싶을 때 유용하다.

 

결정트리는 데이터를 분류하거나 결괏값을 예측하는 분석 방법이다. 결과 모델이 트리 구조이기 때문에 결정 트리라고 한다.

결정 트리는 데이터를 1차로 분류한 후 각 영역의 순도는 증가하고, 불순도와 불확실성은 감소하는 방향으로 학습을 진행한다.

순도가 증가하고 불확실성이 감소하는 것을 정보 이론에서는 정보 획득이라고 하며, 순도를 계산하는 방법에는 다음 두가지를 많이 사용한다.

 

엔트로피(entropy)

확률 변수의 불확실성을 수치로 나타낸것으로, 엔트로피가 높을수록 불확실성이 높다는 의미이다.

즉, 엔트로피 값이 0과 0.5라고 가정할 때 다음과 같은 도출이 가능하다.

엔트로피 = 0 = 불확실성 최소 = 순도 최대

엔트로피 = 0.5 = 불확실성 최대 = 순도 최소

 

레코드 m개가 A영역에 포함되어 있다면 엔트로피는 다음 식으로 정의된다.

 

지니 계수(Gini index)

불순도를 측정하는 지표로, 데이터의 통계적 분산 정도를 정량화해서 표현한 값이다. 즉 지니 계수는 원소 n개 중에서 임의로 두 개를 추출했을 때, 추출된 두 개가 서로 다른 그룹에 속해 있을 확률을 의미한다.

지니 계수는 다음 공식으로 구할 수 있으며 지니 계수가 높을수록 데이터가 분산되어 있음을 의미한다.

지니 계수는 로그를 계산할 필요가 없어 엔트로피보다 계산이 빠르기 때문에 결정 트리에서 많이 사용한다.

 

결정 트리 실습

 

1. 라이브러리 호출 및 데이터 준비

판다스를 이용하여 train.csv파일을 로드하여 df에 저장하고 train.csv 데이터의 상위 행 다섯 개를 출력한다.

import pandas as pd
df = pd.read_csv('train.csv', index_col = 'PassengerId')
print(df.head())

2. 데이터 전처리

# 승객의 생존 여부를 예측하기 위한 사용
df = df[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Survived']]
# 성별을 나타내는 'sex'를 0 또는 1의 정수값으로 변환
df['Sex'] = df['Sex'].map({'male':0, 'female':1})

# 값이 없는 데이터 삭제
df = df.dropna()
X = df.drop('Survived', axis = 1)
# 'Survived' 값을 예측 레이블로 사용
y = df['Survived']

 

3. 훈련과 테스트 데이터셋으로 분리

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 1)

 

4. 결정 트리 모델 생성

사이킷런에서 제공하는 결정 트리 라이브러리를 이용해 모델을 생성한다.

from sklearn import tree
model = tree.DecisionTreeClassifier()

 

5. 모델 훈련

model.fit(X_train, y_train)

6. 모델 예측

y_predict = model.predict(X_test)
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_predict)

 

7. 혼동 행렬을 이용한 성능 측정

from sklearn.metrics import confusion_matrix
pd.DataFrame(
    confusion_matrix(y_test, y_predict),
    columns = ['Predicted Not Survival', 'Predicted Survival'],
    index = ['True Not Survval', 'True Survival']
)

 

혼동 행렬 용어 정리

  • True Positive : 모델(분류기)이 '1'이라고 예측했는데 실제 값도 '1'인 경우
  • True Negative : 모델(분류기)이 '0'이라고 예측했는데 실제 값도 '0'인 경우
  • False Positive : 모델(분류기)이 '1'이라고 예측했는데 실제 값은 '0'인 경우
  • False Negative : 모델(분류기)이 '0'이라고 예측했는데 실제 값은 '1'인 경우

혼동 행렬을 이용하면 정밀도, 재현율, 정확도 같은 지표를 얻을 수 있다.

혼동 행렬을 바탕으로 모델의 훈련 결과를 확인해보면, 잘못된 예측보다는 정확한 예측의 수치가 더 높으므로 잘 훈련되었다고 할 수 있다.

 

이와 같이 주어진 데이터를 사용하여 트리 형식으로 데이터를 이진 분류(0 또는 1)해 나가는 방법이 결정 트리이며, 결정 트리를 좀 더 확대한 것(결정 트리르 여러 개 묶어 놓은 것)이 랜덤 포레스트(random forest)이다.


3.1.4 로지스틱 회귀와 선형 회귀

회귀 : 변수가 두 개 주어졌을 떄 한 변수에서 다른 변수를 예측하거나 두 변수의 관계를 규명하는 데 사용하는 방법

 

변수 유형

  • 독립 변수(예측 변수) : 영향을 미칠 것으로 예상되는 변수
  • 종속 변수(기준 변수) : 영향을 받을 것으로 예상되는 변수

이 때 두 변수 간 관계에서 독립 변수와 종속 변수의 설정은 논리적인 타당성이 있어야 한다.


로지스틱 회귀

왜 사용할까?

→ 주어진 데이터에 대한 분류

 

언제 사용하면 좋을까?

→ 로지스틱 회귀 분석은 주어진 데이터에 대한 확신이 없거나 향후 추가적으로 훈련 데이터셋을 수집하여 모델을 훈련시킬 수 있는 환경에서사용하면 유용

 

구분 일반적인 회귀 분석 로지스틱 회귀 분석
종속 변수 연속형 변수 이산형 변수
모형 탐색 방법 최소제곱법 최대우도법
모형 검정 F-테스트, t-테스트 X2 테스트

 

로지스틱 회귀 분석은 아래 절차에 따라 분석을 진행한다.

  • 1단계 : 각 집단에 속하는 확률의 추정치를 예측한다. 이 때 추정치는 이진 분류의 경우 집단 1에 속하는 확률 P(Y=1)로 구한다.
  • 2단계 : 분류 기준 값(cut-off)을 설정한 후 특정 범주로 분류한다.

로지스틱 회귀 실습

 

1. 라이브러리 호출 및 데이터 준비

%matplotlib inline
from sklearn.datasets import load_digits
# 숫자 데이터셋(digits)은 사이킷런에서 제공
digits = load_digits()

# digits 데이터셋의 형태(이미지가 1797개 있으며, 8*8 이미지의 64차원을 가짐)
print("Image Data Shape", digits.data.shape)

# 레이블(이미지의 숫자 정보) 이미지 1797개 있음
print("Label Data Shape", digits.target.shape)

2. digits 데이터셋의 시각화

import numpy as np
import matplotlib.pyplot as plt

plt.figure(figsize = (20, 4))
# 예시로 이미지 다섯 개만 확인
for index, (image, label) in enumerate(zip(digits.data[0:5], digits.target[0:5])):
  plt.subplot(1, 5, index + 1)
  plt.imshow(np.reshape(image, (8, 8)), cmap = plt.cm.gray)
  plt.title('Training: %i\n' %label, fontsize = 20)

 

3. 훈련과 테스트 데이터셋으로 분리 및 로지스틱 회귀 모델 생성

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(digits.data, digits.target, test_size = 0.25, random_state = 0)

from sklearn.linear_model import LogisticRegression
# 로지스틱 회귀 모델의 인스턴스 생성
logisticRegr = LogisticRegression()
# 모델 훈련
logisticRegr.fit(x_train, y_train)

 

4. 일부 데이터를 사용한 모델 예측

테스트 이미지에 대한 예측 결과를 넘파이 배열로 출력한다.

logisticRegr.predict(x_test[0].reshape(1, -1))
logisticRegr.predict(x_test[0:10])

 

5. 전체 데이터를 사용한 모델 예측

테스트 데이터셋 전체를 이용하여 정확도를 예측한다.

predictions = logisticRegr.predict(x_test)
score = logisticRegr.score(x_test, y_test)

print(score)

성능 측정 결과가 95%임을 확인할 수 있다.

 

6. 혼동 행렬 시각화

import numpy as np
import seaborn as sns
from sklearn import metrics

# 혼동 행렬(confusion_matrix)
cm = metrics.confusion_matrix(y_test, predictions)
plt.figure(figsize = (9, 9))

# heatmap으로 표현
sns.heatmap(cm, annot = True, fmt = ".3f", linewidths = .5, square = True, cmap = "Blues_r");
plt.ylabel('Actual label');
plt.xlabel('Predicted label');
all_sample_title = 'Accuracy Score : {0}'.format(score)
plt.title(all_sample_title, size = 15);
plt.show();

결과를 확인하기는 좋지만, 직관적으로 이해하기에는 난해하기 때문에 혼동 행렬은 단지 결과 확인용으로만 사용하는 것이 좋다.

 

선형 회귀

왜 사용할까?

→ 주어진 데이터에 대한 분류

 

언제 사용하면 좋을까?

→ 선형 회귀는 주어진 데이터에서 독립 변수(x)와 종속 변수(y)가 선형 관계를 가질 때 사용하면 유용하다. 또한, 복잡한 연산과정이 없기 때문에 컴퓨팅 성능이 낮은 환경에서 사용하면 좋다.

 

선형 회귀는 독립 변수 x를 사용하여 종속 변수 y의 움직임을 예측하고 설명하는 데 사용된다. 독립 변수 x는 하나일 수도 있고, 여러 개일 수도 있다. 하나의 x값으로 y값을 설명할 수 있다면 단순 선형 회귀라고 하며, x값이 여러 개라면 다중 선형 회귀라고 한다.

 

선형 회귀는 종속 변수와 독립 변수 사이의 관계를 설정하는 데 사용된다. 즉, 독립 변수가 변경되었을 때 종속 변수를 추정하는 데 유용하다. 반면 로지스틱 회귀는 사건의 확률(0 또는 1)을 확인하는 데 사용된다. 

위 그림과 같이 선형 회귀는 직선을 출력하고, 로지스틱 회귀는 S-커브를 출력한다.

 

로지스틱 회귀 실습

 

1. 라이브러리 호출

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as seabornInstance
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
%matplotlib inline

2. weather.csv 파일 불러오기

dataset = pd.read_csv('weather.csv')

 

3. 데이터 간 관계를 시간화로 표현

MinTemp와 MaxTemp 데이터 간 분포를 확인하고자 2D 그래프로 시각화한다.

dataset.plot(x='MinTemp', y = 'MaxTemp', style = 'o')
plt.title('MinTemp vs MaxTemp')
plt.xlabel('MinTemp')
plt.ylabel('MaxTemp')
plt.show()

4. 데이터를 독립 변수와 종속 변수로 분리하고 선형 회귀 모델 생성

데이터를 속성과 레이블로 나눈다. 속성은 독립 변수이며 레이블은 종속 변수이다.

따라서 MinTemp에 따라 MaxTemp를 예측하기 위해 x변수는 'MinTemp'로 구성하고, y변수는 'MaxTemp'로 구성한다.

X = dataset['MinTemp'].values.reshape(-1, 1)
y = dataset['MaxTemp'].values.reshape(-1, 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

regressor = LinearRegression()
regressor.fit(X_train, y_train)

 

5. 회귀 모델에 대한 예측

X_test의 실제 출력 값을 예측값과 비교한다.

y_pred = regressor.predict(X_test)
df = pd.DataFrame({'Actual': y_test.flatten(), 'Predicted': y_pred.flatten()})
df

6. 테스트 데이터셋을 사용한 회귀선 표현

plt.scatter(X_test, y_test, color = 'gray')
plt.plot(X_test, y_pred, color = 'red', linewidth = 2)
plt.show()

회귀선이 실제 데이터와 비슷한 것을 출력 그림을 통해 알 수 있다.

 

7. 선형 회귀 모델 평가

print('평균제곱법:', metrics.mean_squared_error(y_test, y_pred))
print('루트 평균제곱법:', np.sqrt(metrics.mean_squared_error(y_test, y_pred)))

루트 평균 제곱법(root mean squared)은 이름에서도 알 수 있듯 평균제곱법(mean squared)에 루트를 씌운 것이다.

루트 평균 제곱법 값(4.12)은 모든 기온 백분율에 대한 평균값(22.41)과 비교하여 10% 이상임을 알 수 있다. 따라서 모델 정확도는 높지 않지만 여전히 합리적으로 좋은 예측을 할 수 있을을 의미한다.