외로운 Nova의 작업실
지도 학습 - 의사결정 트리 본문
- 의사 결정 트리
의사결정 트리는 데이터 분류 및 회귀에 사용되는 지도학습 알고리즘입니다. 의사결정 트리의 가장 큰 장점은 다른 알고리즘에 비해 결괏값이 왜 어떻게 나왔는지 이해하기 쉽다는 것입니다. 또한 수학적인 지식이 없어도 결과를 해석하고 이해하기 쉬우며, 수치 데이터 및 범주 데이터에 모두 사용 가능하다는 장점이 있습니다.
단점으로는 과대적합의 위험이 높다는 것입니다. 의사결정 트리 학습시 적절한 리프 노드의 샘플 개수와 트리의 깊이에 제한을 둬서 학습 데이터에 너무 모델이 치우치지 않게 주의해야합니다.
- 의사 결정 트리 특징
의사결정트리로 사람의 성별이 무엇인지 맞춰본다고하면 아래처럼 한번의 질문으로 바로 맞출 수 있을 것입니다.
이러한 질문을 의미있는 질문이라고 하며 이러한 질문을 가장 먼저 해야합니다. 그래야 좋은 알고리즘이 됩니다. 만약 의미없는 질문을 하나 넣는다면 아래처럼될 것 입니다.
긴생머리에대한 질문은 의미없는 질문이 될 것입니다.
- 실습
위도 경도를 가지고 주어진 동의 위치가 서울 강동인지 강서인지 강남인지 강북인지 맞추는 AI를 의사결정트리를 이용해 학습시켜보고 결과를 확인해보겠습니다.
아래는 강동,강남,강서,강북에대한 위치 학습데이터입니다.
district_dict_list = [
{'district': 'Gangseo-gu', 'latitude': 37.551000, 'longitude': 126.849500, 'label':'Gangseo'},
{'district': 'Yangcheon-gu', 'latitude': 37.52424, 'longitude': 126.855396, 'label':'Gangseo'},
{'district': 'Guro-gu', 'latitude': 37.4954, 'longitude': 126.8874, 'label':'Gangseo'},
{'district': 'Geumcheon-gu', 'latitude': 37.4519, 'longitude': 126.9020, 'label':'Gangseo'},
{'district': 'Mapo-gu', 'latitude': 37.560229, 'longitude': 126.908728, 'label':'Gangseo'},
{'district': 'Gwanak-gu', 'latitude': 37.487517, 'longitude': 126.915065, 'label':'Gangnam'},
{'district': 'Dongjak-gu', 'latitude': 37.5124, 'longitude': 126.9393, 'label':'Gangnam'},
{'district': 'Seocho-gu', 'latitude': 37.4837, 'longitude': 127.0324, 'label':'Gangnam'},
{'district': 'Gangnam-gu', 'latitude': 37.5172, 'longitude': 127.0473, 'label':'Gangnam'},
{'district': 'Songpa-gu', 'latitude': 37.503510, 'longitude': 127.117898, 'label':'Gangnam'},
{'district': 'Yongsan-gu', 'latitude': 37.532561, 'longitude': 127.008605, 'label':'Gangbuk'},
{'district': 'Jongro-gu', 'latitude': 37.5730, 'longitude': 126.9794, 'label':'Gangbuk'},
{'district': 'Seongbuk-gu', 'latitude': 37.603979, 'longitude': 127.056344, 'label':'Gangbuk'},
{'district': 'Nowon-gu', 'latitude': 37.6542, 'longitude': 127.0568, 'label':'Gangbuk'},
{'district': 'Dobong-gu', 'latitude': 37.6688, 'longitude': 127.0471, 'label':'Gangbuk'},
{'district': 'Seongdong-gu', 'latitude': 37.557340, 'longitude': 127.041667, 'label':'Gangdong'},
{'district': 'Dongdaemun-gu', 'latitude': 37.575759, 'longitude': 127.025288, 'label':'Gangdong'},
{'district': 'Gwangjin-gu', 'latitude': 37.557562, 'longitude': 127.083467, 'label':'Gangdong'},
{'district': 'Gangdong-gu', 'latitude': 37.554194, 'longitude': 127.151405, 'label':'Gangdong'},
{'district': 'Jungrang-gu', 'latitude': 37.593684, 'longitude': 127.090384, 'label':'Gangdong'}
]
train_df = pd.DataFrame(district_dict_list)
train_df = train_df[['district', 'longitude', 'latitude', 'label']]
아래는 검증데이터로 사용할 동(경도, 위도)에대한 정보입니다.
dong_dict_list = [
{'dong': 'Gaebong-dong', 'latitude': 37.489853, 'longitude': 126.854547, 'label':'Gangseo'},
{'dong': 'Gochuk-dong', 'latitude': 37.501394, 'longitude': 126.859245, 'label':'Gangseo'},
{'dong': 'Hwagok-dong', 'latitude': 37.537759, 'longitude': 126.847951, 'label':'Gangseo'},
{'dong': 'Banghwa-dong', 'latitude': 37.575817, 'longitude': 126.815719, 'label':'Gangseo'},
{'dong': 'Sangam-dong', 'latitude': 37.577039, 'longitude': 126.891620, 'label':'Gangseo'},
{'dong': 'Nonhyun-dong', 'latitude': 37.508838, 'longitude': 127.030720, 'label':'Gangnam'},
{'dong': 'Daechi-dong', 'latitude': 37.501163, 'longitude': 127.057193, 'label':'Gangnam'},
{'dong': 'Seocho-dong', 'latitude': 37.486401, 'longitude': 127.018281, 'label':'Gangnam'},
{'dong': 'Bangbae-dong', 'latitude': 37.483279, 'longitude': 126.988194, 'label':'Gangnam'},
{'dong': 'Dogok-dong', 'latitude': 37.492896, 'longitude': 127.043159, 'label':'Gangnam'},
{'dong': 'Pyoungchang-dong', 'latitude': 37.612129, 'longitude': 126.975724, 'label':'Gangbuk'},
{'dong': 'Sungbuk-dong', 'latitude': 37.597916, 'longitude': 126.998067, 'label':'Gangbuk'},
{'dong': 'Ssangmoon-dong', 'latitude': 37.648094, 'longitude': 127.030421, 'label':'Gangbuk'},
{'dong': 'Ui-dong', 'latitude': 37.648446, 'longitude': 127.011396, 'label':'Gangbuk'},
{'dong': 'Samcheong-dong', 'latitude': 37.591109, 'longitude': 126.980488, 'label':'Gangbuk'},
{'dong': 'Hwayang-dong', 'latitude': 37.544234, 'longitude': 127.071648, 'label':'Gangdong'},
{'dong': 'Gui-dong', 'latitude': 37.543757, 'longitude': 127.086803, 'label':'Gangdong'},
{'dong': 'Neung-dong', 'latitude': 37.553102, 'longitude': 127.080248, 'label':'Gangdong'},
{'dong': 'Amsa-dong', 'latitude': 37.552370, 'longitude': 127.127124, 'label':'Gangdong'},
{'dong': 'Chunho-dong', 'latitude': 37.547436, 'longitude': 127.137382, 'label':'Gangdong'}
]
test_df = pd.DataFrame(dong_dict_list)
test_df = test_df[['dong', 'longitude', 'latitude', 'label']]
학습 데이터의 갯수를 확인해보겠습니다.
# 현재 가지고 있는 데이터에서, 레이블의 갯수를 확인
train_df.label.value_counts()
각 5개임을 알 수 있습니다.
이제 데이터 시각화를 해보겠습니다.
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# 경도, 위도에 따른 데이터 시각화
sns.lmplot('longitude', 'latitude', data=train_df, fit_reg=False, # x 축, y 축, 데이터, 라인 없음
scatter_kws={"s": 150}, # 좌표 상의 점의 크기
markers=["o", "x", "+", "*"],
hue="label")
# title
plt.title('district visualization in 2d plane')
각 데이터들이 섞여있음을 알 수 있습니다.
이제 데이터를 다듬어보겠습니다. 구이름과 동이름은 없애겠습니다.
train_df.drop(['district'], axis=1, inplace = True)
test_df.drop(['dong'], axis=1, inplace = True)
X_train = train_df[['longitude', 'latitude']]
y_train = train_df[['label']]
X_test = test_df[['longitude', 'latitude']]
y_test = test_df[['label']]
이제 차트 관련 설정을 해주겠습니다.
from sklearn import tree
import numpy as np
import matplotlib.pyplot as plt
from sklearn import preprocessing
def display_decision_surface(clf,X, y):
# 챠트의 범위가 모든 학습 데이터를 포함하도록 설정
x_min = X.longitude.min() - 0.01
x_max = X.longitude.max() + 0.01
y_min = X.latitude.min() - 0.01
y_max = X.latitude.max() + 0.01
# 파라미터 설정
n_classes = len(le.classes_)
plot_colors = "rywb"
plot_step = 0.001
xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
np.arange(y_min, y_max, plot_step))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu)
# 학습 데이터를 챠트에 표시
for i, color in zip(range(n_classes), plot_colors):
idx = np.where(y == i)
plt.scatter(X.loc[idx].longitude,
X.loc[idx].latitude,
c=color,
label=le.classes_[i],
cmap=plt.cm.RdYlBu, edgecolor='black', s=200)
# 챠트 제목
plt.title("Decision surface of a decision tree",fontsize=16)
# 챠트 기호 설명
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0., fontsize=14)
# x축의 이름과 폰트 크기 설정
plt.xlabel('longitude',fontsize=16)
# y축의 이름과 폰트 크기 설정
plt.ylabel('latitude',fontsize=16)
# 챠트 크기 설정
plt.rcParams["figure.figsize"] = [7,5]
# 챠트 폰트 크기 설정
plt.rcParams["font.size"] = 14
# x축 좌표상의 폰트 크기 설정
plt.rcParams["xtick.labelsize"] = 14
# y축 좌표상의 폰트 크기 설정
plt.rcParams["ytick.labelsize"] = 14
# 챠트 그리기
plt.show()
이제 별도의 파라미터(리프 갯수, 노드 깊이)없이 의사결 표면을 시각화해보겠습니다.
# pyplot은 숫자로 표현된 레이블을 시각화할 수 있음
# LabelEncoder로 레이블을 숫자로 변경
le = preprocessing.LabelEncoder()
y_encoded = le.fit_transform(y_train)
clf = tree.DecisionTreeClassifier(random_state=35).fit(X_train, y_encoded)
display_decision_surface(clf,X_train, y_encoded)
과대적합으로 인해 강동사이에 강북이 있는 것을 확인할 수 있습니다. 이는 과대적합으로 인한 현상으로 파라미터를 조절해서 과대적합을 줄여줍니다.
clf = tree.DecisionTreeClassifier(max_depth=4,
min_samples_split=2,
min_samples_leaf=2,
random_state=70).fit(X_train, y_encoded.ravel())
display_decision_surface(clf,X_train, y_encoded)
그러면 적합한 의사결정 표면을 구할 수 있습니다.
- 테스트
이제 모델을 테스트해보겠습니다.
from sklearn.metrics import accuracy_score
pred = clf.predict(X_test)
print("accuracy : " + str( accuracy_score(y_test.values.ravel(), le.classes_[pred])) )
완벽한 1.0이 뜨는 것을 알 수 있습니다.
'AI > machine-learning' 카테고리의 다른 글
앙상블 기법 (0) | 2023.11.03 |
---|---|
지도 학습 - 나이브 베이즈 (0) | 2023.11.02 |
지도학습 - SVM 서포트 벡터 머신 이론 및 실습 (0) | 2023.10.08 |
지도 학습 - knn 알고리즘 이론 및 실습 (1) | 2023.10.07 |
머신러닝 용어 정리 (1) | 2023.10.07 |