python机器学习:决策树(16)
本文是Python 机器学习系列的一部分。您可以在此处找到本系列之前所有帖子的链接
介绍
在上一章中,您学习了多元线性回归。
在本章中,我们将研究决策树。
注意:如果您可以将任何事物与您自己或您的生活联系起来,那么理解这个概念的机会就更大。因此,尝试通过将一切与人类联系起来来理解一切。
什么是决策树?
决策树是一种决策支持工具,它使用树状图或决策模型及其可能的后果,包括机会事件结果、资源成本和效用。这是显示仅包含条件控制语句的算法的一种方式。
决策树 (DT) 是一种用于分类和回归的非参数监督学习方法。决策树从数据中学习以使用一组 if-then-else 决策规则来近似正弦曲线。树越深,决策规则越复杂,模型就越合适。
决策树以树结构的形式构建分类或回归模型,因此称为CART(Classification and Regression Trees)。它将数据集分解为越来越小的子集,同时沿着相关的决策树构建。最终结果是具有决策节点和叶节点的树。一个决策节点有两个或多个分支。叶节点表示分类或决策。树中最顶层的决策节点,对应于称为根节点的最佳预测器。决策树可以处理分类数据和数值数据。
什么时候使用决策树?
- 当用户有一个他试图实现的目标时:最大利润,优化成本
- 当有多个行动方案时
- 有一个计算的各种替代方案的好处的衡量标准
- 当发生决策者无法控制的事件时,即环境因素。
- 关于实际发生的结果的不确定性
决策树的假设
- 一开始,整个训练集被认为是根。
- 特征值最好是分类的。如果这些值是连续的,则在构建模型之前将它们离散化。
- 记录根据属性值递归分布。
- 将属性作为树的根节点或内部节点的顺序是通过使用某种统计方法完成的。
关键术语
- 根节点– 它代表整个总体或样本,并进一步分为两个或更多同质集。
- 拆分– 将一个节点分成两个或多个子节点的过程。
- 决策节点– 当一个子节点分裂成更多的子节点时,它被称为决策节点。
- 叶/终端节点– 不分裂的节点称为叶或终端节点。
- 修剪 – 当我们删除决策节点的子节点时,这个过程称为修剪。你可以说分裂的相反过程。
- 分支/子树 – 整个树的一个子部分称为分支或子树。
- 父子节点 – 分为子节点的节点称为子节点的父节点,而子节点是父节点的子节点。
- 熵(Entropy )– 决策树是从根节点自上而下构建的,涉及将数据划分为包含具有相似值(同类)实例的子集。ID 3 算法使用熵来计算样本的均匀性。如果样本是完全同质的,则熵为零,如果样本等分,则熵为 1。
- 信息增益– 信息增益基于在属性上拆分数据集后熵的减少。构建决策树就是寻找返回最高信息增益的属性(即最同质的分支)。
- 内部节点– 内部节点(也称为内部节点,简称 inode 或分支节点)是具有子节点的树的任何节点
- 分支– 连接元素的线称为“分支”。
如何制作决策树?
计算目标的熵(Entropy )。
然后将数据集拆分为不同的属性。计算每个分支的熵。然后按比例添加,以获得分割的总熵。从分裂前的熵中减去所得的熵。结果是信息增益或熵的减少。
选择信息增益最大的属性作为决策节点,将数据集除以它的分支,在每个分支上重复相同的过程。
决策树的类型
- 分类变量决策树
具有分类目标变量的决策树称为分类变量决策树。例如,我说孟加拉国能否击败印度?,所以我可以将决策参数设置为 YES 或 NO。 - 连续变量决策树 决策树有一个连续的目标变量,称为连续变量决策树。例如,我说印度获胜的几率是多少?所以这里我们有一个不同的值。
- ID3(迭代二分法 3)
- C4.5(ID3的后继者)
- CART(分类和回归树)
- CHAID(卡方自动交互检测器)
- MARS(多元自适应回归样条)
- 条件推理树。
决策树的重要性是什么?
- 简单易用、使用、理解和解释
- 他们做特征选择和变量筛选
- 他们只需要很少的努力来准备和生产
- 也可以接受非线性关系和参数
- 可以使用基于条件概率的推理或贝叶斯定理
- 他们为不确定的情况提供战略答案
回归树与分类树
回归树 | 分类树 | |
因变量 | 当因变量是连续的时使用 | 当因变量是分类变量时使用 |
看不见的数据 | 用平均值进行预测 | 使用模式值进行预测 |
预测空间 | 划分为不同且不重叠的区域 | 划分为不同且不重叠的区域 |
遵循的方法 | 自上而下的贪心算法 | 自上而下的贪心算法 |
熵(Entropy )和信息增益计算
当我们必须找到信息增益时,熵是非常重要的东西,而信息增益又在选择下一个属性时起着至关重要的作用。
以下是我们将在本文中使用的公式,
示例 1
示例 2
=0.1
因此我们可以看到,与示例 1(IG=-0.15)相比,示例 2(IG=0.1)的信息增益更大
决策树示例
到目前为止,我们学习了理论,现在让我们尝试一些动手操作。我将采用一个演示数据集,并将基于该数据集构建一个决策树。
我正在使用以下数据集,我们根据给定的环境条件决定是否参加比赛。
Day | Outlook | Temperature | Humidity | Wind | Play Tennis |
D1 | Sunny | Hot | High | Weak | No |
D2 | Sunny | Hot | High | Strong | No |
D3 | Overcast | Hot | High | Weak | Yes |
D4 | Rain | Mild | High | Weak | Yes |
D5 | Rain | Cool | Normal | Weak | Yes |
D6 | Rain | Cool | Normal | Strong | No |
D7 | Overcast | Cool | Normal | Weak | Yes |
D8 | Sunny | Mild | High | Weak | No |
D9 | Sunny | Cool | Normal | Weak | Yes |
D10 | Rain | Mild | Normal | Strong | Yes |
D11 | Sunny | Mild | Normal | Strong | Yes |
D12 | Overcast | Mild | High | Strong | Yes |
D13 | Overcast | Hot | Normal | Weak | Yes |
D14 | Rain | Mild | High | Strong | No |
计算“Outlook”特征的基尼系数
5. 所以最终的基尼指数是:
“Temperature”特征的基尼系数计算
5. 所以最终的基尼指数是:
= 0.44
“Humidity”特征的基尼系数计算
1.我们可以看到,Humidity 有7个实例(7/14)为“High”,7个实例(7/14)为“Normal”
= 1 – ((3/7)^2+(4/7)^2)
3. Humidity = Normal,Play Tennis = Yes,我们有 6 个实例 (6/7)
= 0.24
= (7/14)*0.49 + (7/14)*0.24
“Wind”特征基尼系数计算
Wind = Weak,Play Tennis = No,我们有 2 个实例 (2/8)
= 1 – ((6/8)^2+(2/8)^2)
使用连接(AND 运算)的决策树
使用析取(OR 操作)的决策树
使用异或的决策树
使用连接析取(AND 操作的 OR)的决策树
让我们看一个粗略的决策树,看看决策树会是什么样子
从上面的树我们无法清楚地推断出任何东西,所以让我向您展示决策树将如何寻找给定的数据集,然后我们将尝试了解该树的确切含义以及如何使用该树为了我们的利益。
你可能想知道为什么我们把“Outlook”作为根节点”,这是因为之前计算的基尼指数值
以下是带有权重的决策树,可用于计算熵和信息增益。
现在让我们试着理解这棵树,
决策树的Python实现
以IRIS数据集为例,可以直接从sklearn数据集仓库中导入。随意使用任何数据集,kaggle 和 Google Colab 上有一些非常好的数据集。
我将在这里使用的数据集是 UCI Zoo Dataset。
1. 使用 NumPy
import pandas as pd
import numpy as np
from pprint import pprint
#Import the dataset and define the feature as well as the target datasets / columns#
dataset = pd.read_csv('zoo.csv',
names=['animal_name','hair','feathers','eggs','milk',
'airbone','aquatic','predator','toothed','backbone',
'breathes','venomous','fins','legs','tail','domestic','catsize','class',])#Import all columns omitting the fist which consists the names of the animals
#We drop the animal names since this is not a good feature to split the data on
dataset=dataset.drop('animal_name',axis=1)
def entropy(target_col):
elements,counts = np.unique(target_col,return_counts = True)
entropy = np.sum([(-counts[i]/np.sum(counts))*np.log2(counts[i]/np.sum(counts)) for i in range(len(elements))])
return entropy
def InfoGain(data,split_attribute_name,target_name="class"):
#Calculate the entropy of the total dataset
total_entropy = entropy(data[target_name])
##Calculate the entropy of the dataset
#Calculate the values and the corresponding counts for the split attribute
vals,counts= np.unique(data[split_attribute_name],return_counts=True)
#Calculate the weighted entropy
Weighted_Entropy = np.sum([(counts[i]/np.sum(counts))*entropy(data.where(data[split_attribute_name]==vals[i]).dropna()[target_name]) for i in range(len(vals))])
#Calculate the information gain
Information_Gain = total_entropy - Weighted_Entropy
return Information_Gain
def ID3(data,originaldata,features,target_attribute_name="class",parent_node_class = None):
#Define the stopping criteria --> If one of this is satisfied, we want to return a leaf node#
#If all target_values have the same value, return this value
if len(np.unique(data[target_attribute_name])) <= 1:
return np.unique(data[target_attribute_name])[0]
#If the dataset is empty, return the mode target feature value in the original dataset
elif len(data)==0:
return np.unique(originaldata[target_attribute_name])[np.argmax(np.unique(originaldata[target_attribute_name],return_counts=True)[1])]
#If the feature space is empty, return the mode target feature value of the direct parent node --> Note that
#the direct parent node is that node which has called the current run of the ID3 algorithm and hence
#the mode target feature value is stored in the parent_node_class variable.
elif len(features) ==0:
return parent_node_class
#If none of the above holds true, grow the tree!
else:
#Set the default value for this node --> The mode target feature value of the current node
parent_node_class = np.unique(data[target_attribute_name])[np.argmax(np.unique(data[target_attribute_name],return_counts=True)[1])]
#Select the feature which best splits the dataset
item_values = [InfoGain(data,feature,target_attribute_name) for feature in features] #Return the information gain values for the features in the dataset
best_feature_index = np.argmax(item_values)
best_feature = features[best_feature_index]
#Create the tree structure. The root gets the name of the feature (best_feature) with the maximum information
#gain in the first run
tree = {best_feature:{}}
#Remove the feature with the best inforamtion gain from the feature space
features = [i for i in features if i != best_feature]
#Grow a branch under the root node for each possible value of the root node feature
for value in np.unique(data[best_feature]):
value = value
#Split the dataset along the value of the feature with the largest information gain and therwith create sub_datasets
sub_data = data.where(data[best_feature] == value).dropna()
#Call the ID3 algorithm for each of those sub_datasets with the new parameters --> Here the recursion comes in!
subtree = ID3(sub_data,dataset,features,target_attribute_name,parent_node_class)
#Add the sub tree, grown from the sub_dataset to the tree under the root node
tree[best_feature][value] = subtree
return(tree)
def predict(query,tree,default = 1):
for key in list(query.keys()):
if key in list(tree.keys()):
try:
result = tree[key][query[key]]
except:
return default
result = tree[key][query[key]]
if isinstance(result,dict):
return predict(query,result)
else:
return result
def train_test_split(dataset):
training_data = dataset.iloc[:80].reset_index(drop=True)#We drop the index respectively relabel the index
#starting form 0, because we do not want to run into errors regarding the row labels / indexes
testing_data = dataset.iloc[80:].reset_index(drop=True)
return training_data,testing_data
training_data = train_test_split(dataset)[0]
testing_data = train_test_split(dataset)[1]
def test(data,tree):
#Create new query instances by simply removing the target feature column from the original dataset and
#convert it to a dictionary
queries = data.iloc[:,:-1].to_dict(orient = "records")
#Create a empty DataFrame in whose columns the prediction of the tree are stored
predicted = pd.DataFrame(columns=["predicted"])
#Calculate the prediction accuracy
for i in range(len(data)):
predicted.loc[i,"predicted"] = predict(queries[i],tree,1.0)
print('The prediction accuracy is: ',(np.sum(predicted["predicted"] == data["class"])/len(data))*100,'%')
tree = ID3(training_data,training_data,training_data.columns[:-1])
pprint(tree)
test(testing_data,tree)
我得到的输出是
2. 使用 SKLearn
#Import the DecisionTreeClassifier
from sklearn.tree import DecisionTreeClassifier
#Import the dataset
dataset = pd.read_csv('zoo.csv',
names=['animal_name','hair','feathers','eggs','milk',
'airbone','aquatic','predator','toothed','backbone',
'breathes','venomous','fins','legs','tail','domestic','catsize','class',])
#We drop the animal names since this is not a good feature to split the data on
dataset=dataset.drop('animal_name',axis=1)
train_features = dataset.iloc[:80,:-1]
test_features = dataset.iloc[80:,:-1]
train_targets = dataset.iloc[:80,-1]
test_targets = dataset.iloc[80:,-1]
tree = DecisionTreeClassifier(criterion = 'entropy').fit(train_features,train_targets)
prediction = tree.predict(test_features)
print("The prediction accuracy is: ",tree.score(test_features,test_targets)*100,"%")
我得到的输出是:
The prediction accuracy is: 80.95238095238095 %
3. 使用 TensorFlow
import numpy as np
import pandas as pd
import tensorflow as tf
from functools import reduce
import matplotlib.pyplot as plt
%matplotlib inline
np.random.seed(1943)
tf.set_random_seed(1943)
iris = [((5.1, 3.5, 1.4, 0.2), (1, 0, 0)),
((4.9, 3.0, 1.4, 0.2), (1, 0, 0)),
((4.7, 3.2, 1.3, 0.2), (1, 0, 0)),
((4.6, 3.1, 1.5, 0.2), (1, 0, 0)),
((5.0, 3.6, 1.4, 0.2), (1, 0, 0)),
((5.4, 3.9, 1.7, 0.4), (1, 0, 0)),
((4.6, 3.4, 1.4, 0.3), (1, 0, 0)),
((5.0, 3.4, 1.5, 0.2), (1, 0, 0)),
((4.4, 2.9, 1.4, 0.2), (1, 0, 0)),
((4.9, 3.1, 1.5, 0.1), (1, 0, 0)),
((5.4, 3.7, 1.5, 0.2), (1, 0, 0)),
((4.8, 3.4, 1.6, 0.2), (1, 0, 0)),
((4.8, 3.0, 1.4, 0.1), (1, 0, 0)),
((4.3, 3.0, 1.1, 0.1), (1, 0, 0)),
((5.8, 4.0, 1.2, 0.2), (1, 0, 0)),
((5.7, 4.4, 1.5, 0.4), (1, 0, 0)),
((5.4, 3.9, 1.3, 0.4), (1, 0, 0)),
((5.1, 3.5, 1.4, 0.3), (1, 0, 0)),
((5.7, 3.8, 1.7, 0.3), (1, 0, 0)),
((5.1, 3.8, 1.5, 0.3), (1, 0, 0)),
((5.4, 3.4, 1.7, 0.2), (1, 0, 0)),
((5.1, 3.7, 1.5, 0.4), (1, 0, 0)),
((4.6, 3.6, 1.0, 0.2), (1, 0, 0)),
((5.1, 3.3, 1.7, 0.5), (1, 0, 0)),
((4.8, 3.4, 1.9, 0.2), (1, 0, 0)),
((5.0, 3.0, 1.6, 0.2), (1, 0, 0)),
((5.0, 3.4, 1.6, 0.4), (1, 0, 0)),
((5.2, 3.5, 1.5, 0.2), (1, 0, 0)),
((5.2, 3.4, 1.4, 0.2), (1, 0, 0)),
((4.7, 3.2, 1.6, 0.2), (1, 0, 0)),
((4.8, 3.1, 1.6, 0.2), (1, 0, 0)),
((5.4, 3.4, 1.5, 0.4), (1, 0, 0)),
((5.2, 4.1, 1.5, 0.1), (1, 0, 0)),
((5.5, 4.2, 1.4, 0.2), (1, 0, 0)),
((4.9, 3.1, 1.5, 0.1), (1, 0, 0)),
((5.0, 3.2, 1.2, 0.2), (1, 0, 0)),
((5.5, 3.5, 1.3, 0.2), (1, 0, 0)),
((4.9, 3.1, 1.5, 0.1), (1, 0, 0)),
((4.4, 3.0, 1.3, 0.2), (1, 0, 0)),
((5.1, 3.4, 1.5, 0.2), (1, 0, 0)),
((5.0, 3.5, 1.3, 0.3), (1, 0, 0)),
((4.5, 2.3, 1.3, 0.3), (1, 0, 0)),
((4.4, 3.2, 1.3, 0.2), (1, 0, 0)),
((5.0, 3.5, 1.6, 0.6), (1, 0, 0)),
((5.1, 3.8, 1.9, 0.4), (1, 0, 0)),
((4.8, 3.0, 1.4, 0.3), (1, 0, 0)),
((5.1, 3.8, 1.6, 0.2), (1, 0, 0)),
((4.6, 3.2, 1.4, 0.2), (1, 0, 0)),
((5.3, 3.7, 1.5, 0.2), (1, 0, 0)),
((5.0, 3.3, 1.4, 0.2), (1, 0, 0)),
((7.0, 3.2, 4.7, 1.4), (0, 1, 0)),
((6.4, 3.2, 4.5, 1.5), (0, 1, 0)),
((6.9, 3.1, 4.9, 1.5), (0, 1, 0)),
((5.5, 2.3, 4.0, 1.3), (0, 1, 0)),
((6.5, 2.8, 4.6, 1.5), (0, 1, 0)),
((5.7, 2.8, 4.5, 1.3), (0, 1, 0)),
((6.3, 3.3, 4.7, 1.6), (0, 1, 0)),
((4.9, 2.4, 3.3, 1.0), (0, 1, 0)),
((6.6, 2.9, 4.6, 1.3), (0, 1, 0)),
((5.2, 2.7, 3.9, 1.4), (0, 1, 0)),
((5.0, 2.0, 3.5, 1.0), (0, 1, 0)),
((5.9, 3.0, 4.2, 1.5), (0, 1, 0)),
((6.0, 2.2, 4.0, 1.0), (0, 1, 0)),
((6.1, 2.9, 4.7, 1.4), (0, 1, 0)),
((5.6, 2.9, 3.6, 1.3), (0, 1, 0)),
((6.7, 3.1, 4.4, 1.4), (0, 1, 0)),
((5.6, 3.0, 4.5, 1.5), (0, 1, 0)),
((5.8, 2.7, 4.1, 1.0), (0, 1, 0)),
((6.2, 2.2, 4.5, 1.5), (0, 1, 0)),
((5.6, 2.5, 3.9, 1.1), (0, 1, 0)),
((5.9, 3.2, 4.8, 1.8), (0, 1, 0)),
((6.1, 2.8, 4.0, 1.3), (0, 1, 0)),
((6.3, 2.5, 4.9, 1.5), (0, 1, 0)),
((6.1, 2.8, 4.7, 1.2), (0, 1, 0)),
((6.4, 2.9, 4.3, 1.3), (0, 1, 0)),
((6.6, 3.0, 4.4, 1.4), (0, 1, 0)),
((6.8, 2.8, 4.8, 1.4), (0, 1, 0)),
((6.7, 3.0, 5.0, 1.7), (0, 1, 0)),
((6.0, 2.9, 4.5, 1.5), (0, 1, 0)),
((5.7, 2.6, 3.5, 1.0), (0, 1, 0)),
((5.5, 2.4, 3.8, 1.1), (0, 1, 0)),
((5.5, 2.4, 3.7, 1.0), (0, 1, 0)),
((5.8, 2.7, 3.9, 1.2), (0, 1, 0)),
((6.0, 2.7, 5.1, 1.6), (0, 1, 0)),
((5.4, 3.0, 4.5, 1.5), (0, 1, 0)),
((6.0, 3.4, 4.5, 1.6), (0, 1, 0)),
((6.7, 3.1, 4.7, 1.5), (0, 1, 0)),
((6.3, 2.3, 4.4, 1.3), (0, 1, 0)),
((5.6, 3.0, 4.1, 1.3), (0, 1, 0)),
((5.5, 2.5, 4.0, 1.3), (0, 1, 0)),
((5.5, 2.6, 4.4, 1.2), (0, 1, 0)),
((6.1, 3.0, 4.6, 1.4), (0, 1, 0)),
((5.8, 2.6, 4.0, 1.2), (0, 1, 0)),
((5.0, 2.3, 3.3, 1.0), (0, 1, 0)),
((5.6, 2.7, 4.2, 1.3), (0, 1, 0)),
((5.7, 3.0, 4.2, 1.2), (0, 1, 0)),
((5.7, 2.9, 4.2, 1.3), (0, 1, 0)),
((6.2, 2.9, 4.3, 1.3), (0, 1, 0)),
((5.1, 2.5, 3.0, 1.1), (0, 1, 0)),
((5.7, 2.8, 4.1, 1.3), (0, 1, 0)),
((6.3, 3.3, 6.0, 2.5), (0, 0, 1)),
((5.8, 2.7, 5.1, 1.9), (0, 0, 1)),
((7.1, 3.0, 5.9, 2.1), (0, 0, 1)),
((6.3, 2.9, 5.6, 1.8), (0, 0, 1)),
((6.5, 3.0, 5.8, 2.2), (0, 0, 1)),
((7.6, 3.0, 6.6, 2.1), (0, 0, 1)),
((4.9, 2.5, 4.5, 1.7), (0, 0, 1)),
((7.3, 2.9, 6.3, 1.8), (0, 0, 1)),
((6.7, 2.5, 5.8, 1.8), (0, 0, 1)),
((7.2, 3.6, 6.1, 2.5), (0, 0, 1)),
((6.5, 3.2, 5.1, 2.0), (0, 0, 1)),
((6.4, 2.7, 5.3, 1.9), (0, 0, 1)),
((6.8, 3.0, 5.5, 2.1), (0, 0, 1)),
((5.7, 2.5, 5.0, 2.0), (0, 0, 1)),
((5.8, 2.8, 5.1, 2.4), (0, 0, 1)),
((6.4, 3.2, 5.3, 2.3), (0, 0, 1)),
((6.5, 3.0, 5.5, 1.8), (0, 0, 1)),
((7.7, 3.8, 6.7, 2.2), (0, 0, 1)),
((7.7, 2.6, 6.9, 2.3), (0, 0, 1)),
((6.0, 2.2, 5.0, 1.5), (0, 0, 1)),
((6.9, 3.2, 5.7, 2.3), (0, 0, 1)),
((5.6, 2.8, 4.9, 2.0), (0, 0, 1)),
((7.7, 2.8, 6.7, 2.0), (0, 0, 1)),
((6.3, 2.7, 4.9, 1.8), (0, 0, 1)),
((6.7, 3.3, 5.7, 2.1), (0, 0, 1)),
((7.2, 3.2, 6.0, 1.8), (0, 0, 1)),
((6.2, 2.8, 4.8, 1.8), (0, 0, 1)),
((6.1, 3.0, 4.9, 1.8), (0, 0, 1)),
((6.4, 2.8, 5.6, 2.1), (0, 0, 1)),
((7.2, 3.0, 5.8, 1.6), (0, 0, 1)),
((7.4, 2.8, 6.1, 1.9), (0, 0, 1)),
((7.9, 3.8, 6.4, 2.0), (0, 0, 1)),
((6.4, 2.8, 5.6, 2.2), (0, 0, 1)),
((6.3, 2.8, 5.1, 1.5), (0, 0, 1)),
((6.1, 2.6, 5.6, 1.4), (0, 0, 1)),
((7.7, 3.0, 6.1, 2.3), (0, 0, 1)),
((6.3, 3.4, 5.6, 2.4), (0, 0, 1)),
((6.4, 3.1, 5.5, 1.8), (0, 0, 1)),
((6.0, 3.0, 4.8, 1.8), (0, 0, 1)),
((6.9, 3.1, 5.4, 2.1), (0, 0, 1)),
((6.7, 3.1, 5.6, 2.4), (0, 0, 1)),
((6.9, 3.1, 5.1, 2.3), (0, 0, 1)),
((5.8, 2.7, 5.1, 1.9), (0, 0, 1)),
((6.8, 3.2, 5.9, 2.3), (0, 0, 1)),
((6.7, 3.3, 5.7, 2.5), (0, 0, 1)),
((6.7, 3.0, 5.2, 2.3), (0, 0, 1)),
((6.3, 2.5, 5.0, 1.9), (0, 0, 1)),
((6.5, 3.0, 5.2, 2.0), (0, 0, 1)),
((6.2, 3.4, 5.4, 2.3), (0, 0, 1)),
((5.9, 3.0, 5.1, 1.8), (0, 0, 1))]
feature = np.vstack([np.array(i[0]) for i in iris])
label = np.vstack([np.array(i[1]) for i in iris])
x = feature[:, 2:4] # use "Petal length" and "Petal width" only
y = label
d = x.shape[1]
def tf_kron_prod(a, b):
res = tf.einsum('ij,ik->ijk', a, b)
res = tf.reshape(res, [-1, tf.reduce_prod(res.shape[1:])])
return res
def tf_bin(x, cut_points, temperature=0.1):
# x is a N-by-1 matrix (column vector)
# cut_points is a D-dim vector (D is the number of cut-points)
# this function produces a N-by-(D+1) matrix, each row has only one element being one and the rest are all zeros
D = cut_points.get_shape().as_list()[0]
W = tf.reshape(tf.linspace(1.0, D + 1.0, D + 1), [1, -1])
cut_points = tf.contrib.framework.sort(cut_points) # make sure cut_points is monotonically increasing
b = tf.cumsum(tf.concat([tf.constant(0.0, shape=[1]), -cut_points], 0))
h = tf.matmul(x, W) + b
res = tf.nn.softmax(h / temperature)
return res
def nn_decision_tree(x, cut_points_list, leaf_score, temperature=0.1):
# cut_points_list contains the cut_points for each dimension of feature
leaf = reduce(tf_kron_prod,
map(lambda z: tf_bin(x[:, z[0]:z[0] + 1], z[1], temperature), enumerate(cut_points_list)))
return tf.matmul(leaf, leaf_score)
num_cut = [1, 1] # "Petal length" and "Petal width"
num_leaf = np.prod(np.array(num_cut) + 1)
num_class = 3
sess = tf.InteractiveSession()
x_ph = tf.placeholder(tf.float32, [None, d])
y_ph = tf.placeholder(tf.float32, [None, num_class])
cut_points_list = [tf.Variable(tf.random_uniform([i])) for i in num_cut]
leaf_score = tf.Variable(tf.random_uniform([num_leaf, num_class]))
y_pred = nn_decision_tree(x_ph, cut_points_list, leaf_score, temperature=0.1)
loss = tf.reduce_mean(tf.losses.softmax_cross_entropy(logits=y_pred, onehot_labels=y_ph))
opt = tf.train.AdamOptimizer(0.1)
train_step = opt.minimize(loss)
sess.run(tf.global_variables_initializer())
for i in range(1000):
_, loss_e = sess.run([train_step, loss], feed_dict={x_ph: x, y_ph: y})
if i % 200 == 0:
print(loss_e)
print('error rate %.2f' % (1 - np.mean(np.argmax(y_pred.eval(feed_dict={x_ph: x}), axis=1) == np.argmax(y, axis=1))))
sample_x0 = np.repeat(np.linspace(0, np.max(x[:,0]), 100), 100).reshape(-1,1)
sample_x1 = np.tile(np.linspace(0, np.max(x[:,1]), 100).reshape(-1,1), [100,1])
sample_x = np.hstack([sample_x0, sample_x1])
sample_label = np.argmax(y_pred.eval(feed_dict={x_ph: sample_x}), axis=1)
plt.figure(figsize=(8,8))
plt.scatter(x[:,0],
x[:,1],
c=np.argmax(y, axis=1),
marker='o',
s=50,
cmap='summer',
edgecolors='black')
plt.scatter(sample_x0.flatten(),
sample_x1.flatten(),
c=sample_label.flatten(),
marker='D',
s=20,
cmap='summer',
edgecolors='none',
alpha=0.33)
我得到的输出是
结论
在本章中,我们研究了决策树。
在下一章中,我们将研究朴素贝叶斯。
常见问题FAQ
- 程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
- 请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!