의사 결정 트리를 처음부터 구현하고 버섯이 식용인지 독성인지를 분류하는 작업에 적용합니다.
가이드 패키지 1개
import numpy as np
import matplotlib.pyplot as plt
from public_tests import *
%matplotlib inline
2 문제 설명
야생 버섯을 재배하고 판매하는 사업을 시작한다고 가정해 보겠습니다. 모든 버섯이 먹을 수 있는 것은 아니므로 해당 버섯의 물리적 특성에 따라 해당 버섯이 먹을 수 있는지 독성이 있는지 구분할 수 있기를 원합니다. 이 작업에 사용할 수 있는 기존 데이터가 있습니다. 판매하기에 안전한 버섯을 결정하는 데 이 데이터를 사용할 수 있습니까? 참고: 사용된 데이터 세트는 설명 목적으로만 사용됩니다. 이것은 식용 버섯을 식별하기 위한 지침이 아닙니다.
원-핫 인코딩된 데이터 세트 3개
갈색 모자 | 가늘어지는 줄기 모양 | 외로운 | 먹을 수 있는 |
---|---|---|---|
1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
0 | 1 | 1 | 0 |
0 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
0 | 1 | 0 | 1 |
1 | 0 | 0 | 0 |
그러므로, |
-
X_train
각 샘플에 대한 세 가지 기능 포함- 모자 색상(갈색 모자
1
의 ,0
빨간색 모자의 값) - 스템 수축("테이퍼형" 스템
1
의 ,0
"확장된" 스템의 값) - 혼자(
1
예 ,0
아니요 값)
- 모자 색상(갈색 모자
-
y_train
버섯은 먹을 수 있습니까?y = 1
식용을 의미y = 0
유독함을 나타냅니다.
X_train = np.array([[1,1,1],[1,0,1],[1,0,0],[1,0,0],[1,1,1],[0,1,1],[0,0,0],[1,0,1],[0,1,0],[1,0,0]])
y_train = np.array([1,1,0,0,1,0,0,1,1,0])
X_train[:5]
array([[1, 1, 1],
[1, 0, 1],
[1, 0, 0],
[1, 0, 0],
[1, 1, 1]])
4개의 결정 트리
이 실습에서는 제공된 데이터 세트를 기반으로 의사 결정 트리를 구축합니다.
-
의사 결정 트리를 구축하는 단계를 상기하십시오.
- 루트 노드에서 시작하는 모든 샘플 사용
- 가능한 모든 특징을 기준으로 분할 시 정보 획득을 계산하고 정보 획득이 가장 높은 특징 선택
- 선택한 기능을 기반으로 데이터 세트를 분할하고 트리의 왼쪽 및 오른쪽 분기를 만듭니다.
- 중지 기준이 충족될 때까지 분할 프로세스를 계속 반복합니다.
-
이 실습에서는 정보 이득이 가장 높은 기능을 사용하여 노드를 왼쪽 및 오른쪽 분기로 분할하는 다음 기능을 구현합니다.
- 노드의 엔트로피 계산
- 주어진 기능을 기반으로 노드에서 데이터 세트를 왼쪽 및 오른쪽 분기로 분할
- 주어진 기능을 분할할 때 정보 이득을 계산합니다.
- 정보 획득을 극대화하는 기능 선택
-
그런 다음 구현한 도우미 기능을 사용하여 중지 기준이 충족될 때까지 분할 프로세스를 반복하여 의사 결정 트리를 구축합니다.
- 이 실험에서 우리가 선택한 중지 기준은 최대 깊이를 2로 설정하는 것이었습니다.
4.1 엔트로피 계산
compute_entropy
먼저 노드에서 엔트로피(불순도 측정)를 계산하는 엔트로피(불순도 측정)라는 도우미 함수를 작성해야 합니다 .
y
이 함수는 이 노드의 예가 식용(1
)인지 독성(0
) 인지를 나타내는 numpy 배열( )을 허용합니다.
다음 기능을 완료하십시오 compute_entropy()
.
- p 1 p_1 계산피1, 이는 식용 예의 비율 입니다 (즉,
y
값 = in1
- 그런 다음 엔트로피를 계산하십시오.
H ( p 1 ) = − p 1 log 2 ( p 1 ) − ( 1 − p 1 ) log 2 ( 1 − p 1 ) H(p_1) = -p_1 \text{log}_2(p_1) - (1- p_1) \text{로그}_2(1- p_1)H ( 피1)=- 피1통나무2( 피1)-( 1-피1) 로그2( 1-피1)
- 알아채다
- 로그는 밑수 2 2를 사용합니다.2
- 편의상 0 log 2 ( 0 ) = 0 0\text{log}_2(0) = 00 로그2( 0 )=0 . 즉, if
p_1 = 0
또는p_1 = 1
이면 엔트로피를 다음으로 설정합니다.0
- 노드의 데이터가 비어 있지 않은지(ie ) 확인하고
len(y) != 0
비어 있으면 반환합니다.0
def compute_entropy(y):
entropy = 0
if len(y) != 0:
p1 = len(y[y==1])/len(y)
if p1 != 0 and p1 !=1:
entropy = -p1 * np.log2(p1)-(1-p1)*np.log2(1-p1)
else:
entropy = 0
return entropy
4.2 데이터셋 나누기
다음으로, 노드에서 데이터와 분할할 기능을 split_dataset
가져와 . 실습 후반부에서 분할이 얼마나 잘 작동하는지 계산하는 코드를 구현합니다.
- 이 함수는 학습 데이터, 이 노드에 있는 데이터 포인트의 인덱스 목록 및 분할할 기능을 사용합니다.
- 데이터를 분할하고 왼쪽 및 오른쪽 분기에 대한 인덱스의 하위 집합을 반환합니다.
- 예를 들어 루트 노드에서 시작하고(따라서
node_indices = [0,1,2,3,4,5,6,7,8,9]
) 분할 기능을 로 선택한다고 가정합니다0
. 즉, 예제에 갈색 모자가 있는지 여부입니다.- 그러면 함수의 출력은,
left_indices = [0,1,2,3,4,7,9]
,right_indices = [5,6,8]
- 그러면 함수의 출력은,
색인 | 갈색 모자 | 축소 핸들 모양 | 독립적인 | 먹을 수 있는 |
---|---|---|---|---|
0 | 1 | 1 | 1 | 1 |
1 | 1 | 0 | 1 | 1 |
2 | 1 | 0 | 0 | 0 |
삼 | 1 | 0 | 0 | 0 |
4 | 1 | 1 | 1 | 1 |
5 | 0 | 1 | 1 | 0 |
6 | 0 | 0 | 0 | 0 |
7 | 1 | 0 | 1 | 1 |
8 | 0 | 1 | 0 | 1 |
9 | 1 | 0 | 0 | 0 |
def split_dataset(X,node_indices,feature):
Lnode_indices = []
Rnote_indices = []
for i in node_indices:
if X[i][feature] ==1:
Lnode_indices.append(i)
else:
Rnote_indices.append(i)
return Lnode_indices,Rnote_indices
다음으로 훈련 데이터, 노드의 인덱스 및 분할할 기능을 information_gain
가져와서 .
계산하려면 아래에 표시된 compute_information_gain()
함수를
정보 이득 = H ( p 1 노드 ) − ( w 왼쪽 H ( p 1 왼쪽 ) + w 오른쪽 H ( p 1 오른쪽 ) ) \text{정보 이득} = H(p_1^\text{노드})- (w^ {\text{왼쪽}}H(p_1^\text{왼쪽}) + w^{\text{오른쪽}}H(p_1^\text{오른쪽}))정보 획득=H ( 피1노드)-( 승왼쪽 H(p1왼쪽)+승오른쪽 H(p1오른쪽))
안에:
- H ( p 1 노드 ) H(p_1^\text{노드})H ( 피1노드) 는 노드의 엔트로피입니다.
- H ( p 1 왼쪽 ) H(p_1^\text{왼쪽})H ( 피1왼쪽) 와H ( p 1 오른쪽 ) H(p_1^\text{오른쪽})H ( 피1오른쪽) 는 분할 후 왼쪽 및 오른쪽 가지의 엔트로피입니다.
- w 왼쪽 w^{\text{왼쪽}}승왼쪽 과w 오른쪽 w^{\text{오른쪽}}승오른쪽 은 각각 왼쪽 및 오른쪽 분기의 예 비율입니다.
알아채다:
- 위에서 구현한
compute_entropy()
함수를 - 이전에 구현한
split_dataset()
함수를
def compute_information_gain(X,y,node_indices,feature):
L_indices,R_indices = split_dataset(X,node_indices,feature)
X_node, y_node = X[node_indices],y[node_indices]
X_left, y_left = X[L_indices], y[L_indices]
X_right, y_right = X[R_indices], y[R_indices]
node_entropy = compute_entropy(y_node)
L_entropy = compute_entropy(y_left)
R_entropy = compute_entropy(y_right)
left_w = len(X_left)/len(X_node)
rigth_w = len(X_right)/len(X_node)
w_entropy = left_w*L_entropy +rigth_w*R_entropy
information_gain = node_entropy - w_entropy
return information_gain
이제 위에서 했던 것처럼 각 특징에 대한 정보 이득을 계산하여 가장 좋은 분할 특징을 얻고 가장 큰 정보 이득을 주는 특징을 반환하는 함수를 작성해 봅시다.
아래 표시된 get_best_split()
기능을 .
- 이 함수는 훈련 데이터와 노드에서 데이터 포인트의 인덱스를 받아들입니다.
- 기능의 출력은 가장 큰 정보 이득을 제공하는 기능입니다.
- 기능을 반복하고
compute_information_gain()
함수를
- 기능을 반복하고
def get_best_split(X,y,node_indices):
num = X.shape[1]
best_feature = -1
max_info_gain = 0
for feature in range(num):
info_gain = compute_information_gain(X,y,node_indices,feature)
if info_gain > max_info_gain:
max_info_gain = info_gain
best_feature = feature
return best_feature
5 나무 만들기
이 섹션에서는 위에서 구현한 함수를 사용하여 중지 조건(최대 깊이 2)에 도달할 때까지 분할할 최상의 기능을 순차적으로 선택하여 의사 결정 트리를 생성합니다.
tree = []
root_indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
def build_tree_recursive(X, y, node_indices, branch_name, max_depth, current_depth):
if current_depth == max_depth:
formatting = " "*current_depth + "-"*current_depth
print(formatting, "%s leaf node with indices" % branch_name, node_indices)
return
best_feature = get_best_split(X, y, node_indices)
tree.append((current_depth, branch_name, best_feature, node_indices))
formatting = "-"*current_depth
print("%s Depth %d, %s: Split on feature: %d" % (formatting, current_depth, branch_name, best_feature))
# Split the dataset at the best feature
left_indices, right_indices = split_dataset(X, node_indices, best_feature)
# continue splitting the left and the right child. Increment current depth
build_tree_recursive(X, y, left_indices, "Left", max_depth, current_depth+1)
build_tree_recursive(X, y, right_indices, "Right", max_depth, current_depth+1)
build_tree_recursive(X_train, y_train, root_indices, "Root", max_depth=2, current_depth=0)
Depth 0, Root: Split on feature: 2
- Depth 1, Left: Split on feature: 0
-- Left leaf node with indices [0, 1, 4, 7]
-- Right leaf node with indices [5]
- Depth 1, Right: Split on feature: 1
-- Left leaf node with indices [8]
-- Right leaf node with indices [2, 3, 6, 9]