决策树
#热力第二,物理
熵:混沌
系统越大,越乱,越看不懂,混乱度越大,熵越大--->熵增
容器,容器有个隔板,一边是氧气,一边是氮气--->熵
#信息论
计算机是二进制
信息熵
中华民族9万个汉字,
有多少本书? 都是用9万个汉字排列,7000个汉字
数据采集,假如说数据没有处理过,咱们可以用决策树去处理这些数据
原则:被处理的数据是有序的,熵就减小了,这是决策树的排序原则,向着熵最大的地方进行排序
#DataFrame 中有很多列 最简单->熵最小 第二简单 ......最难
熵减------>信息增益(更加有顺序了)
?? 决策树-->ID3(算法)
【关键词】树,信息增益
决策树的优缺点
优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。既能用于分类,也能用于回归
缺点:可能会产生过度匹配问题
一、决策树的原理
predict()
【二十个问题的游戏】
游戏的规则很简单:参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案也只能用对或错回答。问问题的人通过推断分解,逐步缩小待猜测事物的范围。决策树的工作原理与20个问题类似,用户输人一系列数据 ,然后给出游戏的答案。
我们经常使用决策树处理分类问题。近来的调查表明决策树也是最经常使用的数据挖掘算法。它之所以如此流行,一个很重要的原因就是使用者基本上不用了解机器学习算法,也不用深究它是如何工作的。
如果以前没有接触过决策树,完全不用担心,它的概念非常简单。即使不知道它也可以通过简单的图形了解其工作原理。
决策树分类的思想类似于找对象。现想象一个女孩的母亲要给这个女孩介绍男朋友,于是有了下面的对话:
女儿:多大年纪了?
母亲:26。
女儿:长的帅不帅?
母亲:挺帅的。
女儿:收入高不?
母亲:不算很高,中等情况。
女儿:是公务员不?
母亲:是,在税务局上班呢。
女儿:那好,我去见见。
这个女孩的决策过程就是典型的分类树决策。相当于通过年龄、长相、收入和是否公务员对将男人分为两个类别:见和不见。假设这个女孩对男人的要求是:30岁以下、长相中等以上并且是高收入者或中等以上收入的公务员,那么这个可以用下图表示女孩的决策逻辑:
上图完整表达了这个女孩决定是否见一个约会对象的策略,其中绿色节点表示判断条件,橙色节点表示决策结果,箭头表示在一个判断条件在不同情况下的决策路径,图中红色箭头表示了上面例子中女孩的决策过程。
这幅图基本可以算是一颗决策树,说它“基本可以算”是因为图中的判定条件没有量化,如收入高中低等等,还不能算是严格意义上的决策树,如果将所有条件量化,则就变成真正的决策树了。
有了上面直观的认识,我们可以正式定义决策树了:
决策树(decision tree)是一个树结构(可以是二叉树或非二叉树)。其每个非叶节点表示一个特征属性上的测试,每个分支代表这个特征属性在某个值域上的输出,而每个叶节点存放一个类别。使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。
可以看到,决策树的决策过程非常直观,容易被人理解。目前决策树已经成功运用于医学、制造产业、天文学、分支生物学以及商业等诸多领域。
之前介绍的K-近邻算法可以完成很多分类任务,但是它最大的缺点就是无法给出数据的内在含义,决策树的主要优势就在于数据形式非常容易理解。
决策树算法能够读取数据集合,构建类似于上面的决策树。决策树很多任务都是为了数据中所蕴含的知识信息,因此决策树可以使用不熟悉的数据集合,并从中提取出一系列规则,机器学习算法最终将使用这些机器从数据集中创造的规则。专家系统中经常使用决策树,而且决策树给出结果往往可以匹敌在当前领域具有几十年工作经验的人类专家。
知道了决策树的定义以及其应用方法,下面介绍决策树的构造算法。
二、决策树的构造
分类解决离散问题,回归解决连续问题
- 决策树:信息论
- 逻辑斯底回归、贝叶斯:概率论
不同于逻辑斯蒂回归和贝叶斯算法,决策树的构造过程不依赖领域知识,它使用属性选择度量来选择将元组最好地划分成不同的类的属性。所谓决策树的构造就是进行属性选择度量确定各个特征属性之间的拓扑结构。
构造决策树的关键步骤是分裂属性。所谓分裂属性就是在某个节点处按照某一特征属性的不同划分构造不同的分支,其目标是让各个分裂子集尽可能地“纯”。尽可能“纯”就是尽量让一个分裂子集中待分类项属于同一类别。分裂属性分为三种不同的情况:
1、属性是离散值且不要求生成二叉决策树。此时用属性的每一个划分作为一个分支。
2、属性是离散值且要求生成二叉决策树。此时使用属性划分的一个子集进行测试,按照“属于此子集”和“不属于此子集”分成两个分支。
3、属性是连续值。此时确定一个值作为分裂点split_point,按照>split_point和<=split_point生成两个分支。
构造决策树的关键性内容是进行属性选择度量,属性选择度量是一种选择分裂准则,它决定了拓扑结构及分裂点split_point的选择。
属性选择度量算法有很多,一般使用自顶向下递归分治法,并采用不回溯的贪心策略。这里介绍常用的ID3算法。
ID3算法
划分数据集的大原则是:将无序的数据变得更加有序。
我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。组织杂乱无章数据的一种方法就是使用信息论度量信息,信息论是量化处理信息的分支科学。我们可以在划分数据之前使用信息论量化度量信息的内容。
在划分数据集之前之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。
在可以评测哪种数据划分方式是最好的数据划分之前,我们必须学习如何计算信息增益。集合信息的度量方式称为香农熵或者简称为熵,这个名字来源于信息论之父克劳德•香农。
entropy
熵定义为信息的期望值,在明晰这个概念之前,我们必须知道信息的定义。如果待分类的事务可能划分在多个分类之中,则符号x的信息定义为:
其中p(x)是选择该分类的概率
为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值,通过下面的公式得到:
其中n是分类的数目。
在决策树当中,设D为用类别对训练元组进行的划分,则D的熵(entropy)表示为:
其中pi表示第i个类别在整个训练元组中出现的概率,可以用属于此类别元素的数量除以训练元组元素总数量作为估计。熵的实际意义表示是D中元组的类标号所需要的平均信息量。
现在我们假设将训练元组D按属性A进行划分,则A对D划分的期望信息为:
而信息增益即为两者的差值:
ID3算法就是在每次需要分裂时,计算每个属性的增益率,然后选择增益率最大的属性进行分裂。下面我们继续用SNS社区中不真实账号检测的例子说明如何使用ID3算法构造决策树。为了简单起见,我们假设训练集合包含10个元素:
其中s、m和l分别表示小、中和大。
设L、F和H表示日志密度、好友密度、是否使用真实头像,下面计算各属性的信息增益。
因此日志密度的信息增益是0.276。
用同样方法得到F和H的信息增益分别为0.553和0.033。
因为F具有最大的信息增益,所以第一次分裂选择F为分裂属性,分裂后的结果如下图表示:
在上图的基础上,再递归使用这个方法计算子节点的分裂属性,最终就可以得到整个决策树。
练习
计算上图的信息熵,确定下一个分类的特征
import numpy as np
#帐号是否真实 熵
#3个no 7个yes
#info_D代表的是整个df的混乱度
info_D = -(0.3*np.log2(0.3)+0.7*np.log2(0.7))
info_D
#熵的
输出
0.8812908992306927
#日志
#求每一列属性的混乱度,要计算信息增益
#日子的熵
# s 3个 l 3个 m 4个
#s 2个no 1个yes
#l 3个yes
#m 3个yes 1个no
info_L_D = -(0.3*(1/3*np.log2(1/3) + 2/3*np.log2(2/3))
+ 0.3*(1*np.log2(1))
+ 0.4*(1/4*np.log2(1/4)+3/4*np.log2(3/4)))
info_L_D
#熵的值是0.6
输出
0.6
info_D-info_L_D
#信息的增益
输出
0.2812908992306927
#好友的密度
#4个s 4个m 2个l
#3个no 1个yes 4个yes 2个yes
info_F_D = -(0.4*(3/4*np.log2(3/4) + 1/4*np.log2(1/4))
+0.4*(1*np.log2(1))
+0.2*(1*np.log2(1)))
info_F_D
输出
0.32451124978365314
info_D-info_F_D
输出
0.5567796494470396
#是否使用真实头像
#5个yes 5个no
#1个no 4个tes 2个no 3个yes
info_T_D = -(0.5*(1/5*np.log2(1/5)+ 4/5*np.log2(4/5))
+ 0.5*(2/5*np.log2(2/5) + 3/5*np.log2(3/5)))
info_T_D
输出
0.8464393446710154
info_D-info_T_D
输出
0.034851554559677256
三、实战
【注意】
参数max_depth越大,越容易过拟合
from sklearn.tree import DecisionTreeClassifier
import sklearn.datasets as datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
1、使用自带的iris数据集
iris = datasets.load_iris()
X_train = iris['data']
y_train = iris['target']
X_train.shape
输出
(150, 4)
使用决策树算法
tree = DecisionTreeClassifier()
tree.fit(X_train,y_train)
#fit 训练的是关系,并没有把数据保存起来
#直接打分
#预测,是否符合算法中的关系
tree.score(X_train,y_train)
输出
1.0
使用KNN算法
knn = KNeighborsClassifier(n_neighbors=15)
knn.fit(X_train,y_train)
knn.score(X_train,y_train)
输出
0.9866666666666667
使用逻辑斯蒂回归算法
lrg = LogisticRegression()
lrg.fit(X_train,y_train)
lrg.score(X_train,y_train)
- 输出
0.96
2、使用回归预测一个椭圆
from sklearn.tree import DecisionTreeRegressor
使用RandomState生成固定随机数
创建-100到100之间的角度
生成正弦值和余弦值
添加噪声
x = np.linspace(-100,100,100)
y = np.array([np.cos(x),np.sin(x)])
y = y.transpose()
y.shape
- 输出
(100, 2)
创建不同深度的决策树
进行数据训练
import matplotlib.pyplot as plt
plt.scatter(y[:,0],y[:,1])
<matplotlib.collections.PathCollection at 0x94bd470>
#添加一些噪点
#找25个点添加噪点
y[::4] += np.random.randn(25,2) * 0.3
plt.scatter(y[:,0],y[:,1])
<matplotlib.collections.PathCollection at 0xb7dbe10>
y.shape
输出
(100, 2)
X=x.reshape(-1,1)
#预测的数据
tree1 = DecisionTreeRegressor()
tree1.fit(X,y)
X_test = np.linspace(-100,100,500)
X_test = X_test.reshape(-1,1)
y_ = tree1.predict(X_test)
plt.scatter(y_[:,0],y_[:,1])
<matplotlib.collections.PathCollection at 0xcf78048>
plt.scatter(y[:,0],y[:,1])
<matplotlib.collections.PathCollection at 0xcfd3550>
tree1.score(X,y)
输出
1.0
创建-100到100的预测数据,间隔为0.01
对数据进行预测
显示图片
朴素贝叶斯
决策树:信息论,信息熵
贝叶斯和逻辑斯底回归:概率论
【关键词】
- 朴素:独立性假设
- 贝叶斯公式
优点:
- 朴素贝叶斯模型发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率;
- 对小规模的数据表现很好;
- 能处理多分类任务,适合增量式训练;
- 对缺失数据不太敏感,算法也比较简单,常用于文本分类
缺点:
- 只能用于分类问题
- 需要计算先验概率;
- 分类决策存在错误率;
- 对输入数据的表达形式很敏感
现分别有 A、B 两个容器,在容器 A 里分别有 7 个红球和 3 个白球,在容器 B 里有 1 个红球和 9 个白球,现已知从这两个容器里任意抽出了一个球,且是红球,问这个红球是来自容器 A 的概率是多少?
#假设已经抽出红球为事件 B,选中容器 A 为事件 A
P(B) = 8/20
P(A) = 1/2
#从A容器中选中红球的概率
P(B|A) = 7/10
#问这个红球是来自容器 A 的概率是多少
#P(A|B) = P(B|A)*P(A)/P(B)
P(A|B) = 0.7 * 0.5 / 0.4 = 0.875
一座别墅在过去的 20 年里一共发生过 2 次被盗,别墅的主人有一条狗,狗平均每周晚上叫 3 次,在盗贼入侵时狗叫的概率被估计为 0.9,问题是:在狗叫的时候发生入侵的概率是多少?
我们假设 A 事件为狗在晚上叫,B 为盗贼入侵
#每天狗叫的概率
P(A) = 3/7
P(B) = 2/7300
P(A|B) = 0.9
P(B|A) = 0.9 * (2/7300) / 3/7 = 0.0005753424657534247
一、朴素贝叶斯原理
朴素贝叶斯
from sklearn.naive_bayes import GaussianNB
朴素贝叶斯算法是一个典型的统计学习方法,主要理论基础就是一个贝叶斯公式,贝叶斯公式的基本定义如下:
这个公式虽然看上去简单,但它却能总结历史,预知未来:
- 公式的右边是总结历史
- 公式的左边是预知未来
如果把Y看成类别,X看成特征,P(Yk|X)就是在已知特征X的情况下求Yk类别的概率,而对P(Yk|X)的计算又全部转化到类别Yk的特征分布上来。
举个例子,大学的时候,某男生经常去图书室晚自习,发现他喜欢的那个女生也常去那个自习室,心中窃喜,于是每天买点好吃点在那个自习室蹲点等她来,可是人家女生不一定每天都来,眼看天气渐渐炎热,图书馆又不开空调,如果那个女生没有去自修室,该男生也就不去,每次男生鼓足勇气说:“嘿,你明天还来不?”,“啊,不知道,看情况”。
然后该男生每天就把她去自习室与否以及一些其他情况做一下记录,用Y表示该女生是否去自习室,即Y={去,不去},X是跟去自修室有关联的一系列条件,比如当天上了哪门主课,蹲点统计了一段时间后,该男生打算今天不再蹲点,而是先预测一下她会不会去,现在已经知道了今天上了常微分方法这么主课,于是计算P(Y=去|常微分方程)与P(Y=不去|常微分方程),看哪个概率大,如果P(Y=去|常微分方程) >P(Y=不去|常微分方程),那这个男生不管多热都屁颠屁颠去自习室了,否则不就去自习室受罪了。P(Y=去|常微分方程)的计算可以转为计算以前她去的情况下,那天主课是常微分的概率P(常微分方程|Y=去),注意公式右边的分母对每个类别(去/不去)都是一样的,所以计算的时候忽略掉分母,这样虽然得到的概率值已经不再是0~1之间,但是通过比较大小还是能选择类别。
后来他发现还有一些其他条件可以挖,比如当天星期几、当天的天气,以及上一次与她在自修室的气氛,统计了一段时间后,该男子一计算,发现不好算了,因为总结历史的公式:
这里n=3,x(1)表示主课,x(2)表示天气,x(3)表示星期几,x(4)表示气氛,Y仍然是{去,不去},现在主课有8门,天气有晴、雨、阴三种、气氛有A+,A,B+,B,C五种,那么总共需要估计的参数有8×3×7×5×2=1680个,每天只能收集到一条数据,那么等凑齐1680条数据,大学都毕业了,男生大呼不妙,于是做了一个独立性假设,假设这些影响她去自习室的原因是独立互不相关的,于是:
有了这个独立假设后,需要估计的参数就变为,(8+3+7+5)×2 = 46个了,而且每天收集的一条数据,可以提供4个参数,这样该男生就预测越来越准了。
朴素的概念:独立性假设,假设各个特征之间是独立不相关的。
朴素贝叶斯分类器
讲了上面的小故事,我们来朴素贝叶斯分类器的表示形式:
当特征为为x时,计算所有类别的条件概率,选取条件概率最大的类别作为待分类的类别。由于上公式的分母对每个类别都是一样的,因此计算时可以不考虑分母,即
朴素贝叶斯的朴素体现在其对各个条件的独立性假设上,加上独立假设后,大大减少了参数假设空间。
在文本分类上的应用
文本分类的应用很多,比如垃圾邮件和垃圾短信的过滤就是一个2分类问题,新闻分类、文本情感分析等都可以看成是文本分类问题,分类问题由两步组成:训练和预测,要建立一个分类模型,至少需要有一个训练数据集。贝叶斯模型可以很自然地应用到文本分类上:现在有一篇文档d(Document),判断它属于哪个类别ck,只需要计算文档d属于哪一个类别的概率最大:
在分类问题中,我们并不是把所有的特征都用上,对一篇文档d,我们只用其中的部分特征词项t1,t2,…,tnd(nd表示d中的总词条数目),因为很多词项对分类是没有价值的,比如一些停用词“的,是,在”在每个类别中都会出现,这个词项还会模糊分类的决策面,关于特征词的选取,我的这篇文章有介绍。用特征词项表示文档后,计算文档d的类别转化为:
注意P(Ck|d)只是正比于后面那部分公式,完整的计算还有一个分母,但我们前面讨论了,对每个类别而已分母都是一样的,于是在我们只需要计算分子就能够进行分类了。实际的计算过程中,多个概率值P(tj|ck)的连乘很容易下溢出为0,因此转化为对数计算,连乘就变成了累加:
我们只需要从训练数据集中,计算每一个类别的出现概率P(ck)和每一个类别中各个特征词项的概率P(tj|ck),而这些概率值的计算都采用最大似然估计,说到底就是统计每个词在各个类别中出现的次数和各个类别的文档的数目:
二、3种贝叶斯模型
1、高斯分布朴素贝叶斯
高斯分布就是正态分布
【用途】用于一般分类问题
使用自带的鸢尾花数据
#实例高斯贝叶斯
gNB = GaussianNB()
import sklearn.datasets as datasets
iris = datasets.load_iris()
data = iris['data']
target = iris['target']
#训练
gNB.fit(data,target)
输出
GaussianNB(priors=None)
gNB.score(data,target)
输出
0.96
2、多项式分布朴素贝叶斯
多项式分布:
【用途】适用于文本数据(特征表示的是次数,例如某个词语的出现次数)
from sklearn.naive_bayes import MultinomialNB
例:延续上面,使用鸢尾花数据
mNB = MultinomialNB()
mNB.fit(data,target)
mNB.score(data,target)
输出
0.9533333333333334
3、伯努利分布朴素贝叶斯
伯努利分布:
【用途】适用于伯努利分布,也适用于文本数据(此时特征表示的是是否出现,例如某个词语的出现为1,不出现为0)
绝大多数情况下表现不如多项式分布,但有的时候伯努利分布表现得要比多项式分布要好,尤其是对于小数量级的文本数据
例:继续使用鸢尾花数据集
from sklearn.naive_bayes import BernoulliNB
bNB = BernoulliNB()
bNB.fit(data,target)
bNB.score(data,target)
输出
0.3333333333333333
三、文本分类实战
import pandas as pd
sms = pd.read_csv('./day12_data/SMSSpamCollection',sep='\t',header=None)
# spam 垃圾短信
# ham 一般的
sms.shape
#5572条短信
输出
(5572, 2)
data = sms[1]
target = sms[0]
导包
导入sklearn.feature_extraction.text.TfidfVectorizer用于转换字符串
读取短信数据
import numpy as np
#咱们短信内容都是字符串,所以不能直接进行学习
from sklearn.feature_extraction.text import TfidfVectorizer
#TfidfVectorizer 它的作用是将我们的字符串映射成ndarray
#图片在读取的时候0--->ndarray
#音频--->ndarray
tf = TfidfVectorizer()
#转换成稀松
#分词
tf.fit(data)
text_vector=tf.transform(data)
#5572样本 8713:代表我们一共使用了8713个单词 with 74169 代表的是短信中一有74169字
text_vector.shape
输出
(5572, 8713)
#先用多项式贝叶斯来进行预测
mNB = MultinomialNB()
mNB.fit(text_vector,target)
输出
MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)
data[2]
输出
“Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C’s apply 08452810075over18’s”
#生成数据
X_test = ["Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's"]
#短信内不能用来学习,现在就不能预测
X_test_vector = tf.transform(X_test)
mNB.predict(X_test_vector)
输出
array([‘spam’], dtype=’
mNB.score(text_vector,target)
输出
0.9761306532663316
伯努利
bNB = BernoulliNB()
bNB.fit(text_vector,target)
bNB.score(text_vector,target)
输出
0.9881550610193827
高斯
#A sparse matrix was passed, but dense data is required. Use X.toarray() to convert to a dense numpy array.
#PIL Image.open() .toarray()
gNB = GaussianNB()
gNB.fit(text_vector.toarray(),target)
gNB.score(text_vector.toarray(),target)
输出
0.9414931801866475
文本类型
邮件
短信
发表的文章
评论
tf.fit_transform()
- 参数必须是字符串的一维数组(比如列表或者Series)
- 返回的是一个稀疏矩阵类型的对象,行数为样本数,列数为所有出现的单词统计个数。
这里输入data[1]是Series类型,返回的是一个5572x8713 sparse matrix
其中5572是data[1]
训练数据
预测数据,使用tf.transform([‘xx’])进行转换生成测试数据