决策树思想的来源非常朴素,程序设计中的条件分支结构就是if-then 结构,最早的决策树就是利用这类结构分割数据的一种分类学习方法。
公式:
H ( X ) = − ∑ x ∈ X P ( x ) log P ( x ) H(X)=-\sum_{x \in X} P(x) \log P(x) H(X)=−x∈X∑P(x)logP(x)
单位为bit
信息和消除不确定性是相联系的
特征 A A A对训练数据集 D D D的信息增益 g ( D , A ) , \mathrm{g}(\mathrm{D}, \mathrm{A}), g(D,A), 定义为集合 D D D的信息熵 H ( D ) \mathrm{H}(\mathrm{D}) H(D) 与特征 A A A给定条件下 D D D的信息条件熵H ( D ∣ A ) (\mathrm{D} \mid \mathrm{A}) (D∣A) 之差,即公式为:
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D, A)=H(D)-H(D \mid A) g(D,A)=H(D)−H(D∣A)
对于待划分的数据集D,其 entroy(前)是一定的,但是划分之后的熵 entroy(后)是不定的,entroy(后)越小说明使用此特征划分得到的子集的不确定性越小(也就是纯度越高),因此 entroy(前) - entroy(后)差异越大,说明使用当前特征划分数据集D的话,其纯度上升的更快。而我们在构建最优的决策树的时候总希望能更快速到达纯度更高的集合,这一点可以参考优化算法中的梯度下降算法,每一步沿着负梯度方法最小化损失函数的原因就是负梯度方向是函数值减小最快的方向。同理:在决策树构建的过程中我们总是希望集合往最快到达纯度更高的子集合方向发展,因此我们总是选择使得信息增益最大的特征来划分当前数据集D。
信息熵的计算:
H ( D ) = − ∑ k = 1 K ∣ C k ∣ ∣ D ∣ log ∣ C k ∣ ∣ D ∣ H(D)=-\sum_{k=1}^{K} \frac{\left|C_{k}\right|}{|D|} \log \frac{\left|C_{k}\right|}{|D|} H(D)=−k=1∑K∣D∣∣Ck∣log∣D∣∣Ck∣
条件熵的计算:
H ( D ∣ A ) = ∑ i = 1 n ∣ D i ∣ ∣ D ∣ H ( D i ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ ∑ k = 1 K ∣ D i k ∣ ∣ D i ∣ log ∣ D i k ∣ ∣ D i ∣ H(D \mid A)=\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} H\left(D_{i}\right)=-\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \sum_{k=1}^{K} \frac{\left|D_{i k}\right|}{\left|D_{i}\right|} \log \frac{\left|D_{i k}\right|}{\left|D_{i}\right|} H(D∣A)=i=1∑n∣D∣∣Di∣H(Di)=−i=1∑n∣D∣∣Di∣k=1∑K∣Di∣∣Dik∣log∣Di∣∣Dik∣
注: C k C_{k} Ck 表示属于某个类别的样本数。
• ID3 信息增益最大的准则
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D, A)=H(D)-H(D \mid A) g(D,A)=H(D)−H(D∣A)
• C4.5 信息增益比最大的准则
g R ( D , A ) = g ( D , A ) H A ( D ) g_{R}(D, A)=\frac{g(D, A)}{H_{A}(D)} gR(D,A)=HA(D)g(D,A)
注意: 其中的H A ( D ) , _{\mathrm{A}}(\mathrm{D}), A(D), 对于样本集合D,将当前特征A作为随机变量 (取值是特征A的各个特征值),求得的经验熵。
(之前是把集合类别作为随机变量, 现在把某个特征作为随机变量, 按照此特征的特征取值对集合D进行划分, 计算熵 H A ( D ) H_{A}(D) HA(D)
H A ( D ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ log 2 ∣ D i ∣ ∣ D ∣ H_{A}(D)=-\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \log _{2} \frac{\left|D_{i}\right|}{|D|} HA(D)=−i=1∑n∣D∣∣Di∣log2∣D∣∣Di∣
• CART 回归树: 平方误差最小分类树: 基尼系数最小的准则在sklearn中可以选择划分的原则。
Gini ( p ) = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 \operatorname{Gini}(\mathrm{p})=\sum_{k=1}^{K} p_{k}\left(1-p_{k}\right)=1-\sum_{k=1}^{K} p_{k}^{2} Gini(p)=k=1∑Kpk(1−pk)=1−k=1∑Kpk2
说明:
pk表示选中的样本属于k类别的概率,则这个样本被分错的概率是(1-pk)
样本集合中有K个类别,一个随机选中的样本可以属于这k个类别中的任意一个,因而对类别就加和
当为二分类是, G i n i ( P ) = 2 p ( 1 − p ) Gini(P) = 2p(1-p) Gini(P)=2p(1−p)
注意: Gini指数越小表示集合中被选中的样本被分错的概率越小,也就是说集合的纯度越高,反之,集合越不纯。
即 基尼指数(基尼不纯度)= 样本被选中的概率 * 样本被分错的概率
样本集合D的Gini指数: 假设集合中有K个类别,则:
Gini ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 \operatorname{Gini}(\mathrm{D})=1-\sum_{k=1}^{K}\left(\frac{\left|C_{k}\right|}{|D|}\right)^{2} Gini(D)=1−k=1∑K(∣D∣∣Ck∣)2
kaggle泰坦尼克生存判别
步骤:
#读取数据
#特征工程
#填补缺失值
#数据类型转换
#One-hot编码
#实例化决策树
#实例化网格搜索,交叉验证
#输出树文件
代码示例:
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn.feature_extraction import DictVectorizer
from sklearn.model_selection import GridSearchCV
import pandas as pd
def decision_tree():
#读取数据
train = pd.read_csv("./titanic/train.csv")
test = pd.read_csv("./titanic/test.csv")
print(train)
print(train.columns)
print(test)
print(test.columns)
print(train.info())
print(test.info())
X_train = train.loc[:, ['Pclass', 'Sex', 'Age']]
y_train = train.loc[:, 'Survived']
X_test = test.loc[:, ['Pclass', 'Sex', 'Age']]
#特征工程
#填补缺失值
X_train['Age'] = X_train['Age'].fillna(X_train['Age'].mean())
X_test['Age'] = X_test['Age'].fillna(X_test['Age'].mean())
#数据类型转换
X_train['Pclass'] = X_train['Pclass'].astype('str')
X_test['Pclass'] = X_test['Pclass'].astype('str')
#One-hot编码
dict = DictVectorizer(sparse=False)
X_train = dict.fit_transform(X_train.to_dict(orient="records"))
X_test = dict.transform(X_test.to_dict(orient="records"))
print(dict.get_feature_names())
print(X_train)
print('*'*100)
print(X_test)
#实例化决策树
dt = DecisionTreeClassifier()
#参数空间
params = {'criterion': ['gini', "entropy"], 'max_depth': [5, 8, 10]}
#实例化网格搜索,与交叉验证
gd = GridSearchCV(dt, param_grid=params, cv=5)
gd.fit(X_train, y_train)
#打印交叉验证结果
print(gd.best_score_)
print(gd.best_estimator_)
dt1 = DecisionTreeClassifier(criterion='entropy', max_depth=10)
dt1.fit(X_train, y_train)
#输出树文件
export_graphviz(dt1, out_file='./tree.dot',
feature_names=['age', 'pclass=1st', 'pclass=2nd', 'pclass=3rd', 'female', 'male'])
return None
if __name__ == '__main__':
decision_tree()
优点:
• 简单的理解和解释,树木可视化。
缺点:
• 决策树学习者可以创建不能很好地推广数据的过于复杂的树, 这被称为过拟合。
• 决策树可能不稳定,因为数据的小变化可能会导致完全不同的树被生成
改进:
• 减枝cart算法
• 随机森林
集成学习通过建立几个模型组合的来解决单一预测问题。它的工作 原理是生成多个分类器/模型,各自独立地学习和作出预测。这些预测 最后结合成单预测,因此优于任何一个单分类的做出预测。
在机器学习中,随机森林是一个包含多个决策树的分类器,并且其输出的类别是由个别树输出的类别的众数而定。
根据下列算法而建造每棵树:
•用N来表示训练用例(样本)的个数,M表示特征数目。
•输入特征数目m,用于确定决策树上一个节点的决策结果; 其中m应远小于M。
•从N个训练用例(样本)中以有放回抽样的方式,取样N次, 形成一个训练集(即bootstrap取样),并用未抽到的用例 (样本)作预测,评估其误差。
• 为什么要随机抽样训练集?
如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分 类结果也是完全一样的 。
• 为什么要有放回地抽样?
如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,都是绝对“片面的”(当然这样说可能不对),也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决。
• class sklearn.ensemble.RandomForestClassifier(n_estimators=10, criterion=’gini’, max_depth=None, bootstrap=True, random_state=None)
• 随机森林分类器 。
• n_estimators:integer,optional(default = 10)
森林里的树木数量。
• criteria:string,可选(default =“gini”)
分割特征的测量方法 。
• max_depth:integer或None,可选(默认=无)
树的最大深度。
• bootstrap:boolean,optional(default = True)
是否在构建树时使用放回抽样。
•在当前所有算法中,具有极好的准确率 。
•能够有效地运行在大数据集上。
•能够处理具有高维特征的输入样本,而且不需要降维。
•能够评估各个特征在分类问题上的重要性。
•对于缺省值问题也能够获得很好得结果。
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction import DictVectorizer
from sklearn.model_selection import GridSearchCV
import pandas as pd
def random_forest():
# 读取数据
train = pd.read_csv("./titanic/train.csv")
test = pd.read_csv("./titanic/test.csv")
# print(train)
# print(train.columns)
# print(test)
# print(test.columns)
# print(train.info())
# print(test.info())
X_train = train.loc[:, ['Pclass', 'Sex', 'Age']]
y_train = train.loc[:, 'Survived']
X_test = test.loc[:, ['Pclass', 'Sex', 'Age']]
# 特征工程
# 填补缺失值
X_train['Age'] = X_train['Age'].fillna(X_train['Age'].mean())
X_test['Age'] = X_test['Age'].fillna(X_test['Age'].mean())
# 数据类型转换
X_train['Pclass'] = X_train['Pclass'].astype('str')
X_test['Pclass'] = X_test['Pclass'].astype('str')
# One-hot编码
dict = DictVectorizer(sparse=False)
X_train = dict.fit_transform(X_train.to_dict(orient="records"))
X_test = dict.transform(X_test.to_dict(orient="records"))
# print(dict.get_feature_names())
# print(X_train)
# print('*' * 100)
# print(X_test)
# 实例化决策树
rf = RandomForestClassifier()
# 参数空间
params = {"n_estimators": [120, 200, 300, 500, 800, 1200], "max_depth": [5, 8, 15, 25, 30]}
# 实例化网格搜索,与交叉验证
gd = GridSearchCV(rf, param_grid=params, cv=5)
gd.fit(X_train, y_train)
# 打印交叉验证结果
print(gd.best_score_)
print(gd.best_estimator_)
return None
if __name__ == '__main__':
random_forest()